@reallyme/crypto 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +140 -0
  2. package/dist/aead.d.ts +9 -0
  3. package/dist/aead.js +62 -0
  4. package/dist/aesKw.d.ts +10 -0
  5. package/dist/aesKw.js +45 -0
  6. package/dist/algorithms.d.ts +18 -0
  7. package/dist/algorithms.js +64 -0
  8. package/dist/argon2id.d.ts +8 -0
  9. package/dist/argon2id.js +40 -0
  10. package/dist/bip340Schnorr.d.ts +26 -0
  11. package/dist/bip340Schnorr.js +72 -0
  12. package/dist/codecs.d.ts +35 -0
  13. package/dist/codecs.js +216 -0
  14. package/dist/cryptoFacade.d.ts +51 -0
  15. package/dist/cryptoFacade.js +329 -0
  16. package/dist/digest.d.ts +14 -0
  17. package/dist/digest.js +33 -0
  18. package/dist/ed25519.d.ts +34 -0
  19. package/dist/ed25519.js +78 -0
  20. package/dist/encodeEcdsaDer.d.ts +2 -0
  21. package/dist/encodeEcdsaDer.js +141 -0
  22. package/dist/errors.d.ts +10 -0
  23. package/dist/errors.js +11 -0
  24. package/dist/hkdf.d.ts +7 -0
  25. package/dist/hkdf.js +27 -0
  26. package/dist/hmac.d.ts +9 -0
  27. package/dist/hmac.js +50 -0
  28. package/dist/hpke.d.ts +15 -0
  29. package/dist/hpke.js +59 -0
  30. package/dist/index.d.ts +41 -0
  31. package/dist/index.js +31 -0
  32. package/dist/jwk.d.ts +42 -0
  33. package/dist/jwk.js +289 -0
  34. package/dist/mlDsa.d.ts +18 -0
  35. package/dist/mlDsa.js +85 -0
  36. package/dist/mlKem.d.ts +25 -0
  37. package/dist/mlKem.js +95 -0
  38. package/dist/p256Ecdh.d.ts +22 -0
  39. package/dist/p256Ecdh.js +53 -0
  40. package/dist/p256Ecdsa.d.ts +25 -0
  41. package/dist/p256Ecdsa.js +85 -0
  42. package/dist/p384Ecdsa.d.ts +23 -0
  43. package/dist/p384Ecdsa.js +83 -0
  44. package/dist/p521Ecdsa.d.ts +23 -0
  45. package/dist/p521Ecdsa.js +83 -0
  46. package/dist/pbkdf2.d.ts +28 -0
  47. package/dist/pbkdf2.js +57 -0
  48. package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.d.ts +618 -0
  49. package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.js +523 -0
  50. package/dist/proto.d.ts +25 -0
  51. package/dist/proto.js +342 -0
  52. package/dist/providerCatalog.d.ts +9 -0
  53. package/dist/providerCatalog.js +15 -0
  54. package/dist/rsa.d.ts +13 -0
  55. package/dist/rsa.js +102 -0
  56. package/dist/secp256k1.d.ts +47 -0
  57. package/dist/secp256k1.js +106 -0
  58. package/dist/slhDsa.d.ts +15 -0
  59. package/dist/slhDsa.js +54 -0
  60. package/dist/validateBytes.d.ts +2 -0
  61. package/dist/validateBytes.js +20 -0
  62. package/dist/verifySignature.d.ts +1 -0
  63. package/dist/verifySignature.js +9 -0
  64. package/dist/wasm/LICENSE +201 -0
  65. package/dist/wasm/reallyme_crypto_wasm.js +1319 -0
  66. package/dist/wasm/reallyme_crypto_wasm_bg.wasm +0 -0
  67. package/dist/wasmModuleTypes.d.ts +72 -0
  68. package/dist/wasmModuleTypes.js +4 -0
  69. package/dist/wasmProvider.d.ts +94 -0
  70. package/dist/wasmProvider.js +173 -0
  71. package/dist/x25519.d.ts +28 -0
  72. package/dist/x25519.js +66 -0
  73. package/dist/xWing.d.ts +23 -0
  74. package/dist/xWing.js +86 -0
  75. package/package.json +48 -0
package/dist/jwk.js ADDED
@@ -0,0 +1,289 @@
1
+ // SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import { p256 } from "@noble/curves/nist.js";
5
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
6
+ import { ReallyMeCryptoError } from "./errors.js";
7
+ const BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
8
+ const DECODE_TABLE = new Map([...BASE64URL_ALPHABET].map((char, index) => [char, index]));
9
+ const jwkSpec = (algorithm) => {
10
+ switch (algorithm) {
11
+ case "Ed25519":
12
+ return { alg: "EdDSA", crv: "Ed25519", kty: "OKP", publicKeyLength: 32, use: "sig" };
13
+ case "X25519":
14
+ return { alg: "ECDH-ES", crv: "X25519", kty: "OKP", publicKeyLength: 32, use: "enc" };
15
+ case "P-256":
16
+ return { alg: "ES256", crv: "P-256", kty: "EC", publicKeyLength: 33, use: "sig" };
17
+ case "secp256k1":
18
+ return { alg: "ES256K", crv: "secp256k1", kty: "EC", publicKeyLength: 33, use: "sig" };
19
+ case "ML-DSA-44":
20
+ return { alg: "ML-DSA-44", kty: "AKP", publicKeyLength: 1_312, use: "sig" };
21
+ case "ML-DSA-65":
22
+ return { alg: "ML-DSA-65", kty: "AKP", publicKeyLength: 1_952, use: "sig" };
23
+ case "ML-DSA-87":
24
+ return { alg: "ML-DSA-87", kty: "AKP", publicKeyLength: 2_592, use: "sig" };
25
+ case "ML-KEM-512":
26
+ return { alg: "ML-KEM-512", kty: "AKP", publicKeyLength: 800, use: "enc" };
27
+ case "ML-KEM-768":
28
+ return { alg: "ML-KEM-768", kty: "AKP", publicKeyLength: 1_184, use: "enc" };
29
+ case "ML-KEM-1024":
30
+ return { alg: "ML-KEM-1024", kty: "AKP", publicKeyLength: 1_568, use: "enc" };
31
+ case "SLH-DSA-SHA2-128s":
32
+ return { alg: "SLH-DSA-SHA2-128s", kty: "AKP", publicKeyLength: 32, use: "sig" };
33
+ case "X-Wing-768":
34
+ 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
+ }
38
+ };
39
+ const ensureLength = (bytes, expectedLength) => {
40
+ if (bytes.length !== expectedLength) {
41
+ throw new ReallyMeCryptoError("invalid-input");
42
+ }
43
+ };
44
+ const byteAt = (bytes, index) => {
45
+ const value = bytes[index];
46
+ if (value === undefined) {
47
+ throw new ReallyMeCryptoError("invalid-input");
48
+ }
49
+ return value;
50
+ };
51
+ const base64urlEncode = (bytes) => {
52
+ let encoded = "";
53
+ let index = 0;
54
+ while (index < bytes.length) {
55
+ const first = byteAt(bytes, index);
56
+ const second = index + 1 < bytes.length ? byteAt(bytes, index + 1) : 0;
57
+ const third = index + 2 < bytes.length ? byteAt(bytes, index + 2) : 0;
58
+ const triple = (first << 16) | (second << 8) | third;
59
+ encoded += BASE64URL_ALPHABET[(triple >> 18) & 0x3f];
60
+ encoded += BASE64URL_ALPHABET[(triple >> 12) & 0x3f];
61
+ if (index + 1 < bytes.length) {
62
+ encoded += BASE64URL_ALPHABET[(triple >> 6) & 0x3f];
63
+ }
64
+ if (index + 2 < bytes.length) {
65
+ encoded += BASE64URL_ALPHABET[triple & 0x3f];
66
+ }
67
+ index += 3;
68
+ }
69
+ return encoded;
70
+ };
71
+ const decodeChar = (char) => {
72
+ const value = DECODE_TABLE.get(char);
73
+ if (value === undefined) {
74
+ throw new ReallyMeCryptoError("invalid-input");
75
+ }
76
+ return value;
77
+ };
78
+ const base64urlDecode = (encoded) => {
79
+ if (encoded.length % 4 === 1) {
80
+ throw new ReallyMeCryptoError("invalid-input");
81
+ }
82
+ const outputLength = Math.floor((encoded.length * 6) / 8);
83
+ const output = new Uint8Array(outputLength);
84
+ let buffer = 0;
85
+ let bits = 0;
86
+ let offset = 0;
87
+ for (const char of encoded) {
88
+ buffer = (buffer << 6) | decodeChar(char);
89
+ bits += 6;
90
+ if (bits >= 8) {
91
+ bits -= 8;
92
+ output[offset] = (buffer >> bits) & 0xff;
93
+ offset += 1;
94
+ }
95
+ }
96
+ if (offset !== output.length) {
97
+ throw new ReallyMeCryptoError("invalid-input");
98
+ }
99
+ return output;
100
+ };
101
+ const readString = (value, name) => {
102
+ if (typeof value !== "object" || value === null) {
103
+ throw new ReallyMeCryptoError("invalid-input");
104
+ }
105
+ const property = Reflect.get(value, name);
106
+ if (typeof property !== "string") {
107
+ throw new ReallyMeCryptoError("invalid-input");
108
+ }
109
+ return property;
110
+ };
111
+ const optionalStringMatches = (value, name, expected) => {
112
+ if (typeof value !== "object" || value === null) {
113
+ throw new ReallyMeCryptoError("invalid-input");
114
+ }
115
+ const property = Reflect.get(value, name);
116
+ if (property !== undefined && property !== expected) {
117
+ throw new ReallyMeCryptoError("invalid-input");
118
+ }
119
+ };
120
+ const specFromCurve = (curve) => {
121
+ switch (curve) {
122
+ case "Ed25519":
123
+ case "X25519":
124
+ case "P-256":
125
+ case "secp256k1":
126
+ return jwkSpec(curve);
127
+ default:
128
+ throw new ReallyMeCryptoError("unsupported-algorithm");
129
+ }
130
+ };
131
+ const specFromAlgorithm = (algorithm) => {
132
+ switch (algorithm) {
133
+ case "ML-DSA-44":
134
+ case "ML-DSA-65":
135
+ case "ML-DSA-87":
136
+ case "ML-KEM-512":
137
+ case "ML-KEM-768":
138
+ case "ML-KEM-1024":
139
+ case "SLH-DSA-SHA2-128s":
140
+ case "X-Wing-768":
141
+ case "X-Wing-1024":
142
+ return jwkSpec(algorithm);
143
+ default:
144
+ throw new ReallyMeCryptoError("unsupported-algorithm");
145
+ }
146
+ };
147
+ const decompressEc = (algorithm, publicKey) => {
148
+ try {
149
+ const point = algorithm === "P-256"
150
+ ? p256.Point.fromBytes(publicKey)
151
+ : secp256k1.Point.fromBytes(publicKey);
152
+ return point.toBytes(false);
153
+ }
154
+ catch {
155
+ throw new ReallyMeCryptoError("invalid-input");
156
+ }
157
+ };
158
+ const compressEc = (algorithm, x, y) => {
159
+ ensureLength(x, 32);
160
+ ensureLength(y, 32);
161
+ const uncompressed = new Uint8Array(65);
162
+ uncompressed[0] = 0x04;
163
+ uncompressed.set(x, 1);
164
+ uncompressed.set(y, 33);
165
+ try {
166
+ const point = algorithm === "P-256"
167
+ ? p256.Point.fromBytes(uncompressed)
168
+ : secp256k1.Point.fromBytes(uncompressed);
169
+ return point.toBytes(true);
170
+ }
171
+ catch {
172
+ throw new ReallyMeCryptoError("invalid-input");
173
+ }
174
+ };
175
+ const toJcs = (jwk) => {
176
+ if (jwk.kty === "EC") {
177
+ return `{"alg":${JSON.stringify(jwk.alg)},"crv":${JSON.stringify(jwk.crv)},"kty":"EC","use":"sig","x":${JSON.stringify(jwk.x)},"y":${JSON.stringify(jwk.y)}}`;
178
+ }
179
+ if (jwk.kty === "AKP") {
180
+ return `{"alg":${JSON.stringify(jwk.alg)},"kty":"AKP","pub":${JSON.stringify(jwk.pub)},"use":${JSON.stringify(jwk.use)}}`;
181
+ }
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
+ };
184
+ export const ReallyMeJwk = {
185
+ toJwk(algorithm, publicKey) {
186
+ const spec = jwkSpec(algorithm);
187
+ ensureLength(publicKey, spec.publicKeyLength);
188
+ if (spec.kty === "EC") {
189
+ const ecAlgorithm = spec.crv === "P-256" ? "P-256" : "secp256k1";
190
+ const uncompressed = decompressEc(ecAlgorithm, publicKey);
191
+ return {
192
+ alg: spec.alg,
193
+ crv: ecAlgorithm,
194
+ kty: "EC",
195
+ use: "sig",
196
+ x: base64urlEncode(uncompressed.slice(1, 33)),
197
+ y: base64urlEncode(uncompressed.slice(33, 65)),
198
+ };
199
+ }
200
+ if (spec.kty === "AKP") {
201
+ return {
202
+ alg: spec.alg,
203
+ kty: "AKP",
204
+ pub: base64urlEncode(publicKey),
205
+ use: spec.use,
206
+ };
207
+ }
208
+ const crv = spec.crv;
209
+ if (crv !== "Ed25519" && crv !== "X25519") {
210
+ throw new ReallyMeCryptoError("unsupported-algorithm");
211
+ }
212
+ return {
213
+ alg: spec.alg,
214
+ crv,
215
+ kty: "OKP",
216
+ use: spec.use,
217
+ x: base64urlEncode(publicKey),
218
+ };
219
+ },
220
+ fromJwk(value) {
221
+ const kty = readString(value, "kty");
222
+ const spec = kty === "AKP"
223
+ ? specFromAlgorithm(readString(value, "alg"))
224
+ : specFromCurve(readString(value, "crv"));
225
+ if (kty !== spec.kty) {
226
+ throw new ReallyMeCryptoError("invalid-input");
227
+ }
228
+ optionalStringMatches(value, "alg", spec.alg);
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");
243
+ }
244
+ const publicKey = base64urlDecode(readString(value, "x"));
245
+ ensureLength(publicKey, spec.publicKeyLength);
246
+ const jwk = ReallyMeJwk.toJwk(crv, publicKey);
247
+ return { algorithm: crv, publicKey, jwk };
248
+ }
249
+ const algorithm = readString(value, "alg");
250
+ const akpSpec = specFromAlgorithm(algorithm);
251
+ const publicKey = base64urlDecode(readString(value, "pub"));
252
+ ensureLength(publicKey, akpSpec.publicKeyLength);
253
+ switch (algorithm) {
254
+ case "ML-DSA-44":
255
+ case "ML-DSA-65":
256
+ case "ML-DSA-87":
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 };
265
+ }
266
+ default:
267
+ throw new ReallyMeCryptoError("unsupported-algorithm");
268
+ }
269
+ },
270
+ toJwks(keys) {
271
+ return { keys: [...keys] };
272
+ },
273
+ fromJwks(value) {
274
+ if (typeof value !== "object" || value === null) {
275
+ throw new ReallyMeCryptoError("invalid-input");
276
+ }
277
+ const keys = Reflect.get(value, "keys");
278
+ if (!Array.isArray(keys)) {
279
+ throw new ReallyMeCryptoError("invalid-input");
280
+ }
281
+ return { keys: keys.map((key) => ReallyMeJwk.fromJwk(key)) };
282
+ },
283
+ publicKeyBytes(jwk) {
284
+ return ReallyMeJwk.fromJwk(jwk).publicKey;
285
+ },
286
+ toJcs(jwk) {
287
+ return toJcs(jwk);
288
+ },
289
+ };
@@ -0,0 +1,18 @@
1
+ import type { ReallyMeSignatureAlgorithm } from "./algorithms.js";
2
+ export declare const ML_DSA_44_PUBLIC_KEY_LENGTH = 1312;
3
+ export declare const ML_DSA_44_SIGNATURE_LENGTH = 2420;
4
+ export declare const ML_DSA_65_PUBLIC_KEY_LENGTH = 1952;
5
+ export declare const ML_DSA_65_SIGNATURE_LENGTH = 3309;
6
+ export declare const ML_DSA_87_PUBLIC_KEY_LENGTH = 2592;
7
+ export declare const ML_DSA_87_SIGNATURE_LENGTH = 4627;
8
+ export declare const ML_DSA_SECRET_KEY_LENGTH = 32;
9
+ export type ReallyMeMlDsaKeyPair = Readonly<{
10
+ publicKey: Uint8Array;
11
+ secretKey: Uint8Array;
12
+ }>;
13
+ export declare const ReallyMeMlDsa: {
14
+ readonly generateKeyPair: (algorithm: ReallyMeSignatureAlgorithm) => ReallyMeMlDsaKeyPair;
15
+ readonly deriveKeyPair: (algorithm: ReallyMeSignatureAlgorithm, secretKey: Uint8Array) => ReallyMeMlDsaKeyPair;
16
+ readonly sign: (algorithm: ReallyMeSignatureAlgorithm, message: Uint8Array, secretKey: Uint8Array) => Uint8Array;
17
+ readonly verify: (algorithm: ReallyMeSignatureAlgorithm, signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array) => void;
18
+ };
package/dist/mlDsa.js ADDED
@@ -0,0 +1,85 @@
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 { ensureBytes, readByteArrayProperty } from "./validateBytes.js";
6
+ import { requireReallyMeWasmProvider } from "./wasmProvider.js";
7
+ export const ML_DSA_44_PUBLIC_KEY_LENGTH = 1_312;
8
+ export const ML_DSA_44_SIGNATURE_LENGTH = 2_420;
9
+ export const ML_DSA_65_PUBLIC_KEY_LENGTH = 1_952;
10
+ export const ML_DSA_65_SIGNATURE_LENGTH = 3_309;
11
+ export const ML_DSA_87_PUBLIC_KEY_LENGTH = 2_592;
12
+ export const ML_DSA_87_SIGNATURE_LENGTH = 4_627;
13
+ export const ML_DSA_SECRET_KEY_LENGTH = 32;
14
+ const mlDsaSuite = (algorithm) => {
15
+ const provider = requireReallyMeWasmProvider();
16
+ switch (algorithm) {
17
+ case "ML-DSA-44":
18
+ return {
19
+ publicKeyLength: ML_DSA_44_PUBLIC_KEY_LENGTH,
20
+ signatureLength: ML_DSA_44_SIGNATURE_LENGTH,
21
+ generateKeyPair: provider.mlDsa44GenerateKeypair,
22
+ deriveKeyPair: provider.mlDsa44DeriveKeypair,
23
+ sign: provider.mlDsa44Sign,
24
+ verify: provider.mlDsa44Verify,
25
+ };
26
+ case "ML-DSA-65":
27
+ return {
28
+ publicKeyLength: ML_DSA_65_PUBLIC_KEY_LENGTH,
29
+ signatureLength: ML_DSA_65_SIGNATURE_LENGTH,
30
+ generateKeyPair: provider.mlDsa65GenerateKeypair,
31
+ deriveKeyPair: provider.mlDsa65DeriveKeypair,
32
+ sign: provider.mlDsa65Sign,
33
+ verify: provider.mlDsa65Verify,
34
+ };
35
+ case "ML-DSA-87":
36
+ return {
37
+ publicKeyLength: ML_DSA_87_PUBLIC_KEY_LENGTH,
38
+ signatureLength: ML_DSA_87_SIGNATURE_LENGTH,
39
+ generateKeyPair: provider.mlDsa87GenerateKeypair,
40
+ deriveKeyPair: provider.mlDsa87DeriveKeypair,
41
+ sign: provider.mlDsa87Sign,
42
+ verify: provider.mlDsa87Verify,
43
+ };
44
+ default:
45
+ throw new ReallyMeCryptoError("unsupported-algorithm");
46
+ }
47
+ };
48
+ const readKeyPair = (value, suite) => ({
49
+ publicKey: readByteArrayProperty(value, "publicKey", suite.publicKeyLength),
50
+ secretKey: readByteArrayProperty(value, "secretKey", ML_DSA_SECRET_KEY_LENGTH),
51
+ });
52
+ const readSignature = (value, suite) => {
53
+ if (!(value instanceof Uint8Array)) {
54
+ throw new ReallyMeCryptoError("provider-failure");
55
+ }
56
+ ensureBytes(value, suite.signatureLength);
57
+ return value;
58
+ };
59
+ const readVoid = (value) => {
60
+ if (value !== undefined) {
61
+ throw new ReallyMeCryptoError("provider-failure");
62
+ }
63
+ };
64
+ export const ReallyMeMlDsa = {
65
+ generateKeyPair(algorithm) {
66
+ const suite = mlDsaSuite(algorithm);
67
+ return readKeyPair(suite.generateKeyPair(), suite);
68
+ },
69
+ deriveKeyPair(algorithm, secretKey) {
70
+ const suite = mlDsaSuite(algorithm);
71
+ ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
72
+ return readKeyPair(suite.deriveKeyPair(secretKey), suite);
73
+ },
74
+ sign(algorithm, message, secretKey) {
75
+ const suite = mlDsaSuite(algorithm);
76
+ ensureBytes(secretKey, ML_DSA_SECRET_KEY_LENGTH);
77
+ return readSignature(suite.sign(secretKey, message), suite);
78
+ },
79
+ verify(algorithm, signature, message, publicKey) {
80
+ const suite = mlDsaSuite(algorithm);
81
+ ensureBytes(publicKey, suite.publicKeyLength);
82
+ ensureBytes(signature, suite.signatureLength);
83
+ readVoid(suite.verify(publicKey, message, signature));
84
+ },
85
+ };
@@ -0,0 +1,25 @@
1
+ import type { ReallyMeKemAlgorithm } from "./algorithms.js";
2
+ export declare const ML_KEM_512_PUBLIC_KEY_LENGTH = 800;
3
+ export declare const ML_KEM_512_CIPHERTEXT_LENGTH = 768;
4
+ export declare const ML_KEM_768_PUBLIC_KEY_LENGTH = 1184;
5
+ export declare const ML_KEM_768_CIPHERTEXT_LENGTH = 1088;
6
+ export declare const ML_KEM_1024_PUBLIC_KEY_LENGTH = 1568;
7
+ export declare const ML_KEM_1024_CIPHERTEXT_LENGTH = 1568;
8
+ export declare const ML_KEM_SECRET_KEY_LENGTH = 64;
9
+ export declare const ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH = 32;
10
+ export declare const ML_KEM_SHARED_SECRET_LENGTH = 32;
11
+ export type ReallyMeMlKemKeyPair = Readonly<{
12
+ publicKey: Uint8Array;
13
+ secretKey: Uint8Array;
14
+ }>;
15
+ export type ReallyMeMlKemEncapsulation = Readonly<{
16
+ ciphertext: Uint8Array;
17
+ sharedSecret: Uint8Array;
18
+ }>;
19
+ export declare const ReallyMeMlKem: {
20
+ readonly generateKeyPair: (algorithm: ReallyMeKemAlgorithm) => ReallyMeMlKemKeyPair;
21
+ readonly deriveKeyPair: (algorithm: ReallyMeKemAlgorithm, secretKey: Uint8Array) => ReallyMeMlKemKeyPair;
22
+ readonly encapsulate: (algorithm: ReallyMeKemAlgorithm, publicKey: Uint8Array) => ReallyMeMlKemEncapsulation;
23
+ readonly encapsulateDeterministicallyForTest: (algorithm: ReallyMeKemAlgorithm, publicKey: Uint8Array, randomness: Uint8Array) => ReallyMeMlKemEncapsulation;
24
+ readonly decapsulate: (algorithm: ReallyMeKemAlgorithm, ciphertext: Uint8Array, secretKey: Uint8Array) => Uint8Array;
25
+ };
package/dist/mlKem.js ADDED
@@ -0,0 +1,95 @@
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 { ensureBytes, readByteArrayProperty } from "./validateBytes.js";
6
+ import { requireReallyMeWasmProvider } from "./wasmProvider.js";
7
+ export const ML_KEM_512_PUBLIC_KEY_LENGTH = 800;
8
+ export const ML_KEM_512_CIPHERTEXT_LENGTH = 768;
9
+ export const ML_KEM_768_PUBLIC_KEY_LENGTH = 1_184;
10
+ export const ML_KEM_768_CIPHERTEXT_LENGTH = 1_088;
11
+ export const ML_KEM_1024_PUBLIC_KEY_LENGTH = 1_568;
12
+ export const ML_KEM_1024_CIPHERTEXT_LENGTH = 1_568;
13
+ export const ML_KEM_SECRET_KEY_LENGTH = 64;
14
+ export const ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH = 32;
15
+ export const ML_KEM_SHARED_SECRET_LENGTH = 32;
16
+ const mlKemSuite = (algorithm) => {
17
+ const provider = requireReallyMeWasmProvider();
18
+ switch (algorithm) {
19
+ case "ML-KEM-512":
20
+ return {
21
+ publicKeyLength: ML_KEM_512_PUBLIC_KEY_LENGTH,
22
+ ciphertextLength: ML_KEM_512_CIPHERTEXT_LENGTH,
23
+ generateKeyPair: provider.mlKem512GenerateKeypair,
24
+ deriveKeyPair: provider.mlKem512DeriveKeypair,
25
+ encapsulate: provider.mlKem512Encapsulate,
26
+ encapsulateDerand: provider.mlKem512EncapsulateDerand,
27
+ decapsulate: provider.mlKem512Decapsulate,
28
+ };
29
+ case "ML-KEM-768":
30
+ return {
31
+ publicKeyLength: ML_KEM_768_PUBLIC_KEY_LENGTH,
32
+ ciphertextLength: ML_KEM_768_CIPHERTEXT_LENGTH,
33
+ generateKeyPair: provider.mlKem768GenerateKeypair,
34
+ deriveKeyPair: provider.mlKem768DeriveKeypair,
35
+ encapsulate: provider.mlKem768Encapsulate,
36
+ encapsulateDerand: provider.mlKem768EncapsulateDerand,
37
+ decapsulate: provider.mlKem768Decapsulate,
38
+ };
39
+ case "ML-KEM-1024":
40
+ return {
41
+ publicKeyLength: ML_KEM_1024_PUBLIC_KEY_LENGTH,
42
+ ciphertextLength: ML_KEM_1024_CIPHERTEXT_LENGTH,
43
+ generateKeyPair: provider.mlKem1024GenerateKeypair,
44
+ deriveKeyPair: provider.mlKem1024DeriveKeypair,
45
+ encapsulate: provider.mlKem1024Encapsulate,
46
+ encapsulateDerand: provider.mlKem1024EncapsulateDerand,
47
+ decapsulate: provider.mlKem1024Decapsulate,
48
+ };
49
+ default:
50
+ throw new ReallyMeCryptoError("unsupported-algorithm");
51
+ }
52
+ };
53
+ const readKeyPair = (value, suite) => ({
54
+ publicKey: readByteArrayProperty(value, "publicKey", suite.publicKeyLength),
55
+ secretKey: readByteArrayProperty(value, "secretKey", ML_KEM_SECRET_KEY_LENGTH),
56
+ });
57
+ const readEncapsulation = (value, suite) => ({
58
+ ciphertext: readByteArrayProperty(value, "ciphertext", suite.ciphertextLength),
59
+ sharedSecret: readByteArrayProperty(value, "sharedSecret", ML_KEM_SHARED_SECRET_LENGTH),
60
+ });
61
+ const readSharedSecret = (value) => {
62
+ if (!(value instanceof Uint8Array)) {
63
+ throw new ReallyMeCryptoError("provider-failure");
64
+ }
65
+ ensureBytes(value, ML_KEM_SHARED_SECRET_LENGTH);
66
+ return value;
67
+ };
68
+ export const ReallyMeMlKem = {
69
+ generateKeyPair(algorithm) {
70
+ const suite = mlKemSuite(algorithm);
71
+ return readKeyPair(suite.generateKeyPair(), suite);
72
+ },
73
+ deriveKeyPair(algorithm, secretKey) {
74
+ const suite = mlKemSuite(algorithm);
75
+ ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
76
+ return readKeyPair(suite.deriveKeyPair(secretKey), suite);
77
+ },
78
+ encapsulate(algorithm, publicKey) {
79
+ const suite = mlKemSuite(algorithm);
80
+ ensureBytes(publicKey, suite.publicKeyLength);
81
+ return readEncapsulation(suite.encapsulate(publicKey), suite);
82
+ },
83
+ encapsulateDeterministicallyForTest(algorithm, publicKey, randomness) {
84
+ const suite = mlKemSuite(algorithm);
85
+ ensureBytes(publicKey, suite.publicKeyLength);
86
+ ensureBytes(randomness, ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH);
87
+ return readEncapsulation(suite.encapsulateDerand(publicKey, randomness), suite);
88
+ },
89
+ decapsulate(algorithm, ciphertext, secretKey) {
90
+ const suite = mlKemSuite(algorithm);
91
+ ensureBytes(ciphertext, suite.ciphertextLength);
92
+ ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
93
+ return readSharedSecret(suite.decapsulate(ciphertext, secretKey));
94
+ },
95
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * P-256 ECDH backed by @noble/curves.
3
+ *
4
+ * Public keys at the SDK boundary are compressed SEC1. The primitive returns
5
+ * the raw 32-byte ECDH x-coordinate; protocols must apply their own labelled
6
+ * KDF rather than relying on this low-level helper to choose one.
7
+ */
8
+ export declare const P256_ECDH_SECRET_KEY_LENGTH = 32;
9
+ export declare const P256_ECDH_COMPRESSED_PUBLIC_KEY_LENGTH = 33;
10
+ export declare const P256_ECDH_SHARED_SECRET_LENGTH = 32;
11
+ export declare const ReallyMeP256Ecdh: {
12
+ readonly generateKeyPair: () => {
13
+ publicKey: Uint8Array;
14
+ secretKey: Uint8Array;
15
+ };
16
+ readonly deriveKeyPair: (secretKey: Uint8Array) => {
17
+ publicKey: Uint8Array;
18
+ secretKey: Uint8Array;
19
+ };
20
+ readonly derivePublicKey: (secretKey: Uint8Array) => Uint8Array;
21
+ readonly deriveSharedSecret: (publicKey: Uint8Array, secretKey: Uint8Array) => Uint8Array;
22
+ };
@@ -0,0 +1,53 @@
1
+ // SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import { p256 } from "@noble/curves/nist.js";
5
+ import { ReallyMeCryptoError } from "./errors.js";
6
+ /**
7
+ * P-256 ECDH backed by @noble/curves.
8
+ *
9
+ * Public keys at the SDK boundary are compressed SEC1. The primitive returns
10
+ * the raw 32-byte ECDH x-coordinate; protocols must apply their own labelled
11
+ * KDF rather than relying on this low-level helper to choose one.
12
+ */
13
+ export const P256_ECDH_SECRET_KEY_LENGTH = 32;
14
+ export const P256_ECDH_COMPRESSED_PUBLIC_KEY_LENGTH = 33;
15
+ export const P256_ECDH_SHARED_SECRET_LENGTH = 32;
16
+ export const ReallyMeP256Ecdh = {
17
+ generateKeyPair() {
18
+ const secretKey = p256.utils.randomSecretKey();
19
+ return {
20
+ publicKey: p256.getPublicKey(secretKey, true),
21
+ secretKey,
22
+ };
23
+ },
24
+ deriveKeyPair(secretKey) {
25
+ return {
26
+ publicKey: this.derivePublicKey(secretKey),
27
+ secretKey: secretKey.slice(),
28
+ };
29
+ },
30
+ derivePublicKey(secretKey) {
31
+ if (secretKey.length !== P256_ECDH_SECRET_KEY_LENGTH) {
32
+ throw new ReallyMeCryptoError("invalid-input");
33
+ }
34
+ try {
35
+ return p256.getPublicKey(secretKey, true);
36
+ }
37
+ catch {
38
+ throw new ReallyMeCryptoError("invalid-input");
39
+ }
40
+ },
41
+ deriveSharedSecret(publicKey, secretKey) {
42
+ if (publicKey.length !== P256_ECDH_COMPRESSED_PUBLIC_KEY_LENGTH ||
43
+ secretKey.length !== P256_ECDH_SECRET_KEY_LENGTH) {
44
+ throw new ReallyMeCryptoError("invalid-input");
45
+ }
46
+ try {
47
+ return p256.getSharedSecret(secretKey, publicKey, false).slice(1, 33);
48
+ }
49
+ catch {
50
+ throw new ReallyMeCryptoError("invalid-input");
51
+ }
52
+ },
53
+ };
@@ -0,0 +1,25 @@
1
+ export declare const P256_ECDSA_SECRET_KEY_LENGTH = 32;
2
+ export declare const P256_ECDSA_COMPRESSED_PUBLIC_KEY_LENGTH = 33;
3
+ export declare const P256_ECDSA_COMPACT_SIGNATURE_LENGTH = 64;
4
+ export declare const P256_ECDSA_DER_SIGNATURE_MAX_LENGTH = 72;
5
+ /**
6
+ * P-256 ECDSA backed by @noble/curves.
7
+ *
8
+ * The workspace contract signs SHA-256(message) exactly once and uses DER
9
+ * encoding for signatures because that is what X.509, JOSE, and the Rust
10
+ * P-256 lane expose. We intentionally preserve the Rust vector bytes instead
11
+ * of applying a TypeScript-only low-S normalization policy.
12
+ */
13
+ export declare const ReallyMeP256Ecdsa: {
14
+ readonly generateKeyPair: () => {
15
+ publicKey: Uint8Array;
16
+ secretKey: Uint8Array;
17
+ };
18
+ readonly deriveKeyPair: (secretKey: Uint8Array) => {
19
+ publicKey: Uint8Array;
20
+ secretKey: Uint8Array;
21
+ };
22
+ readonly derivePublicKey: (secretKey: Uint8Array) => Uint8Array;
23
+ readonly sign: (message: Uint8Array, secretKey: Uint8Array) => Uint8Array;
24
+ readonly verify: (signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array) => void;
25
+ };