#ifndef HYPERSWARM_NOISE_HANDSHAKE_H
#define HYPERSWARM_NOISE_HANDSHAKE_H
#include <cstdint>
#include <cstring>
#include <vector>
// Noise XX handshake state machine.
// Protocol: Noise_XX_25519_ChaChaPoly_BLAKE2b
// Compatible with @hyperswarm/secret-stream (via noise-handshake).
//
// XX pattern (3 messages):
// Msg 1 (initiator → responder): e
// Msg 2 (responder → initiator): e, ee, s, es
// Msg 3 (initiator → responder): s, se
struct NoiseKeypair {
uint8_t public_key[32]; // Curve25519 public key
uint8_t secret_key[32]; // Curve25519 secret key
};
class NoiseHandshake {
public:
static constexpr int HASHLEN = 64; // BLAKE2b output
static constexpr int DHLEN = 32; // Curve25519
static constexpr int PKLEN = 32;
static constexpr int SKLEN = 32;
static constexpr int MACBYTES = 16; // ChaCha20-Poly1305 tag
static constexpr int NONCEBYTES = 12;
static constexpr int KEYBYTES = 32;
static constexpr int BLOCKLEN = 128; // BLAKE2b block size
NoiseHandshake(bool is_initiator, const NoiseKeypair &static_key);
~NoiseHandshake();
// Create the next outgoing handshake message.
// Returns bytes to send to remote.
std::vector<uint8_t> send(const uint8_t *payload = nullptr, size_t payload_len = 0);
// Process an incoming handshake message.
// Returns the decrypted payload (may be empty).
std::vector<uint8_t> recv(const uint8_t *data, size_t len);
// True when the 3-message handshake is complete.
bool is_complete() const { return _complete; }
// After completion: remote peer's static public key (32 bytes).
const uint8_t *remote_public_key() const { return _rs; }
// After completion: derived symmetric keys (32 bytes each).
const uint8_t *tx_key() const { return _tx; }
const uint8_t *rx_key() const { return _rx; }
// After completion: handshake hash (64 bytes).
const uint8_t *handshake_hash() const { return _hash; }
// Generate a Curve25519 keypair (static, for identity).
static NoiseKeypair generate_keypair();
private:
bool _initiator;
int _message_index;
bool _complete;
// Static keypair (Curve25519, long-lived identity).
NoiseKeypair _s;
// Ephemeral keypair (Curve25519, generated per-handshake).
NoiseKeypair _e;
bool _e_generated;
// Remote keys
uint8_t _rs[PKLEN]; // Remote static
uint8_t _re[PKLEN]; // Remote ephemeral
// Symmetric state
uint8_t _digest[HASHLEN];
uint8_t _chaining_key[HASHLEN];
uint8_t _cipher_key[KEYBYTES];
bool _has_key;
uint64_t _nonce;
// Output keys (after completion)
uint8_t _tx[KEYBYTES];
uint8_t _rx[KEYBYTES];
uint8_t _hash[HASHLEN];
// -- Symmetric state operations (matching noise-handshake JS) --
void _mix_hash(const uint8_t *data, size_t len);
void _mix_key_from_dh(const uint8_t *remote_pk, const uint8_t *local_sk);
void _mix_key_normal(const uint8_t *key, size_t key_len);
// Encrypt plaintext, append to out, update hash.
void _encrypt_and_hash(std::vector<uint8_t> &out,
const uint8_t *pt, size_t pt_len);
// Decrypt ciphertext, return plaintext, update hash.
std::vector<uint8_t> _decrypt_and_hash(const uint8_t *ct, size_t ct_len);
// Raw AEAD encrypt/decrypt with current cipher key + nonce.
void _encrypt(uint8_t *ct, const uint8_t *pt, size_t pt_len,
const uint8_t *ad, size_t ad_len);
bool _decrypt(uint8_t *pt, const uint8_t *ct, size_t ct_len,
const uint8_t *ad, size_t ad_len);
// ECDH: output = scalarmult(local_sk, remote_pk).
static void _dh(uint8_t *output, const uint8_t *local_sk, const uint8_t *remote_pk);
// HKDF: extract-and-expand using HMAC-BLAKE2b.
static void _hkdf(const uint8_t *salt, size_t salt_len,
const uint8_t *ikm, size_t ikm_len,
uint8_t *out1, uint8_t *out2, uint8_t *out3 = nullptr);
// HMAC-BLAKE2b
static void _hmac(uint8_t *out, const uint8_t *key, size_t key_len,
const uint8_t *data, size_t data_len);
// Split: derive tx/rx keys from chaining key.
void _split();
// Initialise cipher key.
void _initialise_key(const uint8_t *key);
};
#endif // HYPERSWARM_NOISE_HANDSHAKE_H