Public key cryptography is any cryptographic system that uses pairs of keys:

private keys which are known only to the owner,

public keys which may be distributed widely.

In this system, a person can combine a message with a private key to create a signature on the message. Anyone with the corresponding public key can use it to verify whether the signature was valid — signed by the owner of the appropriate private key.

Let’s see how to implement these functionalities in Elixir. We will leverage crypto module from erlang which provides a set of convenient cryptographic operations. We are particularly interested in:

Hash functions

Hmac functions

Digital signatures Digital Signature Standard (DSS)

Elliptic Curve Digital Signature Algorithm (ECDSA)

KeyPair

Generating a keypair is as simple as calling a specific function with appropriate attributes:

You don’t need anything else to create an ECDH -compliant keys in a convenient to use Base16 (hexadecimal) format.

If you want to store such keys, you can persist them on a disk or use a special kind of “database” called wallet. It’s a place, either digital or physical, which, in its basic form, keeps your keys secretly. It doesn’t require any connection to the network or a reference to the Blockchain.

Signature

Once you have your keypair generated and stored, you can use your private key to sign a message you want to send.

Keep in mind that you have to decode both private key and public key from Base16 encoded string into binary strings firstly!

With the generated signature we can verify if the owner of the public key actually signed the message without knowing their private key:

Address

From a technical perspective, a Bitcoin address is a hash of the public portion of a public/private keypair. It’s a string of digits and characters.

It represents a possible destination for a Bitcoin payment and can be shared with anyone who wants to send you money. That’s why it appears most commonly as the “recipient” of the funds.

Addresses can be generated at no cost by any user of Bitcoin. People can have many different addresses and a unique address should be used for each transaction. Creating them can be done without an Internet connection and does not require any contact or registration with the Bitcoin network.

The entire process looks as follows:

And it can be described programatically as:

Let’s divide it into smaller steps and implement each part of it.

Public Key

Generating public key from private key is again very simple. It’s based on elliptic curve manipulation (irreversible cryptographic algorithm). To get it, it’s enough to do:

Now you have a public key derived from the private one.

Hashing

The next step is to apply a one-way function that produces a fingerprint of the given input. The specific algorithms used for that are:

Secure Hash Algorithm sha256

RACE Integrity Primitives Evaluation Message Digest ripemd160

Starting with the public key, we compute sha256 on it and then apply ripemd160 on the result, producing a 160-bit number.

The hash function is very simple:

Network ID

Blockchain-based currencies use encoded strings, which are Base58Check encoded. The encoding includes a prefix (traditionally a single version byte), which affects the leading symbol in the encoded result.

These are two most common prefixes which are in use in the Bitcoin codebase:

As you see, we use 0 for the main network and 111 for the test one.

To convert a public hash into Base58Check format, we need to firstly prepend it with a version byte which helps to identify the encoded data.

Base58 check

Base58 number system uses 58 characters and a checksum to help human readability, avoid ambiguity and protect against errors. It has also benefits in terms of brevity as long numbers are represented more compact in mixed-alphanumeric systems with a base higher than 10 .

Here is the general flow of performing Base58Check check:

Which can be represented programatically as:

So, to perform Base58Check we should cover the following steps:

1. Perform double SHA-256 hash on the versioned payload.

2. Take the first 4 bytes of the second SHA-256 hash as a checksum.

3. Append checksum to the initial public hash.

The result is the 25-byte Binary Bitcoin Address.

Base58 encoding

Most Bitcoin addresses are 34 characters. It’s because Base58 format consist of random digits and uppercase and lowercase letters, with the exception that the uppercase letter O , uppercase letter I , lowercase letter l , and the number 0 are never used to prevent visual ambiguity. Another advantage is lack of line-breaks, either in emails/chats or while double-clicking and copying, as there’s no punctuation to break at.

To encode a big-endian series of bytes from the previous step, we’ll use a regular mathematical bignumber division with the alphabet described above.