@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.
- package/README.md +107 -17
- package/dist/aead.js +15 -8
- package/dist/aesKw.d.ts +7 -4
- package/dist/aesKw.js +69 -22
- package/dist/algorithms.d.ts +12 -4
- package/dist/algorithms.js +8 -2
- package/dist/argon2id.d.ts +1 -0
- package/dist/argon2id.js +13 -4
- package/dist/cryptoFacade.d.ts +33 -8
- package/dist/cryptoFacade.js +42 -29
- package/dist/hkdf.d.ts +1 -0
- package/dist/hkdf.js +5 -1
- package/dist/hmac.d.ts +3 -0
- package/dist/hmac.js +24 -3
- package/dist/hpke.d.ts +1 -2
- package/dist/hpke.js +33 -22
- package/dist/index.d.ts +9 -7
- package/dist/index.js +8 -6
- package/dist/jwk.d.ts +2 -1
- package/dist/jwk.js +104 -63
- package/dist/kmac.d.ts +11 -0
- package/dist/kmac.js +49 -0
- package/dist/mlDsa.js +22 -11
- package/dist/mlKem.d.ts +0 -3
- package/dist/mlKem.js +19 -37
- package/dist/operationResponse.d.ts +16 -0
- package/dist/operationResponse.js +46 -0
- package/dist/pbkdf2.d.ts +6 -8
- package/dist/pbkdf2.js +12 -12
- package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.d.ts +1741 -328
- package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.js +731 -344
- package/dist/proto.d.ts +6 -22
- package/dist/proto.js +124 -102
- package/dist/rsa.d.ts +2 -1
- package/dist/rsa.js +5 -1
- package/dist/slhDsa.js +21 -11
- package/dist/validateBytes.d.ts +6 -1
- package/dist/validateBytes.js +57 -3
- package/dist/wasm/reallyme_crypto_wasm.js +91 -134
- package/dist/wasm/reallyme_crypto_wasm_bg.wasm +0 -0
- package/dist/wasmModuleTypes.d.ts +7 -10
- package/dist/wasmProvider.d.ts +11 -13
- package/dist/wasmProvider.js +16 -19
- package/dist/xWing.d.ts +0 -5
- package/dist/xWing.js +20 -52
- package/package.json +15 -2
package/dist/jwk.js
CHANGED
|
@@ -5,6 +5,20 @@ import { p256 } from "@noble/curves/nist.js";
|
|
|
5
5
|
import { secp256k1 } from "@noble/curves/secp256k1.js";
|
|
6
6
|
import { base64urlDecode as codecBase64urlDecodeValue, base64urlEncode as codecBase64urlEncodeValue, canonicalizeJson as codecCanonicalizeJson, ReallyMeCodecError, } from "@reallyme/codec";
|
|
7
7
|
import { ReallyMeCryptoError } from "./errors.js";
|
|
8
|
+
// JSON and protobuf JWKS ingress routes share this limit so neither boundary
|
|
9
|
+
// can amplify a small public-key record into unbounded curve validation work.
|
|
10
|
+
export const MAX_JWKS_KEYS = 1_024;
|
|
11
|
+
const withJwkBoundaryErrors = (operation) => {
|
|
12
|
+
try {
|
|
13
|
+
return operation();
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof ReallyMeCryptoError) {
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
20
|
+
}
|
|
21
|
+
};
|
|
8
22
|
const jwkSpec = (algorithm) => {
|
|
9
23
|
switch (algorithm) {
|
|
10
24
|
case "Ed25519":
|
|
@@ -31,8 +45,6 @@ const jwkSpec = (algorithm) => {
|
|
|
31
45
|
return { alg: "SLH-DSA-SHA2-128s", kty: "AKP", publicKeyLength: 32, use: "sig" };
|
|
32
46
|
case "X-Wing-768":
|
|
33
47
|
return { alg: "X-Wing-768", kty: "AKP", publicKeyLength: 1_216, use: "enc" };
|
|
34
|
-
case "X-Wing-1024":
|
|
35
|
-
return { alg: "X-Wing-1024", kty: "AKP", publicKeyLength: 1_600, use: "enc" };
|
|
36
48
|
}
|
|
37
49
|
};
|
|
38
50
|
const ensureLength = (bytes, expectedLength) => {
|
|
@@ -44,11 +56,23 @@ const readString = (value, name) => {
|
|
|
44
56
|
if (typeof value !== "object" || value === null) {
|
|
45
57
|
throw new ReallyMeCryptoError("invalid-input");
|
|
46
58
|
}
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
59
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
|
60
|
+
if (descriptor === undefined ||
|
|
61
|
+
!("value" in descriptor) ||
|
|
62
|
+
typeof descriptor.value !== "string") {
|
|
49
63
|
throw new ReallyMeCryptoError("invalid-input");
|
|
50
64
|
}
|
|
51
|
-
return
|
|
65
|
+
return descriptor.value;
|
|
66
|
+
};
|
|
67
|
+
const ensureExactMembers = (value, allowedNames) => {
|
|
68
|
+
if (typeof value !== "object" || value === null) {
|
|
69
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
70
|
+
}
|
|
71
|
+
for (const name of Reflect.ownKeys(value)) {
|
|
72
|
+
if (typeof name !== "string" || !allowedNames.includes(name)) {
|
|
73
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
52
76
|
};
|
|
53
77
|
const privateJwkMemberNames = [
|
|
54
78
|
"d",
|
|
@@ -77,8 +101,9 @@ const optionalStringMatches = (value, name, expected) => {
|
|
|
77
101
|
if (typeof value !== "object" || value === null) {
|
|
78
102
|
throw new ReallyMeCryptoError("invalid-input");
|
|
79
103
|
}
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
104
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
|
105
|
+
if (descriptor !== undefined &&
|
|
106
|
+
(!("value" in descriptor) || descriptor.value !== expected)) {
|
|
82
107
|
throw new ReallyMeCryptoError("invalid-input");
|
|
83
108
|
}
|
|
84
109
|
};
|
|
@@ -103,7 +128,6 @@ const specFromAlgorithm = (algorithm) => {
|
|
|
103
128
|
case "ML-KEM-1024":
|
|
104
129
|
case "SLH-DSA-SHA2-128s":
|
|
105
130
|
case "X-Wing-768":
|
|
106
|
-
case "X-Wing-1024":
|
|
107
131
|
return jwkSpec(algorithm);
|
|
108
132
|
default:
|
|
109
133
|
throw new ReallyMeCryptoError("unsupported-algorithm");
|
|
@@ -212,68 +236,85 @@ export const ReallyMeJwk = {
|
|
|
212
236
|
};
|
|
213
237
|
},
|
|
214
238
|
fromJwk(value) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
optionalStringMatches(value, "alg", spec.alg);
|
|
224
|
-
optionalStringMatches(value, "use", spec.use);
|
|
225
|
-
if (spec.kty === "EC") {
|
|
226
|
-
const ecAlgorithm = spec.crv === "P-256" ? "P-256" : "secp256k1";
|
|
227
|
-
const x = codecBase64urlDecodeCanonical(readString(value, "x"));
|
|
228
|
-
const y = codecBase64urlDecodeCanonical(readString(value, "y"));
|
|
229
|
-
const publicKey = compressEc(ecAlgorithm, x, y);
|
|
230
|
-
ensureLength(publicKey, spec.publicKeyLength);
|
|
231
|
-
const jwk = ReallyMeJwk.toJwk(ecAlgorithm, publicKey);
|
|
232
|
-
return { algorithm: ecAlgorithm, publicKey, jwk };
|
|
233
|
-
}
|
|
234
|
-
if (spec.kty === "OKP") {
|
|
235
|
-
const crv = spec.crv;
|
|
236
|
-
if (crv !== "Ed25519" && crv !== "X25519") {
|
|
237
|
-
throw new ReallyMeCryptoError("unsupported-algorithm");
|
|
239
|
+
return withJwkBoundaryErrors(() => {
|
|
240
|
+
rejectPrivateKeyMaterial(value);
|
|
241
|
+
const kty = readString(value, "kty");
|
|
242
|
+
const spec = kty === "AKP"
|
|
243
|
+
? specFromAlgorithm(readString(value, "alg"))
|
|
244
|
+
: specFromCurve(readString(value, "crv"));
|
|
245
|
+
if (kty !== spec.kty) {
|
|
246
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
238
247
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
case "ML-KEM-512":
|
|
253
|
-
case "ML-KEM-768":
|
|
254
|
-
case "ML-KEM-1024":
|
|
255
|
-
case "SLH-DSA-SHA2-128s":
|
|
256
|
-
case "X-Wing-768":
|
|
257
|
-
case "X-Wing-1024": {
|
|
258
|
-
const jwk = ReallyMeJwk.toJwk(algorithm, publicKey);
|
|
259
|
-
return { algorithm, publicKey, jwk };
|
|
248
|
+
if (spec.kty === "EC") {
|
|
249
|
+
ensureExactMembers(value, ["alg", "crv", "kty", "use", "x", "y"]);
|
|
250
|
+
if (readString(value, "alg") !== spec.alg ||
|
|
251
|
+
readString(value, "use") !== spec.use) {
|
|
252
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
253
|
+
}
|
|
254
|
+
const ecAlgorithm = spec.crv === "P-256" ? "P-256" : "secp256k1";
|
|
255
|
+
const x = codecBase64urlDecodeCanonical(readString(value, "x"));
|
|
256
|
+
const y = codecBase64urlDecodeCanonical(readString(value, "y"));
|
|
257
|
+
const publicKey = compressEc(ecAlgorithm, x, y);
|
|
258
|
+
ensureLength(publicKey, spec.publicKeyLength);
|
|
259
|
+
const jwk = ReallyMeJwk.toJwk(ecAlgorithm, publicKey);
|
|
260
|
+
return { algorithm: ecAlgorithm, publicKey, jwk };
|
|
260
261
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
262
|
+
if (spec.kty === "OKP") {
|
|
263
|
+
ensureExactMembers(value, ["alg", "crv", "kty", "use", "x"]);
|
|
264
|
+
optionalStringMatches(value, "alg", spec.alg);
|
|
265
|
+
optionalStringMatches(value, "use", spec.use);
|
|
266
|
+
const crv = spec.crv;
|
|
267
|
+
if (crv !== "Ed25519" && crv !== "X25519") {
|
|
268
|
+
throw new ReallyMeCryptoError("unsupported-algorithm");
|
|
269
|
+
}
|
|
270
|
+
const publicKey = codecBase64urlDecodeCanonical(readString(value, "x"));
|
|
271
|
+
ensureLength(publicKey, spec.publicKeyLength);
|
|
272
|
+
const jwk = ReallyMeJwk.toJwk(crv, publicKey);
|
|
273
|
+
return { algorithm: crv, publicKey, jwk };
|
|
274
|
+
}
|
|
275
|
+
const algorithm = readString(value, "alg");
|
|
276
|
+
ensureExactMembers(value, ["alg", "kty", "pub", "use"]);
|
|
277
|
+
if (readString(value, "use") !== spec.use) {
|
|
278
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
279
|
+
}
|
|
280
|
+
const akpSpec = specFromAlgorithm(algorithm);
|
|
281
|
+
const publicKey = codecBase64urlDecodeCanonical(readString(value, "pub"));
|
|
282
|
+
ensureLength(publicKey, akpSpec.publicKeyLength);
|
|
283
|
+
switch (algorithm) {
|
|
284
|
+
case "ML-DSA-44":
|
|
285
|
+
case "ML-DSA-65":
|
|
286
|
+
case "ML-DSA-87":
|
|
287
|
+
case "ML-KEM-512":
|
|
288
|
+
case "ML-KEM-768":
|
|
289
|
+
case "ML-KEM-1024":
|
|
290
|
+
case "SLH-DSA-SHA2-128s":
|
|
291
|
+
case "X-Wing-768": {
|
|
292
|
+
const jwk = ReallyMeJwk.toJwk(algorithm, publicKey);
|
|
293
|
+
return { algorithm, publicKey, jwk };
|
|
294
|
+
}
|
|
295
|
+
default:
|
|
296
|
+
throw new ReallyMeCryptoError("unsupported-algorithm");
|
|
297
|
+
}
|
|
298
|
+
});
|
|
264
299
|
},
|
|
265
300
|
toJwks(keys) {
|
|
266
301
|
return { keys: [...keys] };
|
|
267
302
|
},
|
|
268
303
|
fromJwks(value) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
304
|
+
return withJwkBoundaryErrors(() => {
|
|
305
|
+
ensureExactMembers(value, ["keys"]);
|
|
306
|
+
if (typeof value !== "object" || value === null) {
|
|
307
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
308
|
+
}
|
|
309
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, "keys");
|
|
310
|
+
const keys = descriptor !== undefined && "value" in descriptor
|
|
311
|
+
? descriptor.value
|
|
312
|
+
: undefined;
|
|
313
|
+
if (!Array.isArray(keys) || keys.length > MAX_JWKS_KEYS) {
|
|
314
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
315
|
+
}
|
|
316
|
+
return { keys: keys.map((key) => ReallyMeJwk.fromJwk(key)) };
|
|
317
|
+
});
|
|
277
318
|
},
|
|
278
319
|
publicKeyBytes(jwk) {
|
|
279
320
|
return ReallyMeJwk.fromJwk(jwk).publicKey;
|
package/dist/kmac.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReallyMeWasmProvider } from "./wasmProvider.js";
|
|
2
|
+
export declare const KMAC256_MIN_KEY_LENGTH = 32;
|
|
3
|
+
export declare const KMAC256_MAX_KEY_LENGTH = 4096;
|
|
4
|
+
export declare const KMAC256_MAX_CONTEXT_LENGTH = 65536;
|
|
5
|
+
export declare const KMAC256_MAX_CUSTOMIZATION_LENGTH = 4096;
|
|
6
|
+
export declare const KMAC256_MIN_OUTPUT_LENGTH = 1;
|
|
7
|
+
export declare const KMAC256_MAX_OUTPUT_LENGTH = 65536;
|
|
8
|
+
export declare const ReallyMeKmac: {
|
|
9
|
+
readonly deriveKmac256: (key: Uint8Array, context: Uint8Array, customization: Uint8Array, outputLength: number) => Uint8Array;
|
|
10
|
+
readonly deriveKmac256WithProvider: (provider: ReallyMeWasmProvider, key: Uint8Array, context: Uint8Array, customization: Uint8Array, outputLength: number) => Uint8Array;
|
|
11
|
+
};
|
package/dist/kmac.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { ReallyMeCryptoError } from "./errors.js";
|
|
5
|
+
import { ensureByteArray, ensureIndependentByteArray, } from "./validateBytes.js";
|
|
6
|
+
import { requireReallyMeWasmProvider } from "./wasmProvider.js";
|
|
7
|
+
export const KMAC256_MIN_KEY_LENGTH = 32;
|
|
8
|
+
export const KMAC256_MAX_KEY_LENGTH = 4_096;
|
|
9
|
+
export const KMAC256_MAX_CONTEXT_LENGTH = 65_536;
|
|
10
|
+
export const KMAC256_MAX_CUSTOMIZATION_LENGTH = 4_096;
|
|
11
|
+
export const KMAC256_MIN_OUTPUT_LENGTH = 1;
|
|
12
|
+
export const KMAC256_MAX_OUTPUT_LENGTH = 65_536;
|
|
13
|
+
const validate = (key, context, customization, outputLength) => {
|
|
14
|
+
ensureByteArray(key);
|
|
15
|
+
ensureByteArray(context);
|
|
16
|
+
ensureByteArray(customization);
|
|
17
|
+
if (key.length < KMAC256_MIN_KEY_LENGTH ||
|
|
18
|
+
key.length > KMAC256_MAX_KEY_LENGTH ||
|
|
19
|
+
context.length > KMAC256_MAX_CONTEXT_LENGTH ||
|
|
20
|
+
customization.length > KMAC256_MAX_CUSTOMIZATION_LENGTH ||
|
|
21
|
+
!Number.isInteger(outputLength) ||
|
|
22
|
+
outputLength < KMAC256_MIN_OUTPUT_LENGTH ||
|
|
23
|
+
outputLength > KMAC256_MAX_OUTPUT_LENGTH) {
|
|
24
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const requireDerivedKey = (value, outputLength, inputs) => {
|
|
28
|
+
if (!(value instanceof Uint8Array)) {
|
|
29
|
+
throw new ReallyMeCryptoError("provider-failure");
|
|
30
|
+
}
|
|
31
|
+
// A provider result must have independent ownership. Check overlap before
|
|
32
|
+
// clearing malformed output so validation cannot erase caller-owned input.
|
|
33
|
+
ensureIndependentByteArray(value, inputs);
|
|
34
|
+
if (value.length !== outputLength) {
|
|
35
|
+
value.fill(0);
|
|
36
|
+
throw new ReallyMeCryptoError("provider-failure");
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
};
|
|
40
|
+
export const ReallyMeKmac = {
|
|
41
|
+
deriveKmac256(key, context, customization, outputLength) {
|
|
42
|
+
validate(key, context, customization, outputLength);
|
|
43
|
+
return requireDerivedKey(requireReallyMeWasmProvider().kmac256Derive(key, context, customization, outputLength), outputLength, [key, context, customization]);
|
|
44
|
+
},
|
|
45
|
+
deriveKmac256WithProvider(provider, key, context, customization, outputLength) {
|
|
46
|
+
validate(key, context, customization, outputLength);
|
|
47
|
+
return requireDerivedKey(provider.kmac256Derive(key, context, customization, outputLength), outputLength, [key, context, customization]);
|
|
48
|
+
},
|
|
49
|
+
};
|
package/dist/mlDsa.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 { ensureBytes,
|
|
5
|
+
import { ensureByteArrayAtMost, ensureBytes, ensureIndependentByteArray, MAX_CRYPTO_INPUT_LENGTH, readIndependentByteArrayProperty, } from "./validateBytes.js";
|
|
6
6
|
import { requireReallyMeWasmProvider } from "./wasmProvider.js";
|
|
7
7
|
export const ML_DSA_44_PUBLIC_KEY_LENGTH = 1_312;
|
|
8
8
|
export const ML_DSA_44_SIGNATURE_LENGTH = 2_420;
|
|
@@ -48,15 +48,22 @@ const mlDsaSuiteWithProvider = (algorithm, provider) => {
|
|
|
48
48
|
throw new ReallyMeCryptoError("unsupported-algorithm");
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
|
-
const readKeyPair = (value, suite) =>
|
|
52
|
-
publicKey
|
|
53
|
-
secretKey
|
|
54
|
-
}
|
|
55
|
-
|
|
51
|
+
const readKeyPair = (value, suite, inputs = []) => {
|
|
52
|
+
const publicKey = readIndependentByteArrayProperty(value, "publicKey", suite.publicKeyLength, inputs);
|
|
53
|
+
const secretKey = readIndependentByteArrayProperty(value, "secretKey", ML_DSA_SECRET_KEY_LENGTH, [...inputs, publicKey]);
|
|
54
|
+
return { publicKey, secretKey };
|
|
55
|
+
};
|
|
56
|
+
const readSignature = (value, suite, inputs) => {
|
|
56
57
|
if (!(value instanceof Uint8Array)) {
|
|
57
58
|
throw new ReallyMeCryptoError("provider-failure");
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
// Signature bytes are public, but their shape and ownership are provider
|
|
61
|
+
// postconditions. A malformed provider must never be reported as bad caller
|
|
62
|
+
// input, and an aliased result must not be mistaken for provider-owned data.
|
|
63
|
+
ensureIndependentByteArray(value, inputs);
|
|
64
|
+
if (value.length !== suite.signatureLength) {
|
|
65
|
+
throw new ReallyMeCryptoError("provider-failure");
|
|
66
|
+
}
|
|
60
67
|
return value;
|
|
61
68
|
};
|
|
62
69
|
const readVoid = (value) => {
|
|
@@ -78,31 +85,35 @@ export const ReallyMeMlDsa = {
|
|
|
78
85
|
// feed passwords or other low-entropy material here.
|
|
79
86
|
const suite = mlDsaSuite(algorithm);
|
|
80
87
|
ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
|
|
81
|
-
return readKeyPair(suite.deriveKeyPair(secretKey), suite);
|
|
88
|
+
return readKeyPair(suite.deriveKeyPair(secretKey), suite, [secretKey]);
|
|
82
89
|
},
|
|
83
90
|
deriveKeyPairWithProvider(provider, algorithm, secretKey) {
|
|
84
91
|
const suite = mlDsaSuiteWithProvider(algorithm, provider);
|
|
85
92
|
ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
|
|
86
|
-
return readKeyPair(suite.deriveKeyPair(secretKey), suite);
|
|
93
|
+
return readKeyPair(suite.deriveKeyPair(secretKey), suite, [secretKey]);
|
|
87
94
|
},
|
|
88
95
|
sign(algorithm, message, secretKey) {
|
|
89
96
|
const suite = mlDsaSuite(algorithm);
|
|
97
|
+
ensureByteArrayAtMost(message, MAX_CRYPTO_INPUT_LENGTH);
|
|
90
98
|
ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
|
|
91
|
-
return readSignature(suite.sign(secretKey, message), suite);
|
|
99
|
+
return readSignature(suite.sign(secretKey, message), suite, [secretKey, message]);
|
|
92
100
|
},
|
|
93
101
|
signWithProvider(provider, algorithm, message, secretKey) {
|
|
94
102
|
const suite = mlDsaSuiteWithProvider(algorithm, provider);
|
|
103
|
+
ensureByteArrayAtMost(message, MAX_CRYPTO_INPUT_LENGTH);
|
|
95
104
|
ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
|
|
96
|
-
return readSignature(suite.sign(secretKey, message), suite);
|
|
105
|
+
return readSignature(suite.sign(secretKey, message), suite, [secretKey, message]);
|
|
97
106
|
},
|
|
98
107
|
verify(algorithm, signature, message, publicKey) {
|
|
99
108
|
const suite = mlDsaSuite(algorithm);
|
|
109
|
+
ensureByteArrayAtMost(message, MAX_CRYPTO_INPUT_LENGTH);
|
|
100
110
|
ensureBytes(publicKey, suite.publicKeyLength);
|
|
101
111
|
ensureBytes(signature, suite.signatureLength);
|
|
102
112
|
readVoid(suite.verify(publicKey, message, signature));
|
|
103
113
|
},
|
|
104
114
|
verifyWithProvider(provider, algorithm, signature, message, publicKey) {
|
|
105
115
|
const suite = mlDsaSuiteWithProvider(algorithm, provider);
|
|
116
|
+
ensureByteArrayAtMost(message, MAX_CRYPTO_INPUT_LENGTH);
|
|
106
117
|
ensureBytes(publicKey, suite.publicKeyLength);
|
|
107
118
|
ensureBytes(signature, suite.signatureLength);
|
|
108
119
|
readVoid(suite.verify(publicKey, message, signature));
|
package/dist/mlKem.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ export declare const ML_KEM_768_CIPHERTEXT_LENGTH = 1088;
|
|
|
7
7
|
export declare const ML_KEM_1024_PUBLIC_KEY_LENGTH = 1568;
|
|
8
8
|
export declare const ML_KEM_1024_CIPHERTEXT_LENGTH = 1568;
|
|
9
9
|
export declare const ML_KEM_SECRET_KEY_LENGTH = 64;
|
|
10
|
-
export declare const ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH = 32;
|
|
11
10
|
export declare const ML_KEM_SHARED_SECRET_LENGTH = 32;
|
|
12
11
|
export type ReallyMeMlKemKeyPair = Readonly<{
|
|
13
12
|
publicKey: Uint8Array;
|
|
@@ -24,8 +23,6 @@ export declare const ReallyMeMlKem: {
|
|
|
24
23
|
readonly deriveKeyPairWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeKemAlgorithm, secretKey: Uint8Array) => ReallyMeMlKemKeyPair;
|
|
25
24
|
readonly encapsulate: (algorithm: ReallyMeKemAlgorithm, publicKey: Uint8Array) => ReallyMeMlKemEncapsulation;
|
|
26
25
|
readonly encapsulateWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeKemAlgorithm, publicKey: Uint8Array) => ReallyMeMlKemEncapsulation;
|
|
27
|
-
readonly encapsulateDeterministicallyForTest: (algorithm: ReallyMeKemAlgorithm, publicKey: Uint8Array, randomness: Uint8Array) => ReallyMeMlKemEncapsulation;
|
|
28
|
-
readonly encapsulateDeterministicallyWithProviderForTest: (provider: ReallyMeWasmProvider, algorithm: ReallyMeKemAlgorithm, publicKey: Uint8Array, randomness: Uint8Array) => ReallyMeMlKemEncapsulation;
|
|
29
26
|
readonly decapsulate: (algorithm: ReallyMeKemAlgorithm, ciphertext: Uint8Array, secretKey: Uint8Array) => Uint8Array;
|
|
30
27
|
readonly decapsulateWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeKemAlgorithm, ciphertext: Uint8Array, secretKey: Uint8Array) => Uint8Array;
|
|
31
28
|
};
|
package/dist/mlKem.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 { ensureBytes,
|
|
5
|
+
import { ensureBytes, readIndependentByteArrayProperty, readIndependentProviderBytes, } from "./validateBytes.js";
|
|
6
6
|
import { requireReallyMeWasmProvider } from "./wasmProvider.js";
|
|
7
7
|
export const ML_KEM_512_PUBLIC_KEY_LENGTH = 800;
|
|
8
8
|
export const ML_KEM_512_CIPHERTEXT_LENGTH = 768;
|
|
@@ -11,7 +11,6 @@ export const ML_KEM_768_CIPHERTEXT_LENGTH = 1_088;
|
|
|
11
11
|
export const ML_KEM_1024_PUBLIC_KEY_LENGTH = 1_568;
|
|
12
12
|
export const ML_KEM_1024_CIPHERTEXT_LENGTH = 1_568;
|
|
13
13
|
export const ML_KEM_SECRET_KEY_LENGTH = 64;
|
|
14
|
-
export const ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH = 32;
|
|
15
14
|
export const ML_KEM_SHARED_SECRET_LENGTH = 32;
|
|
16
15
|
const mlKemSuite = (algorithm) => {
|
|
17
16
|
const provider = requireReallyMeWasmProvider();
|
|
@@ -26,7 +25,6 @@ const mlKemSuiteWithProvider = (algorithm, provider) => {
|
|
|
26
25
|
generateKeyPair: provider.mlKem512GenerateKeypair,
|
|
27
26
|
deriveKeyPair: provider.mlKem512DeriveKeypair,
|
|
28
27
|
encapsulate: provider.mlKem512Encapsulate,
|
|
29
|
-
encapsulateDerand: provider.mlKem512EncapsulateDerand,
|
|
30
28
|
decapsulate: provider.mlKem512Decapsulate,
|
|
31
29
|
};
|
|
32
30
|
case "ML-KEM-768":
|
|
@@ -36,7 +34,6 @@ const mlKemSuiteWithProvider = (algorithm, provider) => {
|
|
|
36
34
|
generateKeyPair: provider.mlKem768GenerateKeypair,
|
|
37
35
|
deriveKeyPair: provider.mlKem768DeriveKeypair,
|
|
38
36
|
encapsulate: provider.mlKem768Encapsulate,
|
|
39
|
-
encapsulateDerand: provider.mlKem768EncapsulateDerand,
|
|
40
37
|
decapsulate: provider.mlKem768Decapsulate,
|
|
41
38
|
};
|
|
42
39
|
case "ML-KEM-1024":
|
|
@@ -46,27 +43,24 @@ const mlKemSuiteWithProvider = (algorithm, provider) => {
|
|
|
46
43
|
generateKeyPair: provider.mlKem1024GenerateKeypair,
|
|
47
44
|
deriveKeyPair: provider.mlKem1024DeriveKeypair,
|
|
48
45
|
encapsulate: provider.mlKem1024Encapsulate,
|
|
49
|
-
encapsulateDerand: provider.mlKem1024EncapsulateDerand,
|
|
50
46
|
decapsulate: provider.mlKem1024Decapsulate,
|
|
51
47
|
};
|
|
52
48
|
default:
|
|
53
49
|
throw new ReallyMeCryptoError("unsupported-algorithm");
|
|
54
50
|
}
|
|
55
51
|
};
|
|
56
|
-
const readKeyPair = (value, suite) =>
|
|
57
|
-
publicKey
|
|
58
|
-
secretKey
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
ensureBytes(value, ML_KEM_SHARED_SECRET_LENGTH);
|
|
69
|
-
return value;
|
|
52
|
+
const readKeyPair = (value, suite, inputs = []) => {
|
|
53
|
+
const publicKey = readIndependentByteArrayProperty(value, "publicKey", suite.publicKeyLength, inputs);
|
|
54
|
+
const secretKey = readIndependentByteArrayProperty(value, "secretKey", ML_KEM_SECRET_KEY_LENGTH, [...inputs, publicKey]);
|
|
55
|
+
return { publicKey, secretKey };
|
|
56
|
+
};
|
|
57
|
+
const readEncapsulation = (value, suite, inputs) => {
|
|
58
|
+
const ciphertext = readIndependentByteArrayProperty(value, "ciphertext", suite.ciphertextLength, inputs);
|
|
59
|
+
const sharedSecret = readIndependentByteArrayProperty(value, "sharedSecret", ML_KEM_SHARED_SECRET_LENGTH, [...inputs, ciphertext]);
|
|
60
|
+
return { ciphertext, sharedSecret };
|
|
61
|
+
};
|
|
62
|
+
const readSharedSecret = (value, inputs) => {
|
|
63
|
+
return readIndependentProviderBytes(value, ML_KEM_SHARED_SECRET_LENGTH, inputs);
|
|
70
64
|
};
|
|
71
65
|
export const ReallyMeMlKem = {
|
|
72
66
|
generateKeyPair(algorithm) {
|
|
@@ -82,45 +76,33 @@ export const ReallyMeMlKem = {
|
|
|
82
76
|
// key. Do not feed passwords or other low-entropy material here.
|
|
83
77
|
const suite = mlKemSuite(algorithm);
|
|
84
78
|
ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
|
|
85
|
-
return readKeyPair(suite.deriveKeyPair(secretKey), suite);
|
|
79
|
+
return readKeyPair(suite.deriveKeyPair(secretKey), suite, [secretKey]);
|
|
86
80
|
},
|
|
87
81
|
deriveKeyPairWithProvider(provider, algorithm, secretKey) {
|
|
88
82
|
const suite = mlKemSuiteWithProvider(algorithm, provider);
|
|
89
83
|
ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
|
|
90
|
-
return readKeyPair(suite.deriveKeyPair(secretKey), suite);
|
|
84
|
+
return readKeyPair(suite.deriveKeyPair(secretKey), suite, [secretKey]);
|
|
91
85
|
},
|
|
92
86
|
encapsulate(algorithm, publicKey) {
|
|
93
87
|
const suite = mlKemSuite(algorithm);
|
|
94
88
|
ensureBytes(publicKey, suite.publicKeyLength);
|
|
95
|
-
return readEncapsulation(suite.encapsulate(publicKey), suite);
|
|
89
|
+
return readEncapsulation(suite.encapsulate(publicKey), suite, [publicKey]);
|
|
96
90
|
},
|
|
97
91
|
encapsulateWithProvider(provider, algorithm, publicKey) {
|
|
98
92
|
const suite = mlKemSuiteWithProvider(algorithm, provider);
|
|
99
93
|
ensureBytes(publicKey, suite.publicKeyLength);
|
|
100
|
-
return readEncapsulation(suite.encapsulate(publicKey), suite);
|
|
101
|
-
},
|
|
102
|
-
encapsulateDeterministicallyForTest(algorithm, publicKey, randomness) {
|
|
103
|
-
const suite = mlKemSuite(algorithm);
|
|
104
|
-
ensureBytes(publicKey, suite.publicKeyLength);
|
|
105
|
-
ensureBytes(randomness, ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH);
|
|
106
|
-
return readEncapsulation(suite.encapsulateDerand(publicKey, randomness), suite);
|
|
107
|
-
},
|
|
108
|
-
encapsulateDeterministicallyWithProviderForTest(provider, algorithm, publicKey, randomness) {
|
|
109
|
-
const suite = mlKemSuiteWithProvider(algorithm, provider);
|
|
110
|
-
ensureBytes(publicKey, suite.publicKeyLength);
|
|
111
|
-
ensureBytes(randomness, ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH);
|
|
112
|
-
return readEncapsulation(suite.encapsulateDerand(publicKey, randomness), suite);
|
|
94
|
+
return readEncapsulation(suite.encapsulate(publicKey), suite, [publicKey]);
|
|
113
95
|
},
|
|
114
96
|
decapsulate(algorithm, ciphertext, secretKey) {
|
|
115
97
|
const suite = mlKemSuite(algorithm);
|
|
116
98
|
ensureBytes(ciphertext, suite.ciphertextLength);
|
|
117
99
|
ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
|
|
118
|
-
return readSharedSecret(suite.decapsulate(ciphertext, secretKey));
|
|
100
|
+
return readSharedSecret(suite.decapsulate(ciphertext, secretKey), [ciphertext, secretKey]);
|
|
119
101
|
},
|
|
120
102
|
decapsulateWithProvider(provider, algorithm, ciphertext, secretKey) {
|
|
121
103
|
const suite = mlKemSuiteWithProvider(algorithm, provider);
|
|
122
104
|
ensureBytes(ciphertext, suite.ciphertextLength);
|
|
123
105
|
ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
|
|
124
|
-
return readSharedSecret(suite.decapsulate(ciphertext, secretKey));
|
|
106
|
+
return readSharedSecret(suite.decapsulate(ciphertext, secretKey), [ciphertext, secretKey]);
|
|
125
107
|
},
|
|
126
108
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReallyMeWasmProvider } from "./wasmProvider.js";
|
|
2
|
+
/**
|
|
3
|
+
* Execute one generated binary protobuf request.
|
|
4
|
+
*
|
|
5
|
+
* The returned bytes are always a generated `CryptoOperationResponse` with a
|
|
6
|
+
* typed `result` or typed `error` outcome.
|
|
7
|
+
*/
|
|
8
|
+
export declare const processOperationResponse: (requestBytes: Uint8Array) => Uint8Array;
|
|
9
|
+
export declare const processOperationResponseWithProvider: (provider: ReallyMeWasmProvider, requestBytes: Uint8Array) => Uint8Array;
|
|
10
|
+
/**
|
|
11
|
+
* Execute one permitted non-secret generated ProtoJSON request and return the
|
|
12
|
+
* same binary `CryptoOperationResponse` used by `processOperationResponse`.
|
|
13
|
+
* Secret-bearing selectors fail before JSON value deserialization.
|
|
14
|
+
*/
|
|
15
|
+
export declare const processOperationResponseJson: (requestJson: Uint8Array) => Uint8Array;
|
|
16
|
+
export declare const processOperationResponseJsonWithProvider: (provider: ReallyMeWasmProvider, requestJson: Uint8Array) => Uint8Array;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { ReallyMeCryptoError } from "./errors.js";
|
|
5
|
+
import { ensureByteArray, ensureIndependentByteArray, } from "./validateBytes.js";
|
|
6
|
+
import { requireReallyMeWasmProvider } from "./wasmProvider.js";
|
|
7
|
+
const MAX_CRYPTO_OPERATION_RESPONSE_BYTES = 1_048_608;
|
|
8
|
+
const readOperationResponse = (value, request) => {
|
|
9
|
+
if (!(value instanceof Uint8Array)) {
|
|
10
|
+
throw new ReallyMeCryptoError("provider-failure");
|
|
11
|
+
}
|
|
12
|
+
ensureIndependentByteArray(value, [request]);
|
|
13
|
+
if (value.length === 0 ||
|
|
14
|
+
value.length > MAX_CRYPTO_OPERATION_RESPONSE_BYTES) {
|
|
15
|
+
value.fill(0);
|
|
16
|
+
throw new ReallyMeCryptoError("provider-failure");
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Execute one generated binary protobuf request.
|
|
22
|
+
*
|
|
23
|
+
* The returned bytes are always a generated `CryptoOperationResponse` with a
|
|
24
|
+
* typed `result` or typed `error` outcome.
|
|
25
|
+
*/
|
|
26
|
+
export const processOperationResponse = (requestBytes) => {
|
|
27
|
+
ensureByteArray(requestBytes);
|
|
28
|
+
return processOperationResponseWithProvider(requireReallyMeWasmProvider(), requestBytes);
|
|
29
|
+
};
|
|
30
|
+
export const processOperationResponseWithProvider = (provider, requestBytes) => {
|
|
31
|
+
ensureByteArray(requestBytes);
|
|
32
|
+
return readOperationResponse(provider.processOperationResponse(requestBytes), requestBytes);
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Execute one permitted non-secret generated ProtoJSON request and return the
|
|
36
|
+
* same binary `CryptoOperationResponse` used by `processOperationResponse`.
|
|
37
|
+
* Secret-bearing selectors fail before JSON value deserialization.
|
|
38
|
+
*/
|
|
39
|
+
export const processOperationResponseJson = (requestJson) => {
|
|
40
|
+
ensureByteArray(requestJson);
|
|
41
|
+
return processOperationResponseJsonWithProvider(requireReallyMeWasmProvider(), requestJson);
|
|
42
|
+
};
|
|
43
|
+
export const processOperationResponseJsonWithProvider = (provider, requestJson) => {
|
|
44
|
+
ensureByteArray(requestJson);
|
|
45
|
+
return readOperationResponse(provider.processOperationResponseJson(requestJson), requestJson);
|
|
46
|
+
};
|
package/dist/pbkdf2.d.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
export declare const PBKDF2_MIN_INPUT_LENGTH = 1;
|
|
2
2
|
export declare const PBKDF2_MAX_INPUT_LENGTH = 4096;
|
|
3
|
-
export declare const PBKDF2_MIN_ITERATIONS =
|
|
3
|
+
export declare const PBKDF2_MIN_ITERATIONS = 100000;
|
|
4
|
+
export declare const PBKDF2_MAX_ITERATIONS = 10000000;
|
|
4
5
|
export declare const PBKDF2_MIN_OUTPUT_LENGTH = 1;
|
|
5
6
|
export declare const PBKDF2_MAX_OUTPUT_LENGTH = 4096;
|
|
6
7
|
/**
|
|
7
8
|
* OWASP Password Storage Cheat Sheet minimum iteration counts for deriving a
|
|
8
9
|
* *new* password key.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* pass at least the value for their PRF.
|
|
10
|
+
* These stronger profile recommendations remain above the enforced public
|
|
11
|
+
* baseline and should be selected when protocol latency budgets permit.
|
|
12
12
|
*/
|
|
13
13
|
export declare const PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA256 = 600000;
|
|
14
14
|
export declare const PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA512 = 220000;
|
|
15
15
|
export declare const ReallyMePbkdf2: {
|
|
16
16
|
/**
|
|
17
17
|
* PBKDF2-HMAC-SHA-256. For a new password key pass at least
|
|
18
|
-
* {@link PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA256} iterations
|
|
19
|
-
* are accepted only to verify hashes created elsewhere.
|
|
18
|
+
* {@link PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA256} iterations.
|
|
20
19
|
*/
|
|
21
20
|
readonly deriveHmacSha256: (password: Uint8Array, salt: Uint8Array, iterations: number, outputLength: number) => Uint8Array;
|
|
22
21
|
/**
|
|
23
22
|
* PBKDF2-HMAC-SHA-512. For a new password key pass at least
|
|
24
|
-
* {@link PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA512} iterations
|
|
25
|
-
* are accepted only to verify hashes created elsewhere.
|
|
23
|
+
* {@link PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA512} iterations.
|
|
26
24
|
*/
|
|
27
25
|
readonly deriveHmacSha512: (password: Uint8Array, salt: Uint8Array, iterations: number, outputLength: number) => Uint8Array;
|
|
28
26
|
};
|