@f0rest8/metamorphic-crypto 0.3.7 → 0.5.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 +74 -0
- package/metamorphic_crypto.d.ts +70 -2
- package/metamorphic_crypto.js +302 -0
- package/metamorphic_crypto_bg.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -134,6 +134,80 @@ const secret = recoveryKeyToSecret("ABCD-EFGH-...");
|
|
|
134
134
|
const restored2 = decryptPrivateKeyWithRecovery(backup, secret);
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
### Hashing (SHA-3 / SHA-2)
|
|
138
|
+
|
|
139
|
+
Public, one-shot digest functions — intended for **public** data only, such as
|
|
140
|
+
key fingerprints, safety numbers, and key-transparency-log entries. Each takes
|
|
141
|
+
**base64-encoded input** and returns the digest as **base64**. Decode with
|
|
142
|
+
`atob`, or re-encode to hex, if you need a hex fingerprint.
|
|
143
|
+
|
|
144
|
+
`sha3_512` is the recommended default (highest assurance, NIST Cat-5). The
|
|
145
|
+
others let you match an existing format.
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
import { sha3_512, sha3_256, sha256, sha512 } from "@f0rest8/metamorphic-crypto";
|
|
149
|
+
|
|
150
|
+
const dataBase64 = btoa("public key bytes");
|
|
151
|
+
const digestBase64 = sha3_512(dataBase64); // 64-byte digest, base64
|
|
152
|
+
|
|
153
|
+
// Need hex? decode the base64 digest:
|
|
154
|
+
const hex = [...atob(digestBase64)].map(c => c.charCodeAt(0).toString(16).padStart(2, "0")).join("");
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Domain separation (recommended for fingerprints / transparency logs).** Use
|
|
158
|
+
`sha3_512WithContext` to bind a digest to a versioned context label, so the same
|
|
159
|
+
bytes hashed for different purposes can never collide or be reinterpreted across
|
|
160
|
+
contexts. It is exactly as strong as `sha3_512`:
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
import { sha3_512WithContext } from "@f0rest8/metamorphic-crypto";
|
|
164
|
+
|
|
165
|
+
const dataBase64 = btoa("public key bytes");
|
|
166
|
+
const fingerprint = sha3_512WithContext("mosslet/key-fingerprint/v1", dataBase64);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Stable wire format (reproduce exactly to match native/Elixir):
|
|
170
|
+
`SHA3-512( u64_be(len(context_utf8)) || context_utf8 || data )`.
|
|
171
|
+
|
|
172
|
+
> **Do not hash secrets with these.** A bare hash makes no guarantees about its
|
|
173
|
+
> inputs, and the hashing path intentionally adds no zeroize/constant-time
|
|
174
|
+
> ceremony — wiping a transient copy of already-public data adds cost without
|
|
175
|
+
> protection. To process secret material (passwords, private keys), use the
|
|
176
|
+
> right construction instead: this package's Argon2id `deriveSessionKey` for
|
|
177
|
+
> password-based derivation, or a dedicated KDF/MAC.
|
|
178
|
+
|
|
179
|
+
### Hybrid PQ signatures (ML-DSA + Ed25519)
|
|
180
|
+
|
|
181
|
+
Composite digital signatures: every message is signed by **both** ML-DSA
|
|
182
|
+
(FIPS 204) and Ed25519 (RFC 8032), and `verify` returns `true` only if **both**
|
|
183
|
+
component signatures are valid (strict AND). Keys and signatures are base64; the
|
|
184
|
+
message is **base64** and `context` is a UTF-8 string.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
import { generateSigningKeyPair, sign, verify, deriveSigningPublicKey } from "@f0rest8/metamorphic-crypto";
|
|
188
|
+
|
|
189
|
+
const kp = generateSigningKeyPair("cat3"); // { publicKey, secretKey } — Cat-3 default
|
|
190
|
+
const msg = btoa("transparency log entry");
|
|
191
|
+
const sig = sign(msg, "metamorphic/sign/v1", kp.secretKey);
|
|
192
|
+
const ok = verify(msg, "metamorphic/sign/v1", sig, kp.publicKey); // true
|
|
193
|
+
|
|
194
|
+
// Re-derive the public key from a backed-up secret key:
|
|
195
|
+
const pk = deriveSigningPublicKey(kp.secretKey); // === kp.publicKey
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Levels are `"cat2"` (ML-DSA-44), `"cat3"` (ML-DSA-65, default), and `"cat5"`
|
|
199
|
+
(ML-DSA-87); `verify` auto-detects the level from the signature. ML-DSA uses the
|
|
200
|
+
hedged/randomized FIPS 204 mode, so signature **bytes are non-reproducible**,
|
|
201
|
+
but key derivation and the domain-separation framing
|
|
202
|
+
(`u64_be(len(context)) || context || message`) are deterministic and identical
|
|
203
|
+
across native Rust, WASM, and the Elixir NIF.
|
|
204
|
+
|
|
205
|
+
> **Audit posture.** `ed25519-dalek` (2.x) is mature and widely deployed.
|
|
206
|
+
> `ml-dsa` (0.1.x, RustCrypto FIPS 204) is new and **not yet independently
|
|
207
|
+
> audited** — it is included as defense-in-depth on top of Ed25519, so the
|
|
208
|
+
> composite remains at least as strong as Ed25519 even if a flaw is found in the
|
|
209
|
+
> young post-quantum implementation. Pinned and tracked for the FIPS-mode roadmap.
|
|
210
|
+
|
|
137
211
|
## Security levels
|
|
138
212
|
|
|
139
213
|
| Level | ML-KEM | NIST Category | Equivalent | Default |
|
package/metamorphic_crypto.d.ts
CHANGED
|
@@ -37,6 +37,11 @@ export function decryptSecretboxToString(ciphertext_b64: string, key_b64: string
|
|
|
37
37
|
*/
|
|
38
38
|
export function deriveSessionKey(password: string, salt_b64: string): string;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Re-derive the base64 public key from a base64 hybrid secret key.
|
|
42
|
+
*/
|
|
43
|
+
export function deriveSigningPublicKey(secret_key_b64: string): string;
|
|
44
|
+
|
|
40
45
|
/**
|
|
41
46
|
* Encrypt a base64 private key with a session key. Returns base64 ciphertext.
|
|
42
47
|
*/
|
|
@@ -87,6 +92,14 @@ export function generateRecoveryKey(): any;
|
|
|
87
92
|
*/
|
|
88
93
|
export function generateSalt(): string;
|
|
89
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Generate a hybrid signing keypair. Returns JSON: `{ publicKey, secretKey }`.
|
|
97
|
+
*
|
|
98
|
+
* `level` is `"cat2"` (ML-DSA-44), `"cat3"` (ML-DSA-65, default), or `"cat5"`
|
|
99
|
+
* (ML-DSA-87). Empty/null defaults to Cat-3.
|
|
100
|
+
*/
|
|
101
|
+
export function generateSigningKeyPair(level: string): any;
|
|
102
|
+
|
|
90
103
|
/**
|
|
91
104
|
* Check if a base64 ciphertext is hybrid (v2/v3) format.
|
|
92
105
|
*/
|
|
@@ -115,12 +128,58 @@ export function sealForUser(plaintext_b64: string, public_key_b64: string, pq_pu
|
|
|
115
128
|
*/
|
|
116
129
|
export function sealForUserWithLevel(plaintext_b64: string, public_key_b64: string, pq_public_key_b64: string | null | undefined, level: string): string;
|
|
117
130
|
|
|
131
|
+
/**
|
|
132
|
+
* SHA-256 (SHA-2) digest of base64-encoded `data`. Returns the 32-byte digest as base64.
|
|
133
|
+
*/
|
|
134
|
+
export function sha256(data_b64: string): string;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* SHA3-256 digest of base64-encoded `data`. Returns the 32-byte digest as base64.
|
|
138
|
+
*/
|
|
139
|
+
export function sha3_256(data_b64: string): string;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* SHA3-512 digest of base64-encoded `data`. Returns the 64-byte digest as base64.
|
|
143
|
+
*
|
|
144
|
+
* This is the recommended default hash (NIST Cat-5).
|
|
145
|
+
*/
|
|
146
|
+
export function sha3_512(data_b64: string): string;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Domain-separated SHA3-512: binds the digest to a UTF-8 `context` label.
|
|
150
|
+
*
|
|
151
|
+
* `data` is base64; returns the 64-byte digest as base64. Prefer this over
|
|
152
|
+
* `sha3_512` for fingerprints / safety numbers / key-transparency entries.
|
|
153
|
+
*
|
|
154
|
+
* Wire format (reproduce exactly for parity with native/Elixir):
|
|
155
|
+
* `SHA3-512( u64_be(len(context_utf8)) || context_utf8 || data )`.
|
|
156
|
+
*/
|
|
157
|
+
export function sha3_512WithContext(context: string, data_b64: string): string;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* SHA-512 (SHA-2) digest of base64-encoded `data`. Returns the 64-byte digest as base64.
|
|
161
|
+
*/
|
|
162
|
+
export function sha512(data_b64: string): string;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Sign base64 `message` under `context` with a base64 hybrid `secret_key`.
|
|
166
|
+
* Returns the composite signature as base64.
|
|
167
|
+
*/
|
|
168
|
+
export function sign(message_b64: string, context: string, secret_key_b64: string): string;
|
|
169
|
+
|
|
118
170
|
/**
|
|
119
171
|
* Unseal ciphertext using the user's keys. Auto-detects format.
|
|
120
172
|
* Returns base64-encoded plaintext.
|
|
121
173
|
*/
|
|
122
174
|
export function unsealFromUser(ciphertext_b64: string, public_key_b64: string, private_key_b64: string, pq_secret_key_b64?: string | null): string;
|
|
123
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Verify a base64 composite `signature` over base64 `message`/`context`
|
|
178
|
+
* against a base64 `public_key`. Returns `true` only if **both** the Ed25519
|
|
179
|
+
* and ML-DSA components verify (strict AND).
|
|
180
|
+
*/
|
|
181
|
+
export function verify(message_b64: string, context: string, signature_b64: string, public_key_b64: string): boolean;
|
|
182
|
+
|
|
124
183
|
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
125
184
|
|
|
126
185
|
export interface InitOutput {
|
|
@@ -144,10 +203,19 @@ export interface InitOutput {
|
|
|
144
203
|
readonly generateRecoveryKey: (a: number) => void;
|
|
145
204
|
readonly recoveryKeyToSecret: (a: number, b: number, c: number) => void;
|
|
146
205
|
readonly parseSaltFromKeyHash: (a: number, b: number, c: number) => void;
|
|
147
|
-
readonly
|
|
206
|
+
readonly sha3_512: (a: number, b: number, c: number) => void;
|
|
207
|
+
readonly sha3_512WithContext: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
208
|
+
readonly sha3_256: (a: number, b: number, c: number) => void;
|
|
209
|
+
readonly sha256: (a: number, b: number, c: number) => void;
|
|
210
|
+
readonly sha512: (a: number, b: number, c: number) => void;
|
|
211
|
+
readonly generateSigningKeyPair: (a: number, b: number, c: number) => void;
|
|
212
|
+
readonly deriveSigningPublicKey: (a: number, b: number, c: number) => void;
|
|
213
|
+
readonly sign: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
|
|
214
|
+
readonly verify: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => void;
|
|
215
|
+
readonly encryptSecretboxString: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
148
216
|
readonly encryptPrivateKeyForRecovery: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
217
|
+
readonly decryptSecretboxToString: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
149
218
|
readonly decryptPrivateKeyWithRecovery: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
150
|
-
readonly encryptSecretboxString: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
151
219
|
readonly __wbindgen_export: (a: number, b: number) => number;
|
|
152
220
|
readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
|
153
221
|
readonly __wbindgen_export3: (a: number) => void;
|
package/metamorphic_crypto.js
CHANGED
|
@@ -249,6 +249,38 @@ export function deriveSessionKey(password, salt_b64) {
|
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Re-derive the base64 public key from a base64 hybrid secret key.
|
|
254
|
+
* @param {string} secret_key_b64
|
|
255
|
+
* @returns {string}
|
|
256
|
+
*/
|
|
257
|
+
export function deriveSigningPublicKey(secret_key_b64) {
|
|
258
|
+
let deferred3_0;
|
|
259
|
+
let deferred3_1;
|
|
260
|
+
try {
|
|
261
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
262
|
+
const ptr0 = passStringToWasm0(secret_key_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
263
|
+
const len0 = WASM_VECTOR_LEN;
|
|
264
|
+
wasm.deriveSigningPublicKey(retptr, ptr0, len0);
|
|
265
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
266
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
267
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
268
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
269
|
+
var ptr2 = r0;
|
|
270
|
+
var len2 = r1;
|
|
271
|
+
if (r3) {
|
|
272
|
+
ptr2 = 0; len2 = 0;
|
|
273
|
+
throw takeObject(r2);
|
|
274
|
+
}
|
|
275
|
+
deferred3_0 = ptr2;
|
|
276
|
+
deferred3_1 = len2;
|
|
277
|
+
return getStringFromWasm0(ptr2, len2);
|
|
278
|
+
} finally {
|
|
279
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
280
|
+
wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
252
284
|
/**
|
|
253
285
|
* Encrypt a base64 private key with a session key. Returns base64 ciphertext.
|
|
254
286
|
* @param {string} private_key_b64
|
|
@@ -478,6 +510,32 @@ export function generateSalt() {
|
|
|
478
510
|
}
|
|
479
511
|
}
|
|
480
512
|
|
|
513
|
+
/**
|
|
514
|
+
* Generate a hybrid signing keypair. Returns JSON: `{ publicKey, secretKey }`.
|
|
515
|
+
*
|
|
516
|
+
* `level` is `"cat2"` (ML-DSA-44), `"cat3"` (ML-DSA-65, default), or `"cat5"`
|
|
517
|
+
* (ML-DSA-87). Empty/null defaults to Cat-3.
|
|
518
|
+
* @param {string} level
|
|
519
|
+
* @returns {any}
|
|
520
|
+
*/
|
|
521
|
+
export function generateSigningKeyPair(level) {
|
|
522
|
+
try {
|
|
523
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
524
|
+
const ptr0 = passStringToWasm0(level, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
525
|
+
const len0 = WASM_VECTOR_LEN;
|
|
526
|
+
wasm.generateSigningKeyPair(retptr, ptr0, len0);
|
|
527
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
528
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
529
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
530
|
+
if (r2) {
|
|
531
|
+
throw takeObject(r1);
|
|
532
|
+
}
|
|
533
|
+
return takeObject(r0);
|
|
534
|
+
} finally {
|
|
535
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
481
539
|
/**
|
|
482
540
|
* Check if a base64 ciphertext is hybrid (v2/v3) format.
|
|
483
541
|
* @param {string} ciphertext_b64
|
|
@@ -636,6 +694,216 @@ export function sealForUserWithLevel(plaintext_b64, public_key_b64, pq_public_ke
|
|
|
636
694
|
}
|
|
637
695
|
}
|
|
638
696
|
|
|
697
|
+
/**
|
|
698
|
+
* SHA-256 (SHA-2) digest of base64-encoded `data`. Returns the 32-byte digest as base64.
|
|
699
|
+
* @param {string} data_b64
|
|
700
|
+
* @returns {string}
|
|
701
|
+
*/
|
|
702
|
+
export function sha256(data_b64) {
|
|
703
|
+
let deferred3_0;
|
|
704
|
+
let deferred3_1;
|
|
705
|
+
try {
|
|
706
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
707
|
+
const ptr0 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
708
|
+
const len0 = WASM_VECTOR_LEN;
|
|
709
|
+
wasm.sha256(retptr, ptr0, len0);
|
|
710
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
711
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
712
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
713
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
714
|
+
var ptr2 = r0;
|
|
715
|
+
var len2 = r1;
|
|
716
|
+
if (r3) {
|
|
717
|
+
ptr2 = 0; len2 = 0;
|
|
718
|
+
throw takeObject(r2);
|
|
719
|
+
}
|
|
720
|
+
deferred3_0 = ptr2;
|
|
721
|
+
deferred3_1 = len2;
|
|
722
|
+
return getStringFromWasm0(ptr2, len2);
|
|
723
|
+
} finally {
|
|
724
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
725
|
+
wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* SHA3-256 digest of base64-encoded `data`. Returns the 32-byte digest as base64.
|
|
731
|
+
* @param {string} data_b64
|
|
732
|
+
* @returns {string}
|
|
733
|
+
*/
|
|
734
|
+
export function sha3_256(data_b64) {
|
|
735
|
+
let deferred3_0;
|
|
736
|
+
let deferred3_1;
|
|
737
|
+
try {
|
|
738
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
739
|
+
const ptr0 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
740
|
+
const len0 = WASM_VECTOR_LEN;
|
|
741
|
+
wasm.sha3_256(retptr, ptr0, len0);
|
|
742
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
743
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
744
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
745
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
746
|
+
var ptr2 = r0;
|
|
747
|
+
var len2 = r1;
|
|
748
|
+
if (r3) {
|
|
749
|
+
ptr2 = 0; len2 = 0;
|
|
750
|
+
throw takeObject(r2);
|
|
751
|
+
}
|
|
752
|
+
deferred3_0 = ptr2;
|
|
753
|
+
deferred3_1 = len2;
|
|
754
|
+
return getStringFromWasm0(ptr2, len2);
|
|
755
|
+
} finally {
|
|
756
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
757
|
+
wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* SHA3-512 digest of base64-encoded `data`. Returns the 64-byte digest as base64.
|
|
763
|
+
*
|
|
764
|
+
* This is the recommended default hash (NIST Cat-5).
|
|
765
|
+
* @param {string} data_b64
|
|
766
|
+
* @returns {string}
|
|
767
|
+
*/
|
|
768
|
+
export function sha3_512(data_b64) {
|
|
769
|
+
let deferred3_0;
|
|
770
|
+
let deferred3_1;
|
|
771
|
+
try {
|
|
772
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
773
|
+
const ptr0 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
774
|
+
const len0 = WASM_VECTOR_LEN;
|
|
775
|
+
wasm.sha3_512(retptr, ptr0, len0);
|
|
776
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
777
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
778
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
779
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
780
|
+
var ptr2 = r0;
|
|
781
|
+
var len2 = r1;
|
|
782
|
+
if (r3) {
|
|
783
|
+
ptr2 = 0; len2 = 0;
|
|
784
|
+
throw takeObject(r2);
|
|
785
|
+
}
|
|
786
|
+
deferred3_0 = ptr2;
|
|
787
|
+
deferred3_1 = len2;
|
|
788
|
+
return getStringFromWasm0(ptr2, len2);
|
|
789
|
+
} finally {
|
|
790
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
791
|
+
wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Domain-separated SHA3-512: binds the digest to a UTF-8 `context` label.
|
|
797
|
+
*
|
|
798
|
+
* `data` is base64; returns the 64-byte digest as base64. Prefer this over
|
|
799
|
+
* `sha3_512` for fingerprints / safety numbers / key-transparency entries.
|
|
800
|
+
*
|
|
801
|
+
* Wire format (reproduce exactly for parity with native/Elixir):
|
|
802
|
+
* `SHA3-512( u64_be(len(context_utf8)) || context_utf8 || data )`.
|
|
803
|
+
* @param {string} context
|
|
804
|
+
* @param {string} data_b64
|
|
805
|
+
* @returns {string}
|
|
806
|
+
*/
|
|
807
|
+
export function sha3_512WithContext(context, data_b64) {
|
|
808
|
+
let deferred4_0;
|
|
809
|
+
let deferred4_1;
|
|
810
|
+
try {
|
|
811
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
812
|
+
const ptr0 = passStringToWasm0(context, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
813
|
+
const len0 = WASM_VECTOR_LEN;
|
|
814
|
+
const ptr1 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
815
|
+
const len1 = WASM_VECTOR_LEN;
|
|
816
|
+
wasm.sha3_512WithContext(retptr, ptr0, len0, ptr1, len1);
|
|
817
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
818
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
819
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
820
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
821
|
+
var ptr3 = r0;
|
|
822
|
+
var len3 = r1;
|
|
823
|
+
if (r3) {
|
|
824
|
+
ptr3 = 0; len3 = 0;
|
|
825
|
+
throw takeObject(r2);
|
|
826
|
+
}
|
|
827
|
+
deferred4_0 = ptr3;
|
|
828
|
+
deferred4_1 = len3;
|
|
829
|
+
return getStringFromWasm0(ptr3, len3);
|
|
830
|
+
} finally {
|
|
831
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
832
|
+
wasm.__wbindgen_export4(deferred4_0, deferred4_1, 1);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* SHA-512 (SHA-2) digest of base64-encoded `data`. Returns the 64-byte digest as base64.
|
|
838
|
+
* @param {string} data_b64
|
|
839
|
+
* @returns {string}
|
|
840
|
+
*/
|
|
841
|
+
export function sha512(data_b64) {
|
|
842
|
+
let deferred3_0;
|
|
843
|
+
let deferred3_1;
|
|
844
|
+
try {
|
|
845
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
846
|
+
const ptr0 = passStringToWasm0(data_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
847
|
+
const len0 = WASM_VECTOR_LEN;
|
|
848
|
+
wasm.sha512(retptr, ptr0, len0);
|
|
849
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
850
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
851
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
852
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
853
|
+
var ptr2 = r0;
|
|
854
|
+
var len2 = r1;
|
|
855
|
+
if (r3) {
|
|
856
|
+
ptr2 = 0; len2 = 0;
|
|
857
|
+
throw takeObject(r2);
|
|
858
|
+
}
|
|
859
|
+
deferred3_0 = ptr2;
|
|
860
|
+
deferred3_1 = len2;
|
|
861
|
+
return getStringFromWasm0(ptr2, len2);
|
|
862
|
+
} finally {
|
|
863
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
864
|
+
wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* Sign base64 `message` under `context` with a base64 hybrid `secret_key`.
|
|
870
|
+
* Returns the composite signature as base64.
|
|
871
|
+
* @param {string} message_b64
|
|
872
|
+
* @param {string} context
|
|
873
|
+
* @param {string} secret_key_b64
|
|
874
|
+
* @returns {string}
|
|
875
|
+
*/
|
|
876
|
+
export function sign(message_b64, context, secret_key_b64) {
|
|
877
|
+
let deferred5_0;
|
|
878
|
+
let deferred5_1;
|
|
879
|
+
try {
|
|
880
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
881
|
+
const ptr0 = passStringToWasm0(message_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
882
|
+
const len0 = WASM_VECTOR_LEN;
|
|
883
|
+
const ptr1 = passStringToWasm0(context, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
884
|
+
const len1 = WASM_VECTOR_LEN;
|
|
885
|
+
const ptr2 = passStringToWasm0(secret_key_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
886
|
+
const len2 = WASM_VECTOR_LEN;
|
|
887
|
+
wasm.sign(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
|
|
888
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
889
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
890
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
891
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
892
|
+
var ptr4 = r0;
|
|
893
|
+
var len4 = r1;
|
|
894
|
+
if (r3) {
|
|
895
|
+
ptr4 = 0; len4 = 0;
|
|
896
|
+
throw takeObject(r2);
|
|
897
|
+
}
|
|
898
|
+
deferred5_0 = ptr4;
|
|
899
|
+
deferred5_1 = len4;
|
|
900
|
+
return getStringFromWasm0(ptr4, len4);
|
|
901
|
+
} finally {
|
|
902
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
903
|
+
wasm.__wbindgen_export4(deferred5_0, deferred5_1, 1);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
639
907
|
/**
|
|
640
908
|
* Unseal ciphertext using the user's keys. Auto-detects format.
|
|
641
909
|
* Returns base64-encoded plaintext.
|
|
@@ -677,6 +945,40 @@ export function unsealFromUser(ciphertext_b64, public_key_b64, private_key_b64,
|
|
|
677
945
|
wasm.__wbindgen_export4(deferred6_0, deferred6_1, 1);
|
|
678
946
|
}
|
|
679
947
|
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Verify a base64 composite `signature` over base64 `message`/`context`
|
|
951
|
+
* against a base64 `public_key`. Returns `true` only if **both** the Ed25519
|
|
952
|
+
* and ML-DSA components verify (strict AND).
|
|
953
|
+
* @param {string} message_b64
|
|
954
|
+
* @param {string} context
|
|
955
|
+
* @param {string} signature_b64
|
|
956
|
+
* @param {string} public_key_b64
|
|
957
|
+
* @returns {boolean}
|
|
958
|
+
*/
|
|
959
|
+
export function verify(message_b64, context, signature_b64, public_key_b64) {
|
|
960
|
+
try {
|
|
961
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
962
|
+
const ptr0 = passStringToWasm0(message_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
963
|
+
const len0 = WASM_VECTOR_LEN;
|
|
964
|
+
const ptr1 = passStringToWasm0(context, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
965
|
+
const len1 = WASM_VECTOR_LEN;
|
|
966
|
+
const ptr2 = passStringToWasm0(signature_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
967
|
+
const len2 = WASM_VECTOR_LEN;
|
|
968
|
+
const ptr3 = passStringToWasm0(public_key_b64, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
969
|
+
const len3 = WASM_VECTOR_LEN;
|
|
970
|
+
wasm.verify(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
971
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
972
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
973
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
974
|
+
if (r2) {
|
|
975
|
+
throw takeObject(r1);
|
|
976
|
+
}
|
|
977
|
+
return r0 !== 0;
|
|
978
|
+
} finally {
|
|
979
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
680
982
|
function __wbg_get_imports() {
|
|
681
983
|
const import0 = {
|
|
682
984
|
__proto__: null,
|
|
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.
|
|
5
|
+
"version": "0.5.0",
|
|
6
6
|
"license": "MIT OR Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|