Cleanup crypto comments
Some checks failed
Build Linux Binaries / linux/amd64 (push) Has been cancelled
Build Linux Binaries / linux/arm64 (push) Has been cancelled

This commit is contained in:
2026-07-03 15:37:39 +01:00
parent 826b3bfb86
commit 158a2df062
3 changed files with 18 additions and 20 deletions

View File

@@ -12,7 +12,6 @@
namespace voicecat::crypto { namespace voicecat::crypto {
// ── Helpers ───────────────────────────────────────────────────────────────────
static void throw_if(int rc, const char* msg) { static void throw_if(int rc, const char* msg) {
if (rc != 0) { if (rc != 0) {
@@ -35,7 +34,7 @@ static std::string compute_hex_fingerprint(const uint8_t* data, size_t len) {
return s; return s;
} }
// ── ServerIdentity ──────────────────────────────────────────────────────────── // ServerIdentity
ServerIdentity ServerIdentity::generate() { ServerIdentity ServerIdentity::generate() {
ServerIdentity id; ServerIdentity id;
@@ -75,7 +74,7 @@ std::string ServerIdentity::fingerprint_hex() const {
return s; return s;
} }
// ── ServerCert ──────────────────────────────────────────────────────────────── // ServerCert
ServerCert ServerCert::generate(const std::string& server_name) { ServerCert ServerCert::generate(const std::string& server_name) {
mbedtls_entropy_context entropy; mbedtls_entropy_context entropy;
@@ -171,7 +170,7 @@ void ServerCert::save(const std::filesystem::path& cert_path,
write_file(key_path, pem_key); write_file(key_path, pem_key);
} }
// ── TlsContext ──────────────────────────────────────────────────────────────── // TlsContext
TlsContext::TlsContext(Role role, const ServerCert* server_cert, TlsContext::TlsContext(Role role, const ServerCert* server_cert,
const std::array<uint8_t, 32>* pinned_fp) const std::array<uint8_t, 32>* pinned_fp)
@@ -217,7 +216,7 @@ TlsContext::TlsContext(Role role, const ServerCert* server_cert,
} }
if (role == Role::Client) { if (role == Role::Client) {
// Skip CA chain verification we use TOFU via the server identity fingerprint. // Skip CA chain verification, we use TOFU via the server identity fingerprint.
mbedtls_ssl_conf_authmode(&conf_, MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_authmode(&conf_, MBEDTLS_SSL_VERIFY_NONE);
} }
@@ -284,7 +283,7 @@ bool TlsContext::peer_cert_fingerprint(std::array<uint8_t, 32>& out) const {
return true; return true;
} }
// ── SodiumMediaCrypto ───────────────────────────────────────────────────────── // SodiumMediaCrypto
SodiumMediaCrypto::SodiumMediaCrypto( SodiumMediaCrypto::SodiumMediaCrypto(
const uint8_t key[crypto_aead_chacha20poly1305_ietf_KEYBYTES]) { const uint8_t key[crypto_aead_chacha20poly1305_ietf_KEYBYTES]) {
@@ -347,8 +346,7 @@ long SodiumMediaCrypto::open(const uint8_t* sealed, size_t len, const uint8_t* a
if (out_cap < len - crypto_aead_chacha20poly1305_ietf_ABYTES) return -1; if (out_cap < len - crypto_aead_chacha20poly1305_ietf_ABYTES) return -1;
// Read the full 64-bit nonce counter directly from aad[8..15] (seq, big-endian // Read the full 64-bit nonce counter directly from aad[8..15] (seq, big-endian
// u64). Protocol v2 carries the full counter on the wire, so the nonce is exact — // u64).
// no reconstruction/rollover guessing needed.
if (aad_len < 16) return -1; if (aad_len < 16) return -1;
uint64_t counter = (static_cast<uint64_t>(aad[8]) << 56) | uint64_t counter = (static_cast<uint64_t>(aad[8]) << 56) |
(static_cast<uint64_t>(aad[9]) << 48) | (static_cast<uint64_t>(aad[9]) << 48) |
@@ -364,7 +362,7 @@ long SodiumMediaCrypto::open(const uint8_t* sealed, size_t len, const uint8_t* a
// window before the AEAD tag is verified — otherwise a single corrupted/forged // window before the AEAD tag is verified — otherwise a single corrupted/forged
// packet would shove recv_highest_ far ahead and reject every later legitimate // packet would shove recv_highest_ far ahead and reject every later legitimate
// packet as "too old", permanently wedging the stream. Order per RFC 3711 §3.3: // packet as "too old", permanently wedging the stream. Order per RFC 3711 §3.3:
// replay-check authenticate update. // replay-check authenticate update.
if (recv_initialized_ && counter <= recv_highest_) { if (recv_initialized_ && counter <= recv_highest_) {
uint64_t offset = recv_highest_ - counter; uint64_t offset = recv_highest_ - counter;
if (offset >= 64) return -1; // too old if (offset >= 64) return -1; // too old
@@ -381,7 +379,7 @@ long SodiumMediaCrypto::open(const uint8_t* sealed, size_t len, const uint8_t* a
nonce, key_.data()) != 0) nonce, key_.data()) != 0)
return -1; // auth failure — leave the replay window untouched return -1; // auth failure — leave the replay window untouched
// ── Authenticated: now it's safe to advance the window ──────────────────── // Authenticated: now it's safe to advance the window ────────────────────
if (!recv_initialized_) { if (!recv_initialized_) {
recv_highest_ = counter; recv_highest_ = counter;
recv_window_ = 1; // bit0 = highest itself recv_window_ = 1; // bit0 = highest itself

View File

@@ -1,7 +1,7 @@
/* /*
* crypto/crypto.h TLS 1.3 (mbedTLS) and the media AEAD (libsodium). * crypto/crypto.h: TLS 1.3 (mbedTLS) and the media AEAD (libsodium).
* *
* Design: docs/security.md. Control channel = TLS 1.3. Media = keys exported from the TLS *Control channel = TLS 1.3. Media = keys exported from the TLS
* session (RFC 5705 / 8446) + per-frame ChaCha20-Poly1305 with a counter nonce and a * session (RFC 5705 / 8446) + per-frame ChaCha20-Poly1305 with a counter nonce and a
* sliding-window replay filter. Encryption is MANDATORY — never add a plaintext path. * sliding-window replay filter. Encryption is MANDATORY — never add a plaintext path.
*/ */
@@ -30,7 +30,7 @@
namespace voicecat::crypto { namespace voicecat::crypto {
// ── Server identity ──────────────────────────────────────────────────────────── // Server identity
// Long-lived Ed25519 key identifying this server instance across cert rotations. // Long-lived Ed25519 key identifying this server instance across cert rotations.
// Fingerprint is the 32-byte SHA-256 of the public key. // Fingerprint is the 32-byte SHA-256 of the public key.
struct ServerIdentity { struct ServerIdentity {
@@ -44,7 +44,7 @@ struct ServerIdentity {
std::string fingerprint_hex() const; std::string fingerprint_hex() const;
}; };
// ── Server TLS certificate ───────────────────────────────────────────────────── // Server TLS certificate
// Self-signed ECDSA-P256 cert for TLS. On first run, generated and persisted. // Self-signed ECDSA-P256 cert for TLS. On first run, generated and persisted.
struct ServerCert { struct ServerCert {
std::string pem_cert; std::string pem_cert;
@@ -57,7 +57,7 @@ struct ServerCert {
const std::filesystem::path& key_path) const; const std::filesystem::path& key_path) const;
}; };
// ── TLS 1.3 context ─────────────────────────────────────────────────────────── // TLS 1.3 context
// Wraps mbedTLS for one TLS connection (server or client side). // Wraps mbedTLS for one TLS connection (server or client side).
// All public methods must be called from a single thread at a time. // All public methods must be called from a single thread at a time.
class TlsContext { class TlsContext {
@@ -119,7 +119,7 @@ class TlsContext {
mbedtls_net_context net_ctx_{}; mbedtls_net_context net_ctx_{};
}; };
// ── Media AEAD ───────────────────────────────────────────────────────────────── // Media AEAD
// Per-frame voice encryption. Abstracted so the backend is swappable. // Per-frame voice encryption. Abstracted so the backend is swappable.
class MediaCrypto { class MediaCrypto {
public: public:
@@ -135,7 +135,7 @@ class MediaCrypto {
uint8_t* out, size_t out_cap) = 0; uint8_t* out, size_t out_cap) = 0;
}; };
// ── ChaCha20-Poly1305 backend ────────────────────────────────────────────────── // ChaCha20-Poly1305 backend
// Keys are derived from the TLS session via RFC 5705 / mbedTLS exporter. // Keys are derived from the TLS session via RFC 5705 / mbedTLS exporter.
// Nonce scheme: 4 zero bytes ‖ monotonic-counter(u64 big-endian, 8 bytes). // Nonce scheme: 4 zero bytes ‖ monotonic-counter(u64 big-endian, 8 bytes).
// Anti-replay: 64-bit sliding window keyed on the received counter. // Anti-replay: 64-bit sliding window keyed on the received counter.
@@ -145,10 +145,10 @@ class SodiumMediaCrypto final : public MediaCrypto {
static std::unique_ptr<SodiumMediaCrypto> derive(TlsContext& tls, uint8_t ctx_byte); static std::unique_ptr<SodiumMediaCrypto> derive(TlsContext& tls, uint8_t ctx_byte);
// Convenience: derive the key used to encrypt outgoing frames. // Convenience: derive the key used to encrypt outgoing frames.
// is_client=true ctx=0x00 (client sends); is_client=false ctx=0x01 (server sends). // is_client=true ctx=0x00 (client sends); is_client=false ctx=0x01 (server sends).
static std::unique_ptr<SodiumMediaCrypto> derive_send(TlsContext& tls, bool is_client); static std::unique_ptr<SodiumMediaCrypto> derive_send(TlsContext& tls, bool is_client);
// Convenience: derive the key used to decrypt incoming frames. // Convenience: derive the key used to decrypt incoming frames.
// is_client=true ctx=0x01 (client recvs); is_client=false ctx=0x00 (server recvs). // is_client=true ctx=0x01 (client recvs); is_client=false ctx=0x00 (server recvs).
static std::unique_ptr<SodiumMediaCrypto> derive_recv(TlsContext& tls, bool is_client); static std::unique_ptr<SodiumMediaCrypto> derive_recv(TlsContext& tls, bool is_client);
// Unit-test constructor: supply a raw 32-byte key directly. // Unit-test constructor: supply a raw 32-byte key directly.

View File

@@ -1,5 +1,5 @@
/* /*
* crypto/tofu_store.h Trust-On-First-Use pin storage. * crypto/tofu_store.h: Trust-On-First-Use pin storage.
* *
* File format: one "host:port <hex-fingerprint>\n" line per entry. * File format: one "host:port <hex-fingerprint>\n" line per entry.
* Used by clients to remember server fingerprints across reconnects. * Used by clients to remember server fingerprints across reconnects.