OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer / src / noise_stream.h

64 lines · c ref:
#ifndef HYPERSWARM_NOISE_STREAM_H
#define HYPERSWARM_NOISE_STREAM_H

#include <cstdint>
#include <vector>

#include <sodium.h>

// Symmetric stream encryption/decryption using XChaCha20-Poly1305.
// Used after Noise handshake completes to encrypt all game data.
//
// Each direction has its own state (initialised from the handshake-derived key).
// TX: encrypt outgoing data.
// RX: decrypt incoming data (requires the remote's TX header).

class NoiseStream {
public:
	static constexpr int HEADER_BYTES = crypto_secretstream_xchacha20poly1305_HEADERBYTES; // 24
	static constexpr int TAG_BYTES = crypto_secretstream_xchacha20poly1305_ABYTES;         // 17

	NoiseStream();
	~NoiseStream();

	// Initialise TX state from handshake-derived key.
	// Generates a 24-byte header that must be sent to remote.
	void init_tx(const uint8_t *key);

	// Initialise RX state from handshake-derived key + remote's TX header.
	bool init_rx(const uint8_t *key, const uint8_t *header);

	// Encrypt plaintext → ciphertext (adds TAG_BYTES overhead).
	std::vector<uint8_t> encrypt(const uint8_t *data, size_t len);

	// Decrypt ciphertext → plaintext. Returns empty on auth failure.
	std::vector<uint8_t> decrypt(const uint8_t *data, size_t len);

	// ⛔ THE SAME CALL, WITH THE ONE ANSWER THE OTHER CANNOT GIVE.
	//
	// decrypt() returns {} for an authentication failure AND for a frame whose
	// plaintext is legitimately empty, and those must be told apart: an EMPTY
	// authenticated frame is the keepalive @hyperswarm/secret-stream sends
	// (`sendKeepAlive` writes `this.alloc(0)`), and the reference eats it on
	// receipt rather than treating it as an error. Conflating them means a
	// keepalive is indistinguishable from an attack.
	//
	// Returns false only on authentication failure.
	bool decrypt(const uint8_t *data, size_t len, std::vector<uint8_t> &out);

	// Get the TX header (24 bytes, send to remote for their RX init).
	const uint8_t *tx_header() const { return _tx_header; }

	bool tx_ready() const { return _tx_init; }
	bool rx_ready() const { return _rx_init; }

private:
	crypto_secretstream_xchacha20poly1305_state _tx_state;
	crypto_secretstream_xchacha20poly1305_state _rx_state;
	uint8_t _tx_header[HEADER_BYTES];
	bool _tx_init;
	bool _rx_init;
};

#endif // HYPERSWARM_NOISE_STREAM_H