@reallyme/crypto 0.1.7 → 0.2.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.
Files changed (46) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +43 -3
  3. package/dist/aead.d.ts +4 -0
  4. package/dist/aead.js +32 -15
  5. package/dist/aesKw.d.ts +3 -0
  6. package/dist/aesKw.js +10 -0
  7. package/dist/argon2id.d.ts +2 -0
  8. package/dist/argon2id.js +6 -0
  9. package/dist/cryptoFacade.d.ts +36 -0
  10. package/dist/cryptoFacade.js +71 -23
  11. package/dist/errors.d.ts +1 -1
  12. package/dist/hpke.d.ts +4 -0
  13. package/dist/hpke.js +20 -0
  14. package/dist/index.d.ts +5 -6
  15. package/dist/index.js +4 -4
  16. package/dist/jwaConcatKdf.js +8 -4
  17. package/dist/jwk.js +67 -72
  18. package/dist/memory.d.ts +8 -0
  19. package/dist/memory.js +13 -0
  20. package/dist/mlDsa.d.ts +5 -0
  21. package/dist/mlDsa.js +23 -0
  22. package/dist/mlKem.d.ts +6 -0
  23. package/dist/mlKem.js +29 -0
  24. package/dist/p256Ecdh.js +5 -1
  25. package/dist/p384Ecdh.js +5 -1
  26. package/dist/p521Ecdh.js +5 -1
  27. package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.d.ts +1602 -70
  28. package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.js +624 -16
  29. package/dist/proto.d.ts +113 -3
  30. package/dist/proto.js +706 -2
  31. package/dist/rsa.d.ts +2 -0
  32. package/dist/rsa.js +18 -15
  33. package/dist/slhDsa.d.ts +5 -0
  34. package/dist/slhDsa.js +21 -0
  35. package/dist/validateBytes.d.ts +1 -0
  36. package/dist/validateBytes.js +6 -0
  37. package/dist/wasm/reallyme_crypto_wasm.js +1 -246
  38. package/dist/wasm/reallyme_crypto_wasm_bg.wasm +0 -0
  39. package/dist/wasmModuleTypes.d.ts +2 -11
  40. package/dist/wasmProvider.d.ts +1 -16
  41. package/dist/wasmProvider.js +10 -25
  42. package/dist/xWing.d.ts +6 -0
  43. package/dist/xWing.js +34 -2
  44. package/package.json +7 -2
  45. package/dist/codecs.d.ts +0 -35
  46. package/dist/codecs.js +0 -216
@@ -3,7 +3,7 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { ReallyMeAead } from "./aead.js";
5
5
  import { ReallyMeAesKw } from "./aesKw.js";
6
- import { ReallyMeArgon2id } from "./argon2id.js";
6
+ import { ARGON2ID_DERIVED_KEY_LENGTH, ReallyMeArgon2id } from "./argon2id.js";
7
7
  import { ReallyMeBip340Schnorr } from "./bip340Schnorr.js";
8
8
  import { ReallyMeDigest } from "./digest.js";
9
9
  import { ReallyMeEd25519 } from "./ed25519.js";
@@ -24,6 +24,7 @@ import { ReallyMePbkdf2 } from "./pbkdf2.js";
24
24
  import { ReallyMeRsa } from "./rsa.js";
25
25
  import { ReallyMeSecp256k1 } from "./secp256k1.js";
26
26
  import { ReallyMeSlhDsa } from "./slhDsa.js";
27
+ import { requireReallyMeWasmProvider } from "./wasmProvider.js";
27
28
  import { ReallyMeX25519 } from "./x25519.js";
28
29
  import { ReallyMeXWing } from "./xWing.js";
29
30
  const SECP256K1_ECDSA = "ECDSA-secp256k1-SHA256";
@@ -37,7 +38,7 @@ const ED25519 = "Ed25519";
37
38
  * callers that want direct provider access; this facade gives consumers a
38
39
  * stable typed route that fails closed for not-yet-exposed algorithms.
39
40
  */
40
- export const ReallyMeCrypto = {
41
+ const createReallyMeCryptoFacade = (resolveWasmProvider) => ({
41
42
  hash(algorithm, bytes) {
42
43
  switch (algorithm) {
43
44
  case "SHA2-256":
@@ -66,7 +67,7 @@ export const ReallyMeCrypto = {
66
67
  case "AES-256-GCM-SIV":
67
68
  case "ChaCha20-Poly1305":
68
69
  case "XChaCha20-Poly1305":
69
- return ReallyMeAead.seal(algorithm, key, nonce, aad, plaintext);
70
+ return ReallyMeAead.sealWithProvider(resolveWasmProvider(), algorithm, key, nonce, aad, plaintext);
70
71
  default:
71
72
  throw new ReallyMeCryptoError("unsupported-algorithm");
72
73
  }
@@ -79,7 +80,7 @@ export const ReallyMeCrypto = {
79
80
  case "AES-256-GCM-SIV":
80
81
  case "ChaCha20-Poly1305":
81
82
  case "XChaCha20-Poly1305":
82
- return ReallyMeAead.open(algorithm, key, nonce, aad, ciphertextWithTag);
83
+ return ReallyMeAead.openWithProvider(resolveWasmProvider(), algorithm, key, nonce, aad, ciphertextWithTag);
83
84
  default:
84
85
  throw new ReallyMeCryptoError("unsupported-algorithm");
85
86
  }
@@ -104,10 +105,21 @@ export const ReallyMeCrypto = {
104
105
  throw new ReallyMeCryptoError("unsupported-algorithm");
105
106
  }
106
107
  },
108
+ deriveArgon2id(kdfVersion, secret, salt) {
109
+ return ReallyMeArgon2id.deriveKeyWithProvider(resolveWasmProvider(), kdfVersion, secret, salt);
110
+ },
107
111
  deriveKey(algorithm, password, salt, iterations, outputLength) {
108
112
  switch (algorithm) {
109
113
  case "Argon2id":
110
- return ReallyMeArgon2id.deriveKey(iterations, password, salt);
114
+ if (outputLength !== ARGON2ID_DERIVED_KEY_LENGTH) {
115
+ throw new ReallyMeCryptoError("invalid-input");
116
+ }
117
+ // Argon2id is governed by fixed ReallyMe profile versions, not by
118
+ // caller-selected iteration counts. This legacy generic KDF shape
119
+ // preserves the existing facade contract; new code should call
120
+ // deriveArgon2id(kdfVersion, secret, salt) so the profile selector is
121
+ // explicit at the API boundary.
122
+ return ReallyMeArgon2id.deriveKeyWithProvider(resolveWasmProvider(), iterations, password, salt);
111
123
  case "PBKDF2-HMAC-SHA-256":
112
124
  return ReallyMePbkdf2.deriveHmacSha256(password, salt, iterations, outputLength);
113
125
  case "PBKDF2-HMAC-SHA-512":
@@ -147,7 +159,7 @@ export const ReallyMeCrypto = {
147
159
  wrapKey(algorithm, wrappingKey, keyToWrap) {
148
160
  switch (algorithm) {
149
161
  case "AES-256-KW":
150
- return ReallyMeAesKw.wrapKey(wrappingKey, keyToWrap);
162
+ return ReallyMeAesKw.wrapKeyWithProvider(resolveWasmProvider(), wrappingKey, keyToWrap);
151
163
  default:
152
164
  throw new ReallyMeCryptoError("unsupported-algorithm");
153
165
  }
@@ -155,7 +167,7 @@ export const ReallyMeCrypto = {
155
167
  unwrapKey(algorithm, wrappingKey, wrappedKey) {
156
168
  switch (algorithm) {
157
169
  case "AES-256-KW":
158
- return ReallyMeAesKw.unwrapKey(wrappingKey, wrappedKey);
170
+ return ReallyMeAesKw.unwrapKeyWithProvider(resolveWasmProvider(), wrappingKey, wrappedKey);
159
171
  default:
160
172
  throw new ReallyMeCryptoError("unsupported-algorithm");
161
173
  }
@@ -178,9 +190,9 @@ export const ReallyMeCrypto = {
178
190
  case "ML-DSA-44":
179
191
  case "ML-DSA-65":
180
192
  case "ML-DSA-87":
181
- return ReallyMeMlDsa.generateKeyPair(algorithm);
193
+ return ReallyMeMlDsa.generateKeyPairWithProvider(resolveWasmProvider(), algorithm);
182
194
  case "SLH-DSA-SHA2-128s":
183
- return ReallyMeSlhDsa.generateKeyPair(algorithm);
195
+ return ReallyMeSlhDsa.generateKeyPairWithProvider(resolveWasmProvider(), algorithm);
184
196
  default:
185
197
  throw new ReallyMeCryptoError("unsupported-algorithm");
186
198
  }
@@ -206,7 +218,7 @@ export const ReallyMeCrypto = {
206
218
  case "ML-DSA-44":
207
219
  case "ML-DSA-65":
208
220
  case "ML-DSA-87":
209
- return ReallyMeMlDsa.deriveKeyPair(algorithm, secretKey);
221
+ return ReallyMeMlDsa.deriveKeyPairWithProvider(resolveWasmProvider(), algorithm, secretKey);
210
222
  case "SLH-DSA-SHA2-128s":
211
223
  // SLH-DSA deterministic derivation uses three FIPS seed components,
212
224
  // so it deliberately does not fit this single-secret import shape.
@@ -231,9 +243,9 @@ export const ReallyMeCrypto = {
231
243
  case "ML-DSA-44":
232
244
  case "ML-DSA-65":
233
245
  case "ML-DSA-87":
234
- return ReallyMeMlDsa.sign(algorithm, message, secretKey);
246
+ return ReallyMeMlDsa.signWithProvider(resolveWasmProvider(), algorithm, message, secretKey);
235
247
  case "SLH-DSA-SHA2-128s":
236
- return ReallyMeSlhDsa.sign(algorithm, message, secretKey);
248
+ return ReallyMeSlhDsa.signWithProvider(resolveWasmProvider(), algorithm, message, secretKey);
237
249
  default:
238
250
  throw new ReallyMeCryptoError("unsupported-algorithm");
239
251
  }
@@ -265,17 +277,30 @@ export const ReallyMeCrypto = {
265
277
  case "ML-DSA-44":
266
278
  case "ML-DSA-65":
267
279
  case "ML-DSA-87":
268
- ReallyMeMlDsa.verify(algorithm, signature, message, publicKey);
280
+ ReallyMeMlDsa.verifyWithProvider(resolveWasmProvider(), algorithm, signature, message, publicKey);
269
281
  return;
270
282
  case "SLH-DSA-SHA2-128s":
271
- ReallyMeSlhDsa.verify(algorithm, signature, message, publicKey);
283
+ ReallyMeSlhDsa.verifyWithProvider(resolveWasmProvider(), algorithm, signature, message, publicKey);
272
284
  return;
273
285
  default:
274
286
  throw new ReallyMeCryptoError("unsupported-algorithm");
275
287
  }
276
288
  },
277
289
  verifyRsa(algorithm, signature, message, publicKeyDer, publicKeyEncoding) {
278
- ReallyMeRsa.verify(algorithm, signature, message, publicKeyDer, publicKeyEncoding);
290
+ switch (algorithm) {
291
+ case "RSA-PKCS1v15-SHA1":
292
+ case "RSA-PKCS1v15-SHA256":
293
+ case "RSA-PKCS1v15-SHA384":
294
+ case "RSA-PKCS1v15-SHA512":
295
+ case "RSA-PSS-SHA1-MGF1-SHA1":
296
+ case "RSA-PSS-SHA256-MGF1-SHA256":
297
+ case "RSA-PSS-SHA384-MGF1-SHA384":
298
+ case "RSA-PSS-SHA512-MGF1-SHA512":
299
+ ReallyMeRsa.verifyWithProvider(resolveWasmProvider(), algorithm, signature, message, publicKeyDer, publicKeyEncoding);
300
+ return;
301
+ default:
302
+ throw new ReallyMeCryptoError("unsupported-algorithm");
303
+ }
279
304
  },
280
305
  deriveSharedSecret(algorithm, publicKey, secretKey) {
281
306
  switch (algorithm) {
@@ -309,11 +334,24 @@ export const ReallyMeCrypto = {
309
334
  switch (algorithm) {
310
335
  case "X-Wing-768":
311
336
  case "X-Wing-1024":
312
- return ReallyMeXWing.generateKeyPair(algorithm);
337
+ return ReallyMeXWing.generateKeyPairWithProvider(resolveWasmProvider(), algorithm);
313
338
  case "ML-KEM-512":
314
339
  case "ML-KEM-768":
315
340
  case "ML-KEM-1024":
316
- return ReallyMeMlKem.generateKeyPair(algorithm);
341
+ return ReallyMeMlKem.generateKeyPairWithProvider(resolveWasmProvider(), algorithm);
342
+ default:
343
+ throw new ReallyMeCryptoError("unsupported-algorithm");
344
+ }
345
+ },
346
+ deriveKemKeyPair(algorithm, secretKey) {
347
+ switch (algorithm) {
348
+ case "X-Wing-768":
349
+ case "X-Wing-1024":
350
+ return ReallyMeXWing.deriveKeyPairWithProvider(resolveWasmProvider(), algorithm, secretKey);
351
+ case "ML-KEM-512":
352
+ case "ML-KEM-768":
353
+ case "ML-KEM-1024":
354
+ return ReallyMeMlKem.deriveKeyPairWithProvider(resolveWasmProvider(), algorithm, secretKey);
317
355
  default:
318
356
  throw new ReallyMeCryptoError("unsupported-algorithm");
319
357
  }
@@ -322,11 +360,11 @@ export const ReallyMeCrypto = {
322
360
  switch (algorithm) {
323
361
  case "X-Wing-768":
324
362
  case "X-Wing-1024":
325
- return ReallyMeXWing.encapsulate(algorithm, publicKey);
363
+ return ReallyMeXWing.encapsulateWithProvider(resolveWasmProvider(), algorithm, publicKey);
326
364
  case "ML-KEM-512":
327
365
  case "ML-KEM-768":
328
366
  case "ML-KEM-1024":
329
- return ReallyMeMlKem.encapsulate(algorithm, publicKey);
367
+ return ReallyMeMlKem.encapsulateWithProvider(resolveWasmProvider(), algorithm, publicKey);
330
368
  default:
331
369
  throw new ReallyMeCryptoError("unsupported-algorithm");
332
370
  }
@@ -335,11 +373,11 @@ export const ReallyMeCrypto = {
335
373
  switch (algorithm) {
336
374
  case "X-Wing-768":
337
375
  case "X-Wing-1024":
338
- return ReallyMeXWing.decapsulate(algorithm, ciphertext, secretKey);
376
+ return ReallyMeXWing.decapsulateWithProvider(resolveWasmProvider(), algorithm, ciphertext, secretKey);
339
377
  case "ML-KEM-512":
340
378
  case "ML-KEM-768":
341
379
  case "ML-KEM-1024":
342
- return ReallyMeMlKem.decapsulate(algorithm, ciphertext, secretKey);
380
+ return ReallyMeMlKem.decapsulateWithProvider(resolveWasmProvider(), algorithm, ciphertext, secretKey);
343
381
  default:
344
382
  throw new ReallyMeCryptoError("unsupported-algorithm");
345
383
  }
@@ -348,7 +386,7 @@ export const ReallyMeCrypto = {
348
386
  switch (suite) {
349
387
  case "DHKEM-P256-HKDF-SHA256-HKDF-SHA256-AES-256-GCM":
350
388
  case "DHKEM-X25519-HKDF-SHA256-HKDF-SHA256-CHACHA20-POLY1305":
351
- return ReallyMeHpke.sealBase(suite, recipientPublicKey, info, aad, plaintext);
389
+ return ReallyMeHpke.sealBaseWithProvider(resolveWasmProvider(), suite, recipientPublicKey, info, aad, plaintext);
352
390
  default:
353
391
  throw new ReallyMeCryptoError("unsupported-algorithm");
354
392
  }
@@ -357,9 +395,19 @@ export const ReallyMeCrypto = {
357
395
  switch (suite) {
358
396
  case "DHKEM-P256-HKDF-SHA256-HKDF-SHA256-AES-256-GCM":
359
397
  case "DHKEM-X25519-HKDF-SHA256-HKDF-SHA256-CHACHA20-POLY1305":
360
- return ReallyMeHpke.openBase(suite, recipientSecretKey, encapsulatedKey, info, aad, ciphertext);
398
+ return ReallyMeHpke.openBaseWithProvider(resolveWasmProvider(), suite, recipientSecretKey, encapsulatedKey, info, aad, ciphertext);
361
399
  default:
362
400
  throw new ReallyMeCryptoError("unsupported-algorithm");
363
401
  }
364
402
  },
403
+ });
404
+ export const createReallyMeCrypto = (providers = {}) => {
405
+ const configuredProvider = providers.wasmProvider;
406
+ return createReallyMeCryptoFacade(() => {
407
+ if (configuredProvider === undefined) {
408
+ throw new ReallyMeCryptoError("provider-failure");
409
+ }
410
+ return configuredProvider;
411
+ });
365
412
  };
413
+ export const ReallyMeCrypto = createReallyMeCryptoFacade(requireReallyMeWasmProvider);
package/dist/errors.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * user-provided bytes so callers can log them without leaking key material
4
4
  * or PII.
5
5
  */
6
- export type ReallyMeCryptoErrorCode = "invalid-input" | "invalid-signature" | "provider-failure" | "unsupported-algorithm";
6
+ export type ReallyMeCryptoErrorCode = "invalid-input" | "invalid-signature" | "authentication-failed" | "provider-failure" | "unsupported-algorithm";
7
7
  export declare class ReallyMeCryptoError extends Error {
8
8
  readonly code: ReallyMeCryptoErrorCode;
9
9
  constructor(code: ReallyMeCryptoErrorCode);
package/dist/hpke.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ReallyMeHpkeSuite } from "./algorithms.js";
2
+ import type { ReallyMeWasmProvider } from "./wasmProvider.js";
2
3
  export declare const HPKE_P256_PRIVATE_KEY_LENGTH = 32;
3
4
  export declare const HPKE_P256_PUBLIC_KEY_LENGTH = 65;
4
5
  export declare const HPKE_X25519_PRIVATE_KEY_LENGTH = 32;
@@ -10,6 +11,9 @@ export type ReallyMeHpkeSealedMessage = Readonly<{
10
11
  }>;
11
12
  export declare const ReallyMeHpke: {
12
13
  readonly sealBase: (suite: ReallyMeHpkeSuite, recipientPublicKey: Uint8Array, info: Uint8Array, aad: Uint8Array, plaintext: Uint8Array) => ReallyMeHpkeSealedMessage;
14
+ readonly sealBaseWithProvider: (provider: ReallyMeWasmProvider, suite: ReallyMeHpkeSuite, recipientPublicKey: Uint8Array, info: Uint8Array, aad: Uint8Array, plaintext: Uint8Array) => ReallyMeHpkeSealedMessage;
13
15
  readonly openBase: (suite: ReallyMeHpkeSuite, recipientSecretKey: Uint8Array, encapsulatedKey: Uint8Array, info: Uint8Array, aad: Uint8Array, ciphertext: Uint8Array) => Uint8Array;
16
+ readonly openBaseWithProvider: (provider: ReallyMeWasmProvider, suite: ReallyMeHpkeSuite, recipientSecretKey: Uint8Array, encapsulatedKey: Uint8Array, info: Uint8Array, aad: Uint8Array, ciphertext: Uint8Array) => Uint8Array;
14
17
  };
15
18
  export declare const sealHpkeBaseDeterministicallyForTest: (suite: ReallyMeHpkeSuite, recipientPublicKey: Uint8Array, encapsulationRandomness: Uint8Array, info: Uint8Array, aad: Uint8Array, plaintext: Uint8Array) => ReallyMeHpkeSealedMessage;
19
+ export declare const sealHpkeBaseDeterministicallyWithProviderForTest: (provider: ReallyMeWasmProvider, suite: ReallyMeHpkeSuite, recipientPublicKey: Uint8Array, encapsulationRandomness: Uint8Array, info: Uint8Array, aad: Uint8Array, plaintext: Uint8Array) => ReallyMeHpkeSealedMessage;
package/dist/hpke.js CHANGED
@@ -41,6 +41,11 @@ export const ReallyMeHpke = {
41
41
  ensureBytes(recipientPublicKey, config.publicKeyLength);
42
42
  return readSealedMessage(requireReallyMeWasmProvider().hpkeSealBase(config.id, recipientPublicKey, info, aad, plaintext), config, plaintext.length);
43
43
  },
44
+ sealBaseWithProvider(provider, suite, recipientPublicKey, info, aad, plaintext) {
45
+ const config = hpkeSuite(suite);
46
+ ensureBytes(recipientPublicKey, config.publicKeyLength);
47
+ return readSealedMessage(provider.hpkeSealBase(config.id, recipientPublicKey, info, aad, plaintext), config, plaintext.length);
48
+ },
44
49
  openBase(suite, recipientSecretKey, encapsulatedKey, info, aad, ciphertext) {
45
50
  const config = hpkeSuite(suite);
46
51
  ensureBytes(recipientSecretKey, config.privateKeyLength);
@@ -50,6 +55,15 @@ export const ReallyMeHpke = {
50
55
  }
51
56
  return requirePlaintext(requireReallyMeWasmProvider().hpkeOpenBase(config.id, recipientSecretKey, encapsulatedKey, info, aad, ciphertext));
52
57
  },
58
+ openBaseWithProvider(provider, suite, recipientSecretKey, encapsulatedKey, info, aad, ciphertext) {
59
+ const config = hpkeSuite(suite);
60
+ ensureBytes(recipientSecretKey, config.privateKeyLength);
61
+ ensureBytes(encapsulatedKey, config.publicKeyLength);
62
+ if (ciphertext.length < HPKE_AEAD_TAG_LENGTH) {
63
+ throw new ReallyMeCryptoError("invalid-input");
64
+ }
65
+ return requirePlaintext(provider.hpkeOpenBase(config.id, recipientSecretKey, encapsulatedKey, info, aad, ciphertext));
66
+ },
53
67
  };
54
68
  export const sealHpkeBaseDeterministicallyForTest = (suite, recipientPublicKey, encapsulationRandomness, info, aad, plaintext) => {
55
69
  const config = hpkeSuite(suite);
@@ -57,3 +71,9 @@ export const sealHpkeBaseDeterministicallyForTest = (suite, recipientPublicKey,
57
71
  ensureBytes(encapsulationRandomness, config.privateKeyLength);
58
72
  return readSealedMessage(requireReallyMeWasmProvider().hpkeSealBaseDerand(config.id, recipientPublicKey, encapsulationRandomness, info, aad, plaintext), config, plaintext.length);
59
73
  };
74
+ export const sealHpkeBaseDeterministicallyWithProviderForTest = (provider, suite, recipientPublicKey, encapsulationRandomness, info, aad, plaintext) => {
75
+ const config = hpkeSuite(suite);
76
+ ensureBytes(recipientPublicKey, config.publicKeyLength);
77
+ ensureBytes(encapsulationRandomness, config.privateKeyLength);
78
+ return readSealedMessage(provider.hpkeSealBaseDerand(config.id, recipientPublicKey, encapsulationRandomness, info, aad, plaintext), config, plaintext.length);
79
+ };
package/dist/index.d.ts CHANGED
@@ -2,14 +2,12 @@ export { ReallyMeCryptoError } from "./errors.js";
2
2
  export type { ReallyMeCryptoErrorCode } from "./errors.js";
3
3
  export { REALLYME_AEAD_ALGORITHMS, REALLYME_HASH_ALGORITHMS, REALLYME_HPKE_SUITES, REALLYME_KDF_ALGORITHMS, REALLYME_KEM_ALGORITHMS, REALLYME_KEY_AGREEMENT_ALGORITHMS, REALLYME_KEY_WRAP_ALGORITHMS, REALLYME_MAC_ALGORITHMS, REALLYME_SIGNATURE_ALGORITHMS, } from "./algorithms.js";
4
4
  export type { ReallyMeAeadAlgorithm, ReallyMeHashAlgorithm, ReallyMeHpkeSuite, ReallyMeKdfAlgorithm, ReallyMeKemAlgorithm, ReallyMeKeyAgreementAlgorithm, ReallyMeKeyWrapAlgorithm, ReallyMeMacAlgorithm, ReallyMeSignatureAlgorithm, } from "./algorithms.js";
5
- export { AEAD_KEY_LENGTH, AEAD_NONCE_LENGTH, AEAD_TAG_LENGTH, AES_128_GCM_KEY_LENGTH, AES_192_GCM_KEY_LENGTH, AES_256_GCM_KEY_LENGTH, ReallyMeAead, XCHACHA20_POLY1305_NONCE_LENGTH, } from "./aead.js";
5
+ export { AEAD_KEY_LENGTH, AEAD_NONCE_LENGTH, AEAD_TAG_LENGTH, AES_128_GCM_KEY_LENGTH, AES_192_GCM_KEY_LENGTH, AES_256_GCM_KEY_LENGTH, CHACHA20_POLY1305_KEY_LENGTH, ReallyMeAead, XCHACHA20_POLY1305_NONCE_LENGTH, } from "./aead.js";
6
6
  export { AES_256_KW_KEK_LENGTH, AES_KW_BLOCK_LENGTH, AES_KW_INTEGRITY_CHECK_LENGTH, AES_KW_MAX_KEY_DATA_LENGTH, AES_KW_MIN_KEY_DATA_LENGTH, AES_KW_MIN_WRAPPED_KEY_LENGTH, ReallyMeAesKw, } from "./aesKw.js";
7
7
  export { ARGON2ID_DERIVED_KEY_LENGTH, ARGON2ID_SALT_MAX_LENGTH, ARGON2ID_SALT_MIN_LENGTH, ARGON2ID_V1, ARGON2ID_V2, ReallyMeArgon2id, } from "./argon2id.js";
8
8
  export { BIP340_SCHNORR_AUX_RAND_LENGTH, BIP340_SCHNORR_MESSAGE_LENGTH, BIP340_SCHNORR_PUBLIC_KEY_LENGTH, BIP340_SCHNORR_SECRET_KEY_LENGTH, BIP340_SCHNORR_SIGNATURE_LENGTH, ReallyMeBip340Schnorr, } from "./bip340Schnorr.js";
9
9
  export { HPKE_AEAD_TAG_LENGTH, HPKE_P256_PRIVATE_KEY_LENGTH, HPKE_P256_PUBLIC_KEY_LENGTH, HPKE_X25519_PRIVATE_KEY_LENGTH, HPKE_X25519_PUBLIC_KEY_LENGTH, ReallyMeHpke, } from "./hpke.js";
10
10
  export type { ReallyMeHpkeSealedMessage as ReallyMeHpkeProviderSealedMessage } from "./hpke.js";
11
- export { ReallyMeCodecs } from "./codecs.js";
12
- export type { ReallyMeDagCborCidVerification, ReallyMeKeyMaterialKind, ReallyMeMulticodecMetadata, ReallyMeMulticodecTag, ReallyMeParsedMultikey, } from "./codecs.js";
13
11
  export { ReallyMeJwk } from "./jwk.js";
14
12
  export type { ReallyMeAkpJwk, ReallyMeEcJwk, ReallyMeJwk as ReallyMeJsonWebKey, ReallyMeJwkAlgorithm, ReallyMeJwkKey, ReallyMeJwks, ReallyMeJwksKeySet, ReallyMeOkpJwk, } from "./jwk.js";
15
13
  export { ML_KEM_1024_CIPHERTEXT_LENGTH, ML_KEM_1024_PUBLIC_KEY_LENGTH, ML_KEM_512_CIPHERTEXT_LENGTH, ML_KEM_512_PUBLIC_KEY_LENGTH, ML_KEM_768_CIPHERTEXT_LENGTH, ML_KEM_768_PUBLIC_KEY_LENGTH, ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH, ML_KEM_SECRET_KEY_LENGTH, ML_KEM_SHARED_SECRET_LENGTH, ReallyMeMlKem, } from "./mlKem.js";
@@ -20,12 +18,13 @@ export { ReallyMeSlhDsa, SLH_DSA_SHA2_128S_KEYGEN_SEED_LENGTH, SLH_DSA_SHA2_128S
20
18
  export type { ReallyMeSlhDsaKeyPair } from "./slhDsa.js";
21
19
  export { compiledProviders, REALLYME_CRYPTO_PROVIDERS } from "./providerCatalog.js";
22
20
  export type { ReallyMeCryptoProvider } from "./providerCatalog.js";
23
- export { ReallyMeCrypto } from "./cryptoFacade.js";
24
- export type { ReallyMeHpkeSealedMessage, ReallyMeKeyAgreementKeyPair, ReallyMeKemEncapsulation, ReallyMeKemKeyPair, ReallyMeSignatureKeyPair, } from "./cryptoFacade.js";
21
+ export { createReallyMeCrypto, ReallyMeCrypto } from "./cryptoFacade.js";
22
+ export type { ReallyMeCryptoFacade, ReallyMeCryptoProviders, ReallyMeHpkeSealedMessage, ReallyMeKeyAgreementKeyPair, ReallyMeKemEncapsulation, ReallyMeKemKeyPair, ReallyMeSignatureKeyPair, } from "./cryptoFacade.js";
25
23
  export { ReallyMeDigest } from "./digest.js";
26
24
  export { ED25519_PUBLIC_KEY_LENGTH, ED25519_SECRET_KEY_LENGTH, ED25519_SIGNATURE_LENGTH, ReallyMeEd25519, } from "./ed25519.js";
27
25
  export { HKDF_MAX_INPUT_LENGTH, HKDF_MAX_OUTPUT_LENGTH, HKDF_MIN_INPUT_KEY_MATERIAL_LENGTH, HKDF_MIN_OUTPUT_LENGTH, ReallyMeHkdf, } from "./hkdf.js";
28
26
  export { JWA_CONCAT_KDF_MAX_INFO_LENGTH, JWA_CONCAT_KDF_MAX_OUTPUT_LENGTH, JWA_CONCAT_KDF_MAX_SHARED_SECRET_LENGTH, JWA_CONCAT_KDF_MIN_OUTPUT_LENGTH, JWA_CONCAT_KDF_SHA256_DIGEST_LENGTH, ReallyMeJwaConcatKdf, } from "./jwaConcatKdf.js";
27
+ export { bestEffortClear } from "./memory.js";
29
28
  export { HMAC_MAX_KEY_LENGTH, HMAC_SHA256_TAG_LENGTH, HMAC_SHA512_TAG_LENGTH, ReallyMeHmac, } from "./hmac.js";
30
29
  export { P256_ECDH_COMPRESSED_PUBLIC_KEY_LENGTH, P256_ECDH_SECRET_KEY_LENGTH, P256_ECDH_SHARED_SECRET_LENGTH, ReallyMeP256Ecdh, } from "./p256Ecdh.js";
31
30
  export { P256_ECDSA_COMPACT_SIGNATURE_LENGTH, P256_ECDSA_COMPRESSED_PUBLIC_KEY_LENGTH, P256_ECDSA_DER_SIGNATURE_MAX_LENGTH, P256_ECDSA_SECRET_KEY_LENGTH, ReallyMeP256Ecdsa, } from "./p256Ecdsa.js";
@@ -38,7 +37,7 @@ export { ReallyMeSecp256k1, SECP256K1_COMPRESSED_PUBLIC_KEY_LENGTH, SECP256K1_SE
38
37
  export { ReallyMeRsa, RSA_PUBLIC_KEY_DER_MAX_LENGTH, RSA_SIGNATURE_MAX_LENGTH, } from "./rsa.js";
39
38
  export type { ReallyMeRsaPublicKeyDerEncoding } from "./rsa.js";
40
39
  export { ReallyMeX25519, X25519_PUBLIC_KEY_LENGTH, X25519_SECRET_KEY_LENGTH, X25519_SHARED_SECRET_LENGTH, } from "./x25519.js";
41
- export { installReallyMeWasmProvider } from "./wasmProvider.js";
40
+ export { createReallyMeWasmProvider, installReallyMeWasmProvider } from "./wasmProvider.js";
42
41
  export type { ReallyMeWasmProvider } from "./wasmProvider.js";
43
42
  export { ReallyMeXWing, X_WING_1024_CIPHERTEXT_LENGTH, X_WING_1024_PUBLIC_KEY_LENGTH, X_WING_768_CIPHERTEXT_LENGTH, X_WING_768_PUBLIC_KEY_LENGTH, X_WING_ENCAPSULATION_SEED_LENGTH, X_WING_SECRET_KEY_LENGTH, X_WING_SHARED_SECRET_LENGTH, } from "./xWing.js";
44
43
  export type { ReallyMeXWingEncapsulation, ReallyMeXWingKeyPair } from "./xWing.js";
package/dist/index.js CHANGED
@@ -3,22 +3,22 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  export { ReallyMeCryptoError } from "./errors.js";
5
5
  export { REALLYME_AEAD_ALGORITHMS, REALLYME_HASH_ALGORITHMS, REALLYME_HPKE_SUITES, REALLYME_KDF_ALGORITHMS, REALLYME_KEM_ALGORITHMS, REALLYME_KEY_AGREEMENT_ALGORITHMS, REALLYME_KEY_WRAP_ALGORITHMS, REALLYME_MAC_ALGORITHMS, REALLYME_SIGNATURE_ALGORITHMS, } from "./algorithms.js";
6
- export { AEAD_KEY_LENGTH, AEAD_NONCE_LENGTH, AEAD_TAG_LENGTH, AES_128_GCM_KEY_LENGTH, AES_192_GCM_KEY_LENGTH, AES_256_GCM_KEY_LENGTH, ReallyMeAead, XCHACHA20_POLY1305_NONCE_LENGTH, } from "./aead.js";
6
+ export { AEAD_KEY_LENGTH, AEAD_NONCE_LENGTH, AEAD_TAG_LENGTH, AES_128_GCM_KEY_LENGTH, AES_192_GCM_KEY_LENGTH, AES_256_GCM_KEY_LENGTH, CHACHA20_POLY1305_KEY_LENGTH, ReallyMeAead, XCHACHA20_POLY1305_NONCE_LENGTH, } from "./aead.js";
7
7
  export { AES_256_KW_KEK_LENGTH, AES_KW_BLOCK_LENGTH, AES_KW_INTEGRITY_CHECK_LENGTH, AES_KW_MAX_KEY_DATA_LENGTH, AES_KW_MIN_KEY_DATA_LENGTH, AES_KW_MIN_WRAPPED_KEY_LENGTH, ReallyMeAesKw, } from "./aesKw.js";
8
8
  export { ARGON2ID_DERIVED_KEY_LENGTH, ARGON2ID_SALT_MAX_LENGTH, ARGON2ID_SALT_MIN_LENGTH, ARGON2ID_V1, ARGON2ID_V2, ReallyMeArgon2id, } from "./argon2id.js";
9
9
  export { BIP340_SCHNORR_AUX_RAND_LENGTH, BIP340_SCHNORR_MESSAGE_LENGTH, BIP340_SCHNORR_PUBLIC_KEY_LENGTH, BIP340_SCHNORR_SECRET_KEY_LENGTH, BIP340_SCHNORR_SIGNATURE_LENGTH, ReallyMeBip340Schnorr, } from "./bip340Schnorr.js";
10
10
  export { HPKE_AEAD_TAG_LENGTH, HPKE_P256_PRIVATE_KEY_LENGTH, HPKE_P256_PUBLIC_KEY_LENGTH, HPKE_X25519_PRIVATE_KEY_LENGTH, HPKE_X25519_PUBLIC_KEY_LENGTH, ReallyMeHpke, } from "./hpke.js";
11
- export { ReallyMeCodecs } from "./codecs.js";
12
11
  export { ReallyMeJwk } from "./jwk.js";
13
12
  export { ML_KEM_1024_CIPHERTEXT_LENGTH, ML_KEM_1024_PUBLIC_KEY_LENGTH, ML_KEM_512_CIPHERTEXT_LENGTH, ML_KEM_512_PUBLIC_KEY_LENGTH, ML_KEM_768_CIPHERTEXT_LENGTH, ML_KEM_768_PUBLIC_KEY_LENGTH, ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH, ML_KEM_SECRET_KEY_LENGTH, ML_KEM_SHARED_SECRET_LENGTH, ReallyMeMlKem, } from "./mlKem.js";
14
13
  export { ML_DSA_44_PUBLIC_KEY_LENGTH, ML_DSA_44_SIGNATURE_LENGTH, ML_DSA_65_PUBLIC_KEY_LENGTH, ML_DSA_65_SIGNATURE_LENGTH, ML_DSA_87_PUBLIC_KEY_LENGTH, ML_DSA_87_SIGNATURE_LENGTH, ML_DSA_SECRET_KEY_LENGTH, ReallyMeMlDsa, } from "./mlDsa.js";
15
14
  export { ReallyMeSlhDsa, SLH_DSA_SHA2_128S_KEYGEN_SEED_LENGTH, SLH_DSA_SHA2_128S_PUBLIC_KEY_LENGTH, SLH_DSA_SHA2_128S_SECRET_KEY_LENGTH, SLH_DSA_SHA2_128S_SIGNATURE_LENGTH, } from "./slhDsa.js";
16
15
  export { compiledProviders, REALLYME_CRYPTO_PROVIDERS } from "./providerCatalog.js";
17
- export { ReallyMeCrypto } from "./cryptoFacade.js";
16
+ export { createReallyMeCrypto, ReallyMeCrypto } from "./cryptoFacade.js";
18
17
  export { ReallyMeDigest } from "./digest.js";
19
18
  export { ED25519_PUBLIC_KEY_LENGTH, ED25519_SECRET_KEY_LENGTH, ED25519_SIGNATURE_LENGTH, ReallyMeEd25519, } from "./ed25519.js";
20
19
  export { HKDF_MAX_INPUT_LENGTH, HKDF_MAX_OUTPUT_LENGTH, HKDF_MIN_INPUT_KEY_MATERIAL_LENGTH, HKDF_MIN_OUTPUT_LENGTH, ReallyMeHkdf, } from "./hkdf.js";
21
20
  export { JWA_CONCAT_KDF_MAX_INFO_LENGTH, JWA_CONCAT_KDF_MAX_OUTPUT_LENGTH, JWA_CONCAT_KDF_MAX_SHARED_SECRET_LENGTH, JWA_CONCAT_KDF_MIN_OUTPUT_LENGTH, JWA_CONCAT_KDF_SHA256_DIGEST_LENGTH, ReallyMeJwaConcatKdf, } from "./jwaConcatKdf.js";
21
+ export { bestEffortClear } from "./memory.js";
22
22
  export { HMAC_MAX_KEY_LENGTH, HMAC_SHA256_TAG_LENGTH, HMAC_SHA512_TAG_LENGTH, ReallyMeHmac, } from "./hmac.js";
23
23
  export { P256_ECDH_COMPRESSED_PUBLIC_KEY_LENGTH, P256_ECDH_SECRET_KEY_LENGTH, P256_ECDH_SHARED_SECRET_LENGTH, ReallyMeP256Ecdh, } from "./p256Ecdh.js";
24
24
  export { P256_ECDSA_COMPACT_SIGNATURE_LENGTH, P256_ECDSA_COMPRESSED_PUBLIC_KEY_LENGTH, P256_ECDSA_DER_SIGNATURE_MAX_LENGTH, P256_ECDSA_SECRET_KEY_LENGTH, ReallyMeP256Ecdsa, } from "./p256Ecdsa.js";
@@ -30,5 +30,5 @@ export { PBKDF2_MAX_INPUT_LENGTH, PBKDF2_MAX_OUTPUT_LENGTH, PBKDF2_MIN_INPUT_LEN
30
30
  export { ReallyMeSecp256k1, SECP256K1_COMPRESSED_PUBLIC_KEY_LENGTH, SECP256K1_SECRET_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH, } from "./secp256k1.js";
31
31
  export { ReallyMeRsa, RSA_PUBLIC_KEY_DER_MAX_LENGTH, RSA_SIGNATURE_MAX_LENGTH, } from "./rsa.js";
32
32
  export { ReallyMeX25519, X25519_PUBLIC_KEY_LENGTH, X25519_SECRET_KEY_LENGTH, X25519_SHARED_SECRET_LENGTH, } from "./x25519.js";
33
- export { installReallyMeWasmProvider } from "./wasmProvider.js";
33
+ export { createReallyMeWasmProvider, installReallyMeWasmProvider } from "./wasmProvider.js";
34
34
  export { ReallyMeXWing, X_WING_1024_CIPHERTEXT_LENGTH, X_WING_1024_PUBLIC_KEY_LENGTH, X_WING_768_CIPHERTEXT_LENGTH, X_WING_768_PUBLIC_KEY_LENGTH, X_WING_ENCAPSULATION_SEED_LENGTH, X_WING_SECRET_KEY_LENGTH, X_WING_SHARED_SECRET_LENGTH, } from "./xWing.js";
@@ -3,6 +3,7 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { sha256 } from "@noble/hashes/sha2.js";
5
5
  import { ReallyMeCryptoError } from "./errors.js";
6
+ import { bestEffortClear } from "./memory.js";
6
7
  export const JWA_CONCAT_KDF_SHA256_DIGEST_LENGTH = 32;
7
8
  export const JWA_CONCAT_KDF_MAX_SHARED_SECRET_LENGTH = 4096;
8
9
  export const JWA_CONCAT_KDF_MAX_INFO_LENGTH = 4096;
@@ -17,13 +18,16 @@ export const ReallyMeJwaConcatKdf = {
17
18
  const derived = new Uint8Array(reps * JWA_CONCAT_KDF_SHA256_DIGEST_LENGTH);
18
19
  for (let counter = 1; counter <= reps; counter += 1) {
19
20
  const counterBytes = uint32be(counter);
20
- const digest = sha256(concatBytes(counterBytes, sharedSecret, otherInfo));
21
+ const hashInput = concatBytes(counterBytes, sharedSecret, otherInfo);
22
+ const digest = sha256(hashInput);
21
23
  derived.set(digest, (counter - 1) * JWA_CONCAT_KDF_SHA256_DIGEST_LENGTH);
22
- counterBytes.fill(0);
24
+ bestEffortClear(digest);
25
+ bestEffortClear(hashInput);
26
+ bestEffortClear(counterBytes);
23
27
  }
24
28
  const output = derived.slice(0, outputLength);
25
- derived.fill(0);
26
- otherInfo.fill(0);
29
+ bestEffortClear(derived);
30
+ bestEffortClear(otherInfo);
27
31
  return output;
28
32
  },
29
33
  };
package/dist/jwk.js CHANGED
@@ -3,9 +3,8 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { p256 } from "@noble/curves/nist.js";
5
5
  import { secp256k1 } from "@noble/curves/secp256k1.js";
6
+ import { base64urlDecode as codecBase64urlDecodeValue, base64urlEncode as codecBase64urlEncodeValue, canonicalizeJson as codecCanonicalizeJson, ReallyMeCodecError, } from "@reallyme/codec";
6
7
  import { ReallyMeCryptoError } from "./errors.js";
7
- const BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
8
- const DECODE_TABLE = new Map([...BASE64URL_ALPHABET].map((char, index) => [char, index]));
9
8
  const jwkSpec = (algorithm) => {
10
9
  switch (algorithm) {
11
10
  case "Ed25519":
@@ -41,63 +40,6 @@ const ensureLength = (bytes, expectedLength) => {
41
40
  throw new ReallyMeCryptoError("invalid-input");
42
41
  }
43
42
  };
44
- const byteAt = (bytes, index) => {
45
- const value = bytes[index];
46
- if (value === undefined) {
47
- throw new ReallyMeCryptoError("invalid-input");
48
- }
49
- return value;
50
- };
51
- const base64urlEncode = (bytes) => {
52
- let encoded = "";
53
- let index = 0;
54
- while (index < bytes.length) {
55
- const first = byteAt(bytes, index);
56
- const second = index + 1 < bytes.length ? byteAt(bytes, index + 1) : 0;
57
- const third = index + 2 < bytes.length ? byteAt(bytes, index + 2) : 0;
58
- const triple = (first << 16) | (second << 8) | third;
59
- encoded += BASE64URL_ALPHABET[(triple >> 18) & 0x3f];
60
- encoded += BASE64URL_ALPHABET[(triple >> 12) & 0x3f];
61
- if (index + 1 < bytes.length) {
62
- encoded += BASE64URL_ALPHABET[(triple >> 6) & 0x3f];
63
- }
64
- if (index + 2 < bytes.length) {
65
- encoded += BASE64URL_ALPHABET[triple & 0x3f];
66
- }
67
- index += 3;
68
- }
69
- return encoded;
70
- };
71
- const decodeChar = (char) => {
72
- const value = DECODE_TABLE.get(char);
73
- if (value === undefined) {
74
- throw new ReallyMeCryptoError("invalid-input");
75
- }
76
- return value;
77
- };
78
- const base64urlDecode = (encoded) => {
79
- if (encoded.length % 4 === 1) {
80
- throw new ReallyMeCryptoError("invalid-input");
81
- }
82
- const outputLength = Math.floor((encoded.length * 6) / 8);
83
- const output = new Uint8Array(outputLength);
84
- let buffer = 0;
85
- let bits = 0;
86
- let offset = 0;
87
- for (const char of encoded) {
88
- buffer = (buffer << 6) | decodeChar(char);
89
- bits += 6;
90
- if (bits >= 8) {
91
- bits -= 8;
92
- output[offset] = (buffer >> bits) & 0xff;
93
- offset += 1;
94
- }
95
- }
96
- if (offset !== output.length) {
97
- throw new ReallyMeCryptoError("invalid-input");
98
- }
99
- return output;
100
- };
101
43
  const readString = (value, name) => {
102
44
  if (typeof value !== "object" || value === null) {
103
45
  throw new ReallyMeCryptoError("invalid-input");
@@ -108,6 +50,29 @@ const readString = (value, name) => {
108
50
  }
109
51
  return property;
110
52
  };
53
+ const privateJwkMemberNames = [
54
+ "d",
55
+ "p",
56
+ "q",
57
+ "dp",
58
+ "dq",
59
+ "qi",
60
+ "oth",
61
+ "k",
62
+ "priv",
63
+ "privateKey",
64
+ "secretKey",
65
+ ];
66
+ const rejectPrivateKeyMaterial = (value) => {
67
+ if (typeof value !== "object" || value === null) {
68
+ throw new ReallyMeCryptoError("invalid-input");
69
+ }
70
+ for (const name of privateJwkMemberNames) {
71
+ if (Reflect.has(value, name)) {
72
+ throw new ReallyMeCryptoError("invalid-input");
73
+ }
74
+ }
75
+ };
111
76
  const optionalStringMatches = (value, name, expected) => {
112
77
  if (typeof value !== "object" || value === null) {
113
78
  throw new ReallyMeCryptoError("invalid-input");
@@ -172,14 +137,43 @@ const compressEc = (algorithm, x, y) => {
172
137
  throw new ReallyMeCryptoError("invalid-input");
173
138
  }
174
139
  };
140
+ const mapCodecError = (error) => {
141
+ if (error instanceof ReallyMeCodecError && error.code === "provider-failure") {
142
+ return new ReallyMeCryptoError("provider-failure");
143
+ }
144
+ return new ReallyMeCryptoError("invalid-input");
145
+ };
146
+ const codecBase64urlEncode = (bytes) => {
147
+ try {
148
+ return codecBase64urlEncodeValue(bytes);
149
+ }
150
+ catch (error) {
151
+ throw mapCodecError(error);
152
+ }
153
+ };
154
+ const codecBase64urlDecode = (encoded) => {
155
+ try {
156
+ return codecBase64urlDecodeValue(encoded);
157
+ }
158
+ catch (error) {
159
+ throw mapCodecError(error);
160
+ }
161
+ };
162
+ const codecBase64urlDecodeCanonical = (encoded) => {
163
+ const bytes = codecBase64urlDecode(encoded);
164
+ if (codecBase64urlEncode(bytes) !== encoded) {
165
+ throw new ReallyMeCryptoError("invalid-input");
166
+ }
167
+ return bytes;
168
+ };
175
169
  const toJcs = (jwk) => {
176
- if (jwk.kty === "EC") {
177
- return `{"alg":${JSON.stringify(jwk.alg)},"crv":${JSON.stringify(jwk.crv)},"kty":"EC","use":"sig","x":${JSON.stringify(jwk.x)},"y":${JSON.stringify(jwk.y)}}`;
170
+ const key = ReallyMeJwk.fromJwk(jwk);
171
+ try {
172
+ return codecCanonicalizeJson(key.jwk);
178
173
  }
179
- if (jwk.kty === "AKP") {
180
- return `{"alg":${JSON.stringify(jwk.alg)},"kty":"AKP","pub":${JSON.stringify(jwk.pub)},"use":${JSON.stringify(jwk.use)}}`;
174
+ catch (error) {
175
+ throw mapCodecError(error);
181
176
  }
182
- return `{"alg":${JSON.stringify(jwk.alg)},"crv":${JSON.stringify(jwk.crv)},"kty":"OKP","use":${JSON.stringify(jwk.use)},"x":${JSON.stringify(jwk.x)}}`;
183
177
  };
184
178
  export const ReallyMeJwk = {
185
179
  toJwk(algorithm, publicKey) {
@@ -193,15 +187,15 @@ export const ReallyMeJwk = {
193
187
  crv: ecAlgorithm,
194
188
  kty: "EC",
195
189
  use: "sig",
196
- x: base64urlEncode(uncompressed.slice(1, 33)),
197
- y: base64urlEncode(uncompressed.slice(33, 65)),
190
+ x: codecBase64urlEncode(uncompressed.slice(1, 33)),
191
+ y: codecBase64urlEncode(uncompressed.slice(33, 65)),
198
192
  };
199
193
  }
200
194
  if (spec.kty === "AKP") {
201
195
  return {
202
196
  alg: spec.alg,
203
197
  kty: "AKP",
204
- pub: base64urlEncode(publicKey),
198
+ pub: codecBase64urlEncode(publicKey),
205
199
  use: spec.use,
206
200
  };
207
201
  }
@@ -214,10 +208,11 @@ export const ReallyMeJwk = {
214
208
  crv,
215
209
  kty: "OKP",
216
210
  use: spec.use,
217
- x: base64urlEncode(publicKey),
211
+ x: codecBase64urlEncode(publicKey),
218
212
  };
219
213
  },
220
214
  fromJwk(value) {
215
+ rejectPrivateKeyMaterial(value);
221
216
  const kty = readString(value, "kty");
222
217
  const spec = kty === "AKP"
223
218
  ? specFromAlgorithm(readString(value, "alg"))
@@ -229,8 +224,8 @@ export const ReallyMeJwk = {
229
224
  optionalStringMatches(value, "use", spec.use);
230
225
  if (spec.kty === "EC") {
231
226
  const ecAlgorithm = spec.crv === "P-256" ? "P-256" : "secp256k1";
232
- const x = base64urlDecode(readString(value, "x"));
233
- const y = base64urlDecode(readString(value, "y"));
227
+ const x = codecBase64urlDecodeCanonical(readString(value, "x"));
228
+ const y = codecBase64urlDecodeCanonical(readString(value, "y"));
234
229
  const publicKey = compressEc(ecAlgorithm, x, y);
235
230
  ensureLength(publicKey, spec.publicKeyLength);
236
231
  const jwk = ReallyMeJwk.toJwk(ecAlgorithm, publicKey);
@@ -241,14 +236,14 @@ export const ReallyMeJwk = {
241
236
  if (crv !== "Ed25519" && crv !== "X25519") {
242
237
  throw new ReallyMeCryptoError("unsupported-algorithm");
243
238
  }
244
- const publicKey = base64urlDecode(readString(value, "x"));
239
+ const publicKey = codecBase64urlDecodeCanonical(readString(value, "x"));
245
240
  ensureLength(publicKey, spec.publicKeyLength);
246
241
  const jwk = ReallyMeJwk.toJwk(crv, publicKey);
247
242
  return { algorithm: crv, publicKey, jwk };
248
243
  }
249
244
  const algorithm = readString(value, "alg");
250
245
  const akpSpec = specFromAlgorithm(algorithm);
251
- const publicKey = base64urlDecode(readString(value, "pub"));
246
+ const publicKey = codecBase64urlDecodeCanonical(readString(value, "pub"));
252
247
  ensureLength(publicKey, akpSpec.publicKeyLength);
253
248
  switch (algorithm) {
254
249
  case "ML-DSA-44":
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Best-effort cleanup for caller-owned managed-runtime byte arrays.
3
+ *
4
+ * This overwrites the supplied `Uint8Array` view in place. It does not and
5
+ * cannot clear copies already made by JavaScript engines, WebAssembly
6
+ * marshalling, snapshots, browser developer tools, or native providers.
7
+ */
8
+ export declare const bestEffortClear: (bytes: Uint8Array) => void;
package/dist/memory.js ADDED
@@ -0,0 +1,13 @@
1
+ // SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ /**
5
+ * Best-effort cleanup for caller-owned managed-runtime byte arrays.
6
+ *
7
+ * This overwrites the supplied `Uint8Array` view in place. It does not and
8
+ * cannot clear copies already made by JavaScript engines, WebAssembly
9
+ * marshalling, snapshots, browser developer tools, or native providers.
10
+ */
11
+ export const bestEffortClear = (bytes) => {
12
+ bytes.fill(0);
13
+ };