#pragma once // DragonX Wallet - HushChat crypto primitives. // // crypto_kx (X25519) session-key agreement + crypto_secretstream_xchacha20poly1305 // message encryption, byte-exact per the HushChat wire format so DragonX interoperates // with SilentDragonXLite. See docs/_archive/contacts-chat-phase1-detail-2026-07-05.md // (Appendix A.3/A.4). Pure crypto — no gating, no I/O; the feature gate lives at the // service layer. NEVER logs plaintext, ciphertext, keys, or session material. #include #include #include namespace dragonx::chat { // crypto_kx key sizes (== crypto_kx_PUBLICKEYBYTES / SECRETKEYBYTES == 32). constexpr std::size_t kChatKeyBytes = 32; using ChatPublicKey = std::array; using ChatSecretKey = std::array; struct ChatKeyPair { ChatPublicKey public_key{}; ChatSecretKey secret_key{}; }; enum class ChatCryptoStatus { Ok, SodiumInitFailed, BadPeerKey, // peer public-key hex missing / wrong length / not hex BadHeaderHex, // secretstream header hex missing / wrong length / not hex BadCiphertextHex, // ciphertext hex malformed CiphertextTooShort, // ciphertext shorter than the auth tag SessionKeyFailed, // crypto_kx_*_session_keys rejected the peer key EncryptFailed, DecryptFailed // init_pull / pull / tag mismatch — the single neutral auth failure }; const char* chatCryptoStatusName(ChatCryptoStatus status); // Encrypt `plaintext` addressed to peer `peerPublicKeyHex` (64 lowercase hex chars). // Sender takes the crypto_kx "server" role (server_tx), matching SDXL's send path. // Outputs the secretstream header hex (the memo "e" field) and the ciphertext hex // (the payload memo). Returns Ok on success. ChatCryptoStatus encryptOutgoing(const ChatKeyPair& mine, const std::string& peerPublicKeyHex, const std::string& plaintext, std::string& outStreamHeaderHex, std::string& outCiphertextHex); // Decrypt an incoming message addressed to us from peer `peerPublicKeyHex`. // Receiver takes the crypto_kx "client" role (client_rx), matching SDXL's receive path. // Requires the memo "e" (streamHeaderHex) and the payload ciphertext hex. Enforces the // Poly1305 auth tag AND that the stream tag is TAG_FINAL. Returns Ok + fills outPlaintext. ChatCryptoStatus decryptIncoming(const ChatKeyPair& mine, const std::string& peerPublicKeyHex, const std::string& streamHeaderHex, const std::string& ciphertextHex, std::string& outPlaintext); // Zero both key arrays (call when discarding an identity's keys). void wipeChatKeyPair(ChatKeyPair& keys); } // namespace dragonx::chat