@1auth/crypto 0.0.0-alpha.68 → 0.0.0-alpha.69

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 (2) hide show
  1. package/index.js +615 -0
  2. package/package.json +1 -1
package/index.js ADDED
@@ -0,0 +1,615 @@
1
+ import { promisify } from "node:util";
2
+ import {
3
+ randomBytes,
4
+ randomInt,
5
+ createHash,
6
+ createCipheriv,
7
+ createDecipheriv,
8
+ createHmac,
9
+ timingSafeEqual,
10
+ generateKeyPair as generateKeyPairCallback,
11
+ sign as signCallback,
12
+ verify as verifyCallback,
13
+ } from "node:crypto";
14
+ // https://github.com/napi-rs/node-rs/tree/main/packages/argon2
15
+ import { hash as secretHash, verify as secretVerify } from "@node-rs/argon2";
16
+ import { customAlphabet } from "nanoid";
17
+
18
+ const generateKeyPair = promisify(generateKeyPairCallback);
19
+ const sign = promisify(signCallback);
20
+ const verify = promisify(verifyCallback);
21
+
22
+ const defaults = {
23
+ symmetricEncryptionKey: undefined, // symmetricRandomEncryptionKey()
24
+ symmetricEncryptionAlgorithm: "chacha20-poly1305", // 2025-03: AES-256 GCM (aes-256-gcm) or ChaCha20-Poly1305 (chacha20-poly1305)
25
+ symmetricEncryptionEncoding: undefined, // https://nodejs.org/api/buffer.html#buffers-and-character-encodings
26
+ symmetricSignatureHashAlgorithm: undefined, // fallback to defaultHashAlgorithm
27
+ symmetricSignatureSecret: undefined, // symmetricRandomSignatureSecret()
28
+ symmetricSignatureEncoding: undefined, // fallback to defaultEncoding
29
+ asymmetricKeyNamedCurve: "P-384", // P-512
30
+ asymmetricSignatureHashAlgorithm: undefined, // fallback to defaultHashAlgorithm
31
+ asymmetricSignatureEncoding: undefined, // fallback to defaultEncoding
32
+ digestChecksumHashAlgorithm: undefined, // fallback to defaultHashAlgorithm
33
+ digestChecksumEncoding: undefined,
34
+ digestChecksumSalt: undefined, // randomChecksumSalt()
35
+ digestChecksumPepper: undefined, // randomChecksumPepper()
36
+ defaultEncoding: "base64",
37
+ defaultHashAlgorithm: "sha3-384",
38
+ };
39
+ const symmetricEncryptionEncodingLengths = {};
40
+ const options = {};
41
+ export default (opt = {}) => {
42
+ Object.assign(options, defaults, opt);
43
+
44
+ // Check options, set defaults
45
+ if (!options.symmetricEncryptionKey) {
46
+ throw new Error(
47
+ "@1auth/crypto symmetricEncryptionKey is empty, use a stored secret made from randomBytes(32) Encryption disabled.",
48
+ );
49
+ }
50
+ options.symmetricEncryptionEncoding ??= options.defaultEncoding;
51
+ options.symmetricSignatureHashAlgorithm ??= options.defaultHashAlgorithm;
52
+ if (!options.symmetricSignatureSecret) {
53
+ throw new Error(
54
+ "@1auth/crypto symmetricSignatureSecret is empty, use a stored secret made from randomBytes(32) Signature disabled.",
55
+ );
56
+ }
57
+ options.symmetricSignatureEncoding ??= options.defaultEncoding;
58
+ options.asymmetricSignatureHashAlgorithm ??= options.defaultHashAlgorithm;
59
+ options.asymmetricSignatureEncoding ??= options.defaultEncoding;
60
+ if (!options.digestChecksumSalt) {
61
+ throw new Error(
62
+ "@1auth/crypto digestChecksumSalt is empty, use a stored secret made from randomBytes(32) Checksum salting disabled.",
63
+ );
64
+ }
65
+ if (!options.digestChecksumPepper) {
66
+ throw new Error(
67
+ "@1auth/crypto digestChecksumPepper is empty, use a stored secret made from randomBytes(12) Checksum peppering disabled.",
68
+ );
69
+ }
70
+ options.digestChecksumHashAlgorithm ??= options.defaultHashAlgorithm;
71
+ options.digestChecksumEncoding ??= options.defaultEncoding;
72
+
73
+ // Lengths
74
+ symmetricEncryptionEncodingLengths.iv = randomIV().toString(
75
+ options.symmetricEncryptionEncoding,
76
+ ).length;
77
+ symmetricEncryptionEncodingLengths.ivAndAuthTag =
78
+ symmetricEncryptionEncodingLengths.iv +
79
+ randomBytes(16).toString(options.symmetricEncryptionEncoding).length;
80
+ };
81
+
82
+ export const getOptions = () => options;
83
+
84
+ // *** entropy *** //
85
+ // export const characterPoolSize = (value) => {
86
+ // const chars = value.split('')
87
+ // let min = chars[0].charCodeAt()
88
+ // let max = chars[0].charCodeAt()
89
+ // for(const char of value.split('')) {
90
+ // const code = char.charCodeAt()
91
+ // if (code < min) {
92
+ // min = code
93
+ // } else if (max < code) {
94
+ // max = code
95
+ // }
96
+ // }
97
+ // return max - min
98
+ // }
99
+
100
+ // *** Helpers *** //
101
+ // Ref: https://therootcompany.com/blog/how-many-bits-of-entropy-per-character/
102
+ export const entropyToCharacterLength = (bits, characterPoolSize) => {
103
+ // bits*ln(2)/ln(characterPoolSize)
104
+ return Math.ceil((bits * Math.LN2) / Math.log(characterPoolSize));
105
+ };
106
+ /* export const characterLengthToEntropy = (
107
+ characterLength,
108
+ characterPoolSize,
109
+ ) => {
110
+ // log_2(characterPoolSize^characterLength)
111
+ return Math.floor(Math.log2(characterPoolSize ** characterLength));
112
+ }; */
113
+
114
+ // *** Random generators *** //
115
+ export { randomBytes, randomInt, randomUUID } from "node:crypto";
116
+
117
+ export const charactersNumeric = "0123456789";
118
+ export const charactersAlphaUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
119
+ export const charactersAlphaLower = "abcdefghijklmnopqrstuvwxyz";
120
+ export const charactersAlpha = charactersAlphaUpper + charactersAlphaLower;
121
+ export const charactersAlphaNumeric = charactersAlpha + charactersNumeric;
122
+ export const charactersDistinguishable = "CDEHKMPRTUWXY012458";
123
+
124
+ const randomCharactersCache = {
125
+ charactersAlphaNumeric: customAlphabet(charactersAlphaNumeric),
126
+ };
127
+ export const randomCharacters = (
128
+ length,
129
+ characters = charactersAlphaNumeric,
130
+ ) => {
131
+ randomCharactersCache[characters] ??= customAlphabet(characters);
132
+ return randomCharactersCache[characters](length);
133
+ };
134
+
135
+ export const randomAlphaNumeric = (characterLength) => {
136
+ return randomCharacters(characterLength, charactersAlphaNumeric);
137
+ };
138
+
139
+ export const randomNumeric = (characterLength) => {
140
+ let value = "";
141
+ for (let i = characterLength; i--; ) {
142
+ value += randomInt(9);
143
+ }
144
+ return value;
145
+ };
146
+
147
+ // *** configs *** //
148
+ // Input: {id, prefix, entropy, characters, opt, expire}
149
+ // Output: {id, type, opt, expire, create, ...}
150
+ export const makeRandomConfigObject = ({
151
+ id,
152
+ prefix = "",
153
+ entropy = 64,
154
+ characters = charactersAlphaNumeric,
155
+ ...params
156
+ } = {}) => {
157
+ const minLength = entropyToCharacterLength(entropy, characters.length);
158
+ const config = {
159
+ id,
160
+ type: "id",
161
+ create: () => prefix + randomCharacters(minLength, characters),
162
+ ...params,
163
+ };
164
+ return config;
165
+ };
166
+
167
+ // *** Digests *** //
168
+ export const randomChecksumSalt = () => {
169
+ return randomBytes(32); // 256 bits
170
+ };
171
+ export const randomChecksumPepper = () => {
172
+ return randomIV(); // 96
173
+ };
174
+
175
+ export const createSaltedValue = (value, { checksumSalt } = {}) => {
176
+ checksumSalt ??= options.digestChecksumSalt;
177
+ if (!checksumSalt) {
178
+ return value;
179
+ }
180
+ const newValue = value + checksumSalt;
181
+ return newValue;
182
+ };
183
+ export const createPepperedValue = (
184
+ value,
185
+ { checksumPepper, encryptionKey } = {},
186
+ ) => {
187
+ checksumPepper ??= options.digestChecksumPepper;
188
+ encryptionKey ??= options.symmetricEncryptionKey;
189
+ if (!checksumPepper || !encryptionKey) {
190
+ return value;
191
+ }
192
+ const newValue = symmetricEncrypt(value, {
193
+ encryptionKey,
194
+ sub: "",
195
+ iv: checksumPepper,
196
+ });
197
+ return newValue;
198
+ };
199
+
200
+ export const createChecksum = (value, { hashAlgorithm, encoding } = {}) => {
201
+ hashAlgorithm ??= options.digestChecksumHashAlgorithm;
202
+ encoding ??= options.digestChecksumEncoding;
203
+ return createHash(hashAlgorithm).update(value).digest(encoding);
204
+ };
205
+ export const createSeasonedChecksum = (
206
+ value,
207
+ { hashAlgorithm, encoding, checksumSalt, checksumPepper } = {},
208
+ ) => {
209
+ return createChecksum(
210
+ createPepperedValue(createSaltedValue(value, { checksumSalt }), {
211
+ checksumPepper,
212
+ }),
213
+ {
214
+ hashAlgorithm,
215
+ encoding,
216
+ },
217
+ );
218
+ };
219
+
220
+ export const createDigest = (value, { hashAlgorithm, encoding } = {}) => {
221
+ hashAlgorithm ??= options.digestChecksumHashAlgorithm;
222
+ const checksum = createChecksum(value, { hashAlgorithm, encoding });
223
+ return `${hashAlgorithm}:${checksum}`;
224
+ };
225
+ export const createSaltedDigest = (
226
+ value,
227
+ { hashAlgorithm, encoding, checksumSalt } = {},
228
+ ) => {
229
+ hashAlgorithm ??= options.digestChecksumHashAlgorithm;
230
+ const checksum = createChecksum(createSaltedValue(value, { checksumSalt }), {
231
+ hashAlgorithm,
232
+ encoding,
233
+ });
234
+ return `${hashAlgorithm}:${checksum}`;
235
+ };
236
+ export const createPepperedDigest = (
237
+ value,
238
+ { hashAlgorithm, encoding, checksumPepper, encryptionKey } = {},
239
+ ) => {
240
+ hashAlgorithm ??= options.digestChecksumHashAlgorithm;
241
+ const checksum = createChecksum(
242
+ createPepperedValue(value, { checksumPepper, encryptionKey }),
243
+ {
244
+ hashAlgorithm,
245
+ encoding,
246
+ },
247
+ );
248
+ return `${hashAlgorithm}:${checksum}`;
249
+ };
250
+ export const createSeasonedDigest = (
251
+ value,
252
+ { hashAlgorithm, encoding, checksumSalt, checksumPepper, encryptionKey } = {},
253
+ ) => {
254
+ hashAlgorithm ??= options.digestChecksumHashAlgorithm;
255
+ const checksum = createSeasonedChecksum(value, {
256
+ hashAlgorithm,
257
+ encoding,
258
+ checksumSalt,
259
+ checksumPepper,
260
+ encryptionKey,
261
+ });
262
+ return `${hashAlgorithm}:${checksum}`;
263
+ };
264
+
265
+ // *** Hashing *** //
266
+ const hashOptions = {
267
+ timeCost: 3, // Default 3
268
+ memoryCost: 2 ** 15, // Default 2 ** 12 = 4MB
269
+ saltLength: 16,
270
+ parallelism: 1, // Default 1
271
+ outputLen: 64, // hashLength: 128 // Default 32
272
+ algorithm: 2, // Default 2 = Argon2id
273
+ version: 1, // Default 1 = version 19
274
+ };
275
+
276
+ export const createSecretHash = async (value, options = hashOptions) => {
277
+ return secretHash(value, options);
278
+ };
279
+
280
+ export const verifySecretHash = async (hash, value) => {
281
+ return secretVerify(hash, value);
282
+ };
283
+
284
+ // *** Symmetric Encryption *** //
285
+ const authTagLength = 16;
286
+
287
+ export const symmetricRandomEncryptionKey = () => {
288
+ return randomBytes(32); // 256 bits
289
+ };
290
+
291
+ export const randomIV = () => {
292
+ return randomBytes(12); // 96 bits
293
+ };
294
+
295
+ export const symmetricGenerateEncryptionKey = (
296
+ sub,
297
+ { encryptionKey, signatureSecret } = {},
298
+ ) => {
299
+ encryptionKey ??= options.symmetricEncryptionKey;
300
+ signatureSecret ??= options.symemeticSignatureSecret;
301
+
302
+ const rowEncryptionKey = symmetricRandomEncryptionKey();
303
+ const rowEncryptedKey = symmetricEncrypt(rowEncryptionKey, {
304
+ encryptionKey,
305
+ signatureSecret,
306
+ sub,
307
+ });
308
+ return { encryptionKey: rowEncryptionKey, encryptedKey: rowEncryptedKey };
309
+ };
310
+
311
+ // sub add context to encryption
312
+ export const symmetricEncryptFields = (
313
+ values,
314
+ { encryptedKey, encryptionKey, signatureSecret, sub },
315
+ fields = [],
316
+ ) => {
317
+ if (encryptedKey) {
318
+ encryptionKey ??= symmetricDecryptKey(encryptedKey, {
319
+ signatureSecret,
320
+ sub,
321
+ });
322
+ }
323
+ if (!encryptionKey) return values;
324
+ const encryptedValues = structuredClone(values);
325
+ for (const key of fields) {
326
+ encryptedValues[key] &&= symmetricEncrypt(encryptedValues[key], {
327
+ encryptionKey,
328
+ signatureSecret,
329
+ sub,
330
+ });
331
+ }
332
+ return encryptedValues;
333
+ };
334
+
335
+ export const symmetricEncrypt = (
336
+ data,
337
+ { encryptedKey, encryptionKey, signatureSecret, sub, decoding, encoding, iv },
338
+ ) => {
339
+ if (encryptedKey) {
340
+ encryptionKey ??= symmetricDecryptKey(encryptedKey, {
341
+ signatureSecret,
342
+ sub,
343
+ });
344
+ }
345
+ if (!encryptionKey || !data) return data;
346
+ decoding ??= "utf8";
347
+ encoding ??= options.symmetricEncryptionEncoding;
348
+ iv ??= randomIV();
349
+ const cipher = createCipheriv(
350
+ options.symmetricEncryptionAlgorithm,
351
+ encryptionKey,
352
+ iv,
353
+ {
354
+ authTagLength,
355
+ },
356
+ );
357
+ cipher.setAAD(sub);
358
+ const encryptedData =
359
+ cipher.update(data, decoding, encoding) + cipher.final(encoding);
360
+ const authTag = cipher.getAuthTag();
361
+
362
+ const encryptedDataPacket =
363
+ iv.toString(encoding) + authTag.toString(encoding) + encryptedData;
364
+
365
+ // add signature to end
366
+ return symmetricSignatureSign(encryptedDataPacket, { signatureSecret });
367
+ };
368
+
369
+ export const symmetricDecryptFields = (
370
+ encryptedValues,
371
+ { encryptedKey, encryptionKey, signatureSecret, sub },
372
+ fields = [],
373
+ ) => {
374
+ if (encryptedKey) {
375
+ encryptionKey ??= symmetricDecryptKey(encryptedKey, {
376
+ signatureSecret,
377
+ sub,
378
+ });
379
+ }
380
+ if (!encryptionKey) return encryptedValues;
381
+ const values = structuredClone(encryptedValues);
382
+ for (const key of fields) {
383
+ values[key] &&= symmetricDecrypt(values[key], {
384
+ encryptionKey,
385
+ signatureSecret,
386
+ sub,
387
+ });
388
+ }
389
+ return values;
390
+ };
391
+
392
+ export const symmetricDecryptKey = (
393
+ encryptedKey,
394
+ { sub, encryptionKey, signatureSecret } = {},
395
+ ) => {
396
+ encryptionKey ??= options.symmetricEncryptionKey;
397
+ signatureSecret ??= options.symemeticSignatureSecret;
398
+ return Buffer.from(
399
+ symmetricDecrypt(encryptedKey, {
400
+ encryptionKey,
401
+ signatureSecret,
402
+ sub,
403
+ encoding: options.symmetricEncryptionEncoding,
404
+ }),
405
+ options.symmetricEncryptionEncoding,
406
+ );
407
+ };
408
+
409
+ export const symmetricDecrypt = (
410
+ encryptedDataPacket,
411
+ { encryptedKey, encryptionKey, signatureSecret, sub, decoding, encoding },
412
+ ) => {
413
+ if (encryptedKey) {
414
+ encryptionKey ??= symmetricDecryptKey(encryptedKey, {
415
+ signatureSecret,
416
+ sub,
417
+ });
418
+ }
419
+ if (!encryptionKey || !encryptedDataPacket) return encryptedDataPacket;
420
+ decoding ??= options.symmetricEncryptionEncoding;
421
+ encoding ??= "utf8";
422
+
423
+ // remove signature when successful
424
+ encryptedDataPacket = symmetricSignatureVerify(encryptedDataPacket, {
425
+ signatureSecret,
426
+ });
427
+
428
+ if (encryptedDataPacket === false) {
429
+ throw new Error("Signature incorrect");
430
+ }
431
+ const iv = Buffer.from(
432
+ encryptedDataPacket.substring(0, symmetricEncryptionEncodingLengths.iv),
433
+ decoding,
434
+ );
435
+ const authTag = Buffer.from(
436
+ encryptedDataPacket.substring(
437
+ symmetricEncryptionEncodingLengths.iv,
438
+ symmetricEncryptionEncodingLengths.ivAndAuthTag,
439
+ ),
440
+ decoding,
441
+ );
442
+ const encryptedData = Buffer.from(
443
+ encryptedDataPacket.substring(
444
+ symmetricEncryptionEncodingLengths.ivAndAuthTag,
445
+ ),
446
+ decoding,
447
+ );
448
+
449
+ const decipher = createDecipheriv(
450
+ options.symmetricEncryptionAlgorithm,
451
+ encryptionKey,
452
+ iv,
453
+ {
454
+ authTagLength,
455
+ },
456
+ );
457
+ decipher.setAAD(sub);
458
+
459
+ decipher.setAuthTag(authTag);
460
+ const data =
461
+ decipher.update(encryptedData, decoding, encoding) +
462
+ decipher.final(encoding);
463
+ return data;
464
+ };
465
+
466
+ // *** Symmetric Signatures *** //
467
+ export const symmetricRandomSignatureSecret = () => {
468
+ return randomBytes(32); // 256 bits
469
+ };
470
+
471
+ export const symmetricGenerateSignatureSecret = () => {
472
+ const signatureSecret = symmetricRandomSignatureSecret();
473
+ return { signatureSecret };
474
+ };
475
+
476
+ export const symmetricSignatureSign = (
477
+ data,
478
+ { hashAlgorithm, signatureSecret } = {},
479
+ ) => {
480
+ signatureSecret ??= options.symmetricSignatureSecret;
481
+ hashAlgorithm ??= options.symmetricSignatureHashAlgorithm;
482
+ const signature = createHmac(hashAlgorithm, signatureSecret)
483
+ .update(data)
484
+ .digest(options.symmetricSignatureEncoding)
485
+ .replace(/=+$/, "");
486
+
487
+ const signedData = data + "." + signature;
488
+ return signedData;
489
+ };
490
+
491
+ export const symmetricSignatureVerify = (
492
+ signedData,
493
+ { hashAlgorithm, signatureSecret } = {},
494
+ ) => {
495
+ if (typeof signedData !== "string") return false;
496
+ let lastIndexOf = signedData.lastIndexOf(".");
497
+ // Test for unsigned
498
+ if (lastIndexOf < 0) {
499
+ lastIndexOf = signedData.length;
500
+ }
501
+ const data = signedData.substring(0, lastIndexOf);
502
+ const signedDataExpected = symmetricSignatureSign(data, {
503
+ hashAlgorithm,
504
+ signatureSecret,
505
+ });
506
+ return safeEqual(signedData, signedDataExpected) && data;
507
+ };
508
+
509
+ // Allow rotation of global encryption key, global signature secret, and row encryption key
510
+ export const symmetricRotation = (
511
+ oldEncryptedValues,
512
+ oldOptions, // { encryptionKey, signatureSecret, sub, decoding, encoding }, // old
513
+ oldFields = [],
514
+ newOptions, // { encryptionKey, signatureSecret, sub, decoding, encoding }, // new
515
+ newFields,
516
+ transform = (data) => {
517
+ return data;
518
+ },
519
+ ) => {
520
+ newOptions ??= oldOptions;
521
+ newFields ??= oldFields;
522
+
523
+ if (oldOptions.sub !== newOptions.sub) throw new Error("Mismatching `sub`");
524
+
525
+ oldEncryptedValues = structuredClone(oldEncryptedValues);
526
+ // Don't use structuredClone, converts Buffer to Uint8Array ...
527
+ oldOptions = { ...oldOptions };
528
+ newOptions = { ...newOptions };
529
+
530
+ // decrypt old encryption key
531
+ const { encryptionKey: oldEncryptedKey } = oldEncryptedValues;
532
+ delete oldEncryptedValues.encryptionKey;
533
+
534
+ const oldEncryptionKey = symmetricDecryptKey(oldEncryptedKey, oldOptions);
535
+ oldOptions.encryptionKey = oldEncryptionKey;
536
+
537
+ // decrypt
538
+ const data = transform(
539
+ symmetricDecryptFields(
540
+ oldEncryptedValues,
541
+ { ...oldOptions, encryptionKey: oldEncryptionKey },
542
+ oldFields,
543
+ ),
544
+ );
545
+
546
+ // rotate encryptionKey
547
+ const { encryptionKey: newEncryptionKey, encryptedKey: newEncryptedKey } =
548
+ symmetricGenerateEncryptionKey(newOptions.sub, newOptions);
549
+ newOptions.encryptionKey = newEncryptionKey;
550
+
551
+ // encrypt
552
+ const newEncryptedValues = symmetricEncryptFields(
553
+ data,
554
+ newOptions,
555
+ newFields,
556
+ );
557
+ newEncryptedValues.encryptionKey = newEncryptedKey;
558
+
559
+ return newEncryptedValues;
560
+ };
561
+
562
+ // *** Asymmetric Signatures *** //
563
+ // asymmetricKeyPairType
564
+ export const makeAsymmetricKeys = async () => {
565
+ const { publicKey, privateKey } = await generateKeyPair("ec", {
566
+ namedCurve: options.asymmetricKeyNamedCurve,
567
+ paramEncoding: "named",
568
+ publicKeyEncoding: {
569
+ type: "spki",
570
+ format: "pem",
571
+ },
572
+ privateKeyEncoding: {
573
+ type: "sec1",
574
+ format: "pem",
575
+ // Encryption done at another level for consistency
576
+ // cipher: options.asymmetricEncryptionAlgorithm,
577
+ // passphrase: encryptionKey,
578
+ },
579
+ });
580
+ return { publicKey, privateKey };
581
+ };
582
+
583
+ export const makeAsymmetricSignature = async (
584
+ data,
585
+ privateKey,
586
+ { hashAlgorithm } = {},
587
+ ) => {
588
+ hashAlgorithm ??= options.asymmetricSignatureHashAlgorithm;
589
+ return (await sign(hashAlgorithm, Buffer.from(data), privateKey)).toString(
590
+ options.asymmetricSignatureEncoding,
591
+ );
592
+ };
593
+ export const verifyAsymmetricSignature = async (
594
+ data,
595
+ publicKey,
596
+ signature,
597
+ { hashAlgorithm } = {},
598
+ ) => {
599
+ hashAlgorithm ??= options.asymmetricSignatureHashAlgorithm;
600
+ return await verify(
601
+ hashAlgorithm,
602
+ Buffer.from(data),
603
+ publicKey,
604
+ Buffer.from(signature, options.asymmetricSignatureEncoding),
605
+ );
606
+ };
607
+
608
+ export const safeEqual = (input, expected) => {
609
+ const bufferInput = Buffer.from(input);
610
+ const bufferExpected = Buffer.from(expected);
611
+ return (
612
+ bufferInput.length === bufferExpected.length &&
613
+ timingSafeEqual(bufferInput, bufferExpected)
614
+ );
615
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1auth/crypto",
3
- "version": "0.0.0-alpha.68",
3
+ "version": "0.0.0-alpha.69",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "engines": {