#ifndef HYPERSWARM_NOISE_IK_H
#define HYPERSWARM_NOISE_IK_H
// Noise IK handshake over Ed25519 — wire-compatible with hyperdht/noise-wrap.
// Protocol: Noise_IK_Ed25519_ChaChaPoly_BLAKE2b
//
// IK pattern (2 messages, initiator knows responder's static key):
// Preshare: rs
// Msg 1 (I→R): e, es, s, ss
// Msg 2 (R→I): e, ee, se
//
// Ed25519 DH uses crypto_scalarmult_ed25519_noclamp with SHA512-derived scalar.
// This is SEPARATE from NoiseHandshake (Noise XX, Curve25519) which is for
// post-connection stream encryption. NoiseIK handles DHT relay handshakes.
#include <cstdint>
#include <cstring>
#include <vector>
struct Ed25519KeyPair {
uint8_t public_key[32]; // Ed25519 public key.
uint8_t secret_key[64]; // Ed25519 secret key (seed + public key).
};
class NoiseIK {
public:
static constexpr int HASHLEN = 64; // BLAKE2b output.
static constexpr int DHLEN = 32; // Ed25519 DH output.
static constexpr int PKLEN = 32; // Ed25519 public key.
static constexpr int SKLEN = 64; // Ed25519 secret key.
static constexpr int MACBYTES = 16; // ChaCha20-Poly1305 tag.
static constexpr int KEYBYTES = 32; // Symmetric cipher key.
static constexpr int BLOCKLEN = 128; // BLAKE2b block size for HMAC.
// prologue: 32-byte namespace hash (NS.PEER_HANDSHAKE).
// remote_pk: responder's Ed25519 public key (32 bytes).
// - initiator: must provide remote_pk.
// - responder: remote_pk is nullptr.
NoiseIK(bool initiator, const Ed25519KeyPair &static_key,
const uint8_t *prologue, const uint8_t *remote_pk);
~NoiseIK();
// Create the next outgoing handshake message.
// payload: optional plaintext to encrypt inside the handshake.
// Returns the raw bytes to send.
std::vector<uint8_t> send(const uint8_t *payload = nullptr, size_t payload_len = 0);
// Process an incoming handshake message.
// Returns the decrypted payload.
std::vector<uint8_t> recv(const uint8_t *data, size_t len);
bool is_complete() const { return _complete; }
// After completion: derived keys and state.
const uint8_t *remote_public_key() const { return _rs; }
const uint8_t *tx_key() const { return _tx; }
const uint8_t *rx_key() const { return _rx; }
const uint8_t *handshake_hash() const { return _hash; }
// Derive holepunch secret: BLAKE2b(NS_PEER_HOLEPUNCH, handshake_hash).
void derive_holepunch_secret(const uint8_t *ns_peer_holepunch, uint8_t *out_32) const;
// Generate an Ed25519 keypair.
static Ed25519KeyPair generate_keypair();
// Compute namespace hashes for HyperDHT.
// ns_name: "hyperswarm/dht"
// ids: command IDs to hash [ANNOUNCE=4, UNANNOUNCE=5, MUTABLE_PUT=6, PEER_HANDSHAKE=0, PEER_HOLEPUNCH=1]
// out: array of 32-byte hashes, one per ID.
static void compute_namespace(const char *ns_name, const uint8_t *ids,
int count, uint8_t *out);
private:
bool _initiator;
int _message_index;
bool _complete;
Ed25519KeyPair _s; // Static keypair.
Ed25519KeyPair _e; // Ephemeral keypair.
bool _e_generated;
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.
uint8_t _tx[KEYBYTES];
uint8_t _rx[KEYBYTES];
uint8_t _hash[HASHLEN];
// Ed25519 DH: crypto_scalarmult_ed25519_noclamp with SHA512-derived scalar.
static void _ed25519_dh(uint8_t *output, const uint8_t *secret_key_64,
const uint8_t *remote_public_key);
// Symmetric state operations (identical logic to NoiseHandshake, different DH).
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_64);
void _encrypt_and_hash(std::vector<uint8_t> &out,
const uint8_t *pt, size_t pt_len);
std::vector<uint8_t> _decrypt_and_hash(const uint8_t *ct, size_t ct_len);
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);
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);
static void _hmac(uint8_t *out, const uint8_t *key, size_t key_len,
const uint8_t *data, size_t data_len);
void _initialise_key(const uint8_t *key);
void _split();
};
#endif // HYPERSWARM_NOISE_IK_H