-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BIP-85: Add language code & dice app, TPRV guidance, warn on BIP-32 divergence, grammar & clarity #1679
BIP-85: Add language code & dice app, TPRV guidance, warn on BIP-32 divergence, grammar & clarity #1679
Changes from all commits
274d9b9
503d936
c29be89
c5a74ff
8afbdf5
17a5279
f9b3736
5206a35
a645d94
819f7b7
34b7477
3221abe
f9df9a7
f763695
294db30
0e5f2da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
Layer: Applications | ||
Title: Deterministic Entropy From BIP32 Keychains | ||
Author: Ethan Kosakovsky <ethankosakovsky@protonmail.com> | ||
Aneesh Karve <dowsing.seaport0d@icloud.com> | ||
Comments-Summary: No comments yet. | ||
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0085 | ||
Status: Draft | ||
|
@@ -14,9 +15,9 @@ | |
|
||
==Abstract== | ||
|
||
''"One Seed to rule them all,'' | ||
''One Key to find them,'' | ||
''One Path to bring them all,'' | ||
''"One Seed to rule them all,''<br> | ||
''One Key to find them,''<br> | ||
''One Path to bring them all,''<br> | ||
''And in cryptography bind them."'' | ||
|
||
It is not possible to maintain one single (mnemonic) seed backup for all keychains used across various wallets because there are a variety of incompatible standards. Sharing of seeds across multiple wallets is not desirable for security reasons. Physical storage of multiple seeds is difficult depending on the security and redundancy required. | ||
|
@@ -33,23 +34,26 @@ The terminology related to keychains used in the wild varies widely, for example | |
# '''BIP39 mnemonic''' is the mnemonic phrase that is calculated from the entropy used before hashing of the mnemonic in BIP39. | ||
# '''BIP39 seed''' is the result of hashing the BIP39 mnemonic seed. | ||
|
||
When in doubt, assume big endian byte serialization, such that the leftmost | ||
byte is the most significant. | ||
|
||
==Motivation== | ||
|
||
Most wallets implement BIP32 which defines how a BIP32 root key can be used to derive keychains. As a consequence, a backup of just the BIP32 root key is sufficient to include all keys derived from it. BIP32 does not have a human friendly serialization of the BIP32 root key (or BIP32 extended keys in general) which makes paper backups or manually restoring the key more error-prone. BIP39 was designed to solve this problem but rather than serialize the BIP32 root key, it takes some entropy, encoded to a "seed mnemonic", which is then hashed to derive the BIP39 seed which can be turned into the BIP32 root key. Saving the BIP39 mnemonic is enough to reconstruct the entire BIP32 keychain, but a BIP32 root key cannot be reversed back to the BIP39 mnemonic. | ||
Most wallets implement BIP32 which defines how a BIP32 root key can be used to derive keychains. As a consequence, a backup of just the BIP32 root key is sufficient to include all keys derived from it. BIP32 does not have a human-friendly serialization of the BIP32 root key (or BIP32 extended keys in general), which makes paper backups or manually restoring the key more error-prone. BIP39 was designed to solve this problem, but rather than serialize the BIP32 root key, it takes some entropy, encoded to a "seed mnemonic", which is then hashed to derive the BIP39 seed, which can be turned into the BIP32 root key. Saving the BIP39 mnemonic is enough to reconstruct the entire BIP32 keychain, but a BIP32 root key cannot be reversed back to the BIP39 mnemonic. | ||
|
||
Most wallets implement BIP39, so on initialization or restoration, the user must interact with a BIP39 mnemonic. Most wallets do not support BIP32 extended private keys, so each wallet must either share the same BIP39 mnemonic, or have a separate BIP39 mnemonic entirely. Neither scenarios are particularly satisfactory for security reasons. For example, some wallets may be inherently less secure like hot wallets on smartphones, Join Market servers, or Lightning Network nodes. Having multiple seeds is far from desirable, especially for those who rely on split key or redundancy backups in different geological locations. Adding is necessarily difficult and may result in users being more lazy with subsequent keys, resulting in compromised security or loss of keys. | ||
Most wallets implement BIP39, so on initialization or restoration, the user must interact with a BIP39 mnemonic. Most wallets do not support BIP32 extended private keys, so each wallet must either share the same BIP39 mnemonic, or have a separate BIP39 mnemonic entirely. Neither scenario is particularly satisfactory for security reasons. For example, some wallets may be inherently less secure, like hot wallets on smartphones, JoinMarket servers, or Lightning Network nodes. Having multiple seeds is far from desirable, especially for those who rely on split key or redundancy backups in different geological locations. Adding keys is necessarily difficult and may result in users being more lazy with subsequent keys, resulting in compromised security or loss of keys. | ||
|
||
There is added complication with wallets that implement other standards, or no standards at all. Bitcoin Core wallet uses a WIF as the ''hdseed'', and yet other wallets like Electrum use different mnemonic schemes to derive the BIP32 root key. Other cryptocurrencies like Monero also use an entirely different mnemonic scheme. | ||
There is an added complication with wallets that implement other standards, or no standards at all. The Bitcoin Core wallet uses a WIF as the ''hdseed'', and yet other wallets, like Electrum, use different mnemonic schemes to derive the BIP32 root key. Other cryptocurrencies, like Monero, use an entirely different mnemonic scheme. | ||
|
||
Ultimately, all of the mnemonic/seed schemes start with some "initial entropy" to derive a mnemonic/seed, and then process the mnemonic into a BIP32 key, or private key. We can use BIP32 itself to derive the "initial entropy" to then recreate the same mnemonic or seed according to the specific application standard of the target wallet. We can use a BIP44-like categorization to ensure uniform derivation according to the target application type. | ||
|
||
==Specification== | ||
|
||
We assume a single BIP32 master root key. This specification is not concerned with how this was derived (e.g. directly or via a mnemonic scheme such as BIP39). | ||
|
||
For each application that requires its own wallet, a unique private key is derived from the BIP32 master root key using a fully hardened derivation path. The resulting private key (k) is then processed with HMAC-SHA512, where the key is "bip-entropy-from-k", and the message payload is the private key k: <code>HMAC-SHA512(key="bip-entropy-from-k", msg=k)</code>. The result produces 512 bits of entropy. Each application SHOULD use up to the required number of bits necessary for their operation truncating the rest. | ||
For each application that requires its own wallet, a unique private key is derived from the BIP32 master root key using a fully hardened derivation path. The resulting private key (k) is then processed with HMAC-SHA512, where the key is "bip-entropy-from-k", and the message payload is the private key k: <code>HMAC-SHA512(key="bip-entropy-from-k", msg=k)</code>. The result produces 512 bits of entropy. Each application SHOULD use up to the required number of bits necessary for their operation, and truncate the rest. | ||
|
||
The HMAC-SHA512 function is specified in [http://tools.ietf.org/html/rfc4231 RFC 4231]. | ||
The HMAC-SHA512 function is specified in [https://tools.ietf.org/html/rfc4231 RFC 4231]. | ||
|
||
===Test vectors=== | ||
|
||
|
@@ -78,7 +82,7 @@ BIP85-DRNG-SHAKE256 is a deterministic random number generator for cryptographic | |
RSA key generation is an example of a function that requires orders of magnitude more than 64 bytes of random input. Further, it is not possible to precalculate the amount of random input required until the function has completed. | ||
|
||
drng_reader = BIP85DRNG.new(bip85_entropy) | ||
rsa_key = RSA.generate_key(4096, drng_reader.read()) | ||
rsa_key = RSA.generate_key(4096, drng_reader.read) | ||
|
||
===Test Vectors=== | ||
INPUT: | ||
|
@@ -93,14 +97,17 @@ OUTPUT | |
|
||
==Reference Implementation== | ||
|
||
* Python library implementation: [https://github.com/ethankosakovsky/bip85] | ||
* JavaScript library implementation: [https://github.com/hoganri/bip85-js] | ||
* 1.3.0 Python 3.x library implementation: [https://github.com/akarve/bipsea] | ||
* 1.1.0 Python 2.x library implementation: [https://github.com/ethankosakovsky/bip85] | ||
* 1.0.0 JavaScript library implementation: [https://github.com/hoganri/bip85-js] | ||
|
||
==Applications== | ||
|
||
The Application number defines how entropy will be used post processing. Some basic examples follow: | ||
|
||
Derivation path uses the format <code>m/83696968'/{app_no}'/{index}'</code> where ''{app_no}'' is the path for the application, and ''{index}'' is the index. | ||
Derivation paths follow the format <code>m/83696968'/{app_no}'/{index}'</code>, where ''{app_no}'' is the path for the application, and ''{index}'' is the index. | ||
|
||
Application numbers should be semantic in some way, such as a BIP number or ASCII character code sequence. | ||
|
||
===BIP39=== | ||
Application number: 39' | ||
|
@@ -143,6 +150,10 @@ Language Table | |
|- | ||
| Czech | ||
| 8' | ||
|- | ||
| Portuguese | ||
| 9' | ||
|- | ||
|} | ||
|
||
Words Table | ||
|
@@ -207,7 +218,16 @@ OUTPUT: | |
===HD-Seed WIF=== | ||
Application number: 2' | ||
|
||
Uses 256 bits[1] of entropy as the secret exponent to derive a private key and encode as a compressed WIF which will be used as the hdseed for Bitcoin Core wallets. | ||
akarve marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Uses the most significant 256 bits<ref name="curve-order"> | ||
There is a very small chance that you'll make an invalid | ||
key that is zero or larger than the order of the curve. If this occurs, software | ||
should hard fail (forcing users to iterate to the next index). From BIP32: | ||
<blockquote> | ||
In case parse<sub>256</sub>(I<sub>L</sub>) ≥ n or k<sub>i</sub> = 0, the resulting key is invalid, and one should proceed with the next value for i. (Note: this has probability lower than 1 in 2<sup>127</sup>.) | ||
</blockquote> | ||
</ref> | ||
of entropy as the secret exponent to derive a private key and encode as a compressed | ||
WIF that will be used as the hdseed for Bitcoin Core wallets. | ||
|
||
Path format is <code>m/83696968'/2'/{index}'</code> | ||
|
||
|
@@ -222,7 +242,11 @@ OUTPUT | |
===XPRV=== | ||
Application number: 32' | ||
|
||
Taking 64 bytes of the HMAC digest, the first 32 bytes are the chain code, and second 32 bytes[1] are the private key for BIP32 XPRV value. Child number, depth, and parent fingerprint are forced to zero. | ||
Taking 64 bytes of the HMAC digest, the first 32 bytes are the chain code, and the second 32 bytes<ref name="curve-order" /> are the private key for the BIP32 XPRV value. Child number, depth, and parent fingerprint are forced to zero. | ||
|
||
''Warning'': The above order reverses the order of BIP32, which takes the first 32 bytes as the private key, and the second 32 bytes as the chain code. | ||
|
||
Applications may support Testnet by emitting TPRV keys if and only if the input root key is a Testnet key. | ||
|
||
Path format is <code>m/83696968'/32'/{index}'</code> | ||
|
||
|
@@ -257,7 +281,7 @@ The derivation path format is: <code>m/83696968'/707764'/{pwd_len}'/{index}'</co | |
|
||
`20 <= pwd_len <= 86` | ||
|
||
[https://datatracker.ietf.org/doc/html/rfc4648 Base64] encode the all 64 bytes of entropy. | ||
[https://datatracker.ietf.org/doc/html/rfc4648 Base64] encode all 64 bytes of entropy. | ||
akarve marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Remove any spaces or new lines inserted by Base64 encoding process. Slice base64 result string | ||
on index 0 to `pwd_len`. This slice is the password. As `pwd_len` is limited to 86, passwords will not contain padding. | ||
|
||
|
@@ -295,7 +319,7 @@ The derivation path format is: <code>m/83696968'/707785'/{pwd_len}'/{index}'</co | |
|
||
`10 <= pwd_len <= 80` | ||
|
||
Base85 encode the all 64 bytes of entropy. | ||
Base85 encode all 64 bytes of entropy. | ||
Remove any spaces or new lines inserted by Base64 encoding process. Slice base85 result string | ||
on index 0 to `pwd_len`. This slice is the password. `pwd_len` is limited to 80 characters. | ||
|
||
|
@@ -356,6 +380,41 @@ GPG capable smart-cards SHOULD be loaded as follows: The encryption slot SHOULD | |
|
||
However, depending on available slots on the smart-card, and preferred policy, the CERTIFY capable key MAY be flagged with CERTIFY and SIGNATURE capabilities and loaded into the SIGNATURE capable slot (for example where the smart-card has only three slots and the CERTIFY capability is required on the same card). In this case, the SIGNATURE capable sub-key would be disregarded because the CERTIFY capable key serves a dual purpose. | ||
|
||
===DICE=== | ||
|
||
Application number: 89101' | ||
|
||
The derivation path format is: <code>m/83696968'/89101'/{sides}'/{rolls}'/{index}'</code> | ||
|
||
2 <= sides <= 2^32 - 1 | ||
1 <= rolls <= 2^32 - 1 | ||
|
||
Use this application to generate PIN numbers, numeric secrets, and secrets over custom alphabets. | ||
For example, applications could generate alphanumeric passwords from a 62-sided die (26 + 26 + 10). | ||
|
||
Roll values are zero-indexed, such that an N-sided die produces values in the range | ||
<code>[0, N-1]</code>, inclusive. Applications should separate printed rolls by a comma or similar. | ||
|
||
Create a BIP85 DRNG whose seed is the derived entropy. | ||
|
||
Calculate the following integers: | ||
|
||
bits_per_roll = ceil(log_2(sides)) | ||
bytes_per_roll = ceil(bits_per_roll / 8) | ||
|
||
Read <code>bytes_per_roll</code> bytes from the DRNG. | ||
Trim any bits in excess of <code>bits_per_roll</code> (retain the most | ||
significant bits). The resulting integer represents a single roll or trial. | ||
If the trial is greater than or equal to the number of sides, skip it and | ||
move on to the next one. Repeat as needed until all rolls are complete. | ||
|
||
INPUT: | ||
* MASTER BIP32 ROOT KEY: xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb | ||
* PATH: m/83696968'/89101'/6'/10'/0' | ||
OUTPUT | ||
* DERIVED ENTROPY=5e41f8f5d5d9ac09a20b8a5797a3172b28c806aead00d27e36609e2dd116a59176a738804236586f668da8a51b90c708a4226d7f92259c69f64c51124b6f6cd2 | ||
* DERIVED ROLLS=1,0,0,2,0,1,5,5,2,4 | ||
|
||
==Backwards Compatibility== | ||
|
||
This specification is not backwards compatible with any other existing specification. | ||
|
@@ -370,16 +429,42 @@ The reason for running the derived key through HMAC-SHA512 and truncating the re | |
|
||
Many thanks to Peter Gray and Christopher Allen for their input, and to Peter for suggesting extra application use cases. | ||
|
||
==Change Log== | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jonatack i've added a real changelog so that the semvers are more... semantic. i could go deeper in terms of detail (fixes, etc.) but this seems complete enough to be useful and importantly puts this commit at semver 1.3.0. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest "Changelog" (no space), with entries ordered by most recent first (see https://keepachangelog.com/en/1.1.0/). |
||
|
||
===1.0.0 (2020-06-11)=== | ||
|
||
* Initial version | ||
|
||
===1.1.0 (2020-11-19)=== | ||
|
||
====Added==== | ||
|
||
* BIP85-DRNG-SHAKE256 | ||
* RSA application 828365' | ||
|
||
===1.2.0 (2022-12-04)=== | ||
|
||
====Added==== | ||
|
||
* Base64 application 707764' | ||
* Base85 application 707785' | ||
|
||
===1.3.0 (2024-10-22)=== | ||
|
||
====Added==== | ||
|
||
* Dice application 89101' | ||
* Czech language code to application 39' | ||
* TPRV guidance for application 32' | ||
* Warning on application 32' key and chain code ordering | ||
|
||
==References== | ||
|
||
BIP32, BIP39 | ||
|
||
==Footnotes== | ||
|
||
[1] There is a very small chance that you'll make an invalid key that is zero or bigger than the order of the curve. If this occurs, software should hard fail (forcing users to iterate to the next index). | ||
|
||
From BIP32: | ||
In case parse<sub>256</sub>(I<sub>L</sub>) is 0 or ≥ n, the resulting key is invalid, and one should proceed with the next value for i. (Note: this has probability lower than 1 in 2<sup>127</sup>.) | ||
<references /> | ||
|
||
==Copyright== | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it would be easier for readers to reference these versions by moving this
Reference Implementation
section to just above theChangelog
(and move theAcknowledgements
section further down toward the end).