Skip to content
This repository has been archived by the owner on Mar 1, 2019. It is now read-only.

Hd Random and Sequential Key Derivation Schemes

ryan lemmer edited this page Jan 17, 2019 · 2 revisions

Hd Random and Sequential Key Derivation Schemes

Cardano Hd Random Addresses

+--------------------------------------------------------------------------------+
|              Base-58 Encoded CBOR-Serialized Object with CRC*                  |
|                                                                                |
|      DdzFFzCqrhstiVdBdYEAmpLPtSWxFYy...rYcBLq29xJD4xZw16REKyhJC9PFGgPSbX       |
|                                                                                |
+--------------------------------------------------------------------------------+
                                    |
                                    |
                                    v
+--------------------------------------------------------------------------------+
|     Address Root    |     Address Attributes    |           AddrType           |
|                     |                           |                              |
|   Hash (224 bits)   |  Der. Path* + Stake + NM  |  PubKey | (Script) | Redeem  | 
|                     |    (open for extension)   |     (open for extension)     |
+--------------------------------------------------------------------------------+
             |                 |                                           
             |                 |     +----------------------------------+   
             v                 |     |        Derivation Path           |   
+---------------------------+  |---->|                                  |   
|  SHA3-256 >> Blake2b_224  |  |     | ChaChaPoly* AccountIx/AddressIx  |   
|                           |  |     +----------------------------------+   
|  -AddrType                |  |                                          
|  -ASD* (~AddrType+PubKey) |  |     +----------------------------------+   
|  -Address Attributes      |  |     |       Stake Distribution         |   
+---------------------------+  |     |                                  |   
                               |---->|  BootstrapEra | (Single | Multi) |   
                               |     +----------------------------------+   
                               |                                          
                               |     +----------------------------------+   
                               |     |          Network Magic           |   
                               |---->|                                  |   
                                     | Addr Discr: MainNet vs TestNet   |   
                                     +----------------------------------+   

Fig 1.

  • (*) CRC: Cyclic Redundancy Check; sort of checksum, a bit (pun intended) more reliable.

  • (*) ASD: Address Spending Data; Some data that are bound to an address. It's an extensible object with payload which identifies one of the three elements:

    • A Public Key (Payload is thereby a PublicKey)
    • A Script (Payload is thereby a script and its version)
    • A Redeem Key (Payload is thereby a RedeemPublicKey)
  • (*) Derivation Path: Note that there's no derivation path for Redeem nor Scripts addresses!

  • (*) ChaChaPoly: Authenticated Encryption with Associated Data; See RFC 7539 We use it as a way to cipher the derivation path using a passphrase (the root public key).

Hd Random Key Derivation

An initial key is created by some combination of HMAC and Ed25519 cryptographic function from a seed (encoded in the form of mnemonic words) and a passphrase. From this wallet Key, other keys can be derived provided the passphrase is known.

+--------------------------------------------------------------------------------+
|         BIP-39 Encoded 128 bits Seed with CRC a.k.a 12 Mnemonic Words          |
|                                                                                |
|    squirrel material silly twice direct ... razor become junk kingdom flee     |
|                                       +                                        |
|                            Base58-Encoded Passphrase                           |
|                                                                                |
+--------------------------------------------------------------------------------+
              |                                                     
              |                                                    
              v                                                     
+--------------------------+    +-----------------------+
|    Wallet Private Key    |--->|   Wallet Public Key   |
+--------------------------+    +-----------------------+
              |                                            
              | (+ passphrase)                                          
              v                                            
+--------------------------+    +-----------------------+
|   Account Private Key    |--->|   Account Public Key  |
+--------------------------+    +-----------------------+
              |                                            
              | (+ passphrase)                                            
              v                                            
+--------------------------+    +-----------------------+
|   Address Private Key    |--->|   Address Public Key  |
+--------------------------+    +-----------------------+

Fig 2.

Hd Sequential wallets (Ed25519/BIP-44)

Motivation

BIP-0044 Multi-Account Hierarchy for Deterministic Wallets is a Bitcoin standard defining a structure and algorithm to build a hierarchy tree of keys from a single root private key.

It is built upon BIP-0032 and is a direct application of BIP-0043. It defines a common representation of addresses as a multi-level tree of derivations:

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

Paths with a tilde ' refer to BIP-0032 hardened derivation path (meaning that the private key and passphrase are required to derive children). This leads to a couple of interesting properties:

  1. New addresses (change or not) can be generated from an account's public key alone.
  2. The derivation of addresses is done sequentially / deterministically.
  3. If an account private key is compromised, it doesn't compromise other accounts.

This allows for external keystores and off-loading of key derivation to some external source such that a wallet could be tracking a set of accounts without the need for knowing private keys. This approach is discussed more in details below.

NOTE:
One other important aspect is more of a security-concern. The introduction of such new address scheme makes it possible to change the underlying derivation function to a new better one with stronger cryptographic properties. This is rather ad-hoc to the structural considerations above, but is still a major motivation.

Differences between *Hd Random *and Sequential Addresses

Because BIP-44 public keys (Hd Sequential) are generated in sequence, there's no need to maintain a derivation path in the address attributes (incidentally, this also makes addresses more private). Instead, we can generate pools of addresses up to a certain limit (called address gap) for known accounts and look for those addresses during block application.

We end up with two kind of Cardano addresses:

Address V1 (Hd Random) Address V2 (Icarus, Hd Sequential)
Uses ed25519@V1 (buggy) curve impl for derivation Uses ed25519@V2 curve impl for derivation
Has a derivation path attribute Has no derivation path attribute
New address indexes are random New address indexes are sequential
Need root private key and passphrase to create addresses Need only parent account public key to create addresses
Root keys are obtained from 12-word mnemonic phrases Root keys are obtained from 15-word mnemonic phrases

Although the idea behind the BIP-44 protocol is to be able to have the wallet working in a mode where the wallet doesn't know about the private keys, we still do want to preserve compatibility with the existing address scheme (which can't work without knowing private keys).

This leaves us with three operational modes for a wallet:

  • Compatibility Mode with private key: In this mode, addresses are derived using the wallet root private key and the classic derivation scheme. New address indexes are generated randomly.

  • Deterministic Mode without private key: Here, we review the definition of a wallet down to a list of account public keys with no relationship whatsoever from the wallet's point of view. New addresses can be derived for each account at will and discovered using the address pool discovery algorithm described in BIP-44. Public keys are managed and provided from an external sources.

  • Deterministic Mode with private key: This is a special case of the above. In this mode, the wallet maintain the key hierarchy itself, and leverage the previous mode for block application and restoration.

Those operational modes are detailed in more depth here below. Note that we'll call external wallets wallets who don't own their private key.

Differences between Cardano Hd Sequential and BIP-44

In BIP-44, new derivation paths are obtained by computing points on an elliptic curve where curve parameters are defined by secp256k1. Cardano's implementation relies on ed25519 for it provides better properties in terms of security and performances.

Miscellaneous derivations

When interacting with the Cardano wallet, the following constructions/transformations/derivations may be useful.

The following snippets can be run in an interactive session by running stack ghci in the cardano-wallet repo.

Constructing a Mnemonic from a string of words

ghci> import Cardano.Mnemonic
ghci> import Pos.crypto

let m = mkMnemonic @24 ["boing", "display", "room", "void", "indicate", "stamp", "present", "exist", "extend", "unable", "manage", "exotic", "cute", "private", "undo", "lottery", "depth", "valve", "submit", "episode", "obvious", "grain", "aim", "onion"]

aMnemonic = fromRight (error "mnemnonic error!") m

Notes: the above example is not a valid mnemonic

Derive ESK from Mnemonic

Hd Random derivation scheme

let seedRnd = mnemonicToSeed aMnemonic
let eskRnd = snd $ safeDeterministicKeyGen seedRnd emptyPassphrase -- (PK,ESK)

Hd Sequential derivation scheme

Here, we use an empty mnemonic password "" (a ByteString):

ghci> :module +Cardano.Wallet.Kernel.Ed25519Bip44
let eskSeq = genEncryptedSecretKey (aMnemonic, "") emptyPassphrase

Derive an Account PublicKey from ESK

Hd Random derivation scheme

Here, we derive the public key for account index 0

ghci> import Cardano.Wallet.Kernel.Ed25519Bip44

let accPrivKRnd = Universum.fromMaybe (error "oops") $ deriveAccountPrivateKey emptyPassphrase eskRnd 0

let accPubKRnd = derivePublicKey accPrivKRnd

Hd Sequential derivation scheme

Here, we derive the public key for account index 0 (since accounts are "hardened", we use firstHardened) and address index 0:

let accPrivKSeq = Universum.fromMaybe (error "oops") $ deriveAccountPrivateKey emptyPassphrase eskRnd firstHardened

let accPubKSeq = derivePublicKey accPrivKSeq

Format a PublicKey as Base64

When using the API, we need to use Base64 encoded Public Keys. A Public Key also contains a ChainCode, e.g.

PublicKey (XPub { xpubPublicKey =           "\248\248\...\200"
                , xpubChaincode = ChainCode "\246-^F\...\217xOl7\""})

We need to concatenate the xpubPublicKey and xpubChaincode ByteStrings and then format this combination:

ghci> import Serokell.Util.Base64 (formatBase64)

formatBase64 <ADDR BYTE STRING>

Format an Address as Base58

When using the API, we need to use Base58 encoded Addresses.

To convert from an address string to an address:

let addr =  fromRight (error "oops") $ Pos.Core.decodeTextAddress <SOME ADDR String>

Then we convert from Address to Base58 string:

Pos.Core.addrToBase58 addr