Bitcoin address generation and digital signatures

Tharaka Wijesundara
7 min readMar 22, 2023

--

Have you been exhausted searching for a complete but simple guide to understanding how a Bitcoin transaction takes place? 😰 This is a series of articles that explain Bitcoin from its history of evolution to the complete journey of a Bitcoin transaction with its underlying mathematics. The series consists of six articles and now you are in the sixth article.6️⃣ To kick off from the very beginning, click here. Happy reading.👨‍💻👩‍💻

Image Source

We have covered a whole lot of concepts behind the Bitcoin blockchain in the previous five articles. However, it's necessary to have a secure enough key pair before anything else to make a transaction. And, that is what we touch upon in this article based on one of the leading technologies behind Bitcoin; cryptography.

Asymmetric cryptography

Symmetric and asymmetric cryptography can be compared based on the type of keys that are used for both encryption and decryption. As the name suggests, in a symmetric case one similar key should be shared among all the parties while in asymmetric (bitcoin uses this) two separate keys called private and public (public will be generated from the private key), keys should be generated and managed among the users. During the discussion, we used both private and public keys and now take a look at its mathematical side.

We discussed two use cases of these keys, receiving funds and signing the transactions. In the former, the public key of the receiver is used while in the latter, the sender’s private key is used.

Private key generation

In order to make a transaction, you need to have a key pair consisting of private and public keys. Since the private key is random, you can select whatever number you want from the existing set of numbers (256 bits) but need to be mindful when selecting because if it is in the guessable range, the chance of getting hacked your entire wallet is high. Wallet applications, usually use random word phases based private key generation algorithm.

Have you ever thought about the possibility of two selecting the same key?🙄

It’s not secure that chose a random number based on your own favors or your own random number generators because it should come from a secure source of randomness. Operating systems maintain a separate store of random bits based on human engagements and some other sources(E.g. mouse movement, fan speed, CPU temperature, etc.) to generate random numbers.

One way of achieving this is, by feeding random bits generated from cryptographically secured pseudorandom sources and feeding it into SHA256. Please refer to Bitcoin core client software on how you can generate numbers using command line tools.

Public key generation

In Bitcoin, a one-way function (computationally irreversible) function called elliptic curve multiplication is used to calculate the public key using the private key.

K = k * G

Where,
k - Private key
G - Generator point
K - Public key

Let’s say an attacker who owns our public key wants to reverse and find our private key, in the Elliptic Curve context we refer to it as finding the discrete logarithm. The level of difficulty is similar to iterating through each and every number until he finds the correct one. (Brute force attack)

Elliptic Curve — Image Source

What we calculate will get differ based on the curve that is defined. As an example, bitcoin uses the secp256k1 curve constructed by the National Institute of Standards and Technology (NIST). It has gained a lot of popularity with its use case in bitcoin and its efficient calculation which is 30% faster than normal curves.

The curve is defined by,

y^2mod(p)=(x^3+7)mod(p), where

P = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1

Since the curve is not defined over a real number set, this would look like a scattered plot. Even though it is scattered and finite, it would exhibit the properties that real number curves have.

There are some properties that elliptic curve multiplication would provide as follows.

We have two points called starting point (P) and ending point(Q) and, we define the point where the curve get intersected as R.

Elliptic curve properties — Image Source

Since we can define multiplication with a series of additions, the function which is used in bitcoin (k * G) will be a series of additions as G + G + G + … + G.

Let me take you through a step-by-step process of generating a public key.

Let’s take 1 as the private number and at the end, you will realize why it is not good to use our own private numbers.😉

Since the values are based on a curve we get two values X and Y as the public key.

X - 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
Y - 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8

Since we can calculate Y based on X, we omit the Y part here and prepend X with 02 if Y is even and 03 if Y is odd. Here In our case, the Y part is even. Hence we prepend 02.

The compressed version of public key
0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798

Pass it through SHA-256.

Note: You should convert the string to bytes when hashing, otherwise, you will end up with a different result.

0x0f715baf5d4c2ed329785cef29e562f73488c8a2bb9dbc5700b361d54b9b0554

Pass it through the ripemd160 hash function to reduce the output bytes and makes it even more difficult to reverse. (Two unrelated hash functions). After adding the version byte of bitcoin, 0x00, for P2PKH (Pay-to-Public-Key-Hash),

0x00751e76e8199196d454941c45d1b3a323f1433bd6

Then pass it through SHA-256 twice.

0x510d1634d943109b69da527ef5948106f22b655fb5193b4e9ef7e4dcd342d245

The first 4 bytes of the above result would be the checksum.

510d1634

Append this checksum for the result of ripemd160.

0x00751e76e8199196d454941c45d1b3a323f1433bd6510d1634

Encode this into Base58 to make a more compressed version of the address.

1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH

This is the Bitcoin address for the private key 1.

At the beginning of the address generation process, I highlighted that at the end you will realize why not to use easily guessable numbers as private numbers.😊 Refer to the below link for the details of the generated public address and notice that once someone put money into it, it vanished instantly. This replicates what happens when you use a private key that is in the guessable range.

Link: https://blockchair.com/bitcoin/address/1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH

The address type of bitcoin has been changed now and it's P2SH, or in other words, Pay to a Script hash. It also will follow the same steps except for some versioning bits and conversion.

Digital signature

We have touched this gently in our discussion and let's further dive in. Why do you actually need a signature? Think about real-world examples. You write a letter and put your signature at the end to apprise that you are taking the authorship. How about a cheque without a signature? Is it valid? To exchange it for money, the valid signature of the owner should be there.

Within the context of Bitcoin, to prove to someone that you are the owner of your wallet funds, you need your private address because showing something that is public, a public key, will not guarantee that you are the legitimate owner. How can we do that without explicitly revealing the private key? That is where digital signatures come in.

When you are initiating a transaction, you need to prove to the network that you are the initiator and please send this amount of bitcoin from my address to the recipient’s address. Once you signed that message, it’s not possible to reverse it. We call it as non-repudiation.

These are the steps involved with this signing process.

Then you will attach three things, digital signature, transaction details, and hash, together and broadcast the message. This is what the Bitcoin node does once it receives the message.

Validation

If both matches each other, the node validates this as a valid transaction✔️ and vice versa.❌

Refer to the following Python code snippet to get a hand on experience your own.

from ecdsa import SigningKey, SECP256k1
from hashlib import sha256

# Treating the string as Bytes
transaction_details = "Bob Sends 1 BTC to Alice"
# Pass it through SHA-256
hash_value = bytes(sha256(transaction_details.encode('utf-8')).hexdigest(), 'utf-8')

# Generate private key
private_key = SigningKey.generate(curve=SECP256k1)
print(private_key)

# Generate public key based on the private key
public_key = private_key.verifying_key
print(public_key)

# Sign the transaction details
signature = private_key.sign(hash_value)
print(signature)

# Verification
try:
public_key.verify(signature, hash_value)
print ("Valid Signature")
except:
print ("Invalid Signature")

--

--