|
| 1 | +``` |
| 2 | +BIP: ? |
| 3 | +Layer: Applications |
| 4 | +Title: Compact encryption scheme for non-seed wallet data |
| 5 | +Author: // TBD |
| 6 | +Comments-Summary: No comments yet. |
| 7 | +Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-???? |
| 8 | +Status: Draft |
| 9 | +Type: Informational |
| 10 | +Created: 2025-08-22 |
| 11 | +License: BSD-2-Clause |
| 12 | +``` |
| 13 | + |
| 14 | +## Introduction |
| 15 | + |
| 16 | +### Abstract |
| 17 | + |
| 18 | +This BIP defines a compact encryption scheme for **wallet descriptors** (BIP-0380), |
| 19 | +**wallet policies** (BIP-0388), **labels** (BIP-0329), and |
| 20 | +**wallet backup metadata** (json). The payload must not contains any private key material. |
| 21 | +This scheme enables users to outsource long‑term storage to untrusted media or cloud |
| 22 | +services without revealing which addresses, scripts, or number of cosigners are involved. |
| 23 | +Encryption keys are derived from the lexicographically‑sorted public keys inside the |
| 24 | +descriptor or policy, so any party who already holds one of those keys can later decrypt |
| 25 | +the backup without extra secrets or round‑trips. The format uses AES-GCM-256 with a 96‑bit |
| 26 | +random nonce and a 128‑bit authentication tag to provide confidentiality and integrity. |
| 27 | +While initially designed for descriptors and policies, the same scheme encrypts labels |
| 28 | +and backup metadata, allowing a uniform, vendor‑neutral, and future‑extensible backup format. |
| 29 | + |
| 30 | +### Copyright |
| 31 | + |
| 32 | +This BIP is licensed under the BSD 2-Clause License. |
| 33 | +Redistribution and use in source and binary forms, with or without modification, are |
| 34 | +permitted provided that the above copyright notice and this permission notice appear |
| 35 | +in all copies. |
| 36 | + |
| 37 | +### Motivation |
| 38 | + |
| 39 | +In practice, losing the **wallet descriptor** (or **wallet policy**) is often **as |
| 40 | +catastrophic as losing the wallet’s seed** itself. While the seed grants the |
| 41 | +ability to sign, the descriptor grants a map to the coins. In multisig or |
| 42 | +miniscript contexts, keys alone are **not sufficient** for recovery: without the |
| 43 | +original descriptor the wallet cannot reconstruct the script. |
| 44 | + |
| 45 | +Offline storage of descriptors has two practical obstacles: |
| 46 | + |
| 47 | +1. **Descriptors are hard to store offline.** |
| 48 | + Descriptor string representation can be far longer than a 12/24-word seed phrase. |
| 49 | +Paper, steel, and other long-term analog media quickly become impractical for such |
| 50 | +lengths, or error-prone to transcribe. |
| 51 | + |
| 52 | +2. **Online redundancy carries privacy risk.** |
| 53 | + Keeping backups on USB thumb-drives, computers, phones, or (worst) cloud drives |
| 54 | +avoids the first problem but amplifies surveillance risk: anyone who gains these |
| 55 | +**plaintext descriptors** learns the wallet’s public keys, script structure, |
| 56 | +etc... Even with encryption at the cloud provider, an attacker or a subpoena can |
| 57 | +compel access, and each extra copy multiplies the attack surface. |
| 58 | + |
| 59 | +These constraints lead to an acute need for an **encrypted**, and |
| 60 | +ideally compact backup format that: |
| 61 | + |
| 62 | +* can be **safely stored in multiple places**, including untrusted on-line services, |
| 63 | +* can be **decrypted only by intended holders** of specified public keys, |
| 64 | + |
| 65 | +See the original [Delving post](https://delvingbitcoin.org/t/a-simple-backup-scheme-for-wallet-accounts/1607/31) |
| 66 | +for more background. |
| 67 | + |
| 68 | +### Expected properties |
| 69 | + |
| 70 | +* **Encrypted**: this allows users to outsource its storage to untrusted parties, |
| 71 | +for example, cloud providers, specialized services, etc. |
| 72 | +* **Has access control**: decrypting it should only be available to the desired |
| 73 | +parties (typically, a subset of the cosigners). |
| 74 | +* **Easy to implement**: it should not require any sophisticated tools. |
| 75 | +* **Vendor-independent**: it should be easy to implement using any hardware signing |
| 76 | +device. |
| 77 | + |
| 78 | +### Scope |
| 79 | + |
| 80 | +The primary motivation of this proposal is to store a wallet descriptor(BIP-0380) or a |
| 81 | +wallet policy(BIP-0388), but it seems valuable enough to also use this scheme to encrypt |
| 82 | +payload containing others wallet-related metadata, like Labels(BIP-0329) or |
| 83 | +[wallet backup](https://github.com/pythcoiner/wallet_backup). |
| 84 | + |
| 85 | +Note: For any kind of payload intented to be encrypted with this scheme, private key |
| 86 | +material MUST be removed before encryption. |
| 87 | + |
| 88 | +## Specification |
| 89 | + |
| 90 | +Note: in the followings sections, the operator ⊕ refers to the bitwise XOR operation. |
| 91 | + |
| 92 | +### Secret generation |
| 93 | + |
| 94 | +- Let $p_1, p_2, \dots, p_n$, be the public keys in the descriptor/wallet policy, in increasing lexicographical order |
| 95 | +- Let $s = \textrm{sha256("BEB_DECRYPTION_SECRET"} \| p_1 \| p_2 \| \dots \| p_n\textrm{)}$ |
| 96 | +- Let $s_i = \operatorname{sha256}(``\textrm{BEB_INDIVIDUAL_SECRET}" \| p_i)$ |
| 97 | +- Let $c_i = s \oplus s_i $ |
| 98 | + |
| 99 | +### AES-GCM Encryption |
| 100 | + |
| 101 | +* let `nonce` = random() |
| 102 | +* let `ciphertext` = aes_gcm_256_encrypt(`payload`, `secret`, `nonce`) |
| 103 | + |
| 104 | +### AES-GCM Decryption |
| 105 | + |
| 106 | +In order to decrypt the payload of a backup, the owner of a certain public key p |
| 107 | +computes: |
| 108 | + |
| 109 | +* let `si` = sha256("BEB_INDIVIDUAL_SECRET" ‖ `p`) |
| 110 | +* for each `individual_secret_i` generate `reconstructed_secret_i` = |
| 111 | +`individual_secret_i` ⊕ `si` |
| 112 | +* for each `reconstructed_secret_i` process `payload` = |
| 113 | +aes_gcm_256_decrypt(`ciphertext`, `secret`, `nonce`) |
| 114 | + |
| 115 | +Decryption will succeed if and only if **p** was one of the keys in the |
| 116 | +descriptor/wallet policy. |
| 117 | + |
| 118 | +### Encoding |
| 119 | + |
| 120 | +The encrypted backup must be encoded as follows: |
| 121 | + |
| 122 | +`MAGIC` `VERSION` `DERIVATION_PATHS` `INDIVIDUAL_SECRETS` `CONTENT` `ENCRYPTION` |
| 123 | +`ENCRYPTED_PAYLOAD` |
| 124 | + |
| 125 | +#### Magic |
| 126 | + |
| 127 | +`MAGIC`: 3 bytes which are ASCII/UTF-8 representation of **BEB** (`0x42, 0x45, |
| 128 | +0x42`). |
| 129 | + |
| 130 | +#### Version |
| 131 | + |
| 132 | +`VERSION`: 1 byte unsigned integer representing the format version. The current |
| 133 | +specification defines version `0x01`. |
| 134 | + |
| 135 | +#### Derivation Paths |
| 136 | + |
| 137 | + Note: the derivation-path vector should not contain duplicates. |
| 138 | + Derivation paths are optional; they can be useful to simplify the recovery process |
| 139 | +if one has used a non-common derivation path to derive his key. |
| 140 | + |
| 141 | +`DERIVATION_PATH` follows this format: |
| 142 | + |
| 143 | +`COUNT` |
| 144 | +`CHILD_COUNT` `CHILD` `...` `CHILD` |
| 145 | +`...` |
| 146 | +`CHILD_COUNT` `CHILD` `...` `CHILD` |
| 147 | + |
| 148 | +`COUNT`: 1-byte unsigned integer (0–255) indicating how many derivation paths are |
| 149 | +included. |
| 150 | +`CHILD_COUNT`: 1-byte unsigned integer (1–255) indicating how many children are in |
| 151 | +the current path. |
| 152 | +`CHILD`: 4-byte big-endian unsigned integer representing a child index per BIP-32. |
| 153 | + |
| 154 | +#### Individual Secrets |
| 155 | + |
| 156 | +At least one individual secret must be supplied. |
| 157 | + |
| 158 | +The `INDIVIDUAL_SECRETS` section follows this format: |
| 159 | + |
| 160 | +`COUNT` |
| 161 | +`INDIVIDUAL_SECRET` |
| 162 | +`INDIVIDUAL_SECRET` |
| 163 | + |
| 164 | +`COUNT`: 1-byte unsigned integer (1–255) indicating how many secrets are included. |
| 165 | +`INDIVIDUAL_SECRET`: 32-byte serialization of the derived individual secret. |
| 166 | + |
| 167 | +#### Content |
| 168 | + |
| 169 | +`CONTENT`: 1-byte unsigned integer identifying what has been encrypted. |
| 170 | + |
| 171 | +| Value | Definition | |
| 172 | +|:-------|:---------------------------------------| |
| 173 | +| 0x00 | Undefined | |
| 174 | +| 0x01 | BIP-0380 Descriptor (string) | |
| 175 | +| 0x02 | BIP-0388 Wallet policy (string) | |
| 176 | +| 0x03 | BIP-0329 Labels (JSONL) | |
| 177 | +| 0x04 | Wallet backup (JSON) | |
| 178 | + |
| 179 | +#### Encrypted Payload |
| 180 | + |
| 181 | +`ENCRYPTED_PAYLOAD` follows this format: |
| 182 | + |
| 183 | +`TYPE` `NONCE` `LENGTH` `CIPHERTEXT` |
| 184 | + |
| 185 | +`TYPE`: 1-byte unsigned integer identifying the encryption algorithm. |
| 186 | + |
| 187 | +| Value | Definition | |
| 188 | +|:-------|:---------------------------------------| |
| 189 | +| 0x00 | Undefined | |
| 190 | +| 0x01 | AES-GCM-256 | |
| 191 | + |
| 192 | +`NONCE`: 12-byte nonce for AES-GCM-256. |
| 193 | +`LENGTH`: [compact |
| 194 | +size](https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer) |
| 195 | +integer representing ciphertext length. |
| 196 | +`CIPHERTEXT`: variable-length ciphertext. |
| 197 | + |
| 198 | +Note: `CIPHERTEXT` is followed by the end of the `ENCRYPTED_PAYLOAD` section. |
| 199 | +Compliant parsers MUST stop reading after consuming `LENGTH` bytes of ciphertext; |
| 200 | +additional trailing bytes are reserved for vendor-specific extensions and MUST |
| 201 | +be ignored. |
| 202 | + |
| 203 | +## Rationale |
| 204 | + |
| 205 | + - Why derivation paths are optional: When standard derivation paths are used, they are |
| 206 | + easily discoverable, making them straightforward to brute-force. Omitting them |
| 207 | + enhances privacy by reducing the information shared publicly about the descriptor |
| 208 | + scheme. |
| 209 | + |
| 210 | +- Why avoid including fingerprints in plaintext encoding: Including fingerprints leaks |
| 211 | +direct information about the descriptor participants, which compromises privacy. |
| 212 | + |
| 213 | + |
| 214 | +### Future Extensions |
| 215 | + |
| 216 | +The version field enables possible future enhancements: |
| 217 | + |
| 218 | +- Additional encryption algorithms |
| 219 | +- Support for threshold-based decryption |
| 220 | + |
| 221 | +### Implementation |
| 222 | + |
| 223 | +- rust [implementation](https://github.com/pythcoiner/encrypted_backup) |
| 224 | + |
| 225 | +### Test Vectors |
| 226 | + |
| 227 | +See rust implementation [tests](https://github.com/pythcoiner/encrypted_backup/blob/3280f6f9706497671f08d9365414315159080a84/src/ll.rs#L511) |
| 228 | + |
| 229 | +## Acknowledgements |
| 230 | + |
| 231 | +// TBD |
0 commit comments