Skip to content

Instantly share code, notes, and snippets.

@KeysSoze
Last active September 15, 2025 22:55
Show Gist options
  • Select an option

  • Save KeysSoze/866d009ccd082edf6802df240154b20d to your computer and use it in GitHub Desktop.

Select an option

Save KeysSoze/866d009ccd082edf6802df240154b20d to your computer and use it in GitHub Desktop.
BIP: Standard Encryption Envelope (Password-Protected)
  BIP: ?
  Layer: Applications
  Title: Standard Encryption Envelope (Password-Protected)
  Author: Keyser Söze <keys.soze@proton.me>
  Status: Draft
  Type: Standards Track
  Created: 2025-09-05
  Licence: BSD-2-Clause
  Requires: ?

Table of Contents

BIP ?: Standard Encryption Envelope (Password-Protected)

Abstract

This BIP defines an encryption envelope for wallet payloads. It specifies the CBOR structure encrypted-wallet, which encapsulates a wallet payload (as defined in BIP-XXXX: Standard Wallet Payload) inside a COSE_Encrypt0 structure, using a password-derived key via Argon2id. The envelope authenticates protected headers and allows future extensibility.

Copyright

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

Terminology

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in RFC2119 as updated by RFC8174 when, and only when, they appear in all capitals.

Motivation

Users require a secure, interoperable method of encrypting wallet backups. By standardising the envelope separately from the payload, multiple cryptographic approaches can evolve without changing the base schema. This BIP mandates the use of BIP-XXXX (Standard Wallet Payload) as the embedded plaintext format.

Rationale and Design Decisions

  • Payload vs. Container: The deliberate separation of the data payload from the security container ensures that the core wallet data format defined in BIP XXX remains simple and interoperable, while allowing implementers to use various security mechanisms as defined in companion BIPs.

Relationship to Other BIPs and Standards

  • BIP-XXXX: Every encrypted-wallet must contain a valid wallet-payload as defined in BIP-XXXX.
  • COSE / RFC-9052: Provides envelope standard for encryption.
  • Argon2id / RFC-9106: Secure password-based key derivation.

Workflow

This BIP defines a clear process for secure wallet backups.
  1. Payload Creation: Creator constructs the Wallet Payload CBOR map with integer keys as per BIP-XXX.
  2. Deterministic Encoding: Payload is rendered into canonical CBOR.
  3. Encryption: Encryptor derives CEK via Argon2id, wraps payload in COSE_Encrypt0.
  4. Transmission / Storage: Encrypted result is saved or transferred.
  5. Restoration: Consumer reads kdf-params, derives CEK, decrypts COSE_Encrypt0, verifies consistency, parses deterministic CBOR Wallet Payload.

Specification

Encryption Container

The wallet payload MUST be encrypted according to the procedures outlined in RFC 9052 for COSE_Encrypt0 structures. All CBOR data, including the plaintext payload and any data used in the AAD, MUST be encoded using the canonical deterministic profile specified in RFC 8949, §4.2.1.

Encryption Envelope Map

This map defines the encrypted container for the wallet. It specifies key derivation parameters and the encrypted payload, which MUST be a COSE_Encrypt0 structure containing the wallet map.
Integer Key String Key Type Description Required
0 version uint The version of the this envelope. The first stable version is 1. Yes
1 kdf-params map Parameters for Argon2id key derivation. See KDF Params Map. Yes
2 ciphertext bstr The encrypted wallet payload, encoded as a CBOR `bstr` containing a COSE_Encrypt0 structure. Yes
3.. - any A reserved extension range. No
Notes
  • The wallet map is always embedded inside the COSE_Encrypt0 ciphertext, never referenced externally.

KDF Params Map

This map specifies Argon2id parameters for deriving the encryption key from a passphrase.
Integer Key String Key Type Description Required
1 kdf_type uint MUST be `1` (indicating Argon2id). Yes
2 salt bstr (min 16 bytes) Unique salt value for key derivation. ≥16 bytes, random, from a CSPRNG, unique per encryption operation and MUST NOT repeat Yes
3 time uint Time cost parameter (iterations). MUST be ≥ 1 (SHOULD be ≥ 3). Yes
4 memory uint Memory in KiB, RECOMMENDED ≥ 65,536 KiB (desktop), ≥ 32,758 KiB (mobile), ≥ 8,192 KiB (constrained) Yes
5 parallelism uint Parallelism degree. MUST ≥ 1, RECOMMENDED ≥ 2 Yes
6 key_length uint If present, MUST equal `32`. Derived key length in bytes. No
7 version uint Argon2d version MUST be 0x13 (RFC9106) No
8.. - any A reserved extension range. No

Key Derivation (Argon2id)

The 32-byte Content Encryption Key (CEK) used for encryption MUST be derived from a user-provided passphrase using Argon2id, as specified in RFC 9106.
  • KDF Parameters: The kdf-params map MUST contain all parameters required to perform the key derivation.
    • kdf_type (uint): MUST be 1 (Argon2id).
    • salt (bstr): MUST be at least 16 bytes. Salt bytes MUST be produced by a CSPRNG and MUST be unique per-encryption operation.
    • time (uint): Argon2 time cost (iterations). MUST be >= 1. RECOMMENDED default values: 3 for interactive devices.
    • memory (uint, KiB): Memory cost in KiB. RECOMMENDED default values:
      • desktop: 65536 (64 MiB)
      • mobile: 32768 (32 MiB)
      • constrained: 8192 (8 MiB)
    • The key_length field, if present, MUST be 32 to match the key size of AES-256-GCM. If absent, a length of 32 bytes is assumed.
    • The version Argon2d version field, if present, MUST be 0x13
  • Authentication of KDF Parameters: To prevent parameter substitution attacks, the complete kdf-params map MUST be included in the AAD. Specifically:
    • The complete kdf-params map MUST be authenticated via inclusion in the COSE external_aad (see COSE Structure and Authentication). This ensures that any modification of Argon2 parameters (e.g. salt, iteration count) is detected by AEAD authentication.
  • Argon2id Invocation:
    • Argon2id parameters SHOULD be chosen conservatively to resist GPU/ASIC attacks. Recommended minimums are specified in the KDF Params Map table.
    • The parameters for Argon2id (salt, time, memory, etc.) MUST be fully specified in the kdf-params map within the encrypted-wallet envelope.
      • Time cost parameter time (iterations) MUST be ≥ 1 but SHOULD be ≥ 3.
      • For resource-constrained devices the time cost MAY be set to the minimum requirement of time >= 1 to ensure a usable and responsive experience. While this setting meets the minimum security threshold, it is strongly RECOMMENDED to use a value of time >= 3 wherever possible to increase resistance against brute-force attacks.

COSE Structure and Authentication

To ensure the integrity of all cryptographic parameters, metadata stored outside the encrypted payload MUST be authenticated using COSE's Associated Authenticated Data (AAD) mechanism.
  • Protected Header: The protected header MUST be a CBOR map containing the following key/value pairs:
    • 1 (alg): 3 (A256GCM). This specifies AES-256-GCM as the encryption algorithm.
    • 5 (iv): The 96-bit (12-byte) nonce MUST be random bits from a CSPRNG and MUST be unique per encryption. Re-encrypting the same payload requires a fresh iv. It is placed in the protected header to prevent modification, which is critical for the security of AEAD ciphers.
  • External AAD: To prevent manipulation of unencrypted metadata, the external_aad for the COSE AEAD operation MUST be constructed. This AAD is the canonical CBOR encoding of a map containing:
    • 1: The registered CBOR tag for the payload.
    • 2: The complete kdf-params map.
  • AEAD Input: Per RFC 9052, the data authenticated by the AEAD algorithm is the Enc_structure, which is a CBOR array constructed as: [ context : "Encrypt0", protected : bstr, external_aad: bstr ]. This process cryptographically binds the key derivation parameters, Wallet Payload CBOR tag and protected headers to the ciphertext, ensuring that any tampering with these components will cause decryption to fail.

Encryption Envelope Data Schema (CDDL)

Below is a tightly-constrained CDDL schema defining the encryption envelope.
; Encryption envelope
encrypted-wallet = {
  0 => 1..255,                              ; Envelope version
  1 => kdf-params,                          ; Argon2id parameters (CBOR map)
  2 => bstr,                                ; COSE_Encrypt0 as CBOR-encoded bstr
  ? 3.. => any,                             ; reserved extension range
}

kdf-params = {
  1 => 1,                                   ; kdf_type: 1 = argon2id
  2 => bstr .size 16..,                     ; salt (min 16 bytes), random from CSPRNG
  3 => uint,                                ; time (iterations) >= 1 (RECOMMEND >= 3))
  4 => uint,                                ; memory (KiB) (>= 8192)
  5 => uint,                                ; parallelism (>=1)
  ? 6 => uint,                              ; output key length (bytes), if absent assume 32
  ? 7 => uint                               ; Argon2d version MUST be 0x13 (19 decimal) per RFC9106
  ? 8.. => any,                             ; reserved extension range
}

Long-Term Security Considerations

While this specification mandates strong, standardised password-based key derivation, implementers SHOULD recognise that the security of the entire wallet payload ultimately depends on the entropy of the user-chosen password. For long-term archival, a weak or moderately strong password may be vulnerable to future advances in computational power that could enable offline brute-force attacks. Implementers SHOULD therefore:
  • Actively warn users about the risks of choosing a weak password.
  • Provide clear guidance on creating high-entropy passphrases.
  • Consider offering more advanced, non-password-based security options where feasible, such as hardware-key-based encryption, for users requiring maximum long-term security. See next section Expanding the Security Model

Expanding the Security Model

The current proposal specifies an encrypted container based on a password using Argon2id. However, the separation between the data payload and the security container is a deliberate design choice that allows for future extensions. This modularity could easily accommodate more advanced security mechanisms, such as:
  • Asymmetric Key Exchange: A source wallet could encrypt the payload using a public key provided by the destination wallet (or a key derived via a Diffie-Hellman exchange). This would create a backup that can only be decrypted by the intended recipient, completely removing the reliance on a shared password.
  • Ephemeral Passwords: For direct transfers, two wallets could negotiate a high-entropy, single-use password that is never stored or seen by the user. This would provide strong security for the transfer without the long-term risks of a user-chosen password.
These advanced methods could be defined as new, standardised container formats in future extensions to this BIP or as separate companion BIPs, without requiring any changes to the core wallet payload itself.

Encoding and Compliance Notes

Deterministic/Canonical CBOR

  • Implementations MUST use the canonical deterministic encoding specified in RFC 8949 §4.2.1 (shortest integer encoding, sorted map keys by byte-wise representation, no indefinite-length items).
  • Implementations MUST reject duplicate map keys (any range).
  • Text strings MUST be UTF-8 NFC, no indefinite-length items anywhere.
  • Floats MUST NOT be used and MUST be rejected.
  • CBOR Tag (registered with IANA) MUST prefix the payload in tagged form.

Validation Algorithm

An implementation that parses a encryption envelope MUST perform the following checks, in the order listed, and MUST reject the envelope if any check fails.

General rules

  1. CBOR indefinite length strings and arrays MUST NOT be used.
  2. CBOR floats MUST NOT be used.
  3. On-the-wire maps MUST use integer keys.

Structural checks

  1. The encrypted-envelope MUST contain exactly one top-level wallet-payload object.

Envelope / KDF checks

  1. An implementation MUST verify that the encrypted envelope and its KDF parameters conform to all requirements specified in the Encryption Container section, including the KDF type (Argon2id), minimum costs, key length, and AAD construction. Implementations MUST reject any envelope that does not satisfy these constraints.

KDF Params Checks

  1. The kdf_type MUST be 1 (Argon2id).
  2. The salt MUST be at least 16 bytes.
  3. The time MUST be >= 1.
  4. The memory cost in KiB MUST be >=8192.

Test Vectors

WORK IN PROGESS. Use test vectors from the Standard Encrypted Wallet Payload as payload data.

Reference Implementation

Below is a skeleton of the Python script that implements encoding, encryption and outputs hex values — intended to be completed later and referenced in a public repo.
WORK IN PROGRESS

(This script, once completed, will reproduce the hex blocks above exactly, given fixed inputs.)

References

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