ADR 0008: Standard Account Key Generation
- 2021-05-07: Add test vectors and reference implementation, extend Consequences section
- 2021-04-19: Switch from BIP32-Ed25519 to SLIP-0010 for hierarchical key derivation scheme
- 2021-01-27: Initial draft
Currently, each application interacting with the Oasis Network defines its own method of generating an account's private/public key pair.
Account's public key is in turn used to derive the account's address of the
oasis1 ... 40 characters ... which is used to for a variety of operations
(i.e. token transfers, delegations/undelegations, ...) on the network.
The blockchain ecosystem has developed many standards for generating keys which improve key storage and interoperability between different applications.
Adopting these standards will allow the Oasis ecosystem to:
- Make key derivation the same across different applications (i.e. wallets).
- Allow users to hold keys in hardware wallets.
- Allow users to hold keys in cold storage more reliably (i.e. using the familiar 24 word mnemonics).
- Define how users can generate multiple keys from a single seed (i.e. the 24 or 12 word mnemonic).
Mnemonic Codes for Master Key Derivation
We use Bitcoin's BIP-0039: Mnemonic code for generating deterministic keys to derivate a binary seed from a mnemonic code.
The binary seed is in turn used to derive the master key, the root key from which a hierarchy of deterministic keys is derived, as described in Hierarchical Key Derivation Scheme.
We strongly recommend using 24 word mnemonics which correspond to 256 bits of entropy.
Hierarchical Key Derivation Scheme
We use Sathoshi Labs' SLIP-0010: Universal private key derivation from master private key, which is a superset of Bitcoin's BIP-0032: Hierarchical Deterministic Wallets derivation algorithm, extended to work on other curves.
Key Derivation Paths
The following BIP-0032 path should be used to generate keys:
x represents the key number.
Note that all path levels are hardened, e.g.
44 | 0x8000000 or
44 + 2^31.
The key corresponding to key number 0 (i.e.
m/44'/474'/0') is called the
The account corresponding to the primary key is called the primary account. Applications (i.e. wallets) should use this account as a user's default Oasis account.
BIPs and SLIPs are industry standards used by a majority of blockchain projects and software/hardware wallets.
SLIP-0010 for Hierarchical Key Derivation Scheme
In particular, we use their adaptation for the edwards25519 curve.
It is used by Stellar (SEP-0005).
It is commonly used by Ledger applications, including:
- Stellar's Ledger app,
- Solana's Ledger app,
- NEAR Protocol's Ledger app,
- Siacoin's Ledger app,
- Hedera Hashgraph's Ledger app.
Difficulties in Adapting BIP-0032 to edwards25519 Curve
Creating a hierarchical key derivation scheme for the edwards25519 curve proved to be very challenging due to edwards25519's small cofactor and bit "clamping".
BIP-0032 was designed for the secp256k1 elliptic curve with a prime-order group. For performance reasons, edwards25519 doesn't provide a prime-order group and provides a group of order h * l instead, where h is a small co-factor (8) and l is a 252-bit prime.
While using a co-factor offers better performance, it has proven to be a source of issues and vulnerabilities in higher-layer protocol implementations as described by Risretto authors.
Additionally, edwards25519 curve employs bit "clamping". As described by Trevor Perrin, low bits are "clamped" to deal with small-subgroup attacks and high bits are "clamped" so that:
- the scalar is smaller than the subgroup order, and
- the highest bit set is constant in case the scalar is used with a non-constant-time scalar multiplication algorithm that leaks based on the highest set bit.
SLIP-0010 avoids these issues because it doesn't try to support non-hardened parent public key to child public key derivation and only supports hardened private parent key to private child key derivation when used with the edwards25519 curve.
Shorter Key Derivation Paths
Similar to Stellar's SEP-0005, we decided not to use the full BIP-0032 derivation path specified by BIP-0044 because SLIP-0010's scheme for edwards25519 curve only supports hardened private parent key to private child key derivation and additionally, the Oasis Network is account-based rather than UTXO-based.
"kind": "standard account key generation",
"bip39_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
"kind": "standard account key generation",
"bip39_mnemonic": "equip will roof matter pink blind book anxiety banner elbow sun young",
To generate these test vectors yourself, run:
make -C go staking/gen_account_vectors
We also provide more extensive test vectors. To generate them, run:
make -C go staking/gen_account_vectors_extended
Reference implementation is in Oasis Core's
BIP32-Ed25519 for Hierarchical Key Derivation
The BIP32-Ed25519 (also sometimes referred to as Ed25519 and BIP32 based on Khovratovich) is a key derivation scheme that also adapts BIP-0032's hierarchical derivation scheme for the edwards25519 curve from the Ed25519 signature scheme specified in RFC 8032.
It is commonly used by Ledger applications, including:
Its advantage is that it supports non-hardened parent public key to child public key derivation which enables certain use cases described in [BIP-0032][ BIP-0032-use-cases] (i.e. audits, insecure money receiver, ...).
At the same time, allowing non-hardened parent public key to child public key derivation presents a serious security concern due to edwards25519's co-factor issues.
- The Ed25519 library used in BIP-Ed25519 derivation scheme does clamping immediately before signing.
- Adversary has the power to make numerous small payments in deep hierarchies of key derivations, observe if the victim can cash out each payment, and adaptively continue this process.
The first assumption is very reasonable since the BIP32-Ed25519 paper makes supporting this part of their specification.
The second assumption is a bit more controversial. The BIP32-Ed25519 paper's specification limits the BIP-0032 path length (i.e. the number of levels in the tree) to 220. But in practice, no implementation checks that and the issue is that path length is not an explicit part of the BIP32-Ed25519 algorithm. That means that one doesn't know how deep in the tree the current parent/child node is. Hence, it would be very hard to enforce the 220 path length limit.
One practical issue with BIP32-Ed25519 is that its authors didn't provide a reference implementation and accompanying test vectors.
This has led to a number of incompatible BIP32-Ed25519 implementations.
For example, Vincent Bernardoff's OCaml implementation and Shude Li's Go implementation follow BIP32-Ed25519's original master (i.e. root) key derivation specification and use SHA512 and SHA256 for deriving the private key k and chain code c (respectively) from the seed (i.e. master secret).
On the other hand, Ledger's [Python implementation in orakolo repository][
BIP32-Ed25519-orakolo] and [C implementation for their Speculos emulator][
BIP32-Ed25519-speculos] (variant with
curve equal to
mode equal to
HDW_NORMAL) use HMAC-SHA512 and HMAC-SHA256 for deriving the
private key k and chain code c (respectively) from the seed.
Furthermore, [Vincent Bernardoff's OCaml implementation][ BIP32-Ed25519-OCaml-root-discard] follows BIP32-Ed25519 paper's instructions to discard the seed (i.e. master secret) if the master key's third highest bit of the last byte of kL is not zero.
On the other hand, Shude Li's Go implementation just clears the master key's third highest bit and Ledger's implementations repeatedly set the seed to the master key and restart the derivation process until a master key with the desired property is found.
Lastly, some implementations, notably Oasis' Ledger app, don't use use BIP32-Ed25519's private and public key directly but use the obtained kL (first 32 bytes) of the 64 byte BIP32-Ed25519 derived private key as Ed25519's seed (i.e. non-extended private key). For more details, see Zondax/ledger-oasis#84.
Tor's Next Generation Hidden Service Keys for Hierarchical Key Derivation
Jeff Burdges (Web3 Foundation)'s post on potential key recovery attack on the BIP32-Ed25519 scheme mentions there is nothing wrong with this proposed scheme. Likewise, Justin Starry (Solana)'s summary of approaches to adopting BIP-0032 for Ed25519 recommends this scheme as one of the possible approaches to adapt BIP-0032 for edwards25519 curve.
Hence, there will be no vendor lock-in and users will have the option to easily switch between standards-compliant applications (e.g. different wallets).
Using SLIP-0010 avoids a spectrum of issues when trying to support non-hardened public parent key to public child key derivation with the edwards25519 curve. Non-hardened key derivation is practically impossible to implement securely due to edwards25519 curve's co-factor issues.
This is achieved by SLIP-0010 explicitly disallowing non-hardened public parent key to public child key derivation with the edwards25519 curve.
Using a 3-level BIP-0032 path (i.e.
m/44'/474'/x') allows Oasis' Ledger app to implement automatic switching between existing (legacy) account key generation and the standard account key generation proposed in this ADR.
Since the existing (legacy) account key generation used in Oasis' Ledger app uses a 5-level BIP-0032 path, the Oasis' Ledger app will be able to automatically switch between standard and existing (legacy) account key generation just based on the number of levels of the given BIP-0032 path.
The account key generation proposed in this ADR is incompatible with two existing account key generation schemes deployed in the field:
That means that these two applications will need to support two account key generations schemes simultaneously to allow existing users to access their (old) accounts generated via the existing (legacy) account key generation scheme.
That means it will not be possible to implement wallet features that require non-hardened key derivation, e.g. watch-only feature where one is able to monitor a hierarchical wallet's accounts just by knowing the root public key and deriving all accounts' public keys from that.