@reallyme/crypto 0.2.0 → 0.3.1

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/README.md +107 -17
  2. package/dist/aead.js +15 -8
  3. package/dist/aesKw.d.ts +7 -4
  4. package/dist/aesKw.js +69 -22
  5. package/dist/algorithms.d.ts +12 -4
  6. package/dist/algorithms.js +8 -2
  7. package/dist/argon2id.d.ts +1 -0
  8. package/dist/argon2id.js +13 -4
  9. package/dist/cryptoFacade.d.ts +33 -8
  10. package/dist/cryptoFacade.js +42 -29
  11. package/dist/hkdf.d.ts +1 -0
  12. package/dist/hkdf.js +5 -1
  13. package/dist/hmac.d.ts +3 -0
  14. package/dist/hmac.js +24 -3
  15. package/dist/hpke.d.ts +1 -2
  16. package/dist/hpke.js +33 -22
  17. package/dist/index.d.ts +9 -7
  18. package/dist/index.js +8 -6
  19. package/dist/jwk.d.ts +2 -1
  20. package/dist/jwk.js +104 -63
  21. package/dist/kmac.d.ts +11 -0
  22. package/dist/kmac.js +49 -0
  23. package/dist/mlDsa.js +22 -11
  24. package/dist/mlKem.d.ts +0 -3
  25. package/dist/mlKem.js +19 -37
  26. package/dist/operationResponse.d.ts +16 -0
  27. package/dist/operationResponse.js +46 -0
  28. package/dist/pbkdf2.d.ts +6 -8
  29. package/dist/pbkdf2.js +12 -12
  30. package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.d.ts +1741 -328
  31. package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.js +731 -344
  32. package/dist/proto.d.ts +6 -22
  33. package/dist/proto.js +124 -102
  34. package/dist/rsa.d.ts +2 -1
  35. package/dist/rsa.js +5 -1
  36. package/dist/slhDsa.js +21 -11
  37. package/dist/validateBytes.d.ts +6 -1
  38. package/dist/validateBytes.js +57 -3
  39. package/dist/wasm/reallyme_crypto_wasm.js +91 -134
  40. package/dist/wasm/reallyme_crypto_wasm_bg.wasm +0 -0
  41. package/dist/wasmModuleTypes.d.ts +7 -10
  42. package/dist/wasmProvider.d.ts +11 -13
  43. package/dist/wasmProvider.js +16 -19
  44. package/dist/xWing.d.ts +0 -5
  45. package/dist/xWing.js +20 -52
  46. package/package.json +15 -2
package/README.md CHANGED
@@ -9,14 +9,13 @@ SPDX-License-Identifier: Apache-2.0
9
9
  [![npm](https://img.shields.io/npm/v/@reallyme/crypto?label=npm&color=2563eb)](https://www.npmjs.com/package/@reallyme/crypto)
10
10
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/reallyme/crypto/blob/main/LICENSE)
11
11
 
12
- ReallyMe Crypto provides a platform-agnostic cryptography API for TypeScript,
13
- Rust, Swift, and Kotlin.
12
+ `@reallyme/crypto` is the TypeScript SDK for ReallyMe Crypto. It provides typed,
13
+ synchronous APIs for Node.js and browsers, with explicit JavaScript and
14
+ package-owned WASM providers.
14
15
 
15
- Applications can implement cryptographic operations once and rely on identical
16
- algorithms, key formats, verification behavior, and protocol contracts across
17
- servers, browsers, iOS, Android, and WASM. Native platform providers are used
18
- where appropriate, while shared conformance vectors ensure byte-for-byte
19
- compatible behavior across every supported language.
16
+ The package shares algorithm identifiers, byte formats, typed failures, and
17
+ conformance vectors with the Rust, Swift, Kotlin, and Android SDKs. Availability
18
+ is provider-specific; unsupported routes fail closed.
20
19
 
21
20
  ## Why
22
21
 
@@ -24,10 +23,9 @@ Modern cryptography APIs differ across platforms. Algorithms are exposed
24
23
  differently, key formats vary, providers have different capabilities, and error
25
24
  behavior is inconsistent.
26
25
 
27
- ReallyMe Crypto provides one consistent cryptography contract across every
28
- supported platform. The same application logic can be shared between backend
29
- services, mobile applications, and browsers without maintaining separate
30
- cryptographic implementations.
26
+ ReallyMe Crypto makes platform differences explicit while preserving a stable
27
+ contract for every supported route. Applications can share protocol logic
28
+ without silently changing cryptographic providers.
31
29
 
32
30
  ## Installation
33
31
 
@@ -49,6 +47,11 @@ const digest = ReallyMeCrypto.hash(
49
47
  The API is synchronous. Signature verification throws `ReallyMeCryptoError` on
50
48
  invalid input rather than returning a boolean that can be accidentally ignored.
51
49
 
50
+ KDF facade selectors are family-specific: `deriveKey` accepts only PBKDF2,
51
+ `deriveHkdf` accepts only HKDF, and the JWA Concat KDF and KMAC methods accept
52
+ their exact selectors. Argon2id uses the dedicated `deriveArgon2id` route with
53
+ an immutable profile version rather than an iteration count.
54
+
52
55
  ## Post-Quantum and WASM
53
56
 
54
57
  Classical primitives are backed by pinned `@noble` packages. The primitives that
@@ -62,22 +65,36 @@ explicit provider object. Missing WASM providers fail closed with
62
65
  to the published `@reallyme/codec` package, so applications that use those
63
66
  helpers should also install the Codec WASM provider.
64
67
 
68
+ JSON and protobuf JWKS ingress routes accept at most 1,024 keys. Encoded JWK
69
+ and JWKS protobuf inputs are capped at one mebibyte before decoding, and an
70
+ embedded canonical JCS value is capped at 8 KiB. These limits apply equally to
71
+ the direct message adapters and byte-oriented helpers.
72
+
65
73
  ```ts
66
74
  import { readFileSync } from "node:fs";
67
75
  import { installReallyMeCodecWasmProvider } from "@reallyme/codec";
68
- import * as codecWasmProvider from "@reallyme/codec/wasm/reallyme_codec_wasm.js";
76
+ import { initSync as initCodecWasm } from "@reallyme/codec/wasm/reallyme_codec_wasm.js";
69
77
  import {
70
78
  createReallyMeCrypto,
71
79
  createReallyMeWasmProvider,
72
80
  installReallyMeWasmProvider,
73
81
  ReallyMeCrypto,
74
82
  } from "@reallyme/crypto";
75
- import * as wasmProvider from "@reallyme/crypto/wasm/reallyme_crypto_wasm.js";
83
+ import { initSync as initCryptoWasm } from "@reallyme/crypto/wasm/reallyme_crypto_wasm.js";
84
+
85
+ // Provider constructors validate the dynamically imported module as `unknown`
86
+ // against the package's explicit required-export list.
87
+ const wasmProvider: unknown = await import(
88
+ "@reallyme/crypto/wasm/reallyme_crypto_wasm.js"
89
+ );
90
+ const codecWasmProvider: unknown = await import(
91
+ "@reallyme/codec/wasm/reallyme_codec_wasm.js"
92
+ );
76
93
 
77
94
  const wasmUrl = import.meta.resolve(
78
95
  "@reallyme/crypto/wasm/reallyme_crypto_wasm_bg.wasm",
79
96
  );
80
- wasmProvider.initSync({ module: readFileSync(new URL(wasmUrl)) });
97
+ initCryptoWasm({ module: readFileSync(new URL(wasmUrl)) });
81
98
  installReallyMeWasmProvider(wasmProvider);
82
99
  const isolatedCrypto = createReallyMeCrypto({
83
100
  wasmProvider: createReallyMeWasmProvider(wasmProvider),
@@ -86,7 +103,7 @@ const isolatedCrypto = createReallyMeCrypto({
86
103
  const codecWasmUrl = import.meta.resolve(
87
104
  "@reallyme/codec/wasm/reallyme_codec_wasm_bg.wasm",
88
105
  );
89
- codecWasmProvider.initSync({ module: readFileSync(new URL(codecWasmUrl)) });
106
+ initCodecWasm({ module: readFileSync(new URL(codecWasmUrl)) });
90
107
  installReallyMeCodecWasmProvider(codecWasmProvider);
91
108
 
92
109
  const keyPair = ReallyMeCrypto.generateKemKeyPair("X-Wing-768");
@@ -97,6 +114,52 @@ The explicit facade form is preferred for Workers, SSR, tests, and multi-bundle
97
114
  applications because each instance owns its provider routing and does not depend
98
115
  on package-global mutable state.
99
116
 
117
+ The package-owned WASM provider derives ML-DSA, ML-KEM, and X-Wing seed-based
118
+ keypairs inside Rust and returns the public key produced by that implementation.
119
+ Custom provider objects are trusted providers: if an application supplies its
120
+ own seed-derived keypair provider, it is responsible for public-key
121
+ correspondence and must be covered by its own conformance evidence.
122
+
123
+ ## Raw WASM Module Contract
124
+
125
+ The raw `@reallyme/crypto/wasm/reallyme_crypto_wasm.js` export is an internal
126
+ provider artifact for installing or constructing `ReallyMeWasmProvider`
127
+ instances. Direct raw WASM calls are unsupported for application logic. Use the
128
+ typed TypeScript facades, which validate caller inputs, numeric ranges, provider
129
+ outputs, aliasing, and cleanup before invoking the raw module.
130
+
131
+ This distinction matters for numeric parameters: JavaScript-to-WASM glue can
132
+ coerce raw `number` arguments before Rust receives them. The supported facades
133
+ reject out-of-range Argon2id, KMAC, HPKE, and RSA selector or length values
134
+ before provider dispatch. Package checks verify that the README, package
135
+ exports, TypeScript declarations, and generated WASM glue continue to agree on
136
+ this support boundary and that no ambient global crypto-provider functions are
137
+ used.
138
+
139
+ Variable-length direct primitive inputs are capped at one mebibyte before any
140
+ WASM copy or provider dispatch. Authenticated ciphertext may additionally carry
141
+ the 16-byte tag. HPKE `info` is capped at 65,530 bytes so the fixed five-byte
142
+ key-schedule label remains within RFC 9180's two-byte context-length limit.
143
+
144
+ ### Browser WASM Lifecycle
145
+
146
+ Argon2id V1 and V2 require 256 MiB and 512 MiB of working memory respectively.
147
+ WebAssembly linear memory does not shrink, so an instance can retain that
148
+ resident allocation after derivation. Browser applications should run Argon2id
149
+ in a short-lived dedicated Worker and terminate the Worker after use, especially
150
+ on memory-constrained devices.
151
+
152
+ Treat a WebAssembly trap as fatal for that provider instance. The SDK maps the
153
+ trap to `provider-failure`, but does not guarantee that an instance interrupted
154
+ mid-operation remains reusable; discard its Worker or provider context before
155
+ performing another operation.
156
+
157
+ The generated loader resolves its `.wasm` file by relative URL and does not
158
+ embed a content hash. The npm tarball protects the installed package artifact,
159
+ but applications that separately host WASM bytes should use immutable asset
160
+ URLs and verify those bytes against a deployment-controlled digest before
161
+ initialization.
162
+
100
163
  ## Memory Hygiene
101
164
 
102
165
  Rust-owned secret buffers are zeroized by the Rust implementation and WASM
@@ -109,6 +172,18 @@ as practical:
109
172
  bestEffortClear(secretBytes);
110
173
  ```
111
174
 
175
+ Explicit provider results must be independently owned `Uint8Array` values that
176
+ do not overlap caller inputs or sibling result fields. The facade rejects
177
+ aliases as `provider-failure` without modifying caller storage. Malformed
178
+ provider-owned secret outputs are cleared before the same typed failure is
179
+ returned.
180
+
181
+ AES-KW provider results must have the exact RFC 3394 `plaintext + 8` or
182
+ `wrapped - 8` length. The facade wipes and rejects a returned typed array whose
183
+ length violates that contract. KMAC keys and customization strings are capped
184
+ at 4 KiB, contexts at 64 KiB, and outputs at 64 KiB before WASM copies are
185
+ created.
186
+
112
187
  Do not move private keys, passwords, plaintext, shared secrets, or derived keys
113
188
  through strings or JSON paths.
114
189
 
@@ -119,11 +194,26 @@ through strings or JSON paths.
119
194
  - Shared conformance vectors across Rust, Swift, Kotlin, and TypeScript.
120
195
  - Native providers where available, WASM-backed implementations where needed.
121
196
  - Typed errors and fail-closed verification behavior.
122
- - Protobuf algorithm identifiers for API and storage boundaries, via
123
- `@reallyme/crypto/proto`.
197
+ - Generated protobuf operations, algorithm identifiers, and typed wire errors
198
+ via `@reallyme/crypto/proto`.
124
199
  - JWK, multikey, multicodec, HPKE, ML-KEM, ML-DSA, Ed25519, secp256k1, X25519,
125
200
  AES-GCM, ChaCha20-Poly1305, and more.
126
201
 
202
+ ## Protobuf Process Boundary
203
+
204
+ `processOperationResponse(request)` accepts one generated binary
205
+ `CryptoOperationRequest`. `processOperationResponseJson(requestJson)` accepts
206
+ the strict generated ProtoJSON view only for non-secret hash, verification,
207
+ key-generation, encapsulation, and sender-export requests. Both return binary
208
+ `CryptoOperationResponse` bytes, so malformed input, unsupported algorithms,
209
+ and resource-limit failures preserve their structured protobuf branch and
210
+ reason. This is the single executable structured response contract.
211
+
212
+ ProtoJSON is request-only. Decode returned binary protobuf with the generated
213
+ exports from `@reallyme/crypto/proto`. Secret-bearing operation selectors are
214
+ rejected before JSON value deserialization and must use protobuf bytes. Clear
215
+ caller-owned `Uint8Array` values after use.
216
+
127
217
  ## Documentation
128
218
 
129
219
  The complete documentation, provider matrix, protocol contracts, and conformance
package/dist/aead.js CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { ReallyMeCryptoError } from "./errors.js";
5
- import { ensureByteArray, ensureBytes } from "./validateBytes.js";
5
+ import { ensureByteArrayAtMost, ensureBytes, ensureIndependentByteArray, MAX_CRYPTO_INPUT_LENGTH, } from "./validateBytes.js";
6
6
  import { requireReallyMeWasmProvider } from "./wasmProvider.js";
7
7
  export const AEAD_KEY_LENGTH = 32;
8
8
  export const AES_128_GCM_KEY_LENGTH = 16;
@@ -64,24 +64,31 @@ const aeadSuiteWithProvider = (algorithm, provider) => {
64
64
  const sealWithSuite = (suite, key, nonce, aad, plaintext) => {
65
65
  ensureBytes(key, suite.keyLength);
66
66
  ensureBytes(nonce, suite.nonceLength);
67
- ensureByteArray(aad);
68
- ensureByteArray(plaintext);
69
- return requireBytesOutput(suite.seal(key, nonce, aad, plaintext));
67
+ ensureByteArrayAtMost(aad, MAX_CRYPTO_INPUT_LENGTH);
68
+ ensureByteArrayAtMost(plaintext, MAX_CRYPTO_INPUT_LENGTH);
69
+ return requireBytesOutput(suite.seal(key, nonce, aad, plaintext), plaintext.length + AEAD_TAG_LENGTH, [key, nonce, aad, plaintext]);
70
70
  };
71
71
  const openWithSuite = (suite, key, nonce, aad, ciphertextWithTag) => {
72
72
  ensureBytes(key, suite.keyLength);
73
73
  ensureBytes(nonce, suite.nonceLength);
74
- ensureByteArray(aad);
75
- ensureByteArray(ciphertextWithTag);
74
+ ensureByteArrayAtMost(aad, MAX_CRYPTO_INPUT_LENGTH);
75
+ ensureByteArrayAtMost(ciphertextWithTag, MAX_CRYPTO_INPUT_LENGTH + AEAD_TAG_LENGTH);
76
76
  if (ciphertextWithTag.length < AEAD_TAG_LENGTH) {
77
77
  throw new ReallyMeCryptoError("invalid-input");
78
78
  }
79
- return requireBytesOutput(suite.open(key, nonce, aad, ciphertextWithTag));
79
+ return requireBytesOutput(suite.open(key, nonce, aad, ciphertextWithTag), ciphertextWithTag.length - AEAD_TAG_LENGTH, [key, nonce, aad, ciphertextWithTag]);
80
80
  };
81
- const requireBytesOutput = (value) => {
81
+ const requireBytesOutput = (value, expectedLength, inputs) => {
82
82
  if (!(value instanceof Uint8Array)) {
83
83
  throw new ReallyMeCryptoError("provider-failure");
84
84
  }
85
+ // Validate ownership before clearing malformed provider output so an alias
86
+ // cannot turn error cleanup into mutation of caller-owned secret material.
87
+ ensureIndependentByteArray(value, inputs);
88
+ if (value.length !== expectedLength) {
89
+ value.fill(0);
90
+ throw new ReallyMeCryptoError("provider-failure");
91
+ }
85
92
  return value;
86
93
  };
87
94
  export const ReallyMeAead = {
package/dist/aesKw.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import type { ReallyMeWasmProvider } from "./wasmProvider.js";
2
+ import type { ReallyMeKeyWrapAlgorithm } from "./algorithms.js";
3
+ export declare const AES_128_KW_KEK_LENGTH = 16;
4
+ export declare const AES_192_KW_KEK_LENGTH = 24;
2
5
  export declare const AES_256_KW_KEK_LENGTH = 32;
3
6
  export declare const AES_KW_BLOCK_LENGTH = 8;
4
7
  export declare const AES_KW_INTEGRITY_CHECK_LENGTH = 8;
@@ -6,8 +9,8 @@ export declare const AES_KW_MIN_KEY_DATA_LENGTH = 16;
6
9
  export declare const AES_KW_MIN_WRAPPED_KEY_LENGTH: number;
7
10
  export declare const AES_KW_MAX_KEY_DATA_LENGTH = 4096;
8
11
  export declare const ReallyMeAesKw: {
9
- readonly wrapKey: (wrappingKey: Uint8Array, keyToWrap: Uint8Array) => Uint8Array;
10
- readonly wrapKeyWithProvider: (provider: ReallyMeWasmProvider, wrappingKey: Uint8Array, keyToWrap: Uint8Array) => Uint8Array;
11
- readonly unwrapKey: (wrappingKey: Uint8Array, wrappedKey: Uint8Array) => Uint8Array;
12
- readonly unwrapKeyWithProvider: (provider: ReallyMeWasmProvider, wrappingKey: Uint8Array, wrappedKey: Uint8Array) => Uint8Array;
12
+ readonly wrapKey: (algorithm: ReallyMeKeyWrapAlgorithm, wrappingKey: Uint8Array, keyToWrap: Uint8Array) => Uint8Array;
13
+ readonly wrapKeyWithProvider: (algorithm: ReallyMeKeyWrapAlgorithm, provider: ReallyMeWasmProvider, wrappingKey: Uint8Array, keyToWrap: Uint8Array) => Uint8Array;
14
+ readonly unwrapKey: (algorithm: ReallyMeKeyWrapAlgorithm, wrappingKey: Uint8Array, wrappedKey: Uint8Array) => Uint8Array;
15
+ readonly unwrapKeyWithProvider: (algorithm: ReallyMeKeyWrapAlgorithm, provider: ReallyMeWasmProvider, wrappingKey: Uint8Array, wrappedKey: Uint8Array) => Uint8Array;
13
16
  };
package/dist/aesKw.js CHANGED
@@ -2,8 +2,10 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { ReallyMeCryptoError } from "./errors.js";
5
- import { ensureBytes } from "./validateBytes.js";
5
+ import { ensureByteArray, ensureBytes, ensureIndependentByteArray, } from "./validateBytes.js";
6
6
  import { requireReallyMeWasmProvider } from "./wasmProvider.js";
7
+ export const AES_128_KW_KEK_LENGTH = 16;
8
+ export const AES_192_KW_KEK_LENGTH = 24;
7
9
  export const AES_256_KW_KEK_LENGTH = 32;
8
10
  export const AES_KW_BLOCK_LENGTH = 8;
9
11
  export const AES_KW_INTEGRITY_CHECK_LENGTH = 8;
@@ -25,31 +27,76 @@ const validateWrappedKeyLength = (length) => {
25
27
  throw new ReallyMeCryptoError("invalid-input");
26
28
  }
27
29
  };
28
- const requireBytesOutput = (value) => {
30
+ const requireBytesOutput = (value, expectedLength, inputs) => {
29
31
  if (!(value instanceof Uint8Array)) {
30
32
  throw new ReallyMeCryptoError("provider-failure");
31
33
  }
34
+ // Reject aliases before wiping malformed output. Otherwise a provider could
35
+ // return a caller-owned key/input view and make validation erase that input.
36
+ ensureIndependentByteArray(value, inputs);
37
+ if (value.length !== expectedLength) {
38
+ value.fill(0);
39
+ throw new ReallyMeCryptoError("provider-failure");
40
+ }
32
41
  return value;
33
42
  };
43
+ const kekLengthForAlgorithm = (algorithm) => {
44
+ switch (algorithm) {
45
+ case "AES-128-KW":
46
+ return AES_128_KW_KEK_LENGTH;
47
+ case "AES-192-KW":
48
+ return AES_192_KW_KEK_LENGTH;
49
+ case "AES-256-KW":
50
+ return AES_256_KW_KEK_LENGTH;
51
+ }
52
+ };
53
+ const wrapFunctionForAlgorithm = (provider, algorithm) => {
54
+ switch (algorithm) {
55
+ case "AES-128-KW":
56
+ return provider.aes128KwWrapKey;
57
+ case "AES-192-KW":
58
+ return provider.aes192KwWrapKey;
59
+ case "AES-256-KW":
60
+ return provider.aes256KwWrapKey;
61
+ }
62
+ };
63
+ const unwrapFunctionForAlgorithm = (provider, algorithm) => {
64
+ switch (algorithm) {
65
+ case "AES-128-KW":
66
+ return provider.aes128KwUnwrapKey;
67
+ case "AES-192-KW":
68
+ return provider.aes192KwUnwrapKey;
69
+ case "AES-256-KW":
70
+ return provider.aes256KwUnwrapKey;
71
+ }
72
+ };
73
+ const wrapKey = (algorithm, wrappingKey, keyToWrap) => {
74
+ ensureBytes(wrappingKey, kekLengthForAlgorithm(algorithm));
75
+ ensureByteArray(keyToWrap);
76
+ validateKeyDataLength(keyToWrap.length);
77
+ return requireBytesOutput(wrapFunctionForAlgorithm(requireReallyMeWasmProvider(), algorithm)(wrappingKey, keyToWrap), keyToWrap.length + AES_KW_INTEGRITY_CHECK_LENGTH, [wrappingKey, keyToWrap]);
78
+ };
79
+ const wrapKeyWithProvider = (algorithm, provider, wrappingKey, keyToWrap) => {
80
+ ensureBytes(wrappingKey, kekLengthForAlgorithm(algorithm));
81
+ ensureByteArray(keyToWrap);
82
+ validateKeyDataLength(keyToWrap.length);
83
+ return requireBytesOutput(wrapFunctionForAlgorithm(provider, algorithm)(wrappingKey, keyToWrap), keyToWrap.length + AES_KW_INTEGRITY_CHECK_LENGTH, [wrappingKey, keyToWrap]);
84
+ };
85
+ const unwrapKey = (algorithm, wrappingKey, wrappedKey) => {
86
+ ensureBytes(wrappingKey, kekLengthForAlgorithm(algorithm));
87
+ ensureByteArray(wrappedKey);
88
+ validateWrappedKeyLength(wrappedKey.length);
89
+ return requireBytesOutput(unwrapFunctionForAlgorithm(requireReallyMeWasmProvider(), algorithm)(wrappingKey, wrappedKey), wrappedKey.length - AES_KW_INTEGRITY_CHECK_LENGTH, [wrappingKey, wrappedKey]);
90
+ };
91
+ const unwrapKeyWithProvider = (algorithm, provider, wrappingKey, wrappedKey) => {
92
+ ensureBytes(wrappingKey, kekLengthForAlgorithm(algorithm));
93
+ ensureByteArray(wrappedKey);
94
+ validateWrappedKeyLength(wrappedKey.length);
95
+ return requireBytesOutput(unwrapFunctionForAlgorithm(provider, algorithm)(wrappingKey, wrappedKey), wrappedKey.length - AES_KW_INTEGRITY_CHECK_LENGTH, [wrappingKey, wrappedKey]);
96
+ };
34
97
  export const ReallyMeAesKw = {
35
- wrapKey(wrappingKey, keyToWrap) {
36
- ensureBytes(wrappingKey, AES_256_KW_KEK_LENGTH);
37
- validateKeyDataLength(keyToWrap.length);
38
- return requireBytesOutput(requireReallyMeWasmProvider().aes256KwWrapKey(wrappingKey, keyToWrap));
39
- },
40
- wrapKeyWithProvider(provider, wrappingKey, keyToWrap) {
41
- ensureBytes(wrappingKey, AES_256_KW_KEK_LENGTH);
42
- validateKeyDataLength(keyToWrap.length);
43
- return requireBytesOutput(provider.aes256KwWrapKey(wrappingKey, keyToWrap));
44
- },
45
- unwrapKey(wrappingKey, wrappedKey) {
46
- ensureBytes(wrappingKey, AES_256_KW_KEK_LENGTH);
47
- validateWrappedKeyLength(wrappedKey.length);
48
- return requireBytesOutput(requireReallyMeWasmProvider().aes256KwUnwrapKey(wrappingKey, wrappedKey));
49
- },
50
- unwrapKeyWithProvider(provider, wrappingKey, wrappedKey) {
51
- ensureBytes(wrappingKey, AES_256_KW_KEK_LENGTH);
52
- validateWrappedKeyLength(wrappedKey.length);
53
- return requireBytesOutput(provider.aes256KwUnwrapKey(wrappingKey, wrappedKey));
54
- },
98
+ wrapKey,
99
+ wrapKeyWithProvider,
100
+ unwrapKey,
101
+ unwrapKeyWithProvider,
55
102
  };
@@ -4,15 +4,23 @@ export declare const REALLYME_HASH_ALGORITHMS: readonly ["SHA2-256", "SHA2-384",
4
4
  export type ReallyMeHashAlgorithm = (typeof REALLYME_HASH_ALGORITHMS)[number];
5
5
  export declare const REALLYME_AEAD_ALGORITHMS: readonly ["AES-128-GCM", "AES-192-GCM", "AES-256-GCM", "AES-256-GCM-SIV", "ChaCha20-Poly1305", "XChaCha20-Poly1305"];
6
6
  export type ReallyMeAeadAlgorithm = (typeof REALLYME_AEAD_ALGORITHMS)[number];
7
- export declare const REALLYME_KEM_ALGORITHMS: readonly ["ML-KEM-512", "ML-KEM-768", "ML-KEM-1024", "X-Wing-768", "X-Wing-1024"];
7
+ export declare const REALLYME_KEM_ALGORITHMS: readonly ["ML-KEM-512", "ML-KEM-768", "ML-KEM-1024", "X-Wing-768"];
8
8
  export type ReallyMeKemAlgorithm = (typeof REALLYME_KEM_ALGORITHMS)[number];
9
9
  export declare const REALLYME_KEY_AGREEMENT_ALGORITHMS: readonly ["X25519", "P-256-ECDH", "P-384-ECDH", "P-521-ECDH"];
10
10
  export type ReallyMeKeyAgreementAlgorithm = (typeof REALLYME_KEY_AGREEMENT_ALGORITHMS)[number];
11
- export declare const REALLYME_MAC_ALGORITHMS: readonly ["HMAC-SHA-256", "HMAC-SHA-512"];
11
+ export declare const REALLYME_MAC_ALGORITHMS: readonly ["HMAC-SHA-256", "HMAC-SHA-384", "HMAC-SHA-512"];
12
12
  export type ReallyMeMacAlgorithm = (typeof REALLYME_MAC_ALGORITHMS)[number];
13
- export declare const REALLYME_KDF_ALGORITHMS: readonly ["HKDF-SHA256", "Argon2id", "PBKDF2-HMAC-SHA-256", "PBKDF2-HMAC-SHA-512", "JWA-CONCAT-KDF-SHA256"];
13
+ export declare const REALLYME_KDF_ALGORITHMS: readonly ["HKDF-SHA256", "HKDF-SHA384", "Argon2id", "KMAC256", "PBKDF2-HMAC-SHA-256", "PBKDF2-HMAC-SHA-512", "JWA-CONCAT-KDF-SHA256"];
14
14
  export type ReallyMeKdfAlgorithm = (typeof REALLYME_KDF_ALGORITHMS)[number];
15
- export declare const REALLYME_KEY_WRAP_ALGORITHMS: readonly ["AES-256-KW"];
15
+ /** PBKDF2 selectors accepted by the password-based `deriveKey` facade. */
16
+ export type ReallyMePbkdf2Algorithm = Extract<ReallyMeKdfAlgorithm, "PBKDF2-HMAC-SHA-256" | "PBKDF2-HMAC-SHA-512">;
17
+ /** HKDF selectors accepted by the `deriveHkdf` facade. */
18
+ export type ReallyMeHkdfAlgorithm = Extract<ReallyMeKdfAlgorithm, "HKDF-SHA256" | "HKDF-SHA384">;
19
+ /** JWA Concat KDF selector accepted by its dedicated facade. */
20
+ export type ReallyMeJwaConcatKdfAlgorithm = Extract<ReallyMeKdfAlgorithm, "JWA-CONCAT-KDF-SHA256">;
21
+ /** KMAC KDF selector accepted by its dedicated facade. */
22
+ export type ReallyMeKmacKdfAlgorithm = Extract<ReallyMeKdfAlgorithm, "KMAC256">;
23
+ export declare const REALLYME_KEY_WRAP_ALGORITHMS: readonly ["AES-128-KW", "AES-192-KW", "AES-256-KW"];
16
24
  export type ReallyMeKeyWrapAlgorithm = (typeof REALLYME_KEY_WRAP_ALGORITHMS)[number];
17
25
  export declare const REALLYME_HPKE_SUITES: readonly ["DHKEM-P256-HKDF-SHA256-HKDF-SHA256-AES-256-GCM", "DHKEM-X25519-HKDF-SHA256-HKDF-SHA256-CHACHA20-POLY1305"];
18
26
  export type ReallyMeHpkeSuite = (typeof REALLYME_HPKE_SUITES)[number];
@@ -43,7 +43,6 @@ export const REALLYME_KEM_ALGORITHMS = [
43
43
  "ML-KEM-768",
44
44
  "ML-KEM-1024",
45
45
  "X-Wing-768",
46
- "X-Wing-1024",
47
46
  ];
48
47
  export const REALLYME_KEY_AGREEMENT_ALGORITHMS = [
49
48
  "X25519",
@@ -53,16 +52,23 @@ export const REALLYME_KEY_AGREEMENT_ALGORITHMS = [
53
52
  ];
54
53
  export const REALLYME_MAC_ALGORITHMS = [
55
54
  "HMAC-SHA-256",
55
+ "HMAC-SHA-384",
56
56
  "HMAC-SHA-512",
57
57
  ];
58
58
  export const REALLYME_KDF_ALGORITHMS = [
59
59
  "HKDF-SHA256",
60
+ "HKDF-SHA384",
60
61
  "Argon2id",
62
+ "KMAC256",
61
63
  "PBKDF2-HMAC-SHA-256",
62
64
  "PBKDF2-HMAC-SHA-512",
63
65
  "JWA-CONCAT-KDF-SHA256",
64
66
  ];
65
- export const REALLYME_KEY_WRAP_ALGORITHMS = ["AES-256-KW"];
67
+ export const REALLYME_KEY_WRAP_ALGORITHMS = [
68
+ "AES-128-KW",
69
+ "AES-192-KW",
70
+ "AES-256-KW",
71
+ ];
66
72
  export const REALLYME_HPKE_SUITES = [
67
73
  "DHKEM-P256-HKDF-SHA256-HKDF-SHA256-AES-256-GCM",
68
74
  "DHKEM-X25519-HKDF-SHA256-HKDF-SHA256-CHACHA20-POLY1305",
@@ -2,6 +2,7 @@ import type { ReallyMeWasmProvider } from "./wasmProvider.js";
2
2
  export declare const ARGON2ID_DERIVED_KEY_LENGTH = 32;
3
3
  export declare const ARGON2ID_SALT_MIN_LENGTH = 16;
4
4
  export declare const ARGON2ID_SALT_MAX_LENGTH = 32;
5
+ export declare const ARGON2ID_SECRET_MAX_LENGTH = 1048576;
5
6
  export declare const ARGON2ID_V1 = 1;
6
7
  export declare const ARGON2ID_V2 = 2;
7
8
  export declare const ReallyMeArgon2id: {
package/dist/argon2id.js CHANGED
@@ -2,10 +2,12 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { ReallyMeCryptoError } from "./errors.js";
5
+ import { ensureByteArrayAtMost, ensureIndependentByteArray, } from "./validateBytes.js";
5
6
  import { requireReallyMeWasmProvider } from "./wasmProvider.js";
6
7
  export const ARGON2ID_DERIVED_KEY_LENGTH = 32;
7
8
  export const ARGON2ID_SALT_MIN_LENGTH = 16;
8
9
  export const ARGON2ID_SALT_MAX_LENGTH = 32;
10
+ export const ARGON2ID_SECRET_MAX_LENGTH = 1_048_576;
9
11
  export const ARGON2ID_V1 = 1;
10
12
  export const ARGON2ID_V2 = 2;
11
13
  const validateVersion = (kdfVersion) => {
@@ -15,17 +17,24 @@ const validateVersion = (kdfVersion) => {
15
17
  }
16
18
  };
17
19
  const validateSecret = (secret) => {
20
+ ensureByteArrayAtMost(secret, ARGON2ID_SECRET_MAX_LENGTH);
18
21
  if (secret.length === 0) {
19
22
  throw new ReallyMeCryptoError("invalid-input");
20
23
  }
21
24
  };
22
25
  const validateSalt = (salt) => {
26
+ ensureByteArrayAtMost(salt, ARGON2ID_SALT_MAX_LENGTH);
23
27
  if (salt.length < ARGON2ID_SALT_MIN_LENGTH || salt.length > ARGON2ID_SALT_MAX_LENGTH) {
24
28
  throw new ReallyMeCryptoError("invalid-input");
25
29
  }
26
30
  };
27
- const requireDerivedKey = (value) => {
28
- if (!(value instanceof Uint8Array) || value.length !== ARGON2ID_DERIVED_KEY_LENGTH) {
31
+ const requireDerivedKey = (value, inputs) => {
32
+ if (!(value instanceof Uint8Array)) {
33
+ throw new ReallyMeCryptoError("provider-failure");
34
+ }
35
+ ensureIndependentByteArray(value, inputs);
36
+ if (value.length !== ARGON2ID_DERIVED_KEY_LENGTH) {
37
+ value.fill(0);
29
38
  throw new ReallyMeCryptoError("provider-failure");
30
39
  }
31
40
  return value;
@@ -35,12 +44,12 @@ export const ReallyMeArgon2id = {
35
44
  validateVersion(kdfVersion);
36
45
  validateSecret(secret);
37
46
  validateSalt(salt);
38
- return requireDerivedKey(requireReallyMeWasmProvider().argon2idDeriveKey(kdfVersion, secret, salt));
47
+ return requireDerivedKey(requireReallyMeWasmProvider().argon2idDeriveKey(kdfVersion, secret, salt), [secret, salt]);
39
48
  },
40
49
  deriveKeyWithProvider(provider, kdfVersion, secret, salt) {
41
50
  validateVersion(kdfVersion);
42
51
  validateSecret(secret);
43
52
  validateSalt(salt);
44
- return requireDerivedKey(provider.argon2idDeriveKey(kdfVersion, secret, salt));
53
+ return requireDerivedKey(provider.argon2idDeriveKey(kdfVersion, secret, salt), [secret, salt]);
45
54
  },
46
55
  };
@@ -1,4 +1,4 @@
1
- import type { ReallyMeAeadAlgorithm, ReallyMeHashAlgorithm, ReallyMeHpkeSuite, ReallyMeKdfAlgorithm, ReallyMeKemAlgorithm, ReallyMeKeyAgreementAlgorithm, ReallyMeKeyWrapAlgorithm, ReallyMeMacAlgorithm, ReallyMeSignatureAlgorithm } from "./algorithms.js";
1
+ import type { ReallyMeAeadAlgorithm, ReallyMeHashAlgorithm, ReallyMeHkdfAlgorithm, ReallyMeHpkeSuite, ReallyMeJwaConcatKdfAlgorithm, ReallyMeKemAlgorithm, ReallyMeKeyAgreementAlgorithm, ReallyMeKeyWrapAlgorithm, ReallyMeKmacKdfAlgorithm, ReallyMeMacAlgorithm, ReallyMePbkdf2Algorithm, ReallyMeSignatureAlgorithm } from "./algorithms.js";
2
2
  import type { ReallyMeRsaPublicKeyDerEncoding } from "./rsa.js";
3
3
  import type { ReallyMeWasmProvider } from "./wasmProvider.js";
4
4
  export type ReallyMeSignatureKeyPair = Readonly<{
@@ -27,18 +27,31 @@ export type ReallyMeCryptoProviders = Readonly<{
27
27
  /**
28
28
  * Generic package facade. Algorithm-specific objects remain available for
29
29
  * callers that want direct provider access; this facade gives consumers a
30
- * stable typed route that fails closed for not-yet-exposed algorithms.
30
+ * stable typed route and rejects algorithm/operation combinations that the
31
+ * selected method does not define.
31
32
  */
32
33
  declare const createReallyMeCryptoFacade: (resolveWasmProvider: () => ReallyMeWasmProvider) => {
34
+ /**
35
+ * Executes a generated binary request and returns a binary
36
+ * `CryptoOperationResponse`.
37
+ */
38
+ readonly processOperationResponse: (request: Uint8Array) => Uint8Array;
39
+ /**
40
+ * Executes a permitted non-secret generated ProtoJSON request and returns
41
+ * the same binary `CryptoOperationResponse`. Secret-bearing selectors fail
42
+ * before JSON value deserialization.
43
+ */
44
+ readonly processOperationResponseJson: (requestJson: Uint8Array) => Uint8Array;
33
45
  readonly hash: (algorithm: ReallyMeHashAlgorithm, bytes: Uint8Array) => Uint8Array;
34
46
  readonly seal: (algorithm: ReallyMeAeadAlgorithm, key: Uint8Array, nonce: Uint8Array, aad: Uint8Array, plaintext: Uint8Array) => Uint8Array;
35
47
  readonly open: (algorithm: ReallyMeAeadAlgorithm, key: Uint8Array, nonce: Uint8Array, aad: Uint8Array, ciphertextWithTag: Uint8Array) => Uint8Array;
36
48
  readonly authenticate: (algorithm: ReallyMeMacAlgorithm, key: Uint8Array, message: Uint8Array) => Uint8Array;
37
49
  readonly verifyMac: (algorithm: ReallyMeMacAlgorithm, tag: Uint8Array, key: Uint8Array, message: Uint8Array) => boolean;
38
50
  readonly deriveArgon2id: (kdfVersion: number, secret: Uint8Array, salt: Uint8Array) => Uint8Array;
39
- readonly deriveKey: (algorithm: ReallyMeKdfAlgorithm, password: Uint8Array, salt: Uint8Array, iterations: number, outputLength: number) => Uint8Array;
40
- readonly deriveHkdf: (algorithm: ReallyMeKdfAlgorithm, inputKeyMaterial: Uint8Array, salt: Uint8Array, info: Uint8Array, outputLength: number) => Uint8Array;
41
- readonly deriveJwaConcatKdfSha256: (algorithm: ReallyMeKdfAlgorithm, sharedSecret: Uint8Array, algorithmId: Uint8Array, partyUInfo: Uint8Array, partyVInfo: Uint8Array, outputLength: number) => Uint8Array;
51
+ readonly deriveKey: (algorithm: ReallyMePbkdf2Algorithm, password: Uint8Array, salt: Uint8Array, iterations: number, outputLength: number) => Uint8Array;
52
+ readonly deriveHkdf: (algorithm: ReallyMeHkdfAlgorithm, inputKeyMaterial: Uint8Array, salt: Uint8Array, info: Uint8Array, outputLength: number) => Uint8Array;
53
+ readonly deriveJwaConcatKdfSha256: (algorithm: ReallyMeJwaConcatKdfAlgorithm, sharedSecret: Uint8Array, algorithmId: Uint8Array, partyUInfo: Uint8Array, partyVInfo: Uint8Array, outputLength: number) => Uint8Array;
54
+ readonly deriveKmac256: (algorithm: ReallyMeKmacKdfAlgorithm, key: Uint8Array, context: Uint8Array, customization: Uint8Array, outputLength: number) => Uint8Array;
42
55
  readonly wrapKey: (algorithm: ReallyMeKeyWrapAlgorithm, wrappingKey: Uint8Array, keyToWrap: Uint8Array) => Uint8Array;
43
56
  readonly unwrapKey: (algorithm: ReallyMeKeyWrapAlgorithm, wrappingKey: Uint8Array, wrappedKey: Uint8Array) => Uint8Array;
44
57
  readonly generateKeyPair: (algorithm: ReallyMeSignatureAlgorithm) => ReallyMeSignatureKeyPair;
@@ -59,15 +72,27 @@ declare const createReallyMeCryptoFacade: (resolveWasmProvider: () => ReallyMeWa
59
72
  export type ReallyMeCryptoFacade = ReturnType<typeof createReallyMeCryptoFacade>;
60
73
  export declare const createReallyMeCrypto: (providers?: ReallyMeCryptoProviders) => ReallyMeCryptoFacade;
61
74
  export declare const ReallyMeCrypto: {
75
+ /**
76
+ * Executes a generated binary request and returns a binary
77
+ * `CryptoOperationResponse`.
78
+ */
79
+ readonly processOperationResponse: (request: Uint8Array) => Uint8Array;
80
+ /**
81
+ * Executes a permitted non-secret generated ProtoJSON request and returns
82
+ * the same binary `CryptoOperationResponse`. Secret-bearing selectors fail
83
+ * before JSON value deserialization.
84
+ */
85
+ readonly processOperationResponseJson: (requestJson: Uint8Array) => Uint8Array;
62
86
  readonly hash: (algorithm: ReallyMeHashAlgorithm, bytes: Uint8Array) => Uint8Array;
63
87
  readonly seal: (algorithm: ReallyMeAeadAlgorithm, key: Uint8Array, nonce: Uint8Array, aad: Uint8Array, plaintext: Uint8Array) => Uint8Array;
64
88
  readonly open: (algorithm: ReallyMeAeadAlgorithm, key: Uint8Array, nonce: Uint8Array, aad: Uint8Array, ciphertextWithTag: Uint8Array) => Uint8Array;
65
89
  readonly authenticate: (algorithm: ReallyMeMacAlgorithm, key: Uint8Array, message: Uint8Array) => Uint8Array;
66
90
  readonly verifyMac: (algorithm: ReallyMeMacAlgorithm, tag: Uint8Array, key: Uint8Array, message: Uint8Array) => boolean;
67
91
  readonly deriveArgon2id: (kdfVersion: number, secret: Uint8Array, salt: Uint8Array) => Uint8Array;
68
- readonly deriveKey: (algorithm: ReallyMeKdfAlgorithm, password: Uint8Array, salt: Uint8Array, iterations: number, outputLength: number) => Uint8Array;
69
- readonly deriveHkdf: (algorithm: ReallyMeKdfAlgorithm, inputKeyMaterial: Uint8Array, salt: Uint8Array, info: Uint8Array, outputLength: number) => Uint8Array;
70
- readonly deriveJwaConcatKdfSha256: (algorithm: ReallyMeKdfAlgorithm, sharedSecret: Uint8Array, algorithmId: Uint8Array, partyUInfo: Uint8Array, partyVInfo: Uint8Array, outputLength: number) => Uint8Array;
92
+ readonly deriveKey: (algorithm: ReallyMePbkdf2Algorithm, password: Uint8Array, salt: Uint8Array, iterations: number, outputLength: number) => Uint8Array;
93
+ readonly deriveHkdf: (algorithm: ReallyMeHkdfAlgorithm, inputKeyMaterial: Uint8Array, salt: Uint8Array, info: Uint8Array, outputLength: number) => Uint8Array;
94
+ readonly deriveJwaConcatKdfSha256: (algorithm: ReallyMeJwaConcatKdfAlgorithm, sharedSecret: Uint8Array, algorithmId: Uint8Array, partyUInfo: Uint8Array, partyVInfo: Uint8Array, outputLength: number) => Uint8Array;
95
+ readonly deriveKmac256: (algorithm: ReallyMeKmacKdfAlgorithm, key: Uint8Array, context: Uint8Array, customization: Uint8Array, outputLength: number) => Uint8Array;
71
96
  readonly wrapKey: (algorithm: ReallyMeKeyWrapAlgorithm, wrappingKey: Uint8Array, keyToWrap: Uint8Array) => Uint8Array;
72
97
  readonly unwrapKey: (algorithm: ReallyMeKeyWrapAlgorithm, wrappingKey: Uint8Array, wrappedKey: Uint8Array) => Uint8Array;
73
98
  readonly generateKeyPair: (algorithm: ReallyMeSignatureAlgorithm) => ReallyMeSignatureKeyPair;