User Wallet Data Format Encryption Extension
A file containing everything about your wallet is not something to leave lying around unprotected. This defines the encrypted wrapper around such an export, so a backup can be stored safely and restored with a password.
Summary
- Why
- Wallet data needs to leave a wallet as one downloadable file that is safely encrypted, self-describing, and openable by any conforming wallet, not just the one that made it.
- What
- BRC-39 is a binary, password-encrypted file format for exporting and importing a wallet's full BRC-38 data as a single portable file, conventionally named wallet.brc39.
- How
- An exporter serializes the wallet's BRC-38 data as canonical JSON, derives a key from the user's password with Argon2id, encrypts the JSON with AES-256-GCM, and writes it into a versioned binary header plus salt, nonce, and ciphertext.
What this lets you do
- Export a full BRC-38 wallet document as one encrypted binary file
- Protect the export with a user-chosen password run through Argon2id
- Detect tampering or wrong passwords via AES-256-GCM authentication
- Reject malformed or non-conforming files before decryption completes
- Move a wallet's data between different wallet implementations
Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.
Reference for an AI
Everything an assistant needs to answer questions about BRC-39 accurately, including what it depends on.
The specification
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:
- serialize a valid BRC-38 payload
- encrypt that payload into a valid BRC-39 file
- decrypt and validate a valid BRC-39 file
- 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:
- interpreted as a Unicode string
- normalized to NFC
- 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:
57 44 41 54
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:
0x02to0xff
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 FormatMUST be38
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:
FlagsMUST be0x00
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:
- construct a valid BRC-38 document
- serialize it as canonical UTF-8 JSON
- normalize the user password to NFC and encode it as UTF-8
- generate a random 32-byte salt
- derive a 32-byte key using Argon2id and the encoded password
- generate a random 32-byte nonce
- construct the BRC-39 header
- encrypt the BRC-38 plaintext using AES-256-GCM with:
- key = derived key
- nonce = header nonce
- append
CiphertextAndTag - emit the resulting bytes as
wallet.brc39
8. Import Procedure
To import a BRC-39 file, an importer MUST:
- parse and validate the binary header
- verify
Magic,Format Version,Protector Type,Inner Format,KDF Type,Flags, and reserved bytes - obtain the user password
- normalize the password to NFC and encode it as UTF-8
- derive the decryption key using the encoded KDF parameters from the file
- decrypt
CiphertextAndTagwith AES-256-GCM using:- key = derived key
- nonce = header nonce
- reject the file if GCM authentication fails
- parse the plaintext as UTF-8 JSON
- 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:
Magicis notWDATFormat Versionis unsupportedProtector Typeis unsupportedInner Formatis not38KDF Typeis not0x01Flagsis not0x00Salt Lengthis zeroNonce Lengthis 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:
- exporting a full BRC-38 payload for the selected user
- deriving a password key with Argon2id using the standardized Toolbox-compatible default parameters
- encrypting the payload with AES-256-GCM
- writing the standardized binary envelope described above