Skip to content

Repository files navigation

BED — Bitcoin Encrypted Backup

License: MIT

A small Rust + Svelte service that encrypts a Bitcoin multisig descriptor using any cosigner xpub, producing a .bed file (binary, ASCII-armored, or QR PNG) that can only be decrypted by someone holding that same xpub.

Implements the draft BIP "Bitcoin Encrypted Backup" (PR bitcoin/bips#1951) and is interoperable with Liana wallet v13+ via the bitcoin-encrypted-backup crate v0.0.2.

TL;DR

  1. Install on StartOS as the BED s9pk.
  2. Open the Tor onion or LAN .local URL the StartOS dashboard provides.
  3. Paste your descriptor → download the .bed (or armored block, or QR).
  4. Distribute one .bed copy per cosigner location.

Golden rule: The cardinal rule of BED — never co-locate a .bed file and a cosigner xpub of the same multisig. If an attacker finds both, they can decrypt the descriptor and learn your full wallet structure. See Threat Model for the complete model.

Usage

Encrypt a descriptor

  1. Open the Cifrar tab.
  2. Paste a multisig descriptor that uses the multipath wildcard <0;1>/*. Descriptors without this wildcard are rejected — see Common Pitfalls.
  3. Click Cifrar. The page returns three outputs simultaneously:
    • A binary .bed file (download).
    • An ASCII-armored block with -----BEGIN BITCOIN ENCRYPTED BACKUP----- headers, copyable to clipboard.
    • A QR PNG of the armored payload (download), if the payload fits within QR ECC-L capacity (~2,900 bytes).
  4. Optionally enable the history toggle. With history enabled the .bed is also saved to /data/encrypted/<YYYYMMDDTHHMMSSZ>-<8hex>.bed for later listing and deletion. Default is OFF — the cleartext descriptor and the .bed are returned to the browser and immediately forgotten.

Decrypt a .bed file

  1. Open the Descifrar tab.
  2. Either paste the armored block or upload the binary .bed file.
  3. Either paste the cosigner xpub (bare, e.g. xpub6...) or upload a file containing it.
  4. Click Descifrar. The recovered descriptor appears with a Copy-to-clipboard button. The descriptor is never persisted on disk.

Wallet exports beyond plain descriptors (since v0.3.0)

Beyond a bare descriptor string, BED also encrypts and decrypts two wallet-export formats round-trip byte-identical:

  • Liana JSON. Paste or load a Liana wallet export (the JSON file with the nested descriptor and metadata). BED detects it by the leading {, encrypts the JSON in full, and on decrypt returns the original JSON intact. Useful when you want a single .bed to reconstruct a Liana wallet (descriptor + metadata) instead of just the descriptor.
  • Sparrow Wallet BIP-329 JSONL. Paste or load a Sparrow labels export (multi-line JSONL where each line is {type, ref, label} per BIP-329). BED detects it by the leading { on the first line, encrypts the JSONL in full, and on decrypt returns the original labels intact — byte for byte, including line endings. The decrypt panel visually distinguishes Sparrow exports from classic / Liana payloads so you know which file to re-import where.

Both formats coexist with the classic descriptor flow. The encrypt file picker accepts .txt, .descriptor, .json, and .jsonl (drag-and-drop also supported).

Workflow example. Recover a multisig wallet from cold storage: recreate the wallet in Liana from the decrypted JSON (descriptor + metadata in one file), then import the decrypted Sparrow JSONL into Sparrow if you also keep label history. Two .bed files cover both the wallet structure and the label history.

Modo BED-Web (single HTML)

Además de la app StartOS/Umbrel, este proyecto distribuye bed.html: un único archivo HTML autocontenido que ejecuta el mismo cifrado/descifrado en cualquier navegador moderno (Chromium y Firefox últimas 2 versiones), sin servidor, sin instalación, y opcionalmente sin conexión a internet.

Cuándo usarlo

Útil cuando:

  • No tienes un servidor StartOS/Umbrel a mano y necesitas cifrar un descriptor puntualmente.
  • Quieres una segunda implementación que verifica que el .bed cifrado por la app StartOS es interoperable.
  • Operas con un airgap estricto: descarga bed.html en una máquina, transfiérelo a la máquina airgapped, y cífralo allí.

Cómo descargarlo y verificarlo

  1. Descarga bed.html del Release asset (inmutable por tag): https://github.com/semillabitcoin/descriptor-cifrado/releases/latest → adjunto bed.html

  2. Verifica el sha256 contra el publicado en la GitHub Release (mismo tag):

    sha256sum bed.html
    

    Compara con el sha256 mostrado en la página del Release. Si no coinciden, NO uses el archivo.

Cómo usarlo

  1. Abre bed.html desde tu sistema de archivos (file://) en Chromium o Firefox.
  2. Para máxima privacidad, desconecta el equipo de internet antes de pegar el descriptor. El archivo no realiza llamadas de red; un test automatizado (Playwright Chromium con fetch/XMLHttpRequest interceptados) verifica esto en cada CI build.
  3. Pega tu descriptor multisig en la pestaña "Cifrar"; obtén los tres outputs (.bed binario, armored, QR).
  4. Para recuperar: pestaña "Descifrar", pega el .bed (o el armored) y la xpub de cualquier cosigner.

Lo que NO protege (importante)

La regla de oro (Golden rule, ver Threat Model) sigue aplicando intacta:

Ninguna ubicación debe contener simultáneamente el .bed y una xpub que pertenezca al descriptor cifrado.

Si descargas bed.html y lo usas en la misma máquina donde guardas las xpubs cosigners, anulas el modelo de amenazas. El cifrado WASM corre el mismo crypto que la app StartOS (bitcoin-encrypted-backup v0.0.2, AES-256-GCM, magic BEB), pero el modelo de amenazas operacional depende de tu disciplina de aislamiento — no del archivo en sí.

Específicamente, bed.html NO protege contra:

  • Compromiso del navegador o del sistema operativo durante una sesión activa de cifrado.
  • Almacenamiento de bed.html y xpubs cosigners en el mismo equipo (rompe la regla de oro).
  • Pérdida simultánea de todas las xpubs cosigners (descifrar requiere AL MENOS una).

Interoperabilidad con la app StartOS/Umbrel

Un .bed producido por bed.html se descifra por la app StartOS/Umbrel y viceversa. CI verifica esta paridad en cada release vía un test Playwright que cruza ambas implementaciones contra la fixture canónica.

Threat Model

What BED protects

  • Descriptor privacy. A .bed file reveals nothing about the wallet's structure, xpubs, derivation paths, or spending policy to an attacker who only has the file. AES-256-GCM authenticated encryption guarantees that any tampering is detected on decryption.
  • xpub distribution. Each cosigner can safely store a .bed backup without exposing the full wallet policy — they only need their own xpub to decrypt it. This enables redundant distribution of the descriptor (the only piece needed to fully recover a multisig wallet) without coupling that distribution to the privacy of the policy.
  • Cleartext-on-disk leakage. The cleartext descriptor never touches disk. It is wrapped in secrecy::SecretString from parse through encryption, zeroized after the operation, and excluded from logs by an explicit tracing skip-all guard. A CI test grep-asserts the descriptor string does not appear in any log output.

What BED does NOT protect against

Golden rule (repeated): The security model requires that you never co-locate a .bed file with any cosigner xpub of the same multisig. If an attacker finds both, they can decrypt the .bed and learn the full wallet structure. Each .bed copy must live in a different physical location than the xpub needed to decrypt it.

  • Compromise of the StartOS device during an active encrypt session. The descriptor passes through the device's memory in cleartext during encryption. If the device is compromised at that moment (malicious app, physical access, kernel-level attacker) the descriptor may be exposed. BED cannot protect against an attacker who controls the execution environment.
  • Loss of all cosigner xpubs simultaneously. If every xpub needed to decrypt is lost or destroyed, the .bed becomes undecryptable. BED provides redundancy for distribution, not a substitute for independent xpub backups.
  • An attacker who already holds one cosigner xpub. A .bed encrypted with xpub A can be decrypted by anyone holding xpub A. The model is sound only when each co-location (.bed + xpub_N) is in a different physical location.
  • Side-channel and traffic analysis. BED runs locally on StartOS over Tor or LAN. It does not defend against advanced traffic-analysis adversaries who already control the local network.
  • Hardware wallet support. USB device support is intentionally omitted (StartOS 0.4.0 does not pass USB into containers). Use a software wallet to extract the descriptor; sign with hardware after.

Model assumptions

  • The StartOS device is trusted during the session.
  • Each .bed copy lives in a different physical location than any xpub that could decrypt it.
  • The .bed file format integrity is guaranteed by AES-256-GCM authentication — any tampering is detected.
  • The cosigner xpub used to encrypt is not also the only one that decrypts; in a 2-of-3 multisig you typically encrypt one .bed per cosigner using a different cosigner's xpub each time.

Crypto Details

Property Value
Encryption AES-256-GCM
Magic header BEB (binary identifier)
KDF Per BIP draft (xpub-derived; see PR for details)
Spec bitcoin/bips PR #1951 — draft
Implementation bitcoin-encrypted-backup crate, pinned at v0.0.2 (rev cd7ee382bf5ca0798d4f81697e2f9efb5e32fe40)
Interop Liana wallet v13+ reads .bed files produced here, and BED reads .bed files produced by Liana
Descriptor requirement Must use the BIP-380 multipath wildcard <0;1>/*. Without it, spending from address 0 exposes the xpub on-chain and breaks the encryption model.

The .bed format is an external contract: BED will not break it without a new milestone. If a future BIP revision changes the format (e.g. ChaCha20-Poly1305 instead of AES-GCM), the old version is archived in CHANGELOG and a separate "BED v0.x archive" branch is kept available for decrypting legacy files.

Common Pitfalls

  1. Descriptor without <0;1>/*. BED rejects single-path descriptors with a typed validation error. Convert your descriptor to multipath form before encrypting.
  2. xpub vs descriptor-style format. The Decrypt tab expects a bare xpub (e.g. xpub6FHa3...). Descriptor-style xpubs with a [fingerprint/path] prefix or a /* suffix are rejected. Strip the prefix and suffix before pasting.
  3. QR size limit (~2,900 bytes ECC-L). Multisig descriptors with five or more cosigners may produce armored payloads that exceed the QR ECC-L capacity. Since v0.3.0 the QR panel degrades cleanly to "QR not available — payload too large"; binary and ASCII-armored downloads remain available for those cases. Use them with cold-storage paper for QR-bound transport.
  4. History mode default is OFF. The history toggle is opt-in. If you cifrar without enabling it, the .bed is returned to the browser and the server forgets it immediately. Enable the toggle explicitly if you want the file persisted to /data/encrypted/.

References

License

MIT. See LICENSE.

About

Encrypted Descriptor (BED) — encrypts Bitcoin multisig descriptors with AES-256-GCM using a cosigner xpub. BIP draft bitcoin/bips#1951 + Liana wallet interop.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages