Skip to content

Bip draft: Bitcoin Encrypted Backup#1951

Draft
pythcoiner wants to merge 1 commit intobitcoin:masterfrom
pythcoiner:encrypted_descriptor
Draft

Bip draft: Bitcoin Encrypted Backup#1951
pythcoiner wants to merge 1 commit intobitcoin:masterfrom
pythcoiner:encrypted_descriptor

Conversation

@pythcoiner
Copy link
Copy Markdown

@pythcoiner pythcoiner commented Sep 4, 2025

This is a bip for encrypted backup, an encryption scheme for bitcoin wallet related metadata.

Mailing list post: https://groups.google.com/g/bitcoindev/c/5NgJbpVDgEc

@pythcoiner pythcoiner marked this pull request as draft September 4, 2025 06:47
@pythcoiner
Copy link
Copy Markdown
Author

thanks for the review! will address comments tmr!

@Sjors
Copy link
Copy Markdown
Member

Sjors commented Sep 4, 2025

Open questions

  • Deterministic nonce: Currently the nonce is generated randomly. Is it safe to produce a deterministic nonce, e.g. hash("NONCE" || plaintext || key_1 || … || key_n), or are there known security concerns with this approach?

In general nonce reuse is unsafe because if you make multiple backups over time, e.g. as you add more transaction labels, you would be reusing the nonce with different message. By including the plaintext in the nonce, you do address that concern.

However it still seems unwise to mess with cryptographic standards. It doesn't seem worth the risk for saving 32 bytes on something that's going to be at least a few hundred bytes for a typical multisig.

@shocknet-justin
Copy link
Copy Markdown

Concept ACK, seems adjacent to how some lightning tools enable users to recover SCB's with just their seed to identify and decrypt the backup. Makes sense for descriptors to have something similar.

@pythcoiner pythcoiner force-pushed the encrypted_descriptor branch 7 times, most recently from 1e4ca34 to 3b6b6ad Compare September 5, 2025 06:30
@Sjors
Copy link
Copy Markdown
Member

Sjors commented Sep 5, 2025

Concept ACK

@pythcoiner
Copy link
Copy Markdown
Author

(not yet finish addressing comments)

@KeysSoze
Copy link
Copy Markdown

KeysSoze commented Sep 9, 2025

Hi @pythcoiner,

By coincidence, two weeks ago I started working on a proposal for a "Standard Encrypted Wallet Payload" to be placed inside an "Encrypted Envelope". The "Wallet Payload" contains descriptors and metadata but can also act as a full wallet backup including transactions, UTXOs and addresses. The proposal is very much a work in progress.

I only just found this discussion so am reading through it to compare it to my proposal. The descriptor backup in the "Wallet Payload" of my proposal seems to have some overlap with the BIP proposed here. If there is too much overlap I may reconsider progressing with my proposal.

As mentioned, my proposal is very much a work in progress but the wallet payload proposal can be found here:

https://gist.github.com/KeysSoze/7109a7f0455897b1930f851bde6337e3

Maybe jump to the test vector section to see what a basic backup of a descriptor and some meta data would look like prior to encryption.

https://gist.github.com/KeysSoze/7109a7f0455897b1930f851bde6337e3#test-vectors

As my proposal is designed to be modular and extensible the encryption envelopes may be extended to offer Multiparty Encryption and Authentication. See:

https://gist.github.com/KeysSoze/7109a7f0455897b1930f851bde6337e3#user-content-Expanding_the_Security_Model

I have already started documenting an encryption envelope that uses AES-256-GCM and password protection:

https://gist.github.com/KeysSoze/866d009ccd082edf6802df240154b20d

I have not written a reference implementation yet but there are well established python and Rust libraries for CBOR and COSE that should make implementing the BIPs relatively simple.

@pythcoiner
Copy link
Copy Markdown
Author

pythcoiner commented Sep 13, 2025

Hi @pythcoiner,

By coincidence, two weeks ago I started working on a proposal for a "Standard Encrypted Wallet Payload" to be placed inside an "Encrypted Envelope". The "Wallet Payload" contains descriptors and metadata but can also act as a full wallet backup including transactions, UTXOs and addresses. The proposal is very much a work in progress.

Hi @KeysSoze, this work seems more related/parallel to the wallet_backup specs I've work on few month ago.

But I've adopted a slightly different approach by simply using JSON.

FYI we already implemented this wallet backup format in Liana wallet and I plan to work on a BIP proposal relatively soon.

@Sjors
Copy link
Copy Markdown
Member

Sjors commented Jan 9, 2026

@murchandamus or other editor: I think this has progressed enough to be BIP number worthy. It would make it easier to generate stable test vectors.

@pythcoiner
Copy link
Copy Markdown
Author

@Sjors thanks for the comments, will address during the week end or next week.

The PR is still draft. Are there things you're still working on?

I'm working on another BIP draft for the payload of a wallet backup, i'd like to undraft them together, as they are quite related: the payload is expected to be encrypted with this spec...

In the meantime, I've "played" with a C implementation of this BIP draft, I wanted to check how hard it should be to implement in bitcoin core and I figure out something: it seems there is actually no dependency in core for AES-GCM-256, while there is already usage of CHACHA20 so i'm wondering if we should not use CHACHA20 as default encryption algo? (I'll cross-post to delving)

@Sjors
Copy link
Copy Markdown
Member

Sjors commented Jan 9, 2026

@pythcoiner using AEAD_CHACHA20_POLY1305 is an interesting idea! Would make it very easy in Bitcoin Core, and perfectly doable in other projects since these are standard components. Good idea to ask on Delving.

In theory we don't need the Poly1305 part, since we're not worried about man-in-the-middle attacks on the backup, but I suspect any library with ChaCha20 will also have Poly1305.

C implementation of this BIP draft

For Bitcoin Core you can use c++, no need to torture yourself. But if it also works in C that makes it easier for hardware wallets to have low level support - not sure if they needed, but still. You could also try with embedded Rust and MicroPython.

Perhaps an extension of the protocol could have hardware wallet sign off on the descriptor / policy, by appending CONTENT with e.g. an HMAC field.

@pythcoiner pythcoiner force-pushed the encrypted_descriptor branch from bfdf6f7 to ab5d450 Compare January 10, 2026 07:56
@pythcoiner
Copy link
Copy Markdown
Author

What's the bip.diff file about? Accidentally committed?

yes, I've dropped it

@pythcoiner
Copy link
Copy Markdown
Author

no need to torture yourself

not felt the torture part 😄

the idea was more, all languages can bind to C, it's not always the case for c++ (or at least less easy) and maybe it worth to have a single implem to maintain

@pythcoiner pythcoiner force-pushed the encrypted_descriptor branch from ab5d450 to f73a0d5 Compare January 14, 2026 01:37
@pythcoiner
Copy link
Copy Markdown
Author

pythcoiner commented Jan 14, 2026

@Sjors I think all comments has been addressed, not yet entirely finnish updating the rust implementation. There is still some double trailing spaces, necessary for formating.

@pythcoiner pythcoiner force-pushed the encrypted_descriptor branch from f73a0d5 to 160c34a Compare January 14, 2026 01:43
@craigraw
Copy link
Copy Markdown
Contributor

This proposal makes multiple references to BIP 380 as defining wallet descriptors. This is incorrect - BIP 380 defines output script descriptors. Since we may have a need to define wallet descriptors as specific format in future (including, for example, a wallet birthday) I believe these references should be corrected.

@Sjors
Copy link
Copy Markdown
Member

Sjors commented Jan 14, 2026

I vibe coded an implementation on top of Bitcoin Core: Sjors/bitcoin#109

It's nice to see how we can mostly reuse existing tooling. It still adds over 1000 lines of non-test code though.

Doing so brought up some issues to reconsider:

  • should we just use VarInt everywhere to simplify parsing at the expense of a few extra bytes?
  • tagged hashes seem to deviate from how they work in BIP340, so we can't use TaggedHash(): (I didn't investigate), LLM says:
/ Current implementation (14 lines):
uint256 ComputeDecryptionSecret(const std::vector<uint256>& keys)
{
    HashWriter hasher{};
    hasher << std::span{reinterpret_cast<const uint8_t*>(BIP_DECRYPTION_SECRET_TAG.data()),
                        BIP_DECRYPTION_SECRET_TAG.size()};
    for (const auto& key : keys) {
        hasher << std::span{key.data(), 32};
    }
    return hasher.GetSHA256();
}

// With BIP340-style tags (could become):
uint256 ComputeDecryptionSecret(const std::vector<uint256>& keys)
{
    HashWriter hasher = TaggedHash("BIP_XXXX_DECRYPTION_SECRET");
    for (const auto& key : keys) hasher << key;
    return hasher.GetSHA256();
}

(that seems like a fairly trivial difference anyway)

@pythcoiner
Copy link
Copy Markdown
Author

This proposal makes multiple references to BIP 380 as defining wallet descriptors. This is incorrect - BIP 380 defines output script descriptors. Since we may have a need to define wallet descriptors as specific format in future (including, for example, a wallet birthday) I believe these references should be corrected.

@craigraw Right, I'll change this

I vibe coded an implementation on top of Bitcoin Core: Sjors/bitcoin#109

@Sjors Thanks for looking on this!

I'm ok to change stuff if it make integration in core simpler

@murchandamus murchandamus added the PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author label Jan 27, 2026
@pythcoiner pythcoiner force-pushed the encrypted_descriptor branch 4 times, most recently from 7d4250c to f807871 Compare March 27, 2026 08:12
@pythcoiner
Copy link
Copy Markdown
Author

adressed @Sjors & @craigraw comments:

  • wallet descriptor => output script descriptor
  • dropped AES_GCM_256 encryption algo
  • use BIP340 tagged hash

@pythcoiner
Copy link
Copy Markdown
Author

  • should we just use VarInt everywhere to simplify parsing at the expense of a few extra bytes?

@Sjors I'm not sure it simplify the parsing for fixed length integers?

@pythcoiner pythcoiner force-pushed the encrypted_descriptor branch from f807871 to a6650a1 Compare April 8, 2026 06:58
@Sjors
Copy link
Copy Markdown
Member

Sjors commented Apr 8, 2026

Thanks for the updates. I plan to update my branch as well, but might be some time.

Regarding varint, it might slightly simplify a function like DecodeContent, but it's not a big deal.

@pythcoiner
Copy link
Copy Markdown
Author

Thanks for the updates. I plan to update my branch as well, but might be some time.

Regarding varint, it might slightly simplify a function like DecodeContent, but it's not a big deal.

I not yet take time to look at your implem, but I plan to do so soon as I'm quite done with implem update on the rust implem and want to try tests vectors on both implem before finalizing the spec

@Sjors
Copy link
Copy Markdown
Member

Sjors commented Apr 8, 2026

My implementation should be taken with a grain of salt, since I haven't had time to polish it.

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

Labels

New BIP PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author

Projects

None yet

Development

Successfully merging this pull request may close these issues.