Skip to content

Instantly share code, notes, and snippets.

Last active May 18, 2020 03:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonasschnelli/245f35894f6ff585b3f3d33c6f208991 to your computer and use it in GitHub Desktop.
Save jonasschnelli/245f35894f6ff585b3f3d33c6f208991 to your computer and use it in GitHub Desktop.

  BIP: ??? (tbr after sending to mailing list)
  Layer: Applications
  Title: Cipherseed – encrypted wallet seed
  Author: Jonas Schnelli <>
  Comments-Summary: No comments yet.
  Status: Draft
  Type: Standards Track
  Created: 2018-06-04
  License: BSD-2-Clause

Table of Contents



This document proposes an encrypted wallet seed format including metadata called Cipherseed


This BIP is licensed under the 2-clause BSD license.


BIP0039, a widely used seed scheme defined in [1] lacks of the following properties:

  • No encryption (only salt based derivation)
  • Mnemonic implies that users can (and eventually should) memorize the words
  • Impossible to change the derivation „passphrase“ after creation
  • Lack of versioning
  • Lack of seed birthday
  • Lack of seed type
  • Non effective KDF
This proposal introduces a new seed format for 128bit or 256bit entropy that adds those missing properties while not completely loosing the plausible deniability feature.

This proposal is heavily inspired by LNDs aezeed [2].


The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119[3].


As KDF, PBKDF2 SHA2_HMAC_512 with 20000 rounds and a random 5 byte salt MUST be used


ChaCha20 with 256bit keys MUST be used for encryption.

Poly1305 MUST be used to produce two 32bit MAC tags.

The first ChaCha20 key k0 MUST be derived with KDF(userpassphrase0) (first 256 bits of the output).

The optional second ChaCha20 key k1 MUST be derived with KDF(userpassphrase1) (first 256 bits of the output). If userpassphrase1 has not been provided, 256 secure random bit for k1 MUST be used (no KDF execution for k1).

The first Poly1305 key mac_k0 MUST be derived in context0 with ChaCha20_Encrypt(nonce=0, data=k0, counter=0).

The first Poly1305 key mac_k1 MUST be derived in context1 with ChaCha20_Encrypt(nonce=0, data=k1, counter=0).

Encryption of the header and the seed MUST be done in context0 with ChaCha20_Encrypt(nonce=1, data=k0, counter=1)

The first Poly1305 authentication tag MUST be calculated by Poly1305(k=mac_k0, data=(salt || encrypted_header[type,birthsday] || encrypted_seed))

The second Poly1305 authentication tag MUST be calculated by Poly1305(k=mac_k1, data=(salt || encrypted_header[type,birthsday] || encrypted_seed))

Before decrypting the seed, the MAC tags MUST be verified and only proceed with the decryption if one of the two tags are valid (authenticated).

Seed Metadata (header)

  • 1 byte seed usage-type (scripts restriction and future extensions)
  • 2 byte key birthday timestamp in days since genesis (until year 2188)

Seed usage-type

Value Type Description
0x00 USAGE_TYPE_UNKNOWN Unknown usage type
USAGE_TYPE_BIP44_P2PKH The seed is only used to derive P2PKH scripts after BIP44[4].
USAGE_TYPE_BIP44_P2WPKH The seed is only used to derive P2WPKH scripts after BIPXXX.
USAGE_TYPE_BIP44_P2SH_P2WPKH The seed is only used to derive P2SH(P2WPKH) scripts after BIP49[5].
USAGE_TYPE_BIP32_P2PKH The seed is only used to derive P2PKH scripts after BIP32[6] (m/i'/0|1/k).
USAGE_TYPE_BIP32_P2WPKH The seed is only used to derive P2WPKH scripts after BIP32.
USAGE_TYPE_BIP32_P2SH_P2WPKH The seed is only used to derive P2SH(P2WPKH) scripts after BIP32.

Plausible deniability

In Cipherseed, we append two 32bit truncated Poly1305 MAC tags, each produced with a user defined key, stretched with the KDF. The MAC covers (salt || header[type,birthsday] || seed).

If only one key will be provided by the user during seed encryption, the second key MUST be set with secure random bytes.

The order of the appended tags MUST be random (also if only one passphrase was provided).

The usage of two MAC tags will result in having two "valid", indistinguishable passphrases and thus two valid and indistinguishable plaintext seeds.

The practical usage of the plausible deniability feature is in general questionable. Allowing more then two keys will very likely not increase the robustness against such attacks.


Length Name Description
4 byte version byte & salt 1 version bit, must be zero, 31 bits KDF salt that make precomputation tables (rainbow-tables) for KDF impracticable
16 or 32 bytes
ciphertext-seed Encrypted 128bit or 256bit seed
1 bytes
ciphertext-usage-type Encrypted usage type (see table below)
2 bytes
ciphertext-birthday Encrypted birthday in days since genesis
4 bytes
MAC tag 0 MAC tag done with either key0 or key1 (random order)
4 bytes
MAC tag 1 MAC tag done with other key then MAC tag 0

  • = Total 33/49 bytes
  • 31 bytes == 248 bits == 50 base32 chars (without checksum/hrp)
  • 47 bytes == 375 bits == 75 base32 chars (without checksum/hrp)



The encrypted seed MUST be encoded with Bech32X into a 82 character (50 chars base32, 26 chars checksum, 3 chars hrp+separator) resp. 104 character string.

Bech32X is an error correction code optimised for strings with a maximum length of 341 characters with a 26 characters checksum that can correct up to 7 errors (8.86% resp. 6.73% in case of a Cipherseed).

Bech32X is defined in BIPXXXX

Optimizing the notation of Cipherseeds

An example Cipherseed string could be xp1kc8s5qhz4rsgv54z9a92yla4m2yrsqdlwdl7dvwkuh3zrg66z8ad2snf832tgaxcuv3kmwtnkj2q3a03ky0kg8p7dvv4czpjqg9trlw9.

Applications supporting Cipherseed MUST show Cipherseeds in a block of 22 times 5 chars. Missing characters can be identified easier with that method.

1. xp1kc   12. 2snf8
2. 8s5qh   13. 32tga
3. z4rsg   14. xcuv3
4. v54z9   15. kmwtn
5. a92yl   16. kj2q3
6. a4m2y   17. a03ky
7. rsqdl   18. 0kg8p
8. wdl7d   19. 7dvv4
9. vwkuh   20. czpjq
10. 3zrg6  21. g9trl
11. 6z8ad  22. w9

Uppercase characters are possible and will be transformed to lowercase within the Bech32X deserialization.

Applications that support importing of Cipherseeds MAY help during the import phase in reducing the typeable charset to the Bech32X charset.

Tests have shown that writing down a 256bit entropy Cipherseed takes no longer then writing down a 24-word BIP39 mnemonic. The process of importing a Cipherseed may take a couple of seconds longer since auto-completing the string-chunks are not possible


  • Only new software will be able to use this format.
  • There is no interoperability with BIP39

Reference implementation


  • Gregory Maxwell for the idea of using two MAC tags to allow a form of plausible deniability
  • Olaoluwa Osuntokun for specifying Aezeed in LND


  1. ^ BIP0039
  2. ^ LNDs aezeed
  3. ^ RFC 2119
  4. ^ BIP0044
  5. ^ BIP0049
  6. ^ BIP0032

Copy link

andronoob commented May 17, 2020

The "seed birthday" should be taken more care of, otherwise this feature could turn into a bug, if the encoded birthday is later than the actual birthday.

Edit: even if address reuse has been discouraged since the Satoshi era, it's never forbidden. Therefore, the seed birthday will never be reliable in my opinion, since even encountering the first address during the scanning cannot rule out the possiblity of an earlier received transaction.

Also, it's not quite unusual to see system clock to be disturbed - like, booting from different OSes like Windows and Ubuntu, which have different default system clock policy - both have the ability to automatically sync the clock with NTP servers, but by default, Windows uses local time, while Ubuntu uses UTC time.

Copy link

The “lack of seed type/version" is actually against the universality of BIP39. BIP39 is not only for Bitcoin. It's not only for Base58 P2PKH (or Bech32 P2WPKH) either.

At least I could speak for my self that I don't like to create "a seed for native segwit" "a seed for legacy 1-address" "a seed for litecoin" "a seed for Electrum" "a seed for classical wallet X"...

It seems really good for me that BIP39 makes one backup good once and for all.

Copy link

Encryption of seed doesn't seem very useful to me, either. The seed itself is already a form of cryptographic key. Encrypting the seed with another key won't eliminate the problem that "losing the key is losing the bitcoins". The user still has to take care of the encryption key - see, there are more than one key that should be taken care of!

Copy link

@gmaxwell had once pointed out that BIP39 doesn't really have plausible deniability, since different passphrases are inherently linked together with one common mnemonic words. I wonder whether cipherseed has similar problem? If so, how could it be considered better or improved comparing to BIP39?

Copy link

This is a really old document. Why did it just start getting comments?

Copy link

@gmaxwell Sorry for disturbance. It was mentioned here: spesmilo/electrum#6155 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment