@f0rest8/metamorphic-crypto 0.7.0 → 0.7.2

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,6 +1,6 @@
1
1
  # @f0rest8/metamorphic-crypto
2
2
 
3
- Zero-knowledge end-to-end encryption with post-quantum hybrid KEM (ML-KEM-512/768/1024 + X25519).
3
+ Zero-knowledge end-to-end encryption with post-quantum hybrid KEM (ML-KEM-512/768/1024 + X25519), hybrid PQ signatures (ML-DSA + Ed25519), and an opt-in CNSA 2.0 suite axis.
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
 
@@ -218,6 +218,49 @@ across native Rust, WASM, and the Elixir NIF.
218
218
  > composite remains at least as strong as Ed25519 even if a flaw is found in the
219
219
  > young post-quantum implementation. Pinned and tracked for the FIPS-mode roadmap.
220
220
 
221
+ ### CNSA 2.0 suite axis (opt-in)
222
+
223
+ Everything above is the default **`hybrid`** suite (classical+PQ strict-AND). If
224
+ you have no specific mandate, that is the recommended choice. For deployments
225
+ that must follow the NSA's **CNSA 2.0** suite (NIST IR 8547), a `suite` argument
226
+ raises the posture. It is **orthogonal** to the `"cat1"`/`"cat3"`/`"cat5"` level,
227
+ so you have two independent knobs.
228
+
229
+ | `suite` string | What it is | Status |
230
+ |----------------|------------|--------|
231
+ | `"hybrid"` | Existing classical+PQ strict-AND. Byte-for-byte unchanged. | **Default, recommended** |
232
+ | `"hybridMatched"` | Classical partner matched to the PQ category (KEM: Cat-3→X448, Cat-5→P-521 · Sign: Cat-3→Ed448, Cat-5→ECDSA-P-521) | Opt-in |
233
+ | `"pureCnsa2"` | Pure post-quantum, no classical half (ML-KEM-1024 / AES-256-GCM; ML-DSA-87) | Opt-in, **Cat-5 only** |
234
+
235
+ ```js
236
+ import {
237
+ generateHybridKeyPairSuite, hybridSealSuite, hybridOpenWithContext,
238
+ generateSigningKeyPairSuite, sign, verify,
239
+ } from "@f0rest8/metamorphic-crypto";
240
+
241
+ // Pure CNSA-2.0 KEM box (ML-KEM-1024 + AES-256-GCM)
242
+ const kp = generateHybridKeyPairSuite("pureCnsa2", "cat5"); // { publicKey, secretKey }
243
+ const sealed = hybridSealSuite(plaintextBase64, kp.publicKey, "pureCnsa2", "cat5");
244
+ // Open with the context label used at seal time (default "metamorphic/seal/v1"):
245
+ const opened = hybridOpenWithContext(sealed, kp.secretKey, "metamorphic/seal/v1"); // base64
246
+
247
+ // Pure ML-DSA-87 signatures — sign/verify auto-detect the suite from the tag
248
+ const sk = generateSigningKeyPairSuite("pureCnsa2", "cat5");
249
+ const sig = sign(btoa("checkpoint"), "metamorphic/sign/v1", sk.secretKey);
250
+ const ok = verify(btoa("checkpoint"), "metamorphic/sign/v1", sig, sk.publicKey); // true
251
+ ```
252
+
253
+ The new suites bind a versioned **context label** (`"<namespace>/<purpose>/v<major>"`)
254
+ into the seal — use `hybridSealSuiteWithContext(..., "mosslet/seal/v1")` for a
255
+ custom per-tenant namespace and open with the same label. `sealForUserWithSuite`
256
+ mirrors `sealForUser` with the suite/level appended (legacy X25519 fallback when
257
+ no PQ key is present).
258
+
259
+ > **Honest claims.** "CNSA 2.0 algorithm suite, NCC-audited components,
260
+ > pure-Rust, memory-safe (`forbid-unsafe`)" — **not** "FIPS 140-3 validated."
261
+ > `pureCnsa2` drops the classical backstop, so the strict-AND `hybrid` default
262
+ > stays recommended until the lattice implementations are independently audited.
263
+
221
264
  ## Security levels
222
265
 
223
266
  | Level | ML-KEM | NIST Category | Equivalent | Default |
@@ -265,10 +265,10 @@ export interface InitOutput {
265
265
  readonly deriveSigningPublicKey: (a: number, b: number, c: number) => void;
266
266
  readonly sign: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
267
267
  readonly verify: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => void;
268
- readonly decryptPrivateKeyWithRecovery: (a: number, b: number, c: number, d: number, e: number) => void;
269
- readonly decryptSecretboxToString: (a: number, b: number, c: number, d: number, e: number) => void;
270
268
  readonly encryptSecretboxString: (a: number, b: number, c: number, d: number, e: number) => void;
271
269
  readonly encryptPrivateKeyForRecovery: (a: number, b: number, c: number, d: number, e: number) => void;
270
+ readonly decryptSecretboxToString: (a: number, b: number, c: number, d: number, e: number) => void;
271
+ readonly decryptPrivateKeyWithRecovery: (a: number, b: number, c: number, d: number, e: number) => void;
272
272
  readonly __wbindgen_export: (a: number, b: number) => number;
273
273
  readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
274
274
  readonly __wbindgen_export3: (a: number) => void;
@@ -1221,34 +1221,34 @@ export function verify(message_b64, context, signature_b64, public_key_b64) {
1221
1221
  function __wbg_get_imports() {
1222
1222
  const import0 = {
1223
1223
  __proto__: null,
1224
- __wbg___wbindgen_debug_string_56c147eb1a51f0c4: function(arg0, arg1) {
1224
+ __wbg___wbindgen_debug_string_c25d447a39f5578f: function(arg0, arg1) {
1225
1225
  const ret = debugString(getObject(arg1));
1226
1226
  const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
1227
1227
  const len1 = WASM_VECTOR_LEN;
1228
1228
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
1229
1229
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
1230
1230
  },
1231
- __wbg___wbindgen_is_function_147961669f068cd4: function(arg0) {
1231
+ __wbg___wbindgen_is_function_1ff95bcc5517c252: function(arg0) {
1232
1232
  const ret = typeof(getObject(arg0)) === 'function';
1233
1233
  return ret;
1234
1234
  },
1235
- __wbg___wbindgen_is_object_3a2c414391dbf751: function(arg0) {
1235
+ __wbg___wbindgen_is_object_a27215656b807791: function(arg0) {
1236
1236
  const val = getObject(arg0);
1237
1237
  const ret = typeof(val) === 'object' && val !== null;
1238
1238
  return ret;
1239
1239
  },
1240
- __wbg___wbindgen_is_string_6541b0f6ecd4e8e5: function(arg0) {
1240
+ __wbg___wbindgen_is_string_ea5e6cc2e4141dfe: function(arg0) {
1241
1241
  const ret = typeof(getObject(arg0)) === 'string';
1242
1242
  return ret;
1243
1243
  },
1244
- __wbg___wbindgen_is_undefined_4410e3c20a99fa97: function(arg0) {
1244
+ __wbg___wbindgen_is_undefined_c05833b95a3cf397: function(arg0) {
1245
1245
  const ret = getObject(arg0) === undefined;
1246
1246
  return ret;
1247
1247
  },
1248
- __wbg___wbindgen_throw_bbadd78c1bac3a77: function(arg0, arg1) {
1248
+ __wbg___wbindgen_throw_344f42d3211c4765: function(arg0, arg1) {
1249
1249
  throw new Error(getStringFromWasm0(arg0, arg1));
1250
1250
  },
1251
- __wbg_call_ec09a4cf93377d3a: function() { return handleError(function (arg0, arg1, arg2) {
1251
+ __wbg_call_a6e5c5dce5018821: function() { return handleError(function (arg0, arg1, arg2) {
1252
1252
  const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
1253
1253
  return addHeapObject(ret);
1254
1254
  }, arguments); },
@@ -1259,7 +1259,7 @@ function __wbg_get_imports() {
1259
1259
  __wbg_getRandomValues_c44a50d8cfdaebeb: function() { return handleError(function (arg0, arg1) {
1260
1260
  getObject(arg0).getRandomValues(getObject(arg1));
1261
1261
  }, arguments); },
1262
- __wbg_length_68a9d5278d084f4f: function(arg0) {
1262
+ __wbg_length_1f0964f4a5e2c6d8: function(arg0) {
1263
1263
  const ret = getObject(arg0).length;
1264
1264
  return ret;
1265
1265
  },
@@ -1267,11 +1267,11 @@ function __wbg_get_imports() {
1267
1267
  const ret = getObject(arg0).msCrypto;
1268
1268
  return addHeapObject(ret);
1269
1269
  },
1270
- __wbg_new_20b778a4c5c691c3: function() {
1270
+ __wbg_new_da52cf8fe3429cb2: function() {
1271
1271
  const ret = new Object();
1272
1272
  return addHeapObject(ret);
1273
1273
  },
1274
- __wbg_new_with_length_4b57a7a5dc67221c: function(arg0) {
1274
+ __wbg_new_with_length_e6785c33c8e4cce8: function(arg0) {
1275
1275
  const ret = new Uint8Array(arg0 >>> 0);
1276
1276
  return addHeapObject(ret);
1277
1277
  },
@@ -1283,7 +1283,7 @@ function __wbg_get_imports() {
1283
1283
  const ret = getObject(arg0).process;
1284
1284
  return addHeapObject(ret);
1285
1285
  },
1286
- __wbg_prototypesetcall_956c7493c68e29b4: function(arg0, arg1, arg2) {
1286
+ __wbg_prototypesetcall_4770620bbe4688a0: function(arg0, arg1, arg2) {
1287
1287
  Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), getObject(arg2));
1288
1288
  },
1289
1289
  __wbg_randomFillSync_6c25eac9869eb53c: function() { return handleError(function (arg0, arg1) {
@@ -1293,27 +1293,27 @@ function __wbg_get_imports() {
1293
1293
  const ret = module.require;
1294
1294
  return addHeapObject(ret);
1295
1295
  }, arguments); },
1296
- __wbg_set_a6ba3ac0e634b822: function() { return handleError(function (arg0, arg1, arg2) {
1296
+ __wbg_set_8535240470bf2500: function() { return handleError(function (arg0, arg1, arg2) {
1297
1297
  const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2));
1298
1298
  return ret;
1299
1299
  }, arguments); },
1300
- __wbg_static_accessor_GLOBAL_60a4124bab7dcc9a: function() {
1300
+ __wbg_static_accessor_GLOBAL_4ef717fb391d88b7: function() {
1301
1301
  const ret = typeof global === 'undefined' ? null : global;
1302
1302
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
1303
1303
  },
1304
- __wbg_static_accessor_GLOBAL_THIS_95ca6460658b5d13: function() {
1304
+ __wbg_static_accessor_GLOBAL_THIS_8d1badc68b5a74f4: function() {
1305
1305
  const ret = typeof globalThis === 'undefined' ? null : globalThis;
1306
1306
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
1307
1307
  },
1308
- __wbg_static_accessor_SELF_4c95f759a91e9aae: function() {
1308
+ __wbg_static_accessor_SELF_146583524fe1469b: function() {
1309
1309
  const ret = typeof self === 'undefined' ? null : self;
1310
1310
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
1311
1311
  },
1312
- __wbg_static_accessor_WINDOW_44b435597f9e9ee7: function() {
1312
+ __wbg_static_accessor_WINDOW_f2829a2234d7819e: function() {
1313
1313
  const ret = typeof window === 'undefined' ? null : window;
1314
1314
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
1315
1315
  },
1316
- __wbg_subarray_42216645a367cd7a: function(arg0, arg1, arg2) {
1316
+ __wbg_subarray_3ed232c8a6baee09: function(arg0, arg1, arg2) {
1317
1317
  const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0);
1318
1318
  return addHeapObject(ret);
1319
1319
  },
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 + X25519) and an opt-in CNSA 2.0 suite axis (matched-strength hybrid + pure ML-KEM-1024 / ML-DSA-87 / AES-256-GCM)",
5
- "version": "0.7.0",
5
+ "version": "0.7.2",
6
6
  "license": "MIT OR Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",