Difference between revisions of "Public key to bitmessage address"

From Bitmessage Wiki
Jump to navigation Jump to search
(made this thing a lot better)
m (Add key format details and hint about API binary data)
 
(4 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{stub}}
+
This page briefly describes, how to generate a Bitmessage Address from a public key.
 +
The variable after each step in the list is used to refer to the step later.
 +
Numbers used below are encoded as [[Protocol specification#Variable length integer|var_int]]
 +
 
 +
A <code>+</code> in this documentation means concatenation.
 +
 
 +
# Create two key pairs using secp256k1, one for encryption and one for signing (privEnc, pubEnc, privSign, pubSign)
 +
# Merge keys together using <code>pubSign + pubEnc</code>. Be sure to use the uncompressed keys (65 bytes per key, serialized X9.62 format). (A)
 +
# <code>SHA512(A)</code>. (B)
 +
# <code>RIPEMD160(B)</code>. (C)
 +
# Repeat step 1-4 until you have a result that starts with at least one zero. More zeros are permitted and cause a shorter address hash later. (D)
 +
# Remove all zeros at the beginning of D. (E)
 +
# <code>Address Version + Stream number + E</code>. (F)
 +
# <code>SHA512(SHA512(F))</code>. Keep the first four bytes. (G)
 +
# <code>F + G</code>. (H)
 +
# <code>Base58_encode(H)</code>. (I)
 +
# <code>"BM-" + I</code>. (J)
  
This page briefly describes, how to generate a Bitmessage Address from a public key.
+
J is your full address. You want to store this together with privSign and privEnc somewhere.
The variable after the list is used to refer to the step later
+
Note: The secp256k1 public keys don't need to be stored because they can be recreated again later from the private keys.
 +
 
 +
== Deterministic address ==
 +
 
 +
Deterministic addresses are created by using the first 32 bytes of <code>SHA512(passphrase + var_int)</code> as private key instead of randomly generating one.
 +
The var_int for the signing key starts at 0, and for the encryption key at 1.
 +
On every failed attempt to create an address, increment both integers by two and repeat the attempt. This means the integer for the signing key is always even,
 +
and the one for the encryption key always odd and one bigger than the signing key.
 +
 
 +
== Address validation ==
 +
 
 +
Remove the "BM-" at the beginning and Base58 decode the rest.
 +
Remove the last 4 bytes from the decoded data. Calculate SHA512 of the remaining address bytes and check if the first 4 bytes of the hash match the trimmed off 4 bytes.
 +
 
 +
Optional additional test: The address should start with two var_int values.
 +
Currently, only stream 1 is supported, and the address version should be 2-4.
 +
 
 +
== Remarks ==
 +
 
 +
Bitmessage's base58 encoding uses the following sequence (the same as Bitcoin's): "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".
 +
Many existing libraries for base58 do not use this ordering.
 +
 
 +
The BM- formatted address is never transmitted over the network. It's used to find the correct public key and decrypt it.
 +
 
 +
As you may have noticed, the address version and stream number are not part of the keys, this means it's technically possible to move an address between streams and versions, although the Base58 encoding makes the address look completely different each time.
  
# Create a private and a public key for encryption and signing (resulting in 4 keys)
+
The PyBitmessage client doesn't states how the passphrase is converted into bytes.
# Merge the public part of the signing key and the encryption key together. (A)
+
This means using "äöü" as a passphrase will deliver different results across different operating systems because of how strings in python are handled internally.
# Take the SHA512 hash of A. (B)
+
The API should not have this problem because the string is given using base64 encoding, which preserves the original binary data.
# Take the RIPEMD160 of B. (C)
 
# Repeat step 1-4 until you have a result that starts with a zero (Or two zeros, if you want a short address). (D)
 
# Remove the zeros at the beginning of D. (E)
 
# Put the stream number (in big endian) in front of E. (F)
 
# Put the address version (in big endian) in front of F. (G)
 
# Take a double SHA512 (hash of a hash) of G and use the first four bytes as a checksum, that you append to the end. (H)
 
# base58 encode H. (J)
 
# Put "BM-" in front J. (K)
 
  
K is your full address
+
The entire hashing and leading zero restrictions only apply to the public signing key, not the encryption key.

Latest revision as of 10:49, 4 August 2022

This page briefly describes, how to generate a Bitmessage Address from a public key. The variable after each step in the list is used to refer to the step later. Numbers used below are encoded as var_int

A + in this documentation means concatenation.

  1. Create two key pairs using secp256k1, one for encryption and one for signing (privEnc, pubEnc, privSign, pubSign)
  2. Merge keys together using pubSign + pubEnc. Be sure to use the uncompressed keys (65 bytes per key, serialized X9.62 format). (A)
  3. SHA512(A). (B)
  4. RIPEMD160(B). (C)
  5. Repeat step 1-4 until you have a result that starts with at least one zero. More zeros are permitted and cause a shorter address hash later. (D)
  6. Remove all zeros at the beginning of D. (E)
  7. Address Version + Stream number + E. (F)
  8. SHA512(SHA512(F)). Keep the first four bytes. (G)
  9. F + G. (H)
  10. Base58_encode(H). (I)
  11. "BM-" + I. (J)

J is your full address. You want to store this together with privSign and privEnc somewhere. Note: The secp256k1 public keys don't need to be stored because they can be recreated again later from the private keys.

Deterministic address

Deterministic addresses are created by using the first 32 bytes of SHA512(passphrase + var_int) as private key instead of randomly generating one. The var_int for the signing key starts at 0, and for the encryption key at 1. On every failed attempt to create an address, increment both integers by two and repeat the attempt. This means the integer for the signing key is always even, and the one for the encryption key always odd and one bigger than the signing key.

Address validation

Remove the "BM-" at the beginning and Base58 decode the rest. Remove the last 4 bytes from the decoded data. Calculate SHA512 of the remaining address bytes and check if the first 4 bytes of the hash match the trimmed off 4 bytes.

Optional additional test: The address should start with two var_int values. Currently, only stream 1 is supported, and the address version should be 2-4.

Remarks

Bitmessage's base58 encoding uses the following sequence (the same as Bitcoin's): "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz". Many existing libraries for base58 do not use this ordering.

The BM- formatted address is never transmitted over the network. It's used to find the correct public key and decrypt it.

As you may have noticed, the address version and stream number are not part of the keys, this means it's technically possible to move an address between streams and versions, although the Base58 encoding makes the address look completely different each time.

The PyBitmessage client doesn't states how the passphrase is converted into bytes. This means using "äöü" as a passphrase will deliver different results across different operating systems because of how strings in python are handled internally. The API should not have this problem because the string is given using base64 encoding, which preserves the original binary data.

The entire hashing and leading zero restrictions only apply to the public signing key, not the encryption key.