This PR introduces an implementation of Hybrid Public Key Encryption (HPKE) using a secp256k1-based Diffie-Hellman KEM (per RFC 9180 and “secp256k1-based DHKEM for HPKE” specification). It provides the core cryptographic component needed to enable the Payjoin v2 protocol.
This is an exploratory PR intended to kickstart discussion on adding Payjoin v2 support to Bitcoin Core – feedback and reviews are very welcome.
Payjoin v2 makes use of a protocol called Oblivious HTTP (OHTTP) to strip client-identifying metadata from the request. This protocol use a binary-encoded HTTP message encapsulated through HPKE.
The core implementation is contained in the new files src/dhkem_secp256k1.h
and src/dhkem_secp256k1.cpp
:
- Full HPKE Modes: All four HPKE modes are supported –
Base
,PSK
,Auth
, andAuthPSK
. - Secp256k1 DHKEM: Uses secp256k1 for Diffie-Hellman key exchange. The
Encap()
andDecap()
functions perform the base mode KEM (ephemeral ECDH using the receiver’s public key), whileAuthEncap()
andAuthDecap()
extend this to authenticated mode (including the sender’s static key in the shared secret derivation). Internally, the shared secret is derived as specified by HPKE using HKDF (SHA256) extraction and expansion. - Key Schedule & Nonce Derivation: After KEM, the implementation runs the HPKE key schedule to derive the
encryption key
,base nonce
, andexporter secret
. It follows RFC 9180 §7.1, hashing theinfo
andpsk_id
contexts and deriving the AEAD key and nonce. Per-message nonces are computed by XOR-ing the base nonce with a message sequence number as specified in RFC 9180 §5.2. - AEAD Encryption (ChaCha20-Poly1305): The HPKE context uses
ChaCha20-Poly1305
as the authenticated encryption scheme. The code providesSeal()
andOpen()
functions to encrypt and decrypt data using the derived context key and a given nonce. It uses Bitcoin Core’s existing ChaCha20Poly1305 implementation.
The test file validates all the functions mentioned above: Encapsulation/Decapsulation, Encryption/Decryption, Nonce and Key Schedule Logic. This uses the PDK (Payjoin Dev Kit) test vectors. The tests also show how to use the HPKE functions.
This implementation only uses HKDF-SHA256 as Key Derivation Functions and ChaCha20-Poly1305 as AEAD, since these already exist in the Bitcoin Core codebase. It does not support other KDFs (such as HKDF-SHA384 and HKDF-SHA512) or other AEADs (such as AES-128-GCM and AES-256-GCM).
To run the tests:
build/bin/test_bitcoin --log_level=all --run_test=dhkem_secp256k1_tests