@reallyme/crypto 0.1.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +148 -18
- package/dist/aead.d.ts +4 -0
- package/dist/aead.js +40 -16
- package/dist/aesKw.d.ts +8 -2
- package/dist/aesKw.js +69 -12
- package/dist/algorithms.d.ts +12 -4
- package/dist/algorithms.js +8 -2
- package/dist/argon2id.d.ts +3 -0
- package/dist/argon2id.js +18 -3
- package/dist/cryptoFacade.d.ts +66 -5
- package/dist/cryptoFacade.js +99 -38
- package/dist/errors.d.ts +1 -1
- 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 +4 -1
- package/dist/hpke.js +45 -14
- package/dist/index.d.ts +14 -13
- package/dist/index.js +12 -10
- package/dist/jwaConcatKdf.js +8 -4
- package/dist/jwk.d.ts +2 -1
- package/dist/jwk.js +158 -122
- package/dist/kmac.d.ts +11 -0
- package/dist/kmac.js +49 -0
- package/dist/memory.d.ts +8 -0
- package/dist/memory.js +13 -0
- package/dist/mlDsa.d.ts +5 -0
- package/dist/mlDsa.js +43 -9
- package/dist/mlKem.d.ts +5 -2
- package/dist/mlKem.js +37 -26
- package/dist/operationResponse.d.ts +16 -0
- package/dist/operationResponse.js +46 -0
- package/dist/p256Ecdh.js +5 -1
- package/dist/p384Ecdh.js +5 -1
- package/dist/p521Ecdh.js +5 -1
- package/dist/pbkdf2.d.ts +6 -8
- package/dist/pbkdf2.js +12 -12
- package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.d.ts +2859 -128
- package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.js +992 -132
- package/dist/proto.d.ts +99 -7
- package/dist/proto.js +741 -16
- package/dist/rsa.d.ts +4 -1
- package/dist/rsa.js +23 -16
- package/dist/slhDsa.d.ts +5 -0
- package/dist/slhDsa.js +40 -9
- package/dist/validateBytes.d.ts +7 -1
- package/dist/validateBytes.js +63 -3
- package/dist/wasm/reallyme_crypto_wasm.js +77 -365
- package/dist/wasm/reallyme_crypto_wasm_bg.wasm +0 -0
- package/dist/wasmModuleTypes.d.ts +9 -21
- package/dist/wasmProvider.d.ts +12 -29
- package/dist/wasmProvider.js +25 -43
- package/dist/xWing.d.ts +5 -4
- package/dist/xWing.js +40 -40
- package/package.json +20 -2
- package/dist/codecs.d.ts +0 -35
- package/dist/codecs.js +0 -216
- package/dist/proto/generated/reallyme/codec/v1/codec_pb.d.ts +0 -253
- package/dist/proto/generated/reallyme/codec/v1/codec_pb.js +0 -160
package/dist/jwk.js
CHANGED
|
@@ -3,9 +3,22 @@
|
|
|
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
|
-
|
|
8
|
-
|
|
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
|
+
};
|
|
9
22
|
const jwkSpec = (algorithm) => {
|
|
10
23
|
switch (algorithm) {
|
|
11
24
|
case "Ed25519":
|
|
@@ -32,8 +45,6 @@ const jwkSpec = (algorithm) => {
|
|
|
32
45
|
return { alg: "SLH-DSA-SHA2-128s", kty: "AKP", publicKeyLength: 32, use: "sig" };
|
|
33
46
|
case "X-Wing-768":
|
|
34
47
|
return { alg: "X-Wing-768", kty: "AKP", publicKeyLength: 1_216, use: "enc" };
|
|
35
|
-
case "X-Wing-1024":
|
|
36
|
-
return { alg: "X-Wing-1024", kty: "AKP", publicKeyLength: 1_600, use: "enc" };
|
|
37
48
|
}
|
|
38
49
|
};
|
|
39
50
|
const ensureLength = (bytes, expectedLength) => {
|
|
@@ -41,79 +52,58 @@ const ensureLength = (bytes, expectedLength) => {
|
|
|
41
52
|
throw new ReallyMeCryptoError("invalid-input");
|
|
42
53
|
}
|
|
43
54
|
};
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
if (value === undefined) {
|
|
55
|
+
const readString = (value, name) => {
|
|
56
|
+
if (typeof value !== "object" || value === null) {
|
|
47
57
|
throw new ReallyMeCryptoError("invalid-input");
|
|
48
58
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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) {
|
|
59
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
|
60
|
+
if (descriptor === undefined ||
|
|
61
|
+
!("value" in descriptor) ||
|
|
62
|
+
typeof descriptor.value !== "string") {
|
|
74
63
|
throw new ReallyMeCryptoError("invalid-input");
|
|
75
64
|
}
|
|
76
|
-
return value;
|
|
65
|
+
return descriptor.value;
|
|
77
66
|
};
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
67
|
+
const ensureExactMembers = (value, allowedNames) => {
|
|
68
|
+
if (typeof value !== "object" || value === null) {
|
|
80
69
|
throw new ReallyMeCryptoError("invalid-input");
|
|
81
70
|
}
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
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;
|
|
71
|
+
for (const name of Reflect.ownKeys(value)) {
|
|
72
|
+
if (typeof name !== "string" || !allowedNames.includes(name)) {
|
|
73
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
94
74
|
}
|
|
95
75
|
}
|
|
96
|
-
if (offset !== output.length) {
|
|
97
|
-
throw new ReallyMeCryptoError("invalid-input");
|
|
98
|
-
}
|
|
99
|
-
return output;
|
|
100
76
|
};
|
|
101
|
-
const
|
|
77
|
+
const privateJwkMemberNames = [
|
|
78
|
+
"d",
|
|
79
|
+
"p",
|
|
80
|
+
"q",
|
|
81
|
+
"dp",
|
|
82
|
+
"dq",
|
|
83
|
+
"qi",
|
|
84
|
+
"oth",
|
|
85
|
+
"k",
|
|
86
|
+
"priv",
|
|
87
|
+
"privateKey",
|
|
88
|
+
"secretKey",
|
|
89
|
+
];
|
|
90
|
+
const rejectPrivateKeyMaterial = (value) => {
|
|
102
91
|
if (typeof value !== "object" || value === null) {
|
|
103
92
|
throw new ReallyMeCryptoError("invalid-input");
|
|
104
93
|
}
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
94
|
+
for (const name of privateJwkMemberNames) {
|
|
95
|
+
if (Reflect.has(value, name)) {
|
|
96
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
97
|
+
}
|
|
108
98
|
}
|
|
109
|
-
return property;
|
|
110
99
|
};
|
|
111
100
|
const optionalStringMatches = (value, name, expected) => {
|
|
112
101
|
if (typeof value !== "object" || value === null) {
|
|
113
102
|
throw new ReallyMeCryptoError("invalid-input");
|
|
114
103
|
}
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
104
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
|
105
|
+
if (descriptor !== undefined &&
|
|
106
|
+
(!("value" in descriptor) || descriptor.value !== expected)) {
|
|
117
107
|
throw new ReallyMeCryptoError("invalid-input");
|
|
118
108
|
}
|
|
119
109
|
};
|
|
@@ -138,7 +128,6 @@ const specFromAlgorithm = (algorithm) => {
|
|
|
138
128
|
case "ML-KEM-1024":
|
|
139
129
|
case "SLH-DSA-SHA2-128s":
|
|
140
130
|
case "X-Wing-768":
|
|
141
|
-
case "X-Wing-1024":
|
|
142
131
|
return jwkSpec(algorithm);
|
|
143
132
|
default:
|
|
144
133
|
throw new ReallyMeCryptoError("unsupported-algorithm");
|
|
@@ -172,14 +161,43 @@ const compressEc = (algorithm, x, y) => {
|
|
|
172
161
|
throw new ReallyMeCryptoError("invalid-input");
|
|
173
162
|
}
|
|
174
163
|
};
|
|
164
|
+
const mapCodecError = (error) => {
|
|
165
|
+
if (error instanceof ReallyMeCodecError && error.code === "provider-failure") {
|
|
166
|
+
return new ReallyMeCryptoError("provider-failure");
|
|
167
|
+
}
|
|
168
|
+
return new ReallyMeCryptoError("invalid-input");
|
|
169
|
+
};
|
|
170
|
+
const codecBase64urlEncode = (bytes) => {
|
|
171
|
+
try {
|
|
172
|
+
return codecBase64urlEncodeValue(bytes);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
throw mapCodecError(error);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const codecBase64urlDecode = (encoded) => {
|
|
179
|
+
try {
|
|
180
|
+
return codecBase64urlDecodeValue(encoded);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw mapCodecError(error);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const codecBase64urlDecodeCanonical = (encoded) => {
|
|
187
|
+
const bytes = codecBase64urlDecode(encoded);
|
|
188
|
+
if (codecBase64urlEncode(bytes) !== encoded) {
|
|
189
|
+
throw new ReallyMeCryptoError("invalid-input");
|
|
190
|
+
}
|
|
191
|
+
return bytes;
|
|
192
|
+
};
|
|
175
193
|
const toJcs = (jwk) => {
|
|
176
|
-
|
|
177
|
-
|
|
194
|
+
const key = ReallyMeJwk.fromJwk(jwk);
|
|
195
|
+
try {
|
|
196
|
+
return codecCanonicalizeJson(key.jwk);
|
|
178
197
|
}
|
|
179
|
-
|
|
180
|
-
|
|
198
|
+
catch (error) {
|
|
199
|
+
throw mapCodecError(error);
|
|
181
200
|
}
|
|
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
201
|
};
|
|
184
202
|
export const ReallyMeJwk = {
|
|
185
203
|
toJwk(algorithm, publicKey) {
|
|
@@ -193,15 +211,15 @@ export const ReallyMeJwk = {
|
|
|
193
211
|
crv: ecAlgorithm,
|
|
194
212
|
kty: "EC",
|
|
195
213
|
use: "sig",
|
|
196
|
-
x:
|
|
197
|
-
y:
|
|
214
|
+
x: codecBase64urlEncode(uncompressed.slice(1, 33)),
|
|
215
|
+
y: codecBase64urlEncode(uncompressed.slice(33, 65)),
|
|
198
216
|
};
|
|
199
217
|
}
|
|
200
218
|
if (spec.kty === "AKP") {
|
|
201
219
|
return {
|
|
202
220
|
alg: spec.alg,
|
|
203
221
|
kty: "AKP",
|
|
204
|
-
pub:
|
|
222
|
+
pub: codecBase64urlEncode(publicKey),
|
|
205
223
|
use: spec.use,
|
|
206
224
|
};
|
|
207
225
|
}
|
|
@@ -214,71 +232,89 @@ export const ReallyMeJwk = {
|
|
|
214
232
|
crv,
|
|
215
233
|
kty: "OKP",
|
|
216
234
|
use: spec.use,
|
|
217
|
-
x:
|
|
235
|
+
x: codecBase64urlEncode(publicKey),
|
|
218
236
|
};
|
|
219
237
|
},
|
|
220
238
|
fromJwk(value) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
optionalStringMatches(value, "use", spec.use);
|
|
230
|
-
if (spec.kty === "EC") {
|
|
231
|
-
const ecAlgorithm = spec.crv === "P-256" ? "P-256" : "secp256k1";
|
|
232
|
-
const x = base64urlDecode(readString(value, "x"));
|
|
233
|
-
const y = base64urlDecode(readString(value, "y"));
|
|
234
|
-
const publicKey = compressEc(ecAlgorithm, x, y);
|
|
235
|
-
ensureLength(publicKey, spec.publicKeyLength);
|
|
236
|
-
const jwk = ReallyMeJwk.toJwk(ecAlgorithm, publicKey);
|
|
237
|
-
return { algorithm: ecAlgorithm, publicKey, jwk };
|
|
238
|
-
}
|
|
239
|
-
if (spec.kty === "OKP") {
|
|
240
|
-
const crv = spec.crv;
|
|
241
|
-
if (crv !== "Ed25519" && crv !== "X25519") {
|
|
242
|
-
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");
|
|
243
247
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
case "ML-KEM-512":
|
|
258
|
-
case "ML-KEM-768":
|
|
259
|
-
case "ML-KEM-1024":
|
|
260
|
-
case "SLH-DSA-SHA2-128s":
|
|
261
|
-
case "X-Wing-768":
|
|
262
|
-
case "X-Wing-1024": {
|
|
263
|
-
const jwk = ReallyMeJwk.toJwk(algorithm, publicKey);
|
|
264
|
-
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 };
|
|
265
261
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
+
});
|
|
269
299
|
},
|
|
270
300
|
toJwks(keys) {
|
|
271
301
|
return { keys: [...keys] };
|
|
272
302
|
},
|
|
273
303
|
fromJwks(value) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
+
});
|
|
282
318
|
},
|
|
283
319
|
publicKeyBytes(jwk) {
|
|
284
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/memory.d.ts
ADDED
|
@@ -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
|
+
};
|
package/dist/mlDsa.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReallyMeSignatureAlgorithm } from "./algorithms.js";
|
|
2
|
+
import type { ReallyMeWasmProvider } from "./wasmProvider.js";
|
|
2
3
|
export declare const ML_DSA_44_PUBLIC_KEY_LENGTH = 1312;
|
|
3
4
|
export declare const ML_DSA_44_SIGNATURE_LENGTH = 2420;
|
|
4
5
|
export declare const ML_DSA_65_PUBLIC_KEY_LENGTH = 1952;
|
|
@@ -12,7 +13,11 @@ export type ReallyMeMlDsaKeyPair = Readonly<{
|
|
|
12
13
|
}>;
|
|
13
14
|
export declare const ReallyMeMlDsa: {
|
|
14
15
|
readonly generateKeyPair: (algorithm: ReallyMeSignatureAlgorithm) => ReallyMeMlDsaKeyPair;
|
|
16
|
+
readonly generateKeyPairWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeSignatureAlgorithm) => ReallyMeMlDsaKeyPair;
|
|
15
17
|
readonly deriveKeyPair: (algorithm: ReallyMeSignatureAlgorithm, secretKey: Uint8Array) => ReallyMeMlDsaKeyPair;
|
|
18
|
+
readonly deriveKeyPairWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeSignatureAlgorithm, secretKey: Uint8Array) => ReallyMeMlDsaKeyPair;
|
|
16
19
|
readonly sign: (algorithm: ReallyMeSignatureAlgorithm, message: Uint8Array, secretKey: Uint8Array) => Uint8Array;
|
|
20
|
+
readonly signWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeSignatureAlgorithm, message: Uint8Array, secretKey: Uint8Array) => Uint8Array;
|
|
17
21
|
readonly verify: (algorithm: ReallyMeSignatureAlgorithm, signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array) => void;
|
|
22
|
+
readonly verifyWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeSignatureAlgorithm, signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array) => void;
|
|
18
23
|
};
|
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;
|
|
@@ -13,6 +13,9 @@ export const ML_DSA_87_SIGNATURE_LENGTH = 4_627;
|
|
|
13
13
|
export const ML_DSA_SECRET_KEY_LENGTH = 32;
|
|
14
14
|
const mlDsaSuite = (algorithm) => {
|
|
15
15
|
const provider = requireReallyMeWasmProvider();
|
|
16
|
+
return mlDsaSuiteWithProvider(algorithm, provider);
|
|
17
|
+
};
|
|
18
|
+
const mlDsaSuiteWithProvider = (algorithm, provider) => {
|
|
16
19
|
switch (algorithm) {
|
|
17
20
|
case "ML-DSA-44":
|
|
18
21
|
return {
|
|
@@ -45,15 +48,22 @@ const mlDsaSuite = (algorithm) => {
|
|
|
45
48
|
throw new ReallyMeCryptoError("unsupported-algorithm");
|
|
46
49
|
}
|
|
47
50
|
};
|
|
48
|
-
const readKeyPair = (value, suite) =>
|
|
49
|
-
publicKey
|
|
50
|
-
secretKey
|
|
51
|
-
}
|
|
52
|
-
|
|
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) => {
|
|
53
57
|
if (!(value instanceof Uint8Array)) {
|
|
54
58
|
throw new ReallyMeCryptoError("provider-failure");
|
|
55
59
|
}
|
|
56
|
-
|
|
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
|
+
}
|
|
57
67
|
return value;
|
|
58
68
|
};
|
|
59
69
|
const readVoid = (value) => {
|
|
@@ -66,20 +76,44 @@ export const ReallyMeMlDsa = {
|
|
|
66
76
|
const suite = mlDsaSuite(algorithm);
|
|
67
77
|
return readKeyPair(suite.generateKeyPair(), suite);
|
|
68
78
|
},
|
|
79
|
+
generateKeyPairWithProvider(provider, algorithm) {
|
|
80
|
+
const suite = mlDsaSuiteWithProvider(algorithm, provider);
|
|
81
|
+
return readKeyPair(suite.generateKeyPair(), suite);
|
|
82
|
+
},
|
|
69
83
|
deriveKeyPair(algorithm, secretKey) {
|
|
70
84
|
// Import an existing FIPS 204 seed and reconstruct its public key. Do not
|
|
71
85
|
// feed passwords or other low-entropy material here.
|
|
72
86
|
const suite = mlDsaSuite(algorithm);
|
|
73
87
|
ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
|
|
74
|
-
return readKeyPair(suite.deriveKeyPair(secretKey), suite);
|
|
88
|
+
return readKeyPair(suite.deriveKeyPair(secretKey), suite, [secretKey]);
|
|
89
|
+
},
|
|
90
|
+
deriveKeyPairWithProvider(provider, algorithm, secretKey) {
|
|
91
|
+
const suite = mlDsaSuiteWithProvider(algorithm, provider);
|
|
92
|
+
ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
|
|
93
|
+
return readKeyPair(suite.deriveKeyPair(secretKey), suite, [secretKey]);
|
|
75
94
|
},
|
|
76
95
|
sign(algorithm, message, secretKey) {
|
|
77
96
|
const suite = mlDsaSuite(algorithm);
|
|
97
|
+
ensureByteArrayAtMost(message, MAX_CRYPTO_INPUT_LENGTH);
|
|
98
|
+
ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
|
|
99
|
+
return readSignature(suite.sign(secretKey, message), suite, [secretKey, message]);
|
|
100
|
+
},
|
|
101
|
+
signWithProvider(provider, algorithm, message, secretKey) {
|
|
102
|
+
const suite = mlDsaSuiteWithProvider(algorithm, provider);
|
|
103
|
+
ensureByteArrayAtMost(message, MAX_CRYPTO_INPUT_LENGTH);
|
|
78
104
|
ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
|
|
79
|
-
return readSignature(suite.sign(secretKey, message), suite);
|
|
105
|
+
return readSignature(suite.sign(secretKey, message), suite, [secretKey, message]);
|
|
80
106
|
},
|
|
81
107
|
verify(algorithm, signature, message, publicKey) {
|
|
82
108
|
const suite = mlDsaSuite(algorithm);
|
|
109
|
+
ensureByteArrayAtMost(message, MAX_CRYPTO_INPUT_LENGTH);
|
|
110
|
+
ensureBytes(publicKey, suite.publicKeyLength);
|
|
111
|
+
ensureBytes(signature, suite.signatureLength);
|
|
112
|
+
readVoid(suite.verify(publicKey, message, signature));
|
|
113
|
+
},
|
|
114
|
+
verifyWithProvider(provider, algorithm, signature, message, publicKey) {
|
|
115
|
+
const suite = mlDsaSuiteWithProvider(algorithm, provider);
|
|
116
|
+
ensureByteArrayAtMost(message, MAX_CRYPTO_INPUT_LENGTH);
|
|
83
117
|
ensureBytes(publicKey, suite.publicKeyLength);
|
|
84
118
|
ensureBytes(signature, suite.signatureLength);
|
|
85
119
|
readVoid(suite.verify(publicKey, message, signature));
|
package/dist/mlKem.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReallyMeKemAlgorithm } from "./algorithms.js";
|
|
2
|
+
import type { ReallyMeWasmProvider } from "./wasmProvider.js";
|
|
2
3
|
export declare const ML_KEM_512_PUBLIC_KEY_LENGTH = 800;
|
|
3
4
|
export declare const ML_KEM_512_CIPHERTEXT_LENGTH = 768;
|
|
4
5
|
export declare const ML_KEM_768_PUBLIC_KEY_LENGTH = 1184;
|
|
@@ -6,7 +7,6 @@ export declare const ML_KEM_768_CIPHERTEXT_LENGTH = 1088;
|
|
|
6
7
|
export declare const ML_KEM_1024_PUBLIC_KEY_LENGTH = 1568;
|
|
7
8
|
export declare const ML_KEM_1024_CIPHERTEXT_LENGTH = 1568;
|
|
8
9
|
export declare const ML_KEM_SECRET_KEY_LENGTH = 64;
|
|
9
|
-
export declare const ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH = 32;
|
|
10
10
|
export declare const ML_KEM_SHARED_SECRET_LENGTH = 32;
|
|
11
11
|
export type ReallyMeMlKemKeyPair = Readonly<{
|
|
12
12
|
publicKey: Uint8Array;
|
|
@@ -18,8 +18,11 @@ export type ReallyMeMlKemEncapsulation = Readonly<{
|
|
|
18
18
|
}>;
|
|
19
19
|
export declare const ReallyMeMlKem: {
|
|
20
20
|
readonly generateKeyPair: (algorithm: ReallyMeKemAlgorithm) => ReallyMeMlKemKeyPair;
|
|
21
|
+
readonly generateKeyPairWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeKemAlgorithm) => ReallyMeMlKemKeyPair;
|
|
21
22
|
readonly deriveKeyPair: (algorithm: ReallyMeKemAlgorithm, secretKey: Uint8Array) => ReallyMeMlKemKeyPair;
|
|
23
|
+
readonly deriveKeyPairWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeKemAlgorithm, secretKey: Uint8Array) => ReallyMeMlKemKeyPair;
|
|
22
24
|
readonly encapsulate: (algorithm: ReallyMeKemAlgorithm, publicKey: Uint8Array) => ReallyMeMlKemEncapsulation;
|
|
23
|
-
readonly
|
|
25
|
+
readonly encapsulateWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeKemAlgorithm, publicKey: Uint8Array) => ReallyMeMlKemEncapsulation;
|
|
24
26
|
readonly decapsulate: (algorithm: ReallyMeKemAlgorithm, ciphertext: Uint8Array, secretKey: Uint8Array) => Uint8Array;
|
|
27
|
+
readonly decapsulateWithProvider: (provider: ReallyMeWasmProvider, algorithm: ReallyMeKemAlgorithm, ciphertext: Uint8Array, secretKey: Uint8Array) => Uint8Array;
|
|
25
28
|
};
|