base58check encodings, compressed public keys, compressed WIF

pull/29/head
Andreas M. Antonopoulos 10 years ago
parent 52045782de
commit 89fb743be5

@ -77,6 +77,7 @@ $ sx newkey
A private key is just a number. A public key can be generated from any number, up to 256 bits long. You can pick your keys randomly using a method as simple as tossing a coin, pencil and paper. Toss a coin 256 times and you have the binary digits of a random private key you can use in a bitcoin wallet. Keys really are just a pair of numbers, one calculated from the other.
====
[[pubkey]]
==== Public Keys
The public key is calculated from the private key using elliptic curve multiplication, which is irreversible: latexmath:[\(K = k * G\)]+ where +k+ is the private key, +G+ is a constant point called the _Generator Point_ and +K+ is the resulting public key. The reverse (division), or calculating +k+ if you know +K+ is as difficult as trying all possible values of +k+, i.e. a brute-force search. Before we demonstrate how to generate a public key from a private key, let's look at Elliptic Curve Cryptography in a bit more detail.
@ -186,12 +187,7 @@ Next compute the checksum by "double-SHA", meaning we apply the SHA256 hash-algo
The result of the above is now a prefix, the data and a checksum, concatenated (bytewise). This result is encoded using the base-58 alphabet described in the section above.
Let's look at each of these steps, using the sx tools and other command-line tools, starting with a public key and producing a bitcoin address. The public key is :
----
----
[[base58check_encoding]]
.Base58Check Encoding: A base-58, versioned and checksummed format for unambiguously encoding bitcoin data
image::images/Base58CheckEncoding.png["Base58CheckEncoding"]
@ -208,11 +204,11 @@ The private key can be represented in a number of different formats, all of whic
|=======
|Type|Prefix|Description
| Hex | None | 64 hexadecimal digits
| WIF | 5 | Base-58 encoding with version prefix of 128 and 32-bit checksum
| WIF | 5 | Base58Check encoding: Base-58 with version prefix of 128 and 32-bit checksum
| WIF-compressed | K or L | As above, with added suffix 0x01 before encoding
|=======
The key above, for example can be represented as:
The private key we generated earlier, for example can be represented as:
.Example: Same Key, Different Formats
[options="header"]
@ -226,29 +222,39 @@ The key above, for example can be represented as:
All of the above representations are different ways of showing the same number, the same private key. They look different, but any one format can easily be converted to any other format.
===== Decoded from the Base58Check encoding to Hex
===== Decoded from Base58Check to Hex
The sx-tools package (See <<sx_tools>>) makes key conversion easy on the command line. We use the base58check-decode command:
----
$ sx base58check-decode 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd 128
----
===== Encode from Hex back to Base58Check encoding, with the version prefix "128"
The result is the hexadecimal key, followed by the Wallet Import Format (WIF) version prefix 128
===== Encode from Hex to Base58Check
To encode into Base58Check, we provide the hex private key, followed by the Wallet Import Format (WIF) version prefix 128
----
$ sx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd 128
5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
----
===== Encode from Hex with a suffix of "01" to Base58Check encoding, with the version prefix "128"
===== Encode from Hex (Comrpessed Key) to Base58Check encoding
To encode into Base58Check as a "compressed" private key, we add the suffix +01+ to the end of the hex key and then encode as above:
----
$ sx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01 128
KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
----
The resulting WIF-compressed format, starts with a "K". This denotes that the private key within has a suffic of "01" and will be used to produce compressed public keys only (See <<compressed_pubkey>> below)
===== Public Key Formats
The public key is a point on the elliptic curve, and consists of a pair of coordinates +(x,y)+, normally represented by a 512-bit number with the added prefix +04+.
The public key is a point on the elliptic curve, and consists of a pair of coordinates +(x,y)+, usually represented by a 512-bit number with the added prefix +04+. That's two 256-bit numbers, one for the x-coordinate of the point, the other for the y-coordinate. The prefix +04+ is used to distinguish uncompressed public keys.
Here's the public key generated by the private key we created above, shown as the coordinates +(x,y)+
Here's the public key generated by the private key we created above, shown as the coordinates +x+ and +y+
.Public Key K defined as a point +K = (x,y)+
----
@ -256,24 +262,59 @@ x = 32 5D 52 E3 B7 ... E5 D3 78
y = 7A 3D 41 E6 70 ... CD 90 C2
----
Here's the same public key shown as a 512-bit number (130 hex digits) with the prefix +04+ followed by +x+ and then +y+
Here's the same public key shown as a 512-bit number (130 hex digits) with the prefix +04+ followed by +x+ and then +y+ coordinates, as +04xy+:
.Uncompressed Public Key K shown in hex (130 hex digits) as +04 x y+
.Uncompressed Public Key K shown in hex (130 hex digits) as +04xy+
----
K = 04 32 5D 52 E3 B7 ... CD 90 C2
----
===== Compressed Public Keys
The +y+ coordinate can be deduced from the +x+ coordinate, since they both lie on the same curved line defined by the elliptic curve equation. This makes it possible to store the public key _compressed_, with the +y+ omitted. A +compressed public key+ has the prefix +02+ if the +y+ is above the x-axis, and +03+ if it is below the x-axis, allowing the software to calculate it from +x+.
Compressed public keys were introduced to bitcoin to reduce the size of transaction and conserve disk space on nodes that store the bitcoin blockchain database. Most transactions include a signature and public key, required to spend the funds and "transfer out" of one bitcoin address and into another. Each public key requires 513 bytes (prefix \+ x \+ y) to store, which when multiplied by several hundred transactions per block, or tens of thousands of transactions per day can add a significant amount of data to the blockchain.
As we saw in the section <<pubkey>> above, a public key is a point (x,y) on an elliptic curve. Since the curve expresses a mathematical function, a point on the curve represents a solution to the equation and therefore if we know the x coordinate we can calculate the y coordinate by solving the euqation y^2 mod p = (x^3 + 7) mod p. That allows us to store only the x-coordinate of the public key point, ommitting the y-coordinate and reducing the size of the key and the space required to store it by 256 bits. A 50% reduction in size in every transaction adds up to a lot of data saved over time!
Here's the same public key above, shown as a +compressed public key+ stored in 264-bits (66 hex digits) with the prefix +02+ indicating the +y+ coordinate has a positive sign:
There's one small detail to consider: since the left side of the equation is y^2, that means the solution for y is a square root, which can have a positive or negative value. Visually, this means that the resulting y-coordinate can be above the x-axis or below the x-axis. As you can see from the graph of the elliptic curve, the curve is symmetric, meaning it is reflected like a mirror by the x-axis. So, while we can ommit the y-coordinate we have to store the _sign_ of y (positive or negative), or in other words we have to remember if it was above or below the x-axis, as each of those options represents a different point and a different public key. When calculating the elliptic curve in binary arithmetic on the finite field of prime order p, the y coordinate is either even or odd, which corresponds to the positive/negative sign as explained above. Therefore, to distinguish between the two possible values of y, we store a +compressed public key+ with the prefix +02+ if the +y+ is even (same as above-the-axis), and +03+ if it is odd (same as below the axis), allowing the software to correctly deduce the y-coordinate from the x-coordinate.
Here's the same public key above, shown as a +compressed public key+ stored in 264-bits (66 hex digits) with the prefix +02+ indicating the +y+ coordinate is even:
.Compressed Public Key K shown in hex (66 hex digits) as +K = {02 or 03} x+
----
K = 02 32 5D 52 E3 B7 ... E5 D3 78
----
The compressed public key, above, corresponds to the same private key, meaning that it is generated from the same private key. However it looks different from the uncompressed public key. More importantly, if we convert this compressed public key to a bitcoin address using the double-hash function (RIPEMD160(SHA256(K))) it will produce a _different_ bitcoin address. This can be confusing, because it means that a single private key can produce a public key expressed in two different formats (compressed and uncompressed) which produce two different bitcoin addresses. But the private key is identical and therefore can produce valid signatures that authorize spending funds from _either_ bitcoin address.
Compressed public keys are gradually becoming the default across bitcoin clients, which is having a significant impact on reducing the size of transactions and therefore the blockchain. However, not all clients support compressed public keys yet. Newer clients that support compressed public keys have to account for transactions and older clients which do not support compressed public keys. This is especially important when a wallet application is importing private keys from another bitcoin wallet application, because the new wallet needs to scan the blockchain to find transactions corresponding to these imported keys. Which bitcoin addresses should the bitcoin wallet scan for? The bitcoin addresses produced by uncompressed public keys, or the bitcoin addresses produced by compressed public keys? Both are valid bitcoin addresses, both can be signed for by the private key, but they are different addresses!
To resolve this issue, when private keys are exported from a wallet, the Wallet Import Format that is used to represent them is implemented differently in newer bitcoin wallets, to indicate that these private keys have been used to produce _compressed_ public keys and therefore _compressed_ bitcoin addresses. This allows the importing wallet to distinguish between private keys originating from older or newer wallets and search the blockchain for transactions with bitcoin addresses corresponding to the compressed, or the uncompressed public keys. Let's look at how this works in more detail, in the next section.
===== Compressed Private Keys
If a bitcoin wallet is able to implement compressed public keys, then it will use those in all transactions. The private keys in the wallet will be used to derive the public key points on the curve, which will be compressed. The compressed public keys will be used to produce bitcoin addresses and those will be used in transactions. When exporting private keys from a new wallet that implements compressed public keys, the Wallet Import Format is modified, with the addition of a one-byte suffix +01+to the private key. The resulting base58check encoded private key is called a "Compressed WIF" and starts with the letter K or L, instead of starting with "5" as is the case with WIF encoded (non-compressed) keys from older wallets.
Here's the same key, encoded in WIF and WIF-compressed formats:
.Example: Same Key, Different Formats
[options="header"]
|=======
|Format | Private Key
| Hex | +1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd+
| Hex-compressed | +1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd_01_+
| WIF | +5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn+
| WIF-compressed | +KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ+
|=======
Remember, these formats are _not_ used interchangeably. In a newer wallet that implements compressed public keys, the private keys will only ever be exported as WIF-compressed (K/L prefix). If the wallet is an older implementation and does not use compressed public keys, the private keys will only ever be exported as WIF (5 prefix). The goal here is to signal to the wallet importing these private keys whether it must search the blockchain for compressed or uncompressed public keys and addresses.
Ironically, the name "compressed private key" is misleading, because when a private key is exported as WIF-compressed it is actually one byte _longer_ than an "uncompressed" private key. That is because it has the added 01 suffix which signifies it comes from a newer wallet and should only be used to produce compressed public keys. Private keys are not compressed and cannot be compressed. The term "compressed private key" really means "private key from which compressed public keys should be derived", whereas "uncompressed private key" really means "private key from which uncompressed public keys should be derived".
[TIP]
====
"Compressed private keys" is a misnomer! They are not compressed, rather they signify that they should only be used to derive compressed public keys and their corresponding bitcoin addresses. Ironically, a "compressed" private key is one byte longer because it has the added 01 suffix to distinguish it from an "uncompressed" one.
====
==== Wallets
There are many ways to generate keys for use in bitcoin. The simplest is to pick a large random number and turn it into a key pair (See <<key_derivation>>). A random key can be generated with very simple hardware or even manually with pen, paper and dice. The disadvantage of random keys is that if you generate many of them you must keep copies of all of them. Another method for making keys is _deterministic key generation_. Here you generate each new key as a function of the previous key, linking them in a sequence. As long as you can re-create that sequence, you only need the first key to generate them all. In this section we will examine the different methods for key generation.

Loading…
Cancel
Save