From eca962d7c26bbea57801576935b98f3540e43da6 Mon Sep 17 00:00:00 2001 From: bndw Date: Mon, 9 Mar 2026 17:36:21 -0700 Subject: fix: harden DM crypto — HKDF key derivation, AEAD associated data, ModInverse nil check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Derive symmetric key via HKDF-SHA256 instead of using raw X25519 shared secret - Bind sender + recipient pubkeys as ChaCha20-Poly1305 associated data to prevent key-confusion attacks - Guard against ModInverse panic on degenerate public keys (y=1) - Wrap DecryptDM error instead of swallowing it - Update JS client to match Go implementation - Document encryption details in PROTOCOL.md --- PROTOCOL.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'PROTOCOL.md') diff --git a/PROTOCOL.md b/PROTOCOL.md index 42bf081..cd5253f 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -100,10 +100,11 @@ Two implementations that agree on this layout will always produce the same `id` |---|---|---| | Signing | Ed25519 | `crypto/ed25519` (stdlib) | | Key exchange | X25519 | `golang.org/x/crypto/curve25519` | +| Key derivation | HKDF-SHA256 | `golang.org/x/crypto/hkdf` | | Encryption | ChaCha20-Poly1305 | `golang.org/x/crypto/chacha20poly1305` | | Hashing / event ID | SHA-256 | `crypto/sha256` (stdlib) | -All dependencies are from the Go standard library or `golang.org/x/crypto`. No third-party crypto. Ed25519 keys are converted to X25519 for ECDH — one keypair serves both signing and encryption. ChaCha20-Poly1305 provides authenticated encryption (AEAD); the ciphertext cannot be tampered with without detection. +All dependencies are from the Go standard library or `golang.org/x/crypto`. No third-party crypto. Ed25519 keys are converted to X25519 for ECDH — one keypair serves both signing and encryption. The raw X25519 shared secret is passed through HKDF-SHA256 (info: `"axon-dm-v1"`) to derive the symmetric encryption key. ChaCha20-Poly1305 provides authenticated encryption (AEAD) with the sender and recipient public keys bound as associated data; the ciphertext cannot be tampered with or re-targeted without detection. --- @@ -303,6 +304,16 @@ Root marker is required on all replies. No fallback heuristics. Tag{ name: "p", values: [""] } ``` +**Encryption details:** + +1. Compute the X25519 shared secret from the sender's private key and recipient's public key +2. Derive a 32-byte symmetric key via HKDF-SHA256 (salt: nil, info: `"axon-dm-v1"`) +3. Generate a 12-byte random nonce +4. Encrypt with ChaCha20-Poly1305 using associated data = `sender_pubkey || recipient_pubkey` +5. Wire format of content field: `nonce (12 bytes) || ciphertext` + +The associated data binds the ciphertext to both parties, preventing key-confusion attacks where an attacker re-targets a ciphertext to a different recipient. + The relay indexes the `p` tag to route DMs to the recipient's subscription. Content is opaque; the relay cannot decrypt it. --- -- cgit v1.2.3