1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 13:40:06 +00:00

Use new SDK interface

This commit is contained in:
Katherine Reynolds
2025-11-20 15:54:28 -08:00
parent 0c5356178b
commit 2b482bf385
3 changed files with 42 additions and 193 deletions

View File

@@ -1,63 +1,25 @@
import { Injectable } from "@angular/core";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import * as sdk from "@bitwarden/sdk-internal";
type KeyPair = { publicKey: Uint8Array; secretKey: Uint8Array };
/**
* Noise Protocol WASM adapter for browser extension
* Wraps the Rust/WASM implementation to provide secure Noise_XXpsk3_25519_AESGCM_SHA256 handshakes
* Noise Protocol service using Bitwarden SDK
* Wraps the SDK's Noise Protocol implementation to provide secure Noise_XXpsk3_25519_AESGCM_SHA256 handshakes
*/
@Injectable({
providedIn: "root",
})
export class NoiseProtocolService {
private wasmModule: any = null;
private isInitialized = false;
constructor(private logService: LogService) {}
/**
* Initialize the WASM module
* Must be called before using any other methods
*/
async initialize(): Promise<void> {
if (this.isInitialized) {
return;
}
try {
// Import the WASM module - hardcoded path for demo
// The WASM file will be in the build output directory alongside other assets
const wasmModule = await import("./rust_noise.js");
// Initialize WASM - for --target web, the default export is the init function
if (typeof wasmModule.default === "function") {
// Pass the WASM file path explicitly to avoid import.meta.url issues in bundled code
await wasmModule.default("./rust_noise_bg.wasm");
} else if (typeof wasmModule.init === "function") {
// Fallback for other targets
await wasmModule.init();
}
this.wasmModule = wasmModule;
this.isInitialized = true;
this.logService.info("[NoiseProtocol] WASM module initialized");
} catch (error) {
this.logService.error("[NoiseProtocol] Failed to initialize WASM module:", error);
throw new Error(`Failed to initialize Noise Protocol WASM: ${error}`);
}
}
/**
* Create a new Noise Protocol instance
*/
createProtocol(isInitiator: boolean, staticKeypair?: KeyPair, psk?: Uint8Array): NoiseProtocol {
if (!this.isInitialized || !this.wasmModule) {
throw new Error("NoiseProtocolService not initialized. Call initialize() first.");
}
return new NoiseProtocol(this.wasmModule, isInitiator, staticKeypair, psk, this.logService);
return new NoiseProtocol(isInitiator, staticKeypair, psk, this.logService);
}
/**
@@ -69,8 +31,6 @@ export class NoiseProtocolService {
staticKeypair?: KeyPair,
psk?: Uint8Array,
): Promise<NoiseProtocol> {
await this.initialize();
const noise = this.createProtocol(isInitiator, staticKeypair, psk);
if (isInitiator) {
@@ -98,35 +58,42 @@ export class NoiseProtocolService {
}
/**
* Noise Protocol wrapper using Rust/WASM implementation
* Noise Protocol wrapper using Bitwarden SDK
* Implements Noise_XXpsk3_25519_AESGCM_SHA256 pattern
*/
export class NoiseProtocol {
private wasmProtocol: any;
private isInitiator: boolean;
private handle: number;
constructor(
wasmModule: any,
isInitiator: boolean,
staticKeypair?: KeyPair,
psk?: Uint8Array,
private logService?: LogService,
) {
this.isInitiator = isInitiator;
try {
// Convert optional parameters
const staticSecretKey = staticKeypair ? staticKeypair.secretKey : null;
const pskBytes = psk ? psk : null;
// Create WASM protocol instance
this.wasmProtocol = new wasmModule.NoiseProtocol(isInitiator, staticSecretKey, pskBytes);
this.log(
`Noise XX handshake initialized (${isInitiator ? "initiator" : "responder"}, PSK: ${psk ? "yes" : "no"})`,
`Creating Noise protocol instance (${isInitiator ? "initiator" : "responder"}, ` +
`staticKey: ${staticSecretKey ? staticSecretKey.length : "none"} bytes, ` +
`PSK: ${pskBytes ? pskBytes.length : "none"} bytes)`,
);
// Create SDK protocol instance using the built-in noise functions
this.handle = sdk.create_noise_protocol(isInitiator, staticSecretKey, pskBytes);
if (this.handle === undefined || this.handle === null) {
throw new Error("Failed to create NoiseProtocol - SDK returned invalid handle");
}
this.log(`Noise XX handshake initialized successfully (handle: ${this.handle})`);
} catch (error) {
this.logError("Failed to create WASM NoiseProtocol:", error);
this.logError("Failed to create SDK NoiseProtocol:", error);
if (error instanceof Error) {
this.logError("Error stack:", error.stack);
}
throw error;
}
}
@@ -137,7 +104,7 @@ export class NoiseProtocol {
writeMessage(payload?: Uint8Array): Uint8Array {
try {
const payloadArray = payload && payload.length > 0 ? payload : null;
const message = this.wasmProtocol.writeMessage(payloadArray);
const message = sdk.noise_write_message(this.handle, payloadArray);
this.log(`Sent handshake message (length: ${message.length})`);
return message;
@@ -152,7 +119,7 @@ export class NoiseProtocol {
*/
readMessage(message: Uint8Array): Uint8Array {
try {
const payload = this.wasmProtocol.readMessage(message);
const payload = sdk.noise_read_message(this.handle, message);
this.log(
`Received handshake message (length: ${message.length}, payload: ${payload.length})`,
@@ -169,7 +136,7 @@ export class NoiseProtocol {
*/
split(): void {
try {
this.wasmProtocol.split();
sdk.noise_split(this.handle);
this.log("Handshake complete - transport keys derived");
} catch (error) {
this.logError("Failed to split:", error);
@@ -182,7 +149,7 @@ export class NoiseProtocol {
*/
encryptMessage(plaintext: Uint8Array): Uint8Array {
try {
const ciphertext = this.wasmProtocol.encryptMessage(plaintext);
const ciphertext = sdk.noise_encrypt_message(this.handle, plaintext);
this.log(`Message encrypted (length: ${ciphertext.length})`);
return ciphertext;
@@ -197,7 +164,7 @@ export class NoiseProtocol {
*/
decryptMessage(ciphertext: Uint8Array): Uint8Array {
try {
const plaintext = this.wasmProtocol.decryptMessage(ciphertext);
const plaintext = sdk.noise_decrypt_message(this.handle, ciphertext);
this.log(`Message decrypted (length: ${plaintext.length})`);
return plaintext;
@@ -211,15 +178,27 @@ export class NoiseProtocol {
* Check if handshake is complete
*/
isHandshakeComplete(): boolean {
return this.wasmProtocol.isHandshakeComplete();
return sdk.noise_is_handshake_complete(this.handle);
}
/**
* Get static public key
* Note: WASM implementation doesn't expose this directly
* Note: SDK implementation doesn't expose this directly
*/
getStaticPublicKey(): Uint8Array {
throw new Error("getStaticPublicKey not yet implemented for WASM");
throw new Error("getStaticPublicKey not yet implemented for SDK Noise");
}
/**
* Destroy the protocol instance and free resources
*/
destroy(): void {
try {
sdk.destroy_noise_protocol(this.handle);
this.log("Noise protocol instance destroyed");
} catch (error) {
this.logError("Failed to destroy noise protocol:", error);
}
}
private log(message: string) {

View File

@@ -1,129 +0,0 @@
/* tslint:disable */
/**
* Generate a new Curve25519 keypair
*/
export function generate_keypair(): Keypair;
/**
* Initialize the WASM module
*/
export function init(): void;
/**
* Keypair structure
*/
export class Keypair {
free(): void;
[Symbol.dispose](): void;
constructor(public_key: Uint8Array, secret_key: Uint8Array);
readonly public_key: Uint8Array;
readonly secret_key: Uint8Array;
}
/**
* Noise Protocol state machine
*/
export class NoiseProtocol {
free(): void;
[Symbol.dispose](): void;
/**
* Read a handshake message from the peer
* Returns the payload contained in the message
*/
readMessage(message: Uint8Array): Uint8Array;
/**
* Write a handshake message
* Returns the message to send to the peer
*/
writeMessage(payload?: Uint8Array | null): Uint8Array;
/**
* Decrypt a message (after handshake is complete)
*/
decryptMessage(ciphertext: Uint8Array): Uint8Array;
/**
* Encrypt a message (after handshake is complete)
*/
encryptMessage(plaintext: Uint8Array): Uint8Array;
/**
* Check if handshake is complete
*/
isHandshakeComplete(): boolean;
/**
* Get the remote static public key (available after handshake)
*/
getRemoteStaticPublicKey(): Uint8Array;
/**
* Create a new Noise protocol instance
*
* # Arguments
* * `is_initiator` - Whether this is the initiator (true) or responder (false)
* * `static_keypair` - Optional static keypair (if None, generates new one)
* * `psk` - Optional pre-shared key for additional authentication
*/
constructor(
is_initiator: boolean,
static_secret_key?: Uint8Array | null,
psk?: Uint8Array | null,
);
/**
* Complete the handshake and derive transport keys
*/
split(): void;
}
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly __wbg_keypair_free: (a: number, b: number) => void;
readonly __wbg_noiseprotocol_free: (a: number, b: number) => void;
readonly generate_keypair: (a: number) => void;
readonly init: () => void;
readonly keypair_new: (a: number, b: number, c: number, d: number) => number;
readonly keypair_public_key: (a: number, b: number) => void;
readonly keypair_secret_key: (a: number, b: number) => void;
readonly noiseprotocol_decryptMessage: (a: number, b: number, c: number, d: number) => void;
readonly noiseprotocol_encryptMessage: (a: number, b: number, c: number, d: number) => void;
readonly noiseprotocol_getRemoteStaticPublicKey: (a: number, b: number) => void;
readonly noiseprotocol_isHandshakeComplete: (a: number) => number;
readonly noiseprotocol_new: (
a: number,
b: number,
c: number,
d: number,
e: number,
f: number,
) => void;
readonly noiseprotocol_readMessage: (a: number, b: number, c: number, d: number) => void;
readonly noiseprotocol_split: (a: number, b: number) => void;
readonly noiseprotocol_writeMessage: (a: number, b: number, c: number, d: number) => void;
readonly __wbindgen_export: (a: number) => void;
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
readonly __wbindgen_export2: (a: number, b: number) => number;
readonly __wbindgen_export3: (a: number, b: number, c: number) => void;
readonly __wbindgen_start: () => void;
}
export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
*
* @returns {InitOutput}
*/
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init(
module_or_path?:
| { module_or_path: InitInput | Promise<InitInput> }
| InitInput
| Promise<InitInput>,
): Promise<InitOutput>;

View File

@@ -336,7 +336,6 @@ export class TunnelClientService {
});
// Initialize Noise Protocol as responder
await this.noiseProtocolService.initialize();
this.noiseProtocol = this.noiseProtocolService.createProtocol(
false,
this.staticKeypair,