// examples/handshake.ts — full client+server handshake round trip in-process.
// Uses @noble/post-quantum (ML-KEM-768) + @noble/curves (X25519).

import { ml_kem768 } from '@noble/post-quantum/ml-kem';
import { x25519 } from '@noble/curves/ed25519';
import { hkdf } from '@noble/hashes/hkdf';
import { sha384 } from '@noble/hashes/sha2';
import { randomBytes } from '@noble/hashes/utils';

function concat(...arr: Uint8Array[]) {
  const total = arr.reduce((n, a) => n + a.length, 0);
  const out = new Uint8Array(total);
  let off = 0;
  for (const a of arr) {
    out.set(a, off);
    off += a.length;
  }
  return out;
}

function hex(u: Uint8Array) {
  return Buffer.from(u).toString('hex');
}

function deriveSession(ssX: Uint8Array, ssKem: Uint8Array, transcript: Uint8Array) {
  return hkdf(sha384, concat(ssX, ssKem), transcript, 'qbit404-hybrid', 32);
}

// --- server boots up
const kemSeed = randomBytes(64);
const xServerPriv = randomBytes(32);
const kemKp = ml_kem768.keygen(kemSeed);
const xServerPub = x25519.getPublicKey(xServerPriv);

console.log('server pub bundle:');
console.log('  KEM:', kemKp.publicKey.length, 'B');
console.log('  X25519:', xServerPub.length, 'B');

// --- client establishes
const xClientPriv = randomBytes(32);
const xClientPub = x25519.getPublicKey(xClientPriv);
const { cipherText, sharedSecret: ssKemClient } = ml_kem768.encapsulate(kemKp.publicKey);
const ssXClient = x25519.getSharedSecret(xClientPriv, xServerPub);
const transcript = sha384(concat(cipherText, xClientPub));
const sessionClient = deriveSession(ssXClient, ssKemClient, transcript);

// --- server decapsulates
const ssKemServer = ml_kem768.decapsulate(cipherText, kemKp.secretKey);
const ssXServer = x25519.getSharedSecret(xServerPriv, xClientPub);
const transcript2 = sha384(concat(cipherText, xClientPub));
const sessionServer = deriveSession(ssXServer, ssKemServer, transcript2);

console.log('client session:', hex(sessionClient));
console.log('server session:', hex(sessionServer));
console.log('match:', Buffer.compare(sessionClient, sessionServer) === 0);
