diff options
| author | bndw <ben@bdw.to> | 2026-03-09 17:36:21 -0700 |
|---|---|---|
| committer | bndw <ben@bdw.to> | 2026-03-09 17:36:21 -0700 |
| commit | eca962d7c26bbea57801576935b98f3540e43da6 (patch) | |
| tree | 19653f192107b5b74a8113af570d4b9c979a7e0d /PROTOCOL.md | |
| parent | 9886a5c9054f3308482fdcca0fa545c8befbcf5b (diff) | |
fix: harden DM crypto — HKDF key derivation, AEAD associated data, ModInverse nil check
- 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
Diffstat (limited to 'PROTOCOL.md')
| -rw-r--r-- | PROTOCOL.md | 13 |
1 files changed, 12 insertions, 1 deletions
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` | |||
| 100 | |---|---|---| | 100 | |---|---|---| |
| 101 | | Signing | Ed25519 | `crypto/ed25519` (stdlib) | | 101 | | Signing | Ed25519 | `crypto/ed25519` (stdlib) | |
| 102 | | Key exchange | X25519 | `golang.org/x/crypto/curve25519` | | 102 | | Key exchange | X25519 | `golang.org/x/crypto/curve25519` | |
| 103 | | Key derivation | HKDF-SHA256 | `golang.org/x/crypto/hkdf` | | ||
| 103 | | Encryption | ChaCha20-Poly1305 | `golang.org/x/crypto/chacha20poly1305` | | 104 | | Encryption | ChaCha20-Poly1305 | `golang.org/x/crypto/chacha20poly1305` | |
| 104 | | Hashing / event ID | SHA-256 | `crypto/sha256` (stdlib) | | 105 | | Hashing / event ID | SHA-256 | `crypto/sha256` (stdlib) | |
| 105 | 106 | ||
| 106 | 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. | 107 | 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. |
| 107 | 108 | ||
| 108 | --- | 109 | --- |
| 109 | 110 | ||
| @@ -303,6 +304,16 @@ Root marker is required on all replies. No fallback heuristics. | |||
| 303 | Tag{ name: "p", values: ["<recipient-pubkey>"] } | 304 | Tag{ name: "p", values: ["<recipient-pubkey>"] } |
| 304 | ``` | 305 | ``` |
| 305 | 306 | ||
| 307 | **Encryption details:** | ||
| 308 | |||
| 309 | 1. Compute the X25519 shared secret from the sender's private key and recipient's public key | ||
| 310 | 2. Derive a 32-byte symmetric key via HKDF-SHA256 (salt: nil, info: `"axon-dm-v1"`) | ||
| 311 | 3. Generate a 12-byte random nonce | ||
| 312 | 4. Encrypt with ChaCha20-Poly1305 using associated data = `sender_pubkey || recipient_pubkey` | ||
| 313 | 5. Wire format of content field: `nonce (12 bytes) || ciphertext` | ||
| 314 | |||
| 315 | The associated data binds the ciphertext to both parties, preventing key-confusion attacks where an attacker re-targets a ciphertext to a different recipient. | ||
| 316 | |||
| 306 | The relay indexes the `p` tag to route DMs to the recipient's subscription. Content is opaque; the relay cannot decrypt it. | 317 | The relay indexes the `p` tag to route DMs to the recipient's subscription. Content is opaque; the relay cannot decrypt it. |
| 307 | 318 | ||
| 308 | --- | 319 | --- |
