@the9ines/bolt-core 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/crypto.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Generate a fresh ephemeral X25519 keypair for a single connection.
3
3
  * Discard after session ends.
4
+ *
5
+ * RB3: Uses Rust/WASM when available, falls back to tweetnacl.
4
6
  */
5
7
  export declare function generateEphemeralKeyPair(): {
6
8
  publicKey: Uint8Array;
@@ -10,12 +12,8 @@ export declare function generateEphemeralKeyPair(): {
10
12
  * Seal a plaintext payload using NaCl box (XSalsa20-Poly1305).
11
13
  *
12
14
  * Wire format: base64(nonce || ciphertext)
13
- * This matches the exact format used by all current product repos.
14
15
  *
15
- * @param plaintext - Raw bytes to encrypt
16
- * @param remotePublicKey - Receiver's ephemeral public key (32 bytes)
17
- * @param senderSecretKey - Sender's ephemeral secret key (32 bytes)
18
- * @returns base64-encoded string of nonce + ciphertext
16
+ * RB3: Uses Rust/WASM when available, falls back to tweetnacl.
19
17
  */
20
18
  export declare function sealBoxPayload(plaintext: Uint8Array, remotePublicKey: Uint8Array, senderSecretKey: Uint8Array): string;
21
19
  /**
@@ -23,9 +21,6 @@ export declare function sealBoxPayload(plaintext: Uint8Array, remotePublicKey: U
23
21
  *
24
22
  * Expects wire format: base64(nonce || ciphertext)
25
23
  *
26
- * @param sealed - base64-encoded string from sealBoxPayload
27
- * @param senderPublicKey - Sender's ephemeral public key (32 bytes)
28
- * @param receiverSecretKey - Receiver's ephemeral secret key (32 bytes)
29
- * @returns Decrypted plaintext bytes
24
+ * RB3: Uses Rust/WASM when available, falls back to tweetnacl.
30
25
  */
31
26
  export declare function openBoxPayload(sealed: string, senderPublicKey: Uint8Array, receiverSecretKey: Uint8Array): Uint8Array;
package/dist/crypto.js CHANGED
@@ -2,25 +2,30 @@ import tweetnacl from 'tweetnacl';
2
2
  const { box, randomBytes } = tweetnacl;
3
3
  import { toBase64, fromBase64 } from './encoding.js';
4
4
  import { EncryptionError } from './errors.js';
5
+ import { getWasmCrypto } from './wasm-crypto.js';
5
6
  /**
6
7
  * Generate a fresh ephemeral X25519 keypair for a single connection.
7
8
  * Discard after session ends.
9
+ *
10
+ * RB3: Uses Rust/WASM when available, falls back to tweetnacl.
8
11
  */
9
12
  export function generateEphemeralKeyPair() {
13
+ const wasm = getWasmCrypto();
14
+ if (wasm)
15
+ return wasm.generateEphemeralKeyPair();
10
16
  return box.keyPair();
11
17
  }
12
18
  /**
13
19
  * Seal a plaintext payload using NaCl box (XSalsa20-Poly1305).
14
20
  *
15
21
  * Wire format: base64(nonce || ciphertext)
16
- * This matches the exact format used by all current product repos.
17
22
  *
18
- * @param plaintext - Raw bytes to encrypt
19
- * @param remotePublicKey - Receiver's ephemeral public key (32 bytes)
20
- * @param senderSecretKey - Sender's ephemeral secret key (32 bytes)
21
- * @returns base64-encoded string of nonce + ciphertext
23
+ * RB3: Uses Rust/WASM when available, falls back to tweetnacl.
22
24
  */
23
25
  export function sealBoxPayload(plaintext, remotePublicKey, senderSecretKey) {
26
+ const wasm = getWasmCrypto();
27
+ if (wasm)
28
+ return wasm.sealBoxPayload(plaintext, remotePublicKey, senderSecretKey);
24
29
  const nonce = randomBytes(box.nonceLength);
25
30
  const encrypted = box(plaintext, nonce, remotePublicKey, senderSecretKey);
26
31
  if (!encrypted)
@@ -35,12 +40,12 @@ export function sealBoxPayload(plaintext, remotePublicKey, senderSecretKey) {
35
40
  *
36
41
  * Expects wire format: base64(nonce || ciphertext)
37
42
  *
38
- * @param sealed - base64-encoded string from sealBoxPayload
39
- * @param senderPublicKey - Sender's ephemeral public key (32 bytes)
40
- * @param receiverSecretKey - Receiver's ephemeral secret key (32 bytes)
41
- * @returns Decrypted plaintext bytes
43
+ * RB3: Uses Rust/WASM when available, falls back to tweetnacl.
42
44
  */
43
45
  export function openBoxPayload(sealed, senderPublicKey, receiverSecretKey) {
46
+ const wasm = getWasmCrypto();
47
+ if (wasm)
48
+ return wasm.openBoxPayload(sealed, senderPublicKey, receiverSecretKey);
44
49
  const data = fromBase64(sealed);
45
50
  if (data.length < box.nonceLength) {
46
51
  throw new EncryptionError('Sealed payload too short');
@@ -7,9 +7,7 @@ export interface IdentityKeyPair {
7
7
  /**
8
8
  * Generate a persistent identity keypair (X25519).
9
9
  *
10
- * Identity keys are long-lived and stored by the transport layer.
11
- * They MUST NOT be sent through the signaling server — identity
12
- * material travels only inside encrypted DataChannel messages (HELLO).
10
+ * RB3: Uses Rust/WASM when available, falls back to tweetnacl.
13
11
  */
14
12
  export declare function generateIdentityKeyPair(): IdentityKeyPair;
15
13
  /**
package/dist/identity.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import tweetnacl from 'tweetnacl';
2
2
  const { box } = tweetnacl;
3
3
  import { BoltError } from './errors.js';
4
+ import { getWasmCrypto } from './wasm-crypto.js';
4
5
  /**
5
6
  * Generate a persistent identity keypair (X25519).
6
7
  *
7
- * Identity keys are long-lived and stored by the transport layer.
8
- * They MUST NOT be sent through the signaling server — identity
9
- * material travels only inside encrypted DataChannel messages (HELLO).
8
+ * RB3: Uses Rust/WASM when available, falls back to tweetnacl.
10
9
  */
11
10
  export function generateIdentityKeyPair() {
11
+ const wasm = getWasmCrypto();
12
+ if (wasm)
13
+ return wasm.generateIdentityKeyPair();
12
14
  return box.keyPair();
13
15
  }
14
16
  /**
package/dist/index.d.ts CHANGED
@@ -9,4 +9,6 @@ export type { IdentityKeyPair } from './identity.js';
9
9
  export { BoltError, EncryptionError, ConnectionError, TransferError, IntegrityError } from './errors.js';
10
10
  export { WIRE_ERROR_CODES, isValidWireErrorCode } from './errors.js';
11
11
  export type { WireErrorCode } from './errors.js';
12
+ export { initWasmCrypto, initWasmCryptoFromModule, getWasmCrypto, getWasmModule, createWasmBtrEngine, createWasmSendSession } from './wasm-crypto.js';
13
+ export type { WasmCryptoAdapter, WasmBtrEngineHandle, WasmBtrTransferCtxHandle, WasmSendSessionHandle } from './wasm-crypto.js';
12
14
  export * from './btr/index.js';
package/dist/index.js CHANGED
@@ -16,5 +16,7 @@ export { generateIdentityKeyPair, KeyMismatchError } from './identity.js';
16
16
  export { BoltError, EncryptionError, ConnectionError, TransferError, IntegrityError } from './errors.js';
17
17
  // Wire error code registry (PROTOCOL.md §10)
18
18
  export { WIRE_ERROR_CODES, isValidWireErrorCode } from './errors.js';
19
+ // WASM protocol adapter (RUSTIFY-BROWSER-CORE-1 RB3+RB4)
20
+ export { initWasmCrypto, initWasmCryptoFromModule, getWasmCrypto, getWasmModule, createWasmBtrEngine, createWasmSendSession } from './wasm-crypto.js';
19
21
  // Bolt Transfer Ratchet (BTR) — §16
20
22
  export * from './btr/index.js';
package/dist/sas.d.ts CHANGED
@@ -10,4 +10,10 @@
10
10
  * @param ephemeralB - Raw 32-byte ephemeral public key of peer B
11
11
  * @returns 6-character uppercase hex string (24 bits of entropy)
12
12
  */
13
+ /**
14
+ * Compute a 6-character SAS (Short Authentication String) per PROTOCOL.md.
15
+ *
16
+ * RB3: Uses Rust/WASM (sync) when available, falls back to TS Web Crypto (async).
17
+ * Return type remains Promise<string> for backward compatibility.
18
+ */
13
19
  export declare function computeSas(identityA: Uint8Array, identityB: Uint8Array, ephemeralA: Uint8Array, ephemeralB: Uint8Array): Promise<string>;
package/dist/sas.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { sha256, bufferToHex } from './hash.js';
2
2
  import { PUBLIC_KEY_LENGTH, SAS_LENGTH } from './constants.js';
3
+ import { getWasmCrypto } from './wasm-crypto.js';
3
4
  // CANONICAL: computeSas() is the ONLY SAS implementation in the Bolt ecosystem.
4
5
  // SAS verification is not yet surfaced in products. No SAS logic may exist in
5
6
  // transport or product packages. See scripts/verify-no-shadow-sas.sh.
@@ -35,7 +36,19 @@ function sort32(a, b) {
35
36
  * @param ephemeralB - Raw 32-byte ephemeral public key of peer B
36
37
  * @returns 6-character uppercase hex string (24 bits of entropy)
37
38
  */
39
+ /**
40
+ * Compute a 6-character SAS (Short Authentication String) per PROTOCOL.md.
41
+ *
42
+ * RB3: Uses Rust/WASM (sync) when available, falls back to TS Web Crypto (async).
43
+ * Return type remains Promise<string> for backward compatibility.
44
+ */
38
45
  export async function computeSas(identityA, identityB, ephemeralA, ephemeralB) {
46
+ // RB3: WASM path (sync — Rust SHA-256, no Web Crypto)
47
+ const wasm = getWasmCrypto();
48
+ if (wasm) {
49
+ return wasm.computeSas(identityA, identityB, ephemeralA, ephemeralB);
50
+ }
51
+ // TS fallback (async — Web Crypto digest)
39
52
  if (identityA.length !== PUBLIC_KEY_LENGTH || identityB.length !== PUBLIC_KEY_LENGTH) {
40
53
  throw new Error(`Identity keys must be ${PUBLIC_KEY_LENGTH} bytes`);
41
54
  }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * WASM-backed protocol adapter (RUSTIFY-BROWSER-CORE-1 RB3+RB4).
3
+ *
4
+ * RB3: crypto/session/SAS functions backed by Rust/WASM.
5
+ * RB4: BTR state (BtrEngine, BtrTransferCtx) + transfer state (SendSession)
6
+ * backed by Rust/WASM opaque handles.
7
+ *
8
+ * TS tweetnacl/BTR implementations remain as fallback (PM-RB-03: condition-gated).
9
+ */
10
+ export interface WasmCryptoAdapter {
11
+ generateEphemeralKeyPair(): {
12
+ publicKey: Uint8Array;
13
+ secretKey: Uint8Array;
14
+ };
15
+ generateIdentityKeyPair(): {
16
+ publicKey: Uint8Array;
17
+ secretKey: Uint8Array;
18
+ };
19
+ sealBoxPayload(plaintext: Uint8Array, remotePublicKey: Uint8Array, senderSecretKey: Uint8Array): string;
20
+ openBoxPayload(sealed: string, senderPublicKey: Uint8Array, receiverSecretKey: Uint8Array): Uint8Array;
21
+ computeSas(identityA: Uint8Array, identityB: Uint8Array, ephemeralA: Uint8Array, ephemeralB: Uint8Array): string;
22
+ generateSecurePeerCode(): string;
23
+ isValidPeerCode(code: string): boolean;
24
+ sha256Hex(data: Uint8Array): string;
25
+ }
26
+ /**
27
+ * Get the WASM crypto adapter, or null if not initialized or init failed.
28
+ * Callers should fall back to TS crypto if this returns null.
29
+ */
30
+ export declare function getWasmCrypto(): WasmCryptoAdapter | null;
31
+ /**
32
+ * Initialize WASM crypto from a pre-loaded WASM module.
33
+ *
34
+ * BR2: Accepts an already-loaded+initialized WASM module (provided by
35
+ * transport-web's initProtocolWasm()). This avoids bare module specifier
36
+ * issues — the loader lives in transport-web where the artifact is embedded.
37
+ *
38
+ * PM-RB-03: TS fallback remains operational if this is never called.
39
+ *
40
+ * @param wasmModule - The loaded bolt-protocol-wasm module (with exported functions)
41
+ */
42
+ export declare function initWasmCryptoFromModule(wasmModule: any): boolean;
43
+ /**
44
+ * Initialize WASM crypto. Legacy entry point — attempts dynamic import.
45
+ * Prefer initProtocolWasm() from @the9ines/bolt-transport-web instead.
46
+ */
47
+ export declare function initWasmCrypto(): Promise<boolean>;
48
+ /**
49
+ * Get the raw WASM module for constructing BTR/transfer handles.
50
+ * Returns null if WASM not initialized.
51
+ */
52
+ export declare function getWasmModule(): any;
53
+ /**
54
+ * Opaque BTR engine handle. Rust owns all key material.
55
+ * Wraps WasmBtrEngine from bolt-protocol-wasm.
56
+ *
57
+ * Usage:
58
+ * const engine = createWasmBtrEngine(sharedSecret);
59
+ * const ctx = engine.beginTransferSend(transferId, remoteRatchetPub);
60
+ * const { chainIndex, sealed } = ctx.sealChunk(plaintext);
61
+ * engine.free(); // zeroize when done
62
+ */
63
+ export interface WasmBtrEngineHandle {
64
+ beginTransferSend(transferId: Uint8Array, remoteRatchetPub: Uint8Array): WasmBtrTransferCtxHandle;
65
+ beginTransferReceive(transferId: Uint8Array, remoteRatchetPub: Uint8Array): WasmBtrTransferCtxHandle;
66
+ ratchetGeneration(): number;
67
+ endTransfer(): void;
68
+ cleanupDisconnect(): void;
69
+ free(): void;
70
+ }
71
+ export interface WasmBtrTransferCtxHandle {
72
+ sealChunk(plaintext: Uint8Array): {
73
+ chainIndex: number;
74
+ sealed: Uint8Array;
75
+ };
76
+ openChunk(expectedIndex: number, sealed: Uint8Array): Uint8Array;
77
+ chainIndex(): number;
78
+ generation(): number;
79
+ transferId(): Uint8Array;
80
+ localRatchetPub(): Uint8Array;
81
+ cleanupComplete(): void;
82
+ cleanupCancel(): void;
83
+ free(): void;
84
+ }
85
+ export interface WasmSendSessionHandle {
86
+ beginSend(transferId: string, payload: Uint8Array, filename: string, fileHash?: string): {
87
+ transferId: string;
88
+ filename: string;
89
+ size: number;
90
+ totalChunks: number;
91
+ chunkSize: number;
92
+ fileHash?: string;
93
+ };
94
+ onAccept(transferId: string): void;
95
+ onCancel(transferId: string): void;
96
+ onPause(transferId: string): void;
97
+ onResume(transferId: string): void;
98
+ nextChunk(): {
99
+ transferId: string;
100
+ chunkIndex: number;
101
+ totalChunks: number;
102
+ data: Uint8Array;
103
+ } | null;
104
+ finish(): string;
105
+ state(): string;
106
+ isSendActive(): boolean;
107
+ free(): void;
108
+ }
109
+ /**
110
+ * Create a WASM-backed BTR engine. Returns null if WASM not available.
111
+ * Caller must call .free() when done to zeroize key material.
112
+ */
113
+ export declare function createWasmBtrEngine(sharedSecret: Uint8Array): WasmBtrEngineHandle | null;
114
+ /**
115
+ * Create a WASM-backed send session. Returns null if WASM not available.
116
+ * Rust owns transfer-state transitions. TS proposes events; Rust validates.
117
+ */
118
+ export declare function createWasmSendSession(): WasmSendSessionHandle | null;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * WASM-backed protocol adapter (RUSTIFY-BROWSER-CORE-1 RB3+RB4).
3
+ *
4
+ * RB3: crypto/session/SAS functions backed by Rust/WASM.
5
+ * RB4: BTR state (BtrEngine, BtrTransferCtx) + transfer state (SendSession)
6
+ * backed by Rust/WASM opaque handles.
7
+ *
8
+ * TS tweetnacl/BTR implementations remain as fallback (PM-RB-03: condition-gated).
9
+ */
10
+ /** WASM adapter instance. null until initWasmCrypto() succeeds. */
11
+ let _wasmCrypto = null;
12
+ /** Whether WASM init has been attempted. */
13
+ let _initAttempted = false;
14
+ /**
15
+ * Get the WASM crypto adapter, or null if not initialized or init failed.
16
+ * Callers should fall back to TS crypto if this returns null.
17
+ */
18
+ export function getWasmCrypto() {
19
+ return _wasmCrypto;
20
+ }
21
+ /**
22
+ * Initialize WASM crypto from a pre-loaded WASM module.
23
+ *
24
+ * BR2: Accepts an already-loaded+initialized WASM module (provided by
25
+ * transport-web's initProtocolWasm()). This avoids bare module specifier
26
+ * issues — the loader lives in transport-web where the artifact is embedded.
27
+ *
28
+ * PM-RB-03: TS fallback remains operational if this is never called.
29
+ *
30
+ * @param wasmModule - The loaded bolt-protocol-wasm module (with exported functions)
31
+ */
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ export function initWasmCryptoFromModule(wasmModule) {
34
+ if (_wasmCrypto)
35
+ return true;
36
+ try {
37
+ _wasmModule = wasmModule;
38
+ _wasmCrypto = {
39
+ generateEphemeralKeyPair: () => wasmModule.generateEphemeralKeyPair(),
40
+ generateIdentityKeyPair: () => wasmModule.generateIdentityKeyPair(),
41
+ sealBoxPayload: (p, rpk, sk) => wasmModule.sealBoxPayload(p, rpk, sk),
42
+ openBoxPayload: (s, spk, rsk) => wasmModule.openBoxPayload(s, spk, rsk),
43
+ computeSas: (ia, ib, ea, eb) => wasmModule.computeSas(ia, ib, ea, eb),
44
+ generateSecurePeerCode: () => wasmModule.generateSecurePeerCode(),
45
+ isValidPeerCode: (c) => wasmModule.isValidPeerCode(c),
46
+ sha256Hex: (d) => wasmModule.sha256Hex(d),
47
+ };
48
+ console.log('[BOLT-WASM] Protocol authority initialized (Rust/WASM: crypto + BTR + transfer)');
49
+ return true;
50
+ }
51
+ catch (e) {
52
+ console.warn('[BOLT-WASM] Failed to initialize from module:', e);
53
+ _wasmCrypto = null;
54
+ _wasmModule = null;
55
+ return false;
56
+ }
57
+ }
58
+ /**
59
+ * Initialize WASM crypto. Legacy entry point — attempts dynamic import.
60
+ * Prefer initProtocolWasm() from @the9ines/bolt-transport-web instead.
61
+ */
62
+ export async function initWasmCrypto() {
63
+ if (_initAttempted)
64
+ return _wasmCrypto !== null;
65
+ _initAttempted = true;
66
+ try {
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
+ const wasm = await (Function('return import("bolt-protocol-wasm")')());
69
+ await wasm.default();
70
+ return initWasmCryptoFromModule(wasm);
71
+ }
72
+ catch (e) {
73
+ console.warn('[BOLT-WASM] Failed to initialize — falling back to TS protocol:', e);
74
+ _wasmCrypto = null;
75
+ return false;
76
+ }
77
+ }
78
+ // ══════════════════════════════════════════════════════════════════
79
+ // RB4: BTR + Transfer State Authority
80
+ // ══════════════════════════════════════════════════════════════════
81
+ /** WASM module reference for constructing opaque handles. */
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
+ let _wasmModule = null;
84
+ /**
85
+ * Get the raw WASM module for constructing BTR/transfer handles.
86
+ * Returns null if WASM not initialized.
87
+ */
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ export function getWasmModule() {
90
+ return _wasmModule;
91
+ }
92
+ /**
93
+ * Create a WASM-backed BTR engine. Returns null if WASM not available.
94
+ * Caller must call .free() when done to zeroize key material.
95
+ */
96
+ export function createWasmBtrEngine(sharedSecret) {
97
+ if (!_wasmModule)
98
+ return null;
99
+ try {
100
+ return new _wasmModule.WasmBtrEngine(sharedSecret);
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ /**
107
+ * Create a WASM-backed send session. Returns null if WASM not available.
108
+ * Rust owns transfer-state transitions. TS proposes events; Rust validates.
109
+ */
110
+ export function createWasmSendSession() {
111
+ if (!_wasmModule)
112
+ return null;
113
+ try {
114
+ return new _wasmModule.WasmSendSession();
115
+ }
116
+ catch {
117
+ return null;
118
+ }
119
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@the9ines/bolt-core",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Bolt Protocol core crypto primitives and utilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,8 +15,8 @@
15
15
  "build": "tsc",
16
16
  "test": "vitest run",
17
17
  "audit-exports": "node scripts/audit-exports.mjs",
18
- "generate-vectors": "node scripts/print-test-vectors.mjs",
19
- "check-vectors": "node scripts/print-test-vectors.mjs --check",
18
+ "generate-vectors": "echo '[DEPRECATED] Use: cargo test -p bolt-core --features vectors --test vector_golden_core' && node scripts/print-test-vectors.mjs",
19
+ "check-vectors": "echo '[DEPRECATED] Canonical vectors are now Rust-generated. Use: cargo test -p bolt-core --features vectors' && node scripts/print-test-vectors.mjs --check",
20
20
  "prepublishOnly": "npm run build"
21
21
  },
22
22
  "dependencies": {