@theermite/morphic-wasm-core 2.0.0-alpha.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/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @theermite/morphic-wasm-core
2
+
3
+ Rust → WebAssembly critical paths for the Morphic Adaptation Engine.
4
+
5
+ **Status**: B-018 (live). NaCl box primitives shipped.
6
+ CDC ref: F-017 (Tri-layer Rust→WASM critical).
7
+
8
+ ## What it provides
9
+
10
+ NaCl-compatible authenticated encryption (Curve25519 + XSalsa20 + Poly1305)
11
+ via the audited [`crypto_box`](https://crates.io/crates/crypto_box) crate
12
+ (RustCrypto, pure Rust). Output is byte-identical to `tweetnacl.box`, so
13
+ ciphertexts produced by either side are interchangeable.
14
+
15
+ Exported API (TypeScript via `wasm-bindgen`):
16
+
17
+ | Function | Returns | Purpose |
18
+ |----------|---------|---------|
19
+ | `wasmGenerateKeypair()` | `WasmKeyPair` (publicKey + secretKey) | Curve25519 key pair via OsRng (Web Crypto in browser) |
20
+ | `wasmGenerateNonce()` | `Uint8Array` (24 bytes) | Random XSalsa20 nonce |
21
+ | `wasmRandomBytes(len)` | `Uint8Array` | CSPRNG bytes |
22
+ | `wasmEncryptBox(plaintext, recipientPk, senderSk, nonce)` | `Uint8Array` | Authenticated ciphertext (plaintext + 16-byte Poly1305 tag) |
23
+ | `wasmDecryptBox(ciphertext, nonce, senderPk, recipientSk)` | `Uint8Array` | Plaintext, or throws `JsError` on auth failure |
24
+
25
+ ## Build
26
+
27
+ ```bash
28
+ pnpm --filter @theermite/morphic-wasm-core build # target web
29
+ pnpm --filter @theermite/morphic-wasm-core build:bundler # target bundler (Vite/webpack)
30
+ ```
31
+
32
+ Output lands in `pkg/` (ESM + `.d.ts` + raw `.wasm`, ~58 KB). The bundle
33
+ is loaded **lazily** by `packages/engine/src/wasm-bridge.ts` so projects
34
+ that don't need WASM crypto pay 0 KB.
35
+
36
+ ## Tests
37
+
38
+ ```bash
39
+ pnpm --filter @theermite/morphic-wasm-core test # native cargo tests
40
+ ```
41
+
42
+ Runs 9 tests, including 4 property-based tests × 1024 cases (= 4096
43
+ encrypt/decrypt round-trips) covering:
44
+
45
+ - Round-trip identity (`decrypt(encrypt(m)) == m`)
46
+ - Bit-flip tamper detection (Poly1305 catches every alteration)
47
+ - Wrong-nonce rejection
48
+ - Wrong-key rejection (with a positive sanity check inside)
49
+
50
+ Plus 5 deterministic fixtures (key/nonce lengths, tag overhead, empty
51
+ plaintext, truncated ciphertext).
52
+
53
+ ## Why Rust → WASM (vs staying on tweetnacl-js)
54
+
55
+ - **Maintenance**: `tweetnacl-js` is unmaintained since 2020. `crypto_box`
56
+ is part of the actively-maintained RustCrypto ecosystem.
57
+ - **Audit surface**: pure-Rust, no JS legacy. Smaller dependency tree.
58
+ - **Performance**: WASM crypto ~2-5× faster than JS for sustained workloads
59
+ (large message batches, many keypairs).
60
+ - **Determinism**: same binary across Node, Deno, browsers — no engine
61
+ variance on a critical path.
62
+
63
+ ## Defensive assertions (PET §5)
64
+
65
+ | Function | Assertions |
66
+ |----------|-----------|
67
+ | `wasm_encrypt_box` / `wasm_decrypt_box` | key lengths = 32 bytes; nonce length = 24 bytes |
68
+ | `wasm_decrypt_box` | authentication failures surface as `Err` (no silent corruption) |
69
+ | `wasm_generate_keypair` | uses `OsRng` (browser Web Crypto via `getrandom` js feature) |
70
+
71
+ Length checks pulled into a single `validate_box_inputs` helper to keep
72
+ the audit trail concentrated.
73
+
74
+ ## License
75
+
76
+ AGPL-3.0-or-later (matches the rest of the Morphic Engine).
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@theermite/morphic-wasm-core",
3
+ "version": "2.0.0-alpha.0",
4
+ "description": "Shinkofa Morphic Adaptation — Rust→WASM critical paths (NaCl box, B-018)",
5
+ "license": "AGPL-3.0-or-later",
6
+ "type": "module",
7
+ "main": "./pkg/morphic_wasm_core.js",
8
+ "types": "./pkg/morphic_wasm_core.d.ts",
9
+ "files": [
10
+ "pkg/",
11
+ "README.md"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public",
15
+ "registry": "https://registry.npmjs.org/"
16
+ },
17
+ "scripts": {
18
+ "build": "wasm-pack build --target web --release && rm -f pkg/.gitignore",
19
+ "build:bundler": "wasm-pack build --target bundler --release && rm -f pkg/.gitignore",
20
+ "test": "cargo test --release",
21
+ "test:proptest": "cargo test --release --test proptest_roundtrip",
22
+ "clean": "rm -rf pkg target"
23
+ }
24
+ }
package/pkg/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @morphic/wasm-core
2
+
3
+ Rust → WebAssembly critical paths for the Morphic Adaptation Engine.
4
+
5
+ **Status**: B-018 (live). NaCl box primitives shipped.
6
+ CDC ref: F-017 (Tri-layer Rust→WASM critical).
7
+
8
+ ## What it provides
9
+
10
+ NaCl-compatible authenticated encryption (Curve25519 + XSalsa20 + Poly1305)
11
+ via the audited [`crypto_box`](https://crates.io/crates/crypto_box) crate
12
+ (RustCrypto, pure Rust). Output is byte-identical to `tweetnacl.box`, so
13
+ ciphertexts produced by either side are interchangeable.
14
+
15
+ Exported API (TypeScript via `wasm-bindgen`):
16
+
17
+ | Function | Returns | Purpose |
18
+ |----------|---------|---------|
19
+ | `wasmGenerateKeypair()` | `WasmKeyPair` (publicKey + secretKey) | Curve25519 key pair via OsRng (Web Crypto in browser) |
20
+ | `wasmGenerateNonce()` | `Uint8Array` (24 bytes) | Random XSalsa20 nonce |
21
+ | `wasmRandomBytes(len)` | `Uint8Array` | CSPRNG bytes |
22
+ | `wasmEncryptBox(plaintext, recipientPk, senderSk, nonce)` | `Uint8Array` | Authenticated ciphertext (plaintext + 16-byte Poly1305 tag) |
23
+ | `wasmDecryptBox(ciphertext, nonce, senderPk, recipientSk)` | `Uint8Array` | Plaintext, or throws `JsError` on auth failure |
24
+
25
+ ## Build
26
+
27
+ ```bash
28
+ pnpm --filter @morphic/wasm-core build # target web
29
+ pnpm --filter @morphic/wasm-core build:bundler # target bundler (Vite/webpack)
30
+ ```
31
+
32
+ Output lands in `pkg/` (ESM + `.d.ts` + raw `.wasm`, ~58 KB). The bundle
33
+ is loaded **lazily** by `packages/engine/src/wasm-bridge.ts` so projects
34
+ that don't need WASM crypto pay 0 KB.
35
+
36
+ ## Tests
37
+
38
+ ```bash
39
+ pnpm --filter @morphic/wasm-core test # native cargo tests
40
+ ```
41
+
42
+ Runs 9 tests, including 4 property-based tests × 1024 cases (= 4096
43
+ encrypt/decrypt round-trips) covering:
44
+
45
+ - Round-trip identity (`decrypt(encrypt(m)) == m`)
46
+ - Bit-flip tamper detection (Poly1305 catches every alteration)
47
+ - Wrong-nonce rejection
48
+ - Wrong-key rejection (with a positive sanity check inside)
49
+
50
+ Plus 5 deterministic fixtures (key/nonce lengths, tag overhead, empty
51
+ plaintext, truncated ciphertext).
52
+
53
+ ## Why Rust → WASM (vs staying on tweetnacl-js)
54
+
55
+ - **Maintenance**: `tweetnacl-js` is unmaintained since 2020. `crypto_box`
56
+ is part of the actively-maintained RustCrypto ecosystem.
57
+ - **Audit surface**: pure-Rust, no JS legacy. Smaller dependency tree.
58
+ - **Performance**: WASM crypto ~2-5× faster than JS for sustained workloads
59
+ (large message batches, many keypairs).
60
+ - **Determinism**: same binary across Node, Deno, browsers — no engine
61
+ variance on a critical path.
62
+
63
+ ## Defensive assertions (PET §5)
64
+
65
+ | Function | Assertions |
66
+ |----------|-----------|
67
+ | `wasm_encrypt_box` / `wasm_decrypt_box` | key lengths = 32 bytes; nonce length = 24 bytes |
68
+ | `wasm_decrypt_box` | authentication failures surface as `Err` (no silent corruption) |
69
+ | `wasm_generate_keypair` | uses `OsRng` (browser Web Crypto via `getrandom` js feature) |
70
+
71
+ Length checks pulled into a single `validate_box_inputs` helper to keep
72
+ the audit trail concentrated.
73
+
74
+ ## License
75
+
76
+ AGPL-3.0-or-later (matches the rest of the Morphic Engine).
@@ -0,0 +1,98 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * Result type for a generated key pair, exposed to JS as a struct with
6
+ * `publicKey` and `secretKey` getters returning Uint8Array.
7
+ */
8
+ export class WasmKeyPair {
9
+ private constructor();
10
+ free(): void;
11
+ [Symbol.dispose](): void;
12
+ readonly publicKey: Uint8Array;
13
+ readonly secretKey: Uint8Array;
14
+ }
15
+
16
+ /**
17
+ * Decrypt `ciphertext` using NaCl box.open.
18
+ *
19
+ * Inputs:
20
+ * - ciphertext: bytes produced by `wasm_encrypt_box` (or any NaCl-compatible
21
+ * box implementation, including tweetnacl-js)
22
+ * - nonce: 24 bytes used at encryption time
23
+ * - sender_pk: 32 bytes (peer's public key)
24
+ * - recipient_sk: 32 bytes (our secret key)
25
+ *
26
+ * Returns the plaintext, or Err on any authentication failure (wrong key,
27
+ * tampered ciphertext, wrong nonce, truncated input).
28
+ */
29
+ export function wasmDecryptBox(ciphertext: Uint8Array, nonce: Uint8Array, sender_pk: Uint8Array, recipient_sk: Uint8Array): Uint8Array;
30
+
31
+ /**
32
+ * Encrypt `plaintext` using NaCl box (Curve25519 + XSalsa20Poly1305).
33
+ *
34
+ * Inputs:
35
+ * - plaintext: arbitrary bytes
36
+ * - recipient_pk: 32 bytes (Curve25519 public key)
37
+ * - sender_sk: 32 bytes (Curve25519 secret key)
38
+ * - nonce: 24 bytes (must be unique per (sender, recipient) pair)
39
+ *
40
+ * Output: ciphertext bytes (includes 16-byte Poly1305 tag).
41
+ */
42
+ export function wasmEncryptBox(plaintext: Uint8Array, recipient_pk: Uint8Array, sender_sk: Uint8Array, nonce: Uint8Array): Uint8Array;
43
+
44
+ /**
45
+ * Generate a Curve25519 key pair using the system CSPRNG
46
+ * (Web Crypto API in browser via `getrandom` js feature).
47
+ */
48
+ export function wasmGenerateKeypair(): WasmKeyPair;
49
+
50
+ /**
51
+ * Sample a fresh nonce using the construction's recommended generator.
52
+ */
53
+ export function wasmGenerateNonce(): Uint8Array;
54
+
55
+ /**
56
+ * Generate `len` random bytes using the system CSPRNG.
57
+ */
58
+ export function wasmRandomBytes(len: number): Uint8Array;
59
+
60
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
61
+
62
+ export interface InitOutput {
63
+ readonly memory: WebAssembly.Memory;
64
+ readonly __wbg_wasmkeypair_free: (a: number, b: number) => void;
65
+ readonly wasmDecryptBox: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => void;
66
+ readonly wasmEncryptBox: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => void;
67
+ readonly wasmGenerateKeypair: () => number;
68
+ readonly wasmGenerateNonce: (a: number) => void;
69
+ readonly wasmRandomBytes: (a: number, b: number) => void;
70
+ readonly wasmkeypair_publicKey: (a: number, b: number) => void;
71
+ readonly wasmkeypair_secretKey: (a: number, b: number) => void;
72
+ readonly __wbindgen_export: (a: number) => void;
73
+ readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
74
+ readonly __wbindgen_export2: (a: number, b: number) => number;
75
+ readonly __wbindgen_export3: (a: number, b: number, c: number) => void;
76
+ }
77
+
78
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
79
+
80
+ /**
81
+ * Instantiates the given `module`, which can either be bytes or
82
+ * a precompiled `WebAssembly.Module`.
83
+ *
84
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
85
+ *
86
+ * @returns {InitOutput}
87
+ */
88
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
89
+
90
+ /**
91
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
92
+ * for everything else, calls `WebAssembly.instantiate` directly.
93
+ *
94
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
95
+ *
96
+ * @returns {Promise<InitOutput>}
97
+ */
98
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;