githubEdit

User Wallet Data Format Encryption Extension

Ty Everett ([email protected])

Abstract

This specification defines the canonical encrypted wrapper for BRC-38 user wallet data exports.

It standardizes a downloadable binary file format, conventionally named wallet.brc39, for password-protected wallet export and import. The design is intended for strong user data portability, backups, archival export, GDPR-style data access, and migration between wallet implementations and storage providers.

BRC-39 uses:

  • the BRC-38 payload as its canonical plaintext inner document

  • the AES-256-GCM symmetric encryption primitive used by BRC-2

  • a portable, versioned binary envelope in the spirit of BRC-78

  • Argon2id as the mandatory password key-derivation function for new exports

Motivation

BRC-38 defines what wallet data must be portable. It does not define how that data should be safely delivered to a user as a single downloadable file.

For real-world portability, the default export artifact must be:

  • encrypted

  • password protected

  • self-describing

  • suitable for offline storage

  • stable enough to move between implementations

This document defines that artifact.

Specification

1. Scope

This specification defines:

  • the canonical encrypted file format for a single-user BRC-38 export

  • password normalization rules

  • key derivation rules

  • authenticated encryption rules

  • file parsing, validation, export, and import behavior

It does not define:

  • secret-sharing or recovery-key workflows

  • multi-file backup sets

  • multi-user containers

  • wallet-runtime key snapshots unrelated to BRC-38

2. Conformance

An implementation conforms to BRC-39 if it can:

  1. serialize a valid BRC-38 payload

  2. encrypt that payload into a valid BRC-39 file

  3. decrypt and validate a valid BRC-39 file

  4. recover the original BRC-38 payload without loss

3. Canonical Export Artifact

The canonical encrypted export artifact:

  • MUST be a binary file

  • SHOULD be named wallet.brc39

  • SHOULD use media type application/vnd.brc39.wallet

The inner plaintext payload MUST be a complete BRC-38 document serialized as canonical UTF-8 JSON.

4. Cryptographic Profile

4.1 Plaintext

The plaintext to be encrypted is:

  • the exact UTF-8 byte sequence of a valid canonical BRC-38 JSON document

4.2 Password Encoding

Before key derivation, the user password MUST be:

  1. interpreted as a Unicode string

  2. normalized to NFC

  3. encoded as UTF-8 bytes

No additional trimming, case folding, or whitespace normalization is permitted.

4.3 Key Derivation

New BRC-39 exports MUST use Argon2id.

The canonical derived-key length is:

  • 32 bytes

The canonical default Argon2id parameters are:

  • iterations: 7

  • memoryKiB: 131072

  • parallelism: 1

  • hashLength: 32

  • saltLength: 32

These defaults align with the Wallet Toolbox Argon2id defaults this specification is intended to standardize.

Implementations MAY use stronger Argon2id parameters, but they MUST encode the exact parameters used in the file header.

4.4 Authenticated Encryption

The content-encryption algorithm is AES-256-GCM.

The content-encryption key is the 32-byte Argon2id-derived key.

The file nonce length is:

  • 32 bytes

The nonce MUST be generated randomly for each export.

The GCM authentication tag length MUST be 16 bytes.

4.5 Header Validation

Header fields are validated before decryption as specified below. BRC-39 encrypts only the BRC-38 plaintext payload.

5. File Layout

All integers in the binary file MUST be unsigned and encoded in big-endian byte order.

The canonical BRC-39 file layout is:

Field
Length
Description

Magic

4 bytes

ASCII WDAT

Format Version

1 byte

Currently 0x01

Protector Type

1 byte

Currently 0x01 for password-protected export

Inner Format

1 byte

Currently 0x26 (decimal 38) for BRC-38

KDF Type

1 byte

Currently 0x01 = Argon2id

Flags

1 byte

Currently 0x00; all bits reserved and MUST be zero

Salt Length

1 byte

Currently 0x20 (32)

Nonce Length

1 byte

Currently 0x20 (32)

Argon2id Iterations

4 bytes

Work factor

Argon2id Memory KiB

4 bytes

Memory cost

Argon2id Parallelism

1 byte

Parallelism

Argon2id Hash Length

1 byte

Derived key length; currently 32

Reserved

12 bytes

MUST be all zero

Salt

variable

Salt Length bytes

Nonce

variable

Nonce Length bytes

CiphertextAndTag

variable

AES-256-GCM ciphertext followed by 16-byte tag

6. Header Field Requirements

6.1 Magic

The file MUST begin with the ASCII bytes:

which spell WDAT.

6.2 Format Version

The current format version is 1.

Files with unsupported versions MUST be rejected.

6.3 Protector Type

The following protector types are defined:

  • 0x01: password-based encryption

The following values are reserved for future extension:

  • 0x02 to 0xff

This version of BRC-39 standardizes only password-based encryption.

6.4 Inner Format

The Inner Format field identifies the plaintext payload.

For BRC-39 wallet exports:

  • Inner Format MUST be 38

Meaning the decrypted plaintext MUST be a valid BRC-38 document.

6.5 KDF Type

The following KDF type is defined:

  • 0x01: Argon2id

All other KDF type values are reserved for future specifications. This version of BRC-39 defines no alternate KDF import mode.

6.6 Flags

All flag bits are reserved for future specifications.

For this specification:

  • Flags MUST be 0x00

6.7 Reserved Bytes

All reserved bytes MUST be zero on export and MUST be ignored only if zero on import. Non-zero reserved bytes MUST cause rejection.

7. Export Procedure

To create a BRC-39 file, an exporter MUST:

  1. construct a valid BRC-38 document

  2. serialize it as canonical UTF-8 JSON

  3. normalize the user password to NFC and encode it as UTF-8

  4. generate a random 32-byte salt

  5. derive a 32-byte key using Argon2id and the encoded password

  6. generate a random 32-byte nonce

  7. construct the BRC-39 header

  8. encrypt the BRC-38 plaintext using AES-256-GCM with:

    • key = derived key

    • nonce = header nonce

  9. append CiphertextAndTag

  10. emit the resulting bytes as wallet.brc39

8. Import Procedure

To import a BRC-39 file, an importer MUST:

  1. parse and validate the binary header

  2. verify Magic, Format Version, Protector Type, Inner Format, KDF Type, Flags, and reserved bytes

  3. obtain the user password

  4. normalize the password to NFC and encode it as UTF-8

  5. derive the decryption key using the encoded KDF parameters from the file

  6. decrypt CiphertextAndTag with AES-256-GCM using:

    • key = derived key

    • nonce = header nonce

  7. reject the file if GCM authentication fails

  8. parse the plaintext as UTF-8 JSON

  9. validate that the plaintext is a valid BRC-38 document

Only after successful BRC-38 validation may the importer proceed to import wallet data into local storage.

9. KDF Compatibility

BRC-39 standardizes only Argon2id for password-based wallet export encryption.

Implementations MUST reject files whose KDF Type is not 0x01. Compatibility with pre-standard password-derived wallet internals is outside the BRC-39 file format.

10. Relationship to BRC-2 and BRC-78

10.1 BRC-2

BRC-39 uses the same symmetric encryption primitive family as BRC-2:

  • AES-256-GCM

Unlike BRC-2 application messages, BRC-39 stores the nonce explicitly in the binary file header instead of prepending it to a message payload.

10.2 BRC-78

BRC-39 follows the same general design philosophy as BRC-78:

  • binary, versioned, self-describing encrypted envelopes

BRC-78 is oriented toward portable encrypted inter-party messages using BRC-2/BRC-42/BRC-43 derivation. BRC-39 instead targets password-protected at-rest wallet export. This version does not standardize recipient-encrypted wallet exports, but future protector types MAY define such a mode.

11. Validation Rules

A BRC-39 file MUST be rejected if:

  • Magic is not WDAT

  • Format Version is unsupported

  • Protector Type is unsupported

  • Inner Format is not 38

  • KDF Type is not 0x01

  • Flags is not 0x00

  • Salt Length is zero

  • Nonce Length is zero

  • reserved bytes are non-zero

  • any Argon2id parameter is invalid, including:

    • iterations is zero

    • memoryKiB is zero

    • parallelism is zero

    • hashLength is not 32

  • AES-GCM authentication fails

  • the decrypted payload is not valid UTF-8 JSON

  • the decrypted payload is not a valid BRC-38 document

12. Privacy and Operational Requirements

BRC-39 files contain the complete user wallet export, protected only by the chosen password and KDF parameters.

Wallet implementations:

  • MUST warn users that weak passwords materially weaken the file

  • SHOULD encourage long passwords or passphrases

  • SHOULD avoid embedding password hints in the file

  • SHOULD default to Argon2id parameters at least as strong as the canonical defaults

13. Canonical Example Header Semantics

An example canonical new-export header therefore means:

  • file type: wallet.brc39

  • payload type: BRC-38

  • protection mode: password

  • KDF: Argon2id

  • salt: 32 random bytes

  • nonce: 32 random bytes

  • plaintext: canonical UTF-8 BRC-38 JSON

Implementation

An implementation based on Wallet Toolbox can produce a BRC-39 file by:

  1. exporting a full BRC-38 payload for the selected user

  2. deriving a password key with Argon2id using the standardized Toolbox-compatible default parameters

  3. encrypting the payload with AES-256-GCM

  4. writing the standardized binary envelope described above

References

Last updated

Was this helpful?