descriptor-encrypt 0.1.0

Encrypt any Bitcoin wallet descriptor with a matching access control policy
Documentation

descriptor-encrypt

Overview

A rust library and CLI tool that efficiently encrypts a Bitcoin wallet descriptor such that it can only be recovered by a set of keys that can spend the funds.

Features

descriptor-encrypt provides a robust mechanism for encrypting Bitcoin wallet descriptors with a security model that maps directly to the spending conditions of the descriptor itself. Here's what it does in detail:

Threshold-Based Security Model

  • Threshold Authentication: The descriptor's access control policy (e.g., m-of-n multisig) automatically determines the decryption threshold
  • Policy Mirroring: Encryption security policy directly mirrors the descriptor's spending policy - if a wallet requires 2-of-3 keys to spend, it will also require 2-of-3 keys to decrypt

Cryptographic Implementation

  • Deterministic Key Derivation: Master encryption keys are derived deterministically from the descriptor's structure and content
  • Shamir Secret Sharing: Implements recursive Shamir's Secret Sharing to split the master encryption key according to the descriptor's threshold requirements
  • Public Key-Based Access Control: Each share is encrypted with the corresponding public key from the descriptor
  • ChaCha20-Poly1305 Encryption: Uses modern, efficient encryption for both the payload (ChaCha20) and the key shares (ChaCha20Poly1305)

Complex Descriptor Support

  • Full Descriptor Coverage: Supports all Bitcoin descriptor types including:
    • Single-sig (wpkh, pkh)
    • Multi-sig (sorted, unsorted)
    • Complex scripts (Miniscript expressions)
    • Taproot descriptors with internal keys and script paths
  • Nested Threshold Handling: Properly handles nested threshold conditions (e.g., an OR with AND conditions inside it)
  • Time and Hash Lock Support: Maintains time locks and hash locks in the template while encrypting the specific values

Template and Path Extraction

  • Origin Path Extraction: Can extract derivation paths from encrypted descriptors without full decryption
  • Template Generation: Can reveal the structure of an encrypted descriptor (with dummy values) without revealing the actual keys

Compact Encoding

  • Tag-Based Encoding: Uses tag-based encoding to minimize the size of the descriptor template
  • Variable-Length Encoding: Uses LEB128 variable-length integers to minimize the size of the encrypted data

This ensures that a descriptor can only be decrypted by the same keys needed to spend from it, creating a direct correspondence between fund access and descriptor recovery.

Installation

To build the project, use the following command:

cargo build --release

The executable will be located at target/release/descriptor-encrypt.

CLI Usage

descriptor-encrypt is a command-line tool for encrypting and decrypting Bitcoin descriptors.

Commands

  • Encrypt a Descriptor

    Encrypts a Bitcoin descriptor and outputs the result as hex.

    ./target/release/descriptor-encrypt encrypt <DESCRIPTOR_STRING>
    

    Arguments:

    • <DESCRIPTOR_STRING>: The Bitcoin descriptor string to encrypt.

    Options:

    • -w, --with-full-secrecy: Enables full secrecy mode, which leaks no information about key inclusion without full decryption.
  • Decrypt a Descriptor

    Decrypts hex-encoded encrypted descriptor data using a set of public keys.

    ./target/release/descriptor-encrypt decrypt <DATA> -p <PKS>
    

    Arguments:

    • <DATA>: hex-encoded encrypted data.
    • -p, --pks <PKS>: Comma-separated list of public keys and xpubs (e.g., "pk1,pk2,pk3"). At least one public key must be provided.
  • Get Template Descriptor

    Retrieves a template descriptor (with dummy keys, hashes, and timelocks) from hex-encoded encrypted data.

    ./target/release/descriptor-encrypt get-template <DATA>
    

    Arguments:

    • <DATA>: hex-encoded encrypted data.
  • Get Origin Derivation Paths

    Retrieves the origin derivation paths from hex-encoded encrypted data.

    ./target/release/descriptor-encrypt get-derivation-paths <DATA>
    

    Arguments:

    • <DATA>: hex-encoded encrypted data.

Library Usage

The core logic of descriptor-encrypt can also be used as a library in other Rust projects.

Key Functions

encrypt(desc: Descriptor<DescriptorPublicKey>) -> Result<Vec<u8>>

  • Encrypts a descriptor such that it can only be recovered by a set of keys with access to the funds.

encrypt_with_full_secrecy(desc: Descriptor<DescriptorPublicKey>) -> Result<Vec<u8>>

  • Identical to encrypt except it leaks no information about key inclusion without full decryption.
  • Provides maximum privacy but slower to decrypt, as we must try all possible combinations of shares and keys. This has a running time of $O((N+1)^K)$, where $N$ is the number of provided keys and $K$ is the number of shares.

decrypt(data: &[u8], pks: Vec<DescriptorPublicKey>) -> Result<Descriptor<DescriptorPublicKey>>

  • Decrypts an encrypted descriptor using a set of public keys with access to the funds.

get_template(data: &[u8]) -> Result<Descriptor<DescriptorPublicKey>>

  • Returns a template descriptor with dummy keys, hashes, and timelocks from the encrypted data.

get_origin_derivation_paths(data: &[u8]) -> Result<Vec<DerivationPath>>

  • Returns the origin derivation paths found in the encrypted descriptor.

Demo

License

This project is licensed under the CC0-1.0 License.

Author

Joshua Doman joshsdoman@gmail.com