noise-core.ts

4.8 KB · raw

1import {2  initSync,3  WasmNoiseSession,4} from "./noise-core-wasm/peach_noise_core";5import { PEACH_NOISE_CORE_WASM_BASE64 } from "./noise-core-wasm/peach_noise_core_bytes";67export interface RustNoiseSession {8  encrypt(plaintext: Uint8Array): Uint8Array;9  decrypt(ciphertext: Uint8Array): Uint8Array;10  readonly handshakeHash: Uint8Array;11  rekeyOutgoing(): void;12  rekeyIncoming(): void;13  close(): void;14}1516export interface RustNoiseKeyPair {17  publicKey: Uint8Array;18  secretKey: Uint8Array;19}2021let initialized = false;2223function ensureInitialized(): void {24  if (initialized) return;25  const binary = atob(PEACH_NOISE_CORE_WASM_BASE64);26  const bytes = new Uint8Array(binary.length);27  for (let index = 0; index < binary.length; index += 1) {28    bytes[index] = binary.charCodeAt(index);29  }30  try {31    initSync({ module: bytes });32    initialized = true;33  } finally {34    bytes.fill(0);35  }36}3738function session(native: WasmNoiseSession): RustNoiseSession {39  let closed = false;40  const requireOpen = () => {41    if (closed) throw new Error("Noise session is closed");42  };43  return {44    encrypt(plaintext) {45      requireOpen();46      return native.encrypt(plaintext);47    },48    decrypt(ciphertext) {49      requireOpen();50      return native.decrypt(ciphertext);51    },52    get handshakeHash() {53      requireOpen();54      return native.handshakeHash;55    },56    rekeyOutgoing() {57      requireOpen();58      native.rekeyOutgoing();59    },60    rekeyIncoming() {61      requireOpen();62      native.rekeyIncoming();63    },64    close() {65      if (closed) return;66      closed = true;67      native.free();68    },69  };70}7172async function driveInitiator(73  native: WasmNoiseSession,74  send: (message: Uint8Array) => Promise<void>,75  receive: () => Promise<Uint8Array>,76): Promise<RustNoiseSession> {77  try {78    await send(native.writeHandshake(new Uint8Array(0)));79    native.readHandshake(await receive());80    return session(native);81  } catch (error) {82    native.free();83    throw error;84  }85}8687async function driveResponder(88  native: WasmNoiseSession,89  send: (message: Uint8Array) => Promise<void>,90  receive: () => Promise<Uint8Array>,91): Promise<RustNoiseSession> {92  try {93    native.readHandshake(await receive());94    await send(native.writeHandshake(new Uint8Array(0)));95    return session(native);96  } catch (error) {97    native.free();98    throw error;99  }100}101102export async function rustNoiseNkInitiator(103  responderStaticPublicKey: Uint8Array,104  send: (message: Uint8Array) => Promise<void>,105  receive: () => Promise<Uint8Array>,106  ephemeral?: RustNoiseKeyPair,107): Promise<RustNoiseSession> {108  ensureInitialized();109  const native = ephemeral110    ? WasmNoiseSession.nkInitiatorWithEphemeralForTest(111        responderStaticPublicKey,112        ephemeral.secretKey,113      )114    : WasmNoiseSession.nkInitiator(responderStaticPublicKey);115  return driveInitiator(native, send, receive);116}117118export async function rustNoiseNkResponder(119  ourStaticPrivateKey: Uint8Array,120  ourStaticPublicKey: Uint8Array,121  send: (message: Uint8Array) => Promise<void>,122  receive: () => Promise<Uint8Array>,123  ephemeral?: RustNoiseKeyPair,124): Promise<RustNoiseSession> {125  ensureInitialized();126  const native = ephemeral127    ? WasmNoiseSession.nkResponderWithEphemeralForTest(128        ourStaticPrivateKey,129        ourStaticPublicKey,130        ephemeral.secretKey,131      )132    : WasmNoiseSession.nkResponder(ourStaticPrivateKey, ourStaticPublicKey);133  return driveResponder(native, send, receive);134}135136export async function rustNoiseKkInitiator(137  ourStaticPrivateKey: Uint8Array,138  ourStaticPublicKey: Uint8Array,139  responderStaticPublicKey: Uint8Array,140  send: (message: Uint8Array) => Promise<void>,141  receive: () => Promise<Uint8Array>,142  ephemeral?: RustNoiseKeyPair,143): Promise<RustNoiseSession> {144  ensureInitialized();145  const native = ephemeral146    ? WasmNoiseSession.kkInitiatorWithEphemeralForTest(147        ourStaticPrivateKey,148        ourStaticPublicKey,149        responderStaticPublicKey,150        ephemeral.secretKey,151      )152    : WasmNoiseSession.kkInitiator(153        ourStaticPrivateKey,154        ourStaticPublicKey,155        responderStaticPublicKey,156      );157  return driveInitiator(native, send, receive);158}159160export async function rustNoiseKkResponder(161  ourStaticPrivateKey: Uint8Array,162  ourStaticPublicKey: Uint8Array,163  expectedInitiatorStaticPublicKey: Uint8Array,164  send: (message: Uint8Array) => Promise<void>,165  receive: () => Promise<Uint8Array>,166  ephemeral?: RustNoiseKeyPair,167): Promise<RustNoiseSession> {168  ensureInitialized();169  const native = ephemeral170    ? WasmNoiseSession.kkResponderWithEphemeralForTest(171        ourStaticPrivateKey,172        ourStaticPublicKey,173        expectedInitiatorStaticPublicKey,174        ephemeral.secretKey,175      )176    : WasmNoiseSession.kkResponder(177        ourStaticPrivateKey,178        ourStaticPublicKey,179        expectedInitiatorStaticPublicKey,180      );181  return driveResponder(native, send, receive);182}183