Complete specification of every cryptographic operation in NOONE.
Source: crates/nostrcomm-crypto/src/identity.rs
PBKDF2 iteration count follows OWASP 2024 guidelines. The XOR combination ensures two-factor derivation: hardware possession (PRF) and knowledge (PIN).
Source: crates/nostrcomm-crypto/src/envelope.rs
The recipient performs the reverse: parse the content format (3 parts = hybrid, 2 parts = classical), derive the same shared secret via ECDH + ML-KEM Decapsulate, and decrypt with ChaCha20-Poly1305.
1. Parse content: detect format by colon count - 3 parts = hybrid (kem_ct : nonce : ciphertext) - 2 parts = classical (nonce : ciphertext) 2. Hybrid key agreement: a. Classical: shared_c = ECDH(recipient_x25519_sk, sender_x25519_pk) b. Post-quantum: shared_pq = ML-KEM-768.Decapsulate(recipient_kem_dk, kem_ct) c. Combined: same HKDF as sealing 3. plaintext = ChaCha20-Poly1305.Decrypt(shared, nonce, ciphertext) 4. Parse inner Nostr event from plaintext JSON
Source: crates/nostrcomm-crypto/src/ratchet.rs
Signal-compatible Double Ratchet providing forward secrecy and break-in recovery.
kdf_rk(root_key, dh_output):
HKDF-SHA256(salt=root_key, ikm=dh_output)
-> expand("nostrcomm-ratchet-root-v1") = new_root_key [32 bytes]
-> expand("nostrcomm-ratchet-chain-v1") = chain_key [32 bytes]
kdf_ck(chain_key):
HKDF-SHA256(ikm=chain_key)
-> expand("nostrcomm-ratchet-chain-v1") = new_chain_key [32 bytes]
-> expand("nostrcomm-ratchet-msg-v1") = msg_material [44 bytes]
msg_material[0..32] = AES key, msg_material[32..44] = nonce
1. (new_ck, msg_key) = kdf_ck(ck_send) 2. ck_send = new_ck 3. ciphertext = ChaCha20-Poly1305.Encrypt(msg_key.key, msg_key.nonce, plaintext) 4. Return (ciphertext, n_send++, dh_pk)
1. If sender's DH pk differs from stored dh_remote_pk:
a. Store skipped message keys for current receiving chain
b. Perform DH ratchet step:
- dh_output = X25519(dh_sk, sender_dh_pk)
- (root_key, ck_recv) = kdf_rk(root_key, dh_output)
- Generate new DH keypair
- dh_output2 = X25519(new_dh_sk, sender_dh_pk)
- (root_key, ck_send) = kdf_rk(root_key, dh_output2)
2. Skip message keys up to received n
3. (new_ck, msg_key) = kdf_ck(ck_recv)
4. plaintext = ChaCha20-Poly1305.Decrypt(msg_key.key, msg_key.nonce, ciphertext)
Source: web/src/group-crypto.js
1. session_key = random 256-bit AES key (crypto.getRandomValues)
2. iv = random 96-bit nonce
3. ciphertext = AES-256-GCM.Encrypt(session_key, iv, plaintext)
4. For each group member:
inner_content = JSON.stringify({ g: groupId, sk: hex(session_key), ct: base64(ciphertext), iv: hex(iv) })
sealed_event = seal_event_for_recipient(member.x25519_pk, member.kem_pk, inner_content)
5. Publish N sealed events (one per member)
Each member independently decrypts their sealed event, extracts the session key, and decrypts the message body. The session key is per-message (not reused).
Source: crates/nostrcomm-crypto/src/lib.rs -- p2p_encrypt / p2p_decrypt
Encrypt:
1. shared_c = ECDH(my_x25519_sk, peer_x25519_pk)
2. If peer has KEM key:
(kem_ct, shared_pq) = ML-KEM-768.Encapsulate(peer_kem_pk)
shared = HKDF(shared_c || shared_pq, info="nostrcomm-hybrid-p2p-v1")
3. Else:
shared = HKDF(shared_c, info="nostrcomm-p2p-v1")
4. nonce = random 12 bytes
5. ct = ChaCha20-Poly1305.Encrypt(shared, nonce, plaintext)
6. Return { ct, nonce, kem_ct }
Key: vault_key (32 bytes, derived from identity HKDF) Algorithm: AES-256-GCM via Web Crypto API IV: random 12 bytes per record Each IndexedDB record encrypted independently
Source: crates/nostrcomm-crypto/src/types.rs, identity.rs
Encoding: CBOR (Concise Binary Object Representation) Signing: Ed25519 over canonical CBOR of all fields except signature Verification (chain): 1. Chain[0] must be self-signed (tier 0) and in local trust store 2. Each cert[i] signed by cert[i-1].device_pk 3. All timestamps valid against current time 4. Chain depth <= max_tier policy
| Context | Info String |
|---|---|
| Nostr key derivation | nostrcomm-nostr-v1 |
| X25519 key derivation | nostrcomm-x25519-v1 |
| Ed25519 key derivation | nostrcomm-ed25519-v1 |
| Vault key derivation | nostrcomm-vault-v1 |
| ML-KEM seed derivation | nostrcomm-mlkem-v1 |
| Sealed sender (hybrid) | nostrcomm-hybrid-seal-v1 |
| Sealed sender (classical) | nostrcomm-seal-v1 |
| P2P encryption (hybrid) | nostrcomm-hybrid-p2p-v1 |
| P2P encryption (classical) | nostrcomm-p2p-v1 |
| Ratchet root KDF | nostrcomm-ratchet-root-v1 |
| Ratchet chain KDF | nostrcomm-ratchet-chain-v1 |
| Ratchet message KDF | nostrcomm-ratchet-msg-v1 |
| Emoji code derivation | nostrcomm-emoji-v1 |
All cryptographic code is MIT licensed and available for independent audit.