@f0rest8/metamorphic-crypto 0.3.3 → 0.3.4

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 CHANGED
@@ -1,113 +1,166 @@
1
- # metamorphic-crypto
1
+ # @f0rest8/metamorphic-crypto
2
2
 
3
- Zero-knowledge end-to-end encryption library with post-quantum hybrid KEM.
3
+ Zero-knowledge end-to-end encryption with post-quantum hybrid KEM (ML-KEM-768/1024 + X25519).
4
4
 
5
5
  Built for [Metamorphic](https://metamorphic.app) and [Mosslet](https://mosslet.com) — privacy-first apps by [Moss Piglet Corporation](https://mosspiglet.dev) where all user data is encrypted client-side and the server only stores opaque ciphertext.
6
6
 
7
- ## What this provides
7
+ This package is a WebAssembly build of the [metamorphic-crypto](https://github.com/moss-piglet/metamorphic-crypto) Rust crate.
8
8
 
9
- - **Secretbox** (XSalsa20-Poly1305) — symmetric authenticated encryption
10
- - **Sealed box** (X25519) — anonymous public-key encryption (libsodium-compatible)
11
- - **Hybrid PQ KEM** (ML-KEM-768 + X25519) — NIST Cat-3 post-quantum key encapsulation (default)
12
- - **Hybrid PQ KEM** (ML-KEM-1024 + X25519) — NIST Cat-5 post-quantum key encapsulation (opt-in)
13
- - **Argon2id KDF** — password-based key derivation (libsodium INTERACTIVE parameters)
14
- - **WASM bindings** — browser-ready via `wasm-pack`
15
- - **Recovery keys** — human-readable base32 encoding for key backup
9
+ ## Install
16
10
 
17
- ## Security levels
11
+ ```bash
12
+ npm install @f0rest8/metamorphic-crypto
13
+ ```
18
14
 
19
- | Level | ML-KEM | NIST Category | Equivalent | Version Tag | Default |
20
- |-------|--------|---------------|------------|-------------|---------|
21
- | Cat-3 | 768 | 3 | ~AES-192 | `0x02` | Yes |
22
- | Cat-5 | 1024 | 5 | ~AES-256 | `0x03` | No |
15
+ ## Quick start
23
16
 
24
- Both levels use the same combiner construction and X25519 classical fallback. `hybrid_open` auto-detects the level from the version tag byte — old and new ciphertext coexist seamlessly.
17
+ ```js
18
+ import init, {
19
+ generateKey,
20
+ encryptSecretboxString,
21
+ decryptSecretboxToString,
22
+ } from "@f0rest8/metamorphic-crypto";
23
+
24
+ // Initialize the WASM module (required once before calling any function)
25
+ await init();
26
+
27
+ // All functions are synchronous after init() and throw on error
28
+ const key = generateKey();
29
+ const ciphertext = encryptSecretboxString("sensitive data", key);
30
+ const plaintext = decryptSecretboxToString(ciphertext, key);
31
+ // plaintext === "sensitive data"
32
+ ```
25
33
 
26
- ## Security properties
34
+ ## API overview
27
35
 
28
- - `#![forbid(unsafe_code)]` no unsafe anywhere in the crate
29
- - All secret key material zeroized after use
30
- - Constant-time MAC comparison via RustCrypto
31
- - OS CSPRNG via `getrandom` (no userspace PRNG)
32
- - Hybrid construction: both ML-KEM AND X25519 must be broken to compromise a sealed key
36
+ All values are base64-encoded strings unless noted otherwise.
33
37
 
34
- ## Hybrid KEM construction
38
+ ### Key derivation (Argon2id)
35
39
 
36
- The hybrid combiner matches the format used by [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum)'s `ml_kem768_x25519`:
40
+ ```js
41
+ import { deriveSessionKey, generateSalt, parseSaltFromKeyHash } from "@f0rest8/metamorphic-crypto";
37
42
 
43
+ const salt = generateSalt(); // random 16-byte salt (base64)
44
+ const sessionKey = deriveSessionKey(password, salt); // 32-byte derived key (base64)
45
+ const salt2 = parseSaltFromKeyHash("base64salt$argon2id..."); // extract salt from stored hash
38
46
  ```
39
- Seed expansion: SHAKE256(seed_32) → 96 bytes [ML-KEM seed (64) || X25519 sk (32)]
40
- Combiner: SHA3-256(ss_mlkem || ss_x25519 || ct_x25519 || pk_x25519 || label)
47
+
48
+ ### Symmetric encryption (XSalsa20-Poly1305)
49
+
50
+ ```js
51
+ import {
52
+ generateKey,
53
+ encryptSecretboxString, decryptSecretboxToString,
54
+ encryptSecretbox, decryptSecretbox,
55
+ } from "@f0rest8/metamorphic-crypto";
56
+
57
+ const key = generateKey();
58
+
59
+ // String plaintext
60
+ const ct = encryptSecretboxString("hello", key);
61
+ const pt = decryptSecretboxToString(ct, key); // "hello"
62
+
63
+ // Binary plaintext (base64 in, base64 out)
64
+ const ctBin = encryptSecretbox(plaintextBase64, key);
65
+ const ptBin = decryptSecretbox(ctBin, key); // base64
41
66
  ```
42
67
 
43
- ### Cat-3 (ML-KEM-768, default)
68
+ ### Public-key encryption (X25519 sealed box)
69
+
70
+ ```js
71
+ import { generateKeyPair, boxSeal, boxSealOpen } from "@f0rest8/metamorphic-crypto";
72
+
73
+ const kp = generateKeyPair(); // { publicKey, privateKey }
74
+ const sealed = boxSeal(plaintextBase64, kp.publicKey);
75
+ const opened = boxSealOpen(sealed, kp.publicKey, kp.privateKey); // base64
44
76
  ```
45
- Public key: ML-KEM-768 ek (1184 B) || X25519 pk (32 B) = 1216 bytes
46
- Ciphertext: 0x02 || ML-KEM-768 ct (1088 B) || X25519 eph pk (32 B) || nonce (24 B) || secretbox ct
77
+
78
+ ### Hybrid PQ seal/unseal (ML-KEM + X25519)
79
+
80
+ The recommended API for encrypting data to a user. Uses post-quantum hybrid encryption when a PQ public key is available, falls back to legacy X25519 sealed box otherwise. Decryption auto-detects the format.
81
+
82
+ ```js
83
+ import {
84
+ generateKeyPair, generateHybridKeyPair,
85
+ sealForUser, unsealFromUser,
86
+ } from "@f0rest8/metamorphic-crypto";
87
+
88
+ // Generate keys
89
+ const x25519 = generateKeyPair(); // { publicKey, privateKey }
90
+ const pq = generateHybridKeyPair(); // { publicKey, secretKey } (ML-KEM-768)
91
+
92
+ // Seal (encrypt) — pass both public keys
93
+ const sealed = sealForUser(plaintextBase64, x25519.publicKey, pq.publicKey);
94
+
95
+ // Unseal (decrypt) — pass both secret keys, auto-detects format
96
+ const opened = unsealFromUser(sealed, x25519.publicKey, x25519.privateKey, pq.secretKey);
47
97
  ```
48
98
 
49
99
  ### Cat-5 (ML-KEM-1024, opt-in)
50
- ```
51
- Public key: ML-KEM-1024 ek (1568 B) || X25519 pk (32 B) = 1600 bytes
52
- Ciphertext: 0x03 || ML-KEM-1024 ct (1568 B) || X25519 eph pk (32 B) || nonce (24 B) || secretbox ct
53
- ```
54
100
 
55
- ## Targets
56
-
57
- | Target | Build | Use case |
58
- |--------|-------|----------|
59
- | Native | `cargo build` | Tests, CLI tools, Elixir NIF (`metamorphic_crypto` Hex package) |
60
- | WASM | `wasm-pack build --target web` | Browser (Phoenix LiveView, any SPA) |
61
- | iOS | UniFFI (planned) | Native Swift apps |
62
- | Android | UniFFI (planned) | Native Kotlin apps |
63
-
64
- ## Usage
65
-
66
- ```rust
67
- use metamorphic_crypto::{generate_key, encrypt_secretbox_string, decrypt_secretbox_to_string};
68
- use metamorphic_crypto::{generate_hybrid_keypair, hybrid_seal, hybrid_open};
69
- use metamorphic_crypto::{generate_hybrid_keypair_1024, hybrid_seal_1024};
70
-
71
- // Symmetric encryption
72
- let key = generate_key();
73
- let ciphertext = encrypt_secretbox_string("sensitive data", &key).unwrap();
74
- let plaintext = decrypt_secretbox_to_string(&ciphertext, &key).unwrap();
75
- assert_eq!(plaintext, "sensitive data");
76
-
77
- // Hybrid PQ seal (Cat-3, default)
78
- let kp = generate_hybrid_keypair();
79
- let sealed = hybrid_seal(b"context_key_bytes", &kp.public_key).unwrap();
80
- let opened = hybrid_open(&sealed, &kp.secret_key).unwrap();
81
-
82
- // Hybrid PQ seal (Cat-5)
83
- let kp5 = generate_hybrid_keypair_1024();
84
- let sealed5 = hybrid_seal_1024(b"context_key_bytes", &kp5.public_key).unwrap();
85
- let opened5 = hybrid_open(&sealed5, &kp5.secret_key).unwrap(); // auto-detects level
101
+ ```js
102
+ import { generateHybridKeyPair1024, sealForUserWithLevel } from "@f0rest8/metamorphic-crypto";
103
+
104
+ const pq5 = generateHybridKeyPair1024(); // { publicKey, secretKey } (ML-KEM-1024)
105
+ const sealed = sealForUserWithLevel(plaintextBase64, x25519.publicKey, pq5.publicKey, "cat5");
106
+ // unsealFromUser auto-detects Cat-3 vs Cat-5, no level param needed
86
107
  ```
87
108
 
88
- ## WASM (browser)
109
+ ### Private key management
89
110
 
90
- ```bash
91
- wasm-pack build --target web --release
111
+ ```js
112
+ import { encryptPrivateKey, decryptPrivateKey } from "@f0rest8/metamorphic-crypto";
113
+
114
+ const encrypted = encryptPrivateKey(privateKeyBase64, sessionKeyBase64);
115
+ const decrypted = decryptPrivateKey(encrypted, sessionKeyBase64);
92
116
  ```
93
117
 
118
+ ### Recovery keys
119
+
94
120
  ```js
95
- import init, { deriveSessionKey, encryptSecretboxString } from './pkg/metamorphic_crypto.js';
121
+ import {
122
+ generateRecoveryKey, recoveryKeyToSecret,
123
+ encryptPrivateKeyForRecovery, decryptPrivateKeyWithRecovery,
124
+ } from "@f0rest8/metamorphic-crypto";
125
+
126
+ const rk = generateRecoveryKey(); // { recoveryKey, recoverySecretBase64 }
127
+ // recoveryKey is a human-readable string like "ABCD-EFGH-..." for the user to write down
96
128
 
97
- await init('/path/to/metamorphic_crypto_bg.wasm');
129
+ const backup = encryptPrivateKeyForRecovery(privateKeyBase64, rk.recoverySecretBase64);
130
+ const restored = decryptPrivateKeyWithRecovery(backup, rk.recoverySecretBase64);
98
131
 
99
- const key = deriveSessionKey(password, saltBase64);
100
- const ciphertext = encryptSecretboxString("hello", key);
132
+ // Or recover from the human-readable key:
133
+ const secret = recoveryKeyToSecret("ABCD-EFGH-...");
134
+ const restored2 = decryptPrivateKeyWithRecovery(backup, secret);
101
135
  ```
102
136
 
103
- ## Tests
137
+ ## Security levels
138
+
139
+ | Level | ML-KEM | NIST Category | Equivalent | Default |
140
+ |-------|--------|---------------|------------|---------|
141
+ | Cat-3 | 768 | 3 | ~AES-192 | Yes |
142
+ | Cat-5 | 1024 | 5 | ~AES-256 | No |
143
+
144
+ Decryption always auto-detects the level from the ciphertext version tag.
145
+
146
+ ## Security properties
147
+
148
+ - `#![forbid(unsafe_code)]` — no unsafe anywhere in the Rust source
149
+ - All secret key material zeroized after use
150
+ - Constant-time MAC comparison via RustCrypto
151
+ - OS CSPRNG via `getrandom` (no userspace PRNG)
152
+ - Hybrid construction: both ML-KEM AND X25519 must be broken to compromise a sealed key
153
+
154
+ ## Integrity verification
155
+
156
+ Every release includes SHA-512 checksums and [cosign](https://docs.sigstore.dev/cosign/overview/) signatures for supply chain verification:
104
157
 
105
158
  ```bash
106
- cargo test # unit + integration + cross-level compatibility
107
- cargo clippy # zero warnings
108
- cargo fmt --check # formatted
159
+ cosign verify-blob \
160
+ --bundle metamorphic_crypto_bg.wasm.cosign.bundle \
161
+ metamorphic_crypto_bg.wasm
109
162
  ```
110
163
 
111
164
  ## License
112
165
 
113
- Dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE) at your option.
166
+ Dual-licensed under [MIT](https://github.com/moss-piglet/metamorphic-crypto/blob/main/LICENSE-MIT) or [Apache-2.0](https://github.com/moss-piglet/metamorphic-crypto/blob/main/LICENSE-APACHE) at your option.
Binary file
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@f0rest8/metamorphic-crypto",
3
3
  "type": "module",
4
4
  "description": "Zero-knowledge end-to-end encryption with post-quantum hybrid KEM (ML-KEM-768/1024 + X25519)",
5
- "version": "0.3.3",
5
+ "version": "0.3.4",
6
6
  "license": "MIT OR Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",