Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
382 lines (251 sloc) 26 KB

NIP 6 - Multi-Account Hierarchy for Deterministic Wallets

    NIP: 6
    Layer: Application
    Title: Multi-Account Hierarchy for Deterministic Wallets
    Author: Grégory Saive <greg@nem.foundation>
    Status: Draft
    Discussions-To: #sig-client
    Comments-URI: https://github.com/nemtech/NIP/issues/12
    Type: Standards Track
    Created: 2019-04-09
    License: BSD-2 Clause

Table of contents

Abstract

This document describes hierarchical deterministic wallets for NEM.

This document uses Bitcoin's BIP32, BIP39, BIP43, BIP44 and SLIP-10 as sources of documentation.

A standard for deterministic wallets creation is needed to improve handling of NEM wallets within client applications.

This standard will set the rules for generating extended keys as defined in BIP32, for generating mnemonic pass phrases as defined in BIP39 and for defining a logical hierarchy for deterministic wallets.

Extracts from the Bitcoin documents will be added in quotes and detailed extensions or updates will be annotated for the NEM platform.

Motivation

Previous versions of NEM clients (wallets) used a user password to encrypt private keys, resulting in the need of creating backups every time when a new private key is generated.

Deterministic wallets do not require frequent backups as they will permit the generation of multiple public keys (addresses) without revealing the spending private key, with the use of elliptic curve mathematics.

This multi-account hierarchy will support several chains of keypairs (multiple trees) as to allow selective sharing of wallet public keys.

Additionally, we will be defining Mnemonic codes for generating deterministic keys using the definition in Bitcoin BIP39.

Furthermore, using Bitcoin BIP44, we will define a scheme to build logical hierarchies for deterministic wallets. This will represent the recommended method to work with NEM CATAPULT wallets and keys.

Non-Hardened Child Key Derivation

It has been decided that non-hardened child key derivation may not fill any required use-case.

As such, the implementation proposal will be providing only hardened child key derivation.

At first, the SLIP-10 algorithm was used to implement ED25519 compliant child key derivation. A second proposal implementation includes the possibility to use KMAC instead of HMAC, this will be described more in details in section KMAC vs HMAC.

Use case #1: Facilitate Audit Process

The non-hardened child key derivation model can be used to facilitate the process of auditing a hierarchical deterministic wallet. By sharing the non-hardened extended public key at the top of the tree in a HD wallet, one can give an auditor the ability to view all addresses in the wallet without the ability to generate the associated private keys.

Reference: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#audits-nm

Relevance Issue:

Discussion from https://github.com/nemtech/NIP/issues/12#issuecomment-484525238

Any auditor will simply request ALL your public keys. ownership of public keys can be easily shown (by signing some predefined message), but even without this, I fail to see a reason, where you would like to give to auditor MORE public keys than you actually own...

Use case #2: Unsecure money receiver

The non-hardened child key derivation model can be used when using unsecure VPS or webservers to run e-commerce websites. In those scenarios, the e-commerce will be configured to derive public keys (addresses) from a non-hardened extended public key as this will make sure that, even if the webserver is ever compromised, there is no chance of losing funds associated with the public keys.

In cases of compromised money receivers, there will however be a loss in privacy as the extended public key allows for the derivation of a whole tree of child keys making it possible for the attacker to associate public keys to the parent key.

Reference: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#unsecure-money-receiver-nmih0

Resources:

Following are the proposed implementations for BIP32-ED25519 extended keys. The implementation proposal by the Cardano team was defined in a paper that can be found in the resources as well:

Compatibility

Following table describes compatibility of implementation proposal with regards to hardened child key derivation and non-hardened child key derivaton.

Resource Language Hardened CKD Non-Hardened CKD
IMPL1 rust YES  YES
IMPL2 JS/TS YES NO
IMPL3 rust YES NO

KMAC vs HMAC

In order to improve the key derivation algorithm, we may be using KMAC256k(m) in places where SLIP-10 and BIP-32 use HMAC(k||m). This is because KMAC presents better compatibility with SHA-3 and avoids the danger of colliding uses.

Additionally, the KMAC approach should be simpler and faster than the HMAC approach because it does not require an additional pass of the hash algorithm.

The KMAC wording stands for Keccak Message Authentication Code, it is an alternative to the HMAC algorithm which stands for Hash-Based Message Authentication Code.

More details about KMAC can be found in following NIST publication:

https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf

Conclusion

The described use cases make it potentially useful to implement non-hardened child key derivation scheme.

Due to the complexity of implementation and status of the available resources for the ed25519-compatible non-hardended child key derivation implementation, it may be decided to drop support of non-hardened child key derivation as we are aiming for stable, tested and maintained solutions for this NIP to be a success in cross-client interoperability.

Resolution: Hardened child key derivation will be the only available scheme of derivation for Catapult hierarchical deterministic wallets.

Specification

Following section defines the technical specification of the proposed Multi-Account Hierarchy for Deterministic Wallets.

Hardened Key Derivation

In our implementation proposal, we will be defining methods to derive master and child keys for hardened paths only. This means that any key that is derived using the subsequent library will be a hardened key.

In the BIP32 and BIP44 proposals, hardened keys are to be defined by having an apostrophe (') as the suffix of the path level index. Examples of hardened paths include :

  • m/44'/43'/0'/0'/0': first account, internal keychain, first address.
  • m/44'/43'/0'/0'/1': first account, internal keychain, second address.
  • m/44'/43'/1'/0'/5': second account, internal keychain, sixth address.
  • m/44'/43'/1'/1'/5': second account, external keychain, sixth address.

⚠️ Following this definition, paths will automatically be hardened in case they are non-hardened.

KMAC Derivation

In our implementation proposal, we will permit the use of KMAC. Any key derived for the Catapult engine/network, will be using KMAC, whilst keys derived for the Bitcoin or Bitcoin-alike protocol/network will be using HMAC as to provide compatibility with other hierarchical deterministic keys derivation implementations.

As such, we will provide with a MACType enumeration and MACImpl class implementation that permits to switch between KMAC and HMAC.

⚠️ It is not recommened to generate/derive keys for Catapult with the HMAC approach as this will produce keys that are not compatible with the chosen KMAC approach.

Extended keys

The Bitcoin BIP32 document describes extended keys in detail. With the following extracts:

We represent an extended private key as (k, c), with k the normal private key, and c the chain code. An extended public key is represented as (K, c), with K = point(k) and c the chain code.

Each extended key has 2^31 normal child keys, and 2^31 hardened child keys. Each of these child keys has an index. The normal child keys use indices 0 through 2^31-1. The hardened child keys use indices 2^31 through 2^32-1. To ease notation for hardened key indices, a number iH represents i+2^31.

Multiple child key derivation functions (CKDs) are proposed in BIP32 as well, with the following references :

Catapult updates

With regards to the BIP32 standard definition, Catapult extended keys will be different in several aspects including:

  • Keys are derived using the ed25519 elliptic curve instead of the secp256k curve.
  • Keys are derived using the KMAC codes instead of HMAC codes.
  • Extended Keys have 231 hardened child keys as it is not possible to derive non-hardened child keys with our implementation.

To this extent, our implementation proposal implements the following child key derivation methods:

Private parent key --> Private child key: CKDpriv((kpar, cpar), i) → (ki, ci)

The function CKDpriv((kpar, cpar), i) → (ki, ci) computes a child extended private key from the parent extended private key with steps defined below.

This implementation proposal makes use of the SLIP-10 proposed CKDPriv implementation with the difference that KMAC is used instead of HMAC.

  1. Take hardened child: `let I = KMAC256(Key = cpar, Data = 0x00 || setp(point(kpar)) || ser32(i))
  2. Split I into two 32-byte sequences, IL and IR
  3. The returned chain code ci is IR.
  4. The returned child key ki is IL.

The KMAC256 function is specified in NIST.SP800-185.

Public parent key --> Public child key

This function is not available in our implementation proposal.

Private parent key --> Public child key: N((k, c)) → (K, c)

The function N((k, c)) → (K, c) computes the extended public key corresponding to an extended private key. This will compute the the neutered version and removes the ability to sign transactions.

  • The returned key K is point(k).
  • The returned chain code c is just the passed chain code.

To compute the public child key of a parent private key:

  • N(CKDpriv((kpar, cpar), i))

Key trees

In the source document, a key tree is defined that will make use of the CKDs defined above. The child key derivation functions are cascaded several times to build a tree of keys.

Leaf nodes of that tree each define one key (private or public). A detailed explanation of the created key tree can be found here.

Compatibility

From the Bitcoin BIP32 standard document:

To comply with this standard, a client must at least be able to import an extended public or private key, to give access to its direct descendants as wallet keys. The wallet structure (master/account/chain/subchain) presented in the second part of the specification is advisory only, but is suggested as a minimal structure for easy compatibility - even when no separate accounts or distinction between internal and external chains is made. However, implementations may deviate from it for specific needs; more complex applications may call for a more complex tree structure.

Security implications

Security properties of the Extended Keys proposals can be found here.

From the Bitcoin BIP32 description:

Private and public keys must be kept safe as usual. Leaking a private key means access to coins - leaking a public key can mean loss of privacy.

⚠️ Important Security Implication

Somewhat more care must be taken regarding extended keys, as these correspond to an entire (sub)tree of keys. One weakness that may not be immediately obvious, is that knowledge of a parent extended public key plus any non-hardened private key descending from it is equivalent to knowing the parent extended private key (and thus every private and public key descending from it). This means that extended public keys must be treated more carefully than regular public keys. It is also the reason for the existence of hardened keys, and why they are used for the account level in the tree. This way, a leak of account-specific (or below) private key never risks compromising the master or other accounts.

Wallet structure

The previous sections specified key trees and their nodes as defined by the Bitcoin BIP32 source document. Next we are imposing a wallet structure that leverages this tree of keys.

From the Bitcoin BIP32 standard - The default wallet layout:

The layout defined in this section is a default only, though clients are encouraged to mimic it for compatibility, even if not all features are supported.

An hyperhierarchical deterministic wallet is organized as several accounts. Accounts are numbered, the default account ("") being number 0. Clients are not required to support more than one account - if not, they only use the default account.

Each account is composed of two keypair chains: an internal and an external one. The external keychain is used to generate new public addresses, while the internal keychain is used for all other operations.

We will detail the logical hierarchy more in detail in the section BIP44: A logical hierarchy for deterministic wallets.

Mnemonic seeds

Mnemonic seeds are sentences with words matching a Wordlists as well as holding a checksum as defined in BIP39's Generating the mnemonic.

Mnemonic seeds are created from the available Wordlists in the BIP39 annex.

Following references will be used during reference implementation:

A logical hierarchy with BIP44

Because we want to comply to the BIP44 standard we will define path levels as recommended in BIP44. This gives us the following path levels:

m / purpose' / coin_type' / account' / change' / address_index'

⚠️ Note the addition of hardened change and address_index path levels. Because Catapult makes use of ed25519 elliptic curve cryptography, deriving non-hardened extended keys is non-trivial and resources available on this topic are limited. For our implementation, we decided to use hardened derivation only because of the simplicity of implementation and the availability of tested resources and publications.

  • Our purpose level will be 44' as we are building a logical hierarchy following the BIP44 standard.
  • Our coin_type is 43' as this corresponds to NEM in SLIP-44 annexed to the source document.
  • The next level corresponds to the index of the account that we want to use, starting at 0 for the first account.
  • The change path level is used to define which keychain must be used. Set to 0', the keychain used is said to be external, while any other change path level will result in using the internal keychain.
  • The address_index path level corresponds to the index of the address that we want to use, starting at 0' for the first address.

The appended apostrophe (') in path levels is used to mark hardened key levels. As defined in Bitcoin BIP32 and in this document's Wallet structure, the BIP32 algorithm permits derivation of two entirely independent keyspaces. Those are usually called the hardened key space and the non-hardened key space.

⚠️ Our implementation proposal only permits derivation of one of these keyspaces, namely the hardened key space.

For NEM, we can define a base logical hierarchy of m/44'/43'. It is recommended for client implementations to follow this standard in order to achieve better cross-client compatibility.

For client implementations which do not wish to implement the full capabilities of multi-account hierarchy for deterministic wallets, it is recommended to use only the following default address path: m/44'/43'/0'/0'/0'.

Examples

Following table displays example derivation paths with their corresponding hierarchy details. The two first path levels, namely the purpose and the coin_type, are always expected to contain 44' and 43' respectively. The next path levels, the third, represents the account number to be derived, and so on.

account keychain address path
first external first m/44'/43'/0'/0'/0'
first external second m/44'/43'/0'/0'/1'
first external third m/44'/43'/0'/0'/2'
first internal first m/44'/43'/0'/1'/0'
first internal second m/44'/43'/0'/1'/1'
first internal third m/44'/43'/0'/1'/2'
second external first m/44'/43'/1'/0'/0'
second external second m/44'/43'/1'/0'/1'
second external third m/44'/43'/1'/0'/2'
second internal first m/44'/43'/1'/1'/0'
second internal second m/44'/43'/1'/1'/1'
second internal third m/44'/43'/1'/1'/2'

Implementation

An implementation proposal has been started with following specification:

  • Package name nem2-hd-wallets@0.4.0 at https://github.com/evias/nem2-hd-wallets
  • DeterministicKey class to provide with bitcoinjs/bip32 compatibility.
  • NodeEd25519 class to describe hierarchical deterministic nodes for ED25519 elliptic curve cryptography.
  • CKDPriv function implementation to permit Private parent key --> Private child key derivation.
  • ExtendedKey class to add an abstraction layer for actual keys, higher level layer for working with extended keys.
  • MnemonicPassPhrase class to describe BIP39 mnemonic pass phrases.
  • Wallet class to describe hierarchical deterministic wallets compatible with nem2-sdk classes Account and PublicAccount.
  • MACType enumeration with HMAC and KMAC.
  • MACImpl class with create method to accept MACType.KMAC or MACType.HMACand create message authentication code accordingly.
  • HMAC method implementation in Cryptography class.
  • KMAC method implementation in Cryptography class.

The current implementation is open for suggestions. The library is now BIP32-compatible and allows generating hardened-only ED25519 extended keys.

Ongoing Work

  • Replace HMAC for KMAC with https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf
  • Implement Wallet interface
  • Implement Wallet creation out of extended private key hexadecimal format
  • Implement KMAC specialized unit tests
  • Add network type and network id in binary extended keys
  • Define relevance of base58 encoding of extended keys for Catapult. [NOT RELEVANT]
  • Integrate QR Code interface using nem2-qr-code when available.
  • Make sure privateKeys can not be leaked through, for example, bad memory management in early draft phase.

Integration

This package should aim at following integration examples:

import {MnemonicPassPhrase, ExtendedKey, Wallet} from 'nem2-hd-wallets';

const mnemonic = MnemonicPassPhrase.createRandom();
const extended = ExtendedKey.fromSeed(mnemonic.toEntropy());
const wallet = new Wallet(extended);

// derive *master* account (not recommended)
const masterAccount = wallet.getAccount();

// derive *default* account (recommended)
const defaultAccount = wallet.getChildAccount(`m/44'/43'/0'/0'/0'`);

References

History

Date Version
Apr 6 2019 Initial Draft
Apr 18 2019 Second Draft
Apr 23 2019 Third Draft
Apr 29 2019 Fourth Draft
May 13 2019 Fifth Draft
You can’t perform that action at this time.