New cipher seed scheme implemented for improved wallet recovery
New cipher seed scheme implemented for improved wallet recovery
Release 0.13.0 of Tari LibWallet introduces a new cipher seed scheme to be used for wallet seed phrase backup and recovery. This scheme is inspired by the aezeed scheme pioneered by the Lightning Network.
The previous scheme that Tari employed was a simple BIP39 based scheme, where a 24 word seed phrase was employed. BIP39 defines a number of mnemonic word lists used to encode the seed data. These word lists are carefully chosen so that the included words are unambiguous in their respective language. The lists consist of 2048 words which means a single word can encode 11 bits of data. This means that a 24 word seed phrase can encode 33 bytes of data. The previous implementation of the seed phrase encoded 256-bit of entropy, from which a wallet’s private keys were generated and the few remaining bits were used as a checksum to aid in validating if the seed phrase was entered correctly. This seed phrase is extremely sensitive because every single secret key that the wallet generates can be derived from it.
The new cipher seed scheme provides three new features: versioning, included wallet birthday and encryption. The versioning allows the seed phrase scheme to be upgraded in the future while maintaining support for previous iterations. Including the wallet birthday in the seed phrase allows us to improve wallet recovery times. Now when you enter your seed phrase the wallet will not have to scan the entire blockchain to be sure it has recovered all your funds. The wallet can use the included birthday to only scan the blockchain from that point forwards. Finally, the new scheme provides the ability for the sensitive data encoded within the seed phrase to be encrypted with a passphrase making it much safer in the case that your seed phrase is compromised.
Our implementation of the aezeed scheme differs only in the encryption cipher we use. The aezeed scheme uses the fairly new aez AEAD cipher scheme. This scheme is an arbitrary input length block cipher that allows for a custom MAC length. Unfortunately, there is not an audited Rust implementation of this scheme available and as such we implemented our version of this scheme using the audited primitives provided by the RustCrypto project.
The cipher seed scheme uses a 24 word seed phrase which gives us 33 bytes to work with. The unencrypted data included in scheme is as follows:
Field | Size |
---|---|
version | 1 byte |
birthday | 2 bytes |
entropy | 16 bytes |
MAC | 5 bytes |
salt | 5 bytes |
checksum | 4 bytes |
The birthday value will be expressed as the number of days that have passed since the Unix Epoch. This gives this scheme the lifetime of another 127 years. The entropy used is the same as is provided by a traditional 12 word BIP39 seed phrase. The MAC is used to provide the AEAD properties of the scheme. It is used to verify that the decrypted data is in fact the same data that was originally encrypted with the passphrase. It also gives some ability to confirm that the plaintext associated data is the same as was present during the initial encryption.
The MAC is calculated in the MAC-then-Encrypt configuration, as is used in SSL, and is calculated as
Hash(birthday||entropy||version||salt||passphrase)
. The MAC is first calculated and then appended to birthday and
entropy data. That data is then encrypted using the user provided passphrase. The salt and version data are included as
plain text associated data. The salt is used as a nonce for the encryption process and also as salt for deriving the
encryption key from the user’s passphrase using Argon2. If a passphrase is not
provided by the user then a default string is used. Due to the fact that we have little space to spare in our scheme we
couldn’t use a block cipher, like AES, which requires padding. Instead, we employ the ChaCha20 stream cipher for our
encryption. Finally, the checksum is a CRC32 calculated using version || encrypted data || salt
as input and is
appended to thatdata to produce the 33 byte set of data that is encoded using the BIP39 mnenomic word lists. The final
encrypted data looks as follows:
Field | Size |
---|---|
version | 1 byte |
ciphertext | 23 bytes |
salt | 5 bytes |
checksum | 4 bytes |