Chapter 7: A Deep Dive on Cryptographic Algorithms in Bitcoin#
Introduction#
In this chapter, we’ll explore the key cryptographic algorithms used in Bitcoin: SHA-256, ECDSA, and RIPEMD-160. We’ll also revisit the general concepts of hashing and why Bitcoin chose these particular algorithms.
The Power of Hashing#
Before we dive into specific algorithms, let’s understand the concept of hashing and why it’s so crucial in cryptography and Bitcoin.
Imagine you have a magic box. You can put anything into this box - a letter, a book, or even a digital file. No matter what you put in, the box always gives you back a fixed-size string of characters. This string is unique to what you put in, like a fingerprint. If you change even a tiny bit of your input, the output string changes completely. This is essentially what a hash function does.
Key properties of hash functions:
One-way function: It’s easy to calculate the hash from the input, but practically impossible to recreate the input from the hash.
Deterministic: The same input always produces the same hash.
Avalanche effect: A small change in the input results in a significantly different hash.
Collision resistance: It’s extremely unlikely to find two different inputs that produce the same hash.
These properties make hash functions incredibly useful in cryptography and, by extension, in Bitcoin.
SHA-256: The Workhorse of Bitcoin#
SHA-256 stands for Secure Hash Algorithm 256-bit. It’s part of the SHA-2 family of cryptographic hash functions, designed by the U.S. National Security Agency (NSA).
How SHA-256 Works (Simplified)#
The input message is padded and divided into blocks.
Each block goes through 64 rounds of operations involving bitwise functions, modular addition, and bit rotations.
The result is a 256-bit (32-byte) hash, regardless of the input size.
Why Bitcoin Chose SHA-256#
Bitcoin uses SHA-256 for two main purposes:
In the proof-of-work system
As part of the process of creating Bitcoin addresses
Bitcoin chose SHA-256 for several reasons:
Strong security: As of 2024, SHA-256 remains unbroken and is considered highly secure.
Fixed output size: The 256-bit output provides a good balance between security and efficiency.
Resistance to collisions: It’s extremely difficult to find two different inputs that produce the same hash.
Relatively fast computation: While not the fastest hash function, it’s efficient enough for Bitcoin’s needs.
# Import necessary libraries
import hashlib
import ecdsa
import binascii
print("Cryptographic Algorithms in Bitcoin: Code Examples")
# SHA-256 Example
print("\n1. SHA-256 Example")
def sha256_hash(message):
return hashlib.sha256(message.encode()).hexdigest()
message = "Hello, Bitcoin!"
hashed_message = sha256_hash(message)
print(f"Original message: {message}")
print(f"SHA-256 hash: {hashed_message}")
# Demonstrate avalanche effect
message2 = "Hello, Bitcoin?" # Changed last character
hashed_message2 = sha256_hash(message2)
print(f"\nSlightly changed message: {message2}")
print(f"New SHA-256 hash: {hashed_message2}")
Cryptographic Algorithms in Bitcoin: Code Examples
1. SHA-256 Example
Original message: Hello, Bitcoin!
SHA-256 hash: 8a208c3f523f64f8a52434688d9ca442483cd3007a108fd79325a0fab9b71376
Slightly changed message: Hello, Bitcoin?
New SHA-256 hash: 5968a0a38635aa884d2418d7aa739e24747e44f1389c32880b311fcb9772b0db
ECDSA: Signing and Verifying Transactions#
ECDSA stands for Elliptic Curve Digital Signature Algorithm. It’s used in Bitcoin for creating and verifying digital signatures in transactions.
How ECDSA Works (Simplified)#
A user generates a private key (a random number) and a corresponding public key (a point on the elliptic curve).
To sign a transaction:
The transaction data is hashed.
The hash is combined with the private key to create a signature.
To verify a transaction:
The verifier uses the signer’s public key, the signature, and the transaction data.
If the verification equation holds, the signature is valid.
Why Bitcoin Chose ECDSA#
Bitcoin chose ECDSA for several reasons:
Strong security: ECDSA provides a high level of security with relatively small key sizes.
Efficiency: Compared to other digital signature algorithms like RSA, ECDSA is faster and requires less computational power.
Smaller signatures: ECDSA signatures are compact, which is beneficial for a blockchain where space is at a premium.
# ECDSA Example
print("\n2. ECDSA Example")
def generate_ecdsa_keys():
private_key = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
public_key = private_key.get_verifying_key()
return private_key, public_key
def sign_message(private_key, message):
return private_key.sign(message.encode())
def verify_signature(public_key, message, signature):
try:
return public_key.verify(signature, message.encode())
except:
return False
private_key, public_key = generate_ecdsa_keys()
message = "Send 1 BTC to Alice"
signature = sign_message(private_key, message)
print(f"Message: {message}")
print(f"Signature: {binascii.hexlify(signature)}")
print(f"Signature valid: {verify_signature(public_key, message, signature)}")
# Try to verify with a different message
fake_message = "Send 1 BTC to Eve"
print(f"\nTrying to verify with a fake message: {fake_message}")
print(f"Signature valid: {verify_signature(public_key, fake_message, signature)}")
2. ECDSA Example
Message: Send 1 BTC to Alice
Signature: b'e6c432179da059c60bd7a5fa4842a99d4a33665dbaeba4d3823733806af5661f5cde6fc521e498c077dda48f960d020d7dd9a9c034f60586a5fe93bccb8bb5ad'
Signature valid: True
Trying to verify with a fake message: Send 1 BTC to Eve
Signature valid: False
RIPEMD-160: Creating Shorter Addresses#
RIPEMD-160 stands for RACE Integrity Primitives Evaluation Message Digest 160-bit. In Bitcoin, it’s used in combination with SHA-256 to create Bitcoin addresses.
How RIPEMD-160 is Used in Bitcoin#
First, a public key is hashed with SHA-256.
The resulting hash is then hashed again with RIPEMD-160.
This double-hashed result is used as part of the Bitcoin address.
Why Bitcoin Chose RIPEMD-160#
Bitcoin uses RIPEMD-160 for a specific reason:
Shorter output: RIPEMD-160 produces a 160-bit (20-byte) hash, which helps create shorter Bitcoin addresses while still maintaining a high level of security.
By using both SHA-256 and RIPEMD-160, Bitcoin adds an extra layer of hashing security while keeping addresses reasonably short.
# RIPEMD-160 Example
print("\n3. RIPEMD-160 Example")
def ripemd160_hash(message):
hash_object = hashlib.new('ripemd160')
hash_object.update(message.encode())
return hash_object.hexdigest()
message = "Hello, Bitcoin!"
hashed_message = ripemd160_hash(message)
print(f"Original message: {message}")
print(f"RIPEMD-160 hash: {hashed_message}")
# Bitcoin Address Generation (simplified)
print("\n4. Simplified Bitcoin Address Generation")
def generate_bitcoin_address(public_key):
# Step 1: SHA-256 hash of the public key
sha256_hash = hashlib.sha256(public_key.to_string()).digest()
# Step 2: RIPEMD-160 hash of the result
ripemd160_hash = hashlib.new('ripemd160')
ripemd160_hash.update(sha256_hash)
return ripemd160_hash.hexdigest()
# Generate a new key pair
private_key, public_key = generate_ecdsa_keys()
# Generate a simplified Bitcoin address
address = generate_bitcoin_address(public_key)
print(f"Simplified Bitcoin address: {address}")
print("\nNote: This is a simplified demonstration. Actual Bitcoin addresses include additional steps like version bytes and checksums.")
3. RIPEMD-160 Example
Original message: Hello, Bitcoin!
RIPEMD-160 hash: 772ed84249d4fa71819ed1035673d6377fb6944c
4. Simplified Bitcoin Address Generation
Simplified Bitcoin address: accd5a8408bcfb2ed3ed9aebc2d54ce3d61a3f58
Note: This is a simplified demonstration. Actual Bitcoin addresses include additional steps like version bytes and checksums.
Base58Check: Making Addresses User-Friendly#
While not strictly a cryptographic algorithm, Base58Check encoding plays a crucial role in Bitcoin by making addresses and keys more user-friendly and error-resistant.
What is Base58Check?#
Base58Check is a way of encoding Bitcoin addresses and private keys that makes them easier for humans to read, write, and transcribe without errors. It’s called Base58 because it uses 58 characters (hence, base 58) from the standard ASCII character set.
How Base58Check Works#
Start with binary data (like a hash of a public key).
Add a version byte at the beginning (to indicate what type of address it is).
Calculate a checksum by hashing the result twice with SHA-256 and taking the first 4 bytes.
Append the checksum to the end.
Convert the result to Base58 encoding.
Why Bitcoin Chose Base58Check#
Bitcoin uses Base58Check for several reasons:
Human-friendly: It avoids using characters that might be mistaken for one another, like ‘0’ (zero), ‘O’ (capital o), ‘I’ (capital i), and ‘l’ (lowercase L).
Compact: It’s more compact than standard Base64 encoding.
Error-checking: The checksum allows detection of typos or transcription errors.
Versatility: It can encode different types of data (addresses, private keys) by using different version bytes.
Base58Check encoding is what gives Bitcoin addresses their familiar appearance, typically starting with ‘1’ or ‘3’ for standard addresses, or ‘bc1’ for newer SegWit addresses.
# Base58Check Example
print("\n5. Base58Check Encoding Example")
import hashlib
# Base58 character set
BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def sha256(data):
return hashlib.sha256(data).digest()
def ripemd160(data):
h = hashlib.new('ripemd160')
h.update(data)
return h.digest()
def base58_encode(data):
# Convert data to integer
n = int.from_bytes(data, 'big')
# Convert to base58
res = []
while n > 0:
n, r = divmod(n, 58)
res.append(BASE58_ALPHABET[r])
res = ''.join(res[::-1])
# Add '1' characters for leading zero bytes
pad = 0
for b in data:
if b == 0:
pad += 1
else:
break
return BASE58_ALPHABET[0] * pad + res
def base58check_encode(version, payload):
# Add version byte
data = bytes([version]) + payload
# Double SHA-256 for checksum
checksum = sha256(sha256(data))[:4]
# Combine data and checksum
final_data = data + checksum
# Encode with Base58
return base58_encode(final_data)
# Example: Create a simplified Bitcoin address
public_key_hash = ripemd160(sha256(b'example_public_key'))
version = 0x00 # Mainnet public key hash version
bitcoin_address = base58check_encode(version, public_key_hash)
print(f"Simplified Bitcoin address: {bitcoin_address}")
print("\nNote: This is a simplified demonstration. Real Bitcoin address generation involves additional steps.")
5. Base58Check Encoding Example
Simplified Bitcoin address: 151f3GWNePpmL8aegYpotp6H8f4NUyZiJV
Note: This is a simplified demonstration. Real Bitcoin address generation involves additional steps.
What We Learned#
In this chapter, we explored the key cryptographic algorithms used in Bitcoin:
Hashing Concepts: We learned about the fundamental properties of hash functions and why they’re crucial for Bitcoin’s security.
SHA-256: We discovered how this strong, collision-resistant hash function is used in Bitcoin’s proof-of-work system and address creation.
ECDSA: We explored how this efficient digital signature algorithm enables secure transaction signing and verification in Bitcoin.
RIPEMD-160: We learned how this algorithm is used in combination with SHA-256 to create shorter, yet secure, Bitcoin addresses.
Base58Check Encoding: We understood how this encoding scheme makes Bitcoin addresses more user-friendly and error-resistant.
By understanding these algorithms, we gain insight into the cryptographic foundation that makes Bitcoin secure, efficient, and user-friendly.
Quick Check#
Test your understanding with these questions:
What are the four key properties of hash functions discussed in this chapter?
Why did Bitcoin choose SHA-256 for its proof-of-work system?
What are the main advantages of using ECDSA for digital signatures in Bitcoin?
In the Bitcoin address creation process, why is RIPEMD-160 used after SHA-256?
How does Base58Check encoding help prevent errors when users are working with Bitcoin addresses?
Can you describe the general process of how a Bitcoin transaction is signed using ECDSA?
What would happen if you changed one character in a message before hashing it with SHA-256?
Why is it important that hash functions like SHA-256 and RIPEMD-160 are one-way functions?
(Answers to these questions can be found by reviewing the relevant sections in the chapter.)