@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.
Files changed (61) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +148 -18
  3. package/dist/aead.d.ts +4 -0
  4. package/dist/aead.js +40 -16
  5. package/dist/aesKw.d.ts +8 -2
  6. package/dist/aesKw.js +69 -12
  7. package/dist/algorithms.d.ts +12 -4
  8. package/dist/algorithms.js +8 -2
  9. package/dist/argon2id.d.ts +3 -0
  10. package/dist/argon2id.js +18 -3
  11. package/dist/cryptoFacade.d.ts +66 -5
  12. package/dist/cryptoFacade.js +99 -38
  13. package/dist/errors.d.ts +1 -1
  14. package/dist/hkdf.d.ts +1 -0
  15. package/dist/hkdf.js +5 -1
  16. package/dist/hmac.d.ts +3 -0
  17. package/dist/hmac.js +24 -3
  18. package/dist/hpke.d.ts +4 -1
  19. package/dist/hpke.js +45 -14
  20. package/dist/index.d.ts +14 -13
  21. package/dist/index.js +12 -10
  22. package/dist/jwaConcatKdf.js +8 -4
  23. package/dist/jwk.d.ts +2 -1
  24. package/dist/jwk.js +158 -122
  25. package/dist/kmac.d.ts +11 -0
  26. package/dist/kmac.js +49 -0
  27. package/dist/memory.d.ts +8 -0
  28. package/dist/memory.js +13 -0
  29. package/dist/mlDsa.d.ts +5 -0
  30. package/dist/mlDsa.js +43 -9
  31. package/dist/mlKem.d.ts +5 -2
  32. package/dist/mlKem.js +37 -26
  33. package/dist/operationResponse.d.ts +16 -0
  34. package/dist/operationResponse.js +46 -0
  35. package/dist/p256Ecdh.js +5 -1
  36. package/dist/p384Ecdh.js +5 -1
  37. package/dist/p521Ecdh.js +5 -1
  38. package/dist/pbkdf2.d.ts +6 -8
  39. package/dist/pbkdf2.js +12 -12
  40. package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.d.ts +2859 -128
  41. package/dist/proto/generated/reallyme/crypto/v1/crypto_pb.js +992 -132
  42. package/dist/proto.d.ts +99 -7
  43. package/dist/proto.js +741 -16
  44. package/dist/rsa.d.ts +4 -1
  45. package/dist/rsa.js +23 -16
  46. package/dist/slhDsa.d.ts +5 -0
  47. package/dist/slhDsa.js +40 -9
  48. package/dist/validateBytes.d.ts +7 -1
  49. package/dist/validateBytes.js +63 -3
  50. package/dist/wasm/reallyme_crypto_wasm.js +77 -365
  51. package/dist/wasm/reallyme_crypto_wasm_bg.wasm +0 -0
  52. package/dist/wasmModuleTypes.d.ts +9 -21
  53. package/dist/wasmProvider.d.ts +12 -29
  54. package/dist/wasmProvider.js +25 -43
  55. package/dist/xWing.d.ts +5 -4
  56. package/dist/xWing.js +40 -40
  57. package/package.json +20 -2
  58. package/dist/codecs.d.ts +0 -35
  59. package/dist/codecs.js +0 -216
  60. package/dist/proto/generated/reallyme/codec/v1/codec_pb.d.ts +0 -253
  61. package/dist/proto/generated/reallyme/codec/v1/codec_pb.js +0 -160
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, readByteArrayProperty } from "./validateBytes.js";
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,10 +11,12 @@ 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();
17
+ return mlKemSuiteWithProvider(algorithm, provider);
18
+ };
19
+ const mlKemSuiteWithProvider = (algorithm, provider) => {
18
20
  switch (algorithm) {
19
21
  case "ML-KEM-512":
20
22
  return {
@@ -23,7 +25,6 @@ const mlKemSuite = (algorithm) => {
23
25
  generateKeyPair: provider.mlKem512GenerateKeypair,
24
26
  deriveKeyPair: provider.mlKem512DeriveKeypair,
25
27
  encapsulate: provider.mlKem512Encapsulate,
26
- encapsulateDerand: provider.mlKem512EncapsulateDerand,
27
28
  decapsulate: provider.mlKem512Decapsulate,
28
29
  };
29
30
  case "ML-KEM-768":
@@ -33,7 +34,6 @@ const mlKemSuite = (algorithm) => {
33
34
  generateKeyPair: provider.mlKem768GenerateKeypair,
34
35
  deriveKeyPair: provider.mlKem768DeriveKeypair,
35
36
  encapsulate: provider.mlKem768Encapsulate,
36
- encapsulateDerand: provider.mlKem768EncapsulateDerand,
37
37
  decapsulate: provider.mlKem768Decapsulate,
38
38
  };
39
39
  case "ML-KEM-1024":
@@ -43,55 +43,66 @@ const mlKemSuite = (algorithm) => {
43
43
  generateKeyPair: provider.mlKem1024GenerateKeypair,
44
44
  deriveKeyPair: provider.mlKem1024DeriveKeypair,
45
45
  encapsulate: provider.mlKem1024Encapsulate,
46
- encapsulateDerand: provider.mlKem1024EncapsulateDerand,
47
46
  decapsulate: provider.mlKem1024Decapsulate,
48
47
  };
49
48
  default:
50
49
  throw new ReallyMeCryptoError("unsupported-algorithm");
51
50
  }
52
51
  };
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;
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);
67
64
  };
68
65
  export const ReallyMeMlKem = {
69
66
  generateKeyPair(algorithm) {
70
67
  const suite = mlKemSuite(algorithm);
71
68
  return readKeyPair(suite.generateKeyPair(), suite);
72
69
  },
70
+ generateKeyPairWithProvider(provider, algorithm) {
71
+ const suite = mlKemSuiteWithProvider(algorithm, provider);
72
+ return readKeyPair(suite.generateKeyPair(), suite);
73
+ },
73
74
  deriveKeyPair(algorithm, secretKey) {
74
75
  // Import an existing FIPS 203 seed-form secret and reconstruct its public
75
76
  // key. Do not feed passwords or other low-entropy material here.
76
77
  const suite = mlKemSuite(algorithm);
77
78
  ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
78
- return readKeyPair(suite.deriveKeyPair(secretKey), suite);
79
+ return readKeyPair(suite.deriveKeyPair(secretKey), suite, [secretKey]);
80
+ },
81
+ deriveKeyPairWithProvider(provider, algorithm, secretKey) {
82
+ const suite = mlKemSuiteWithProvider(algorithm, provider);
83
+ ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
84
+ return readKeyPair(suite.deriveKeyPair(secretKey), suite, [secretKey]);
79
85
  },
80
86
  encapsulate(algorithm, publicKey) {
81
87
  const suite = mlKemSuite(algorithm);
82
88
  ensureBytes(publicKey, suite.publicKeyLength);
83
- return readEncapsulation(suite.encapsulate(publicKey), suite);
89
+ return readEncapsulation(suite.encapsulate(publicKey), suite, [publicKey]);
84
90
  },
85
- encapsulateDeterministicallyForTest(algorithm, publicKey, randomness) {
86
- const suite = mlKemSuite(algorithm);
91
+ encapsulateWithProvider(provider, algorithm, publicKey) {
92
+ const suite = mlKemSuiteWithProvider(algorithm, provider);
87
93
  ensureBytes(publicKey, suite.publicKeyLength);
88
- ensureBytes(randomness, ML_KEM_ENCAPSULATION_RANDOMNESS_LENGTH);
89
- return readEncapsulation(suite.encapsulateDerand(publicKey, randomness), suite);
94
+ return readEncapsulation(suite.encapsulate(publicKey), suite, [publicKey]);
90
95
  },
91
96
  decapsulate(algorithm, ciphertext, secretKey) {
92
97
  const suite = mlKemSuite(algorithm);
93
98
  ensureBytes(ciphertext, suite.ciphertextLength);
94
99
  ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
95
- return readSharedSecret(suite.decapsulate(ciphertext, secretKey));
100
+ return readSharedSecret(suite.decapsulate(ciphertext, secretKey), [ciphertext, secretKey]);
101
+ },
102
+ decapsulateWithProvider(provider, algorithm, ciphertext, secretKey) {
103
+ const suite = mlKemSuiteWithProvider(algorithm, provider);
104
+ ensureBytes(ciphertext, suite.ciphertextLength);
105
+ ensureBytes(secretKey, ML_KEM_SECRET_KEY_LENGTH);
106
+ return readSharedSecret(suite.decapsulate(ciphertext, secretKey), [ciphertext, secretKey]);
96
107
  },
97
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/p256Ecdh.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { p256 } from "@noble/curves/nist.js";
5
5
  import { ReallyMeCryptoError } from "./errors.js";
6
+ import { bestEffortClear } from "./memory.js";
6
7
  /**
7
8
  * P-256 ECDH backed by @noble/curves.
8
9
  *
@@ -44,7 +45,10 @@ export const ReallyMeP256Ecdh = {
44
45
  throw new ReallyMeCryptoError("invalid-input");
45
46
  }
46
47
  try {
47
- return p256.getSharedSecret(secretKey, publicKey, false).slice(1, 33);
48
+ const uncompressed = p256.getSharedSecret(secretKey, publicKey, false);
49
+ const sharedSecret = uncompressed.slice(1, 33);
50
+ bestEffortClear(uncompressed);
51
+ return sharedSecret;
48
52
  }
49
53
  catch {
50
54
  throw new ReallyMeCryptoError("invalid-input");
package/dist/p384Ecdh.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { p384 } from "@noble/curves/nist.js";
5
5
  import { ReallyMeCryptoError } from "./errors.js";
6
+ import { bestEffortClear } from "./memory.js";
6
7
  /**
7
8
  * P-384 ECDH backed by @noble/curves.
8
9
  *
@@ -44,7 +45,10 @@ export const ReallyMeP384Ecdh = {
44
45
  throw new ReallyMeCryptoError("invalid-input");
45
46
  }
46
47
  try {
47
- return p384.getSharedSecret(secretKey, publicKey, false).slice(1, 49);
48
+ const uncompressed = p384.getSharedSecret(secretKey, publicKey, false);
49
+ const sharedSecret = uncompressed.slice(1, 49);
50
+ bestEffortClear(uncompressed);
51
+ return sharedSecret;
48
52
  }
49
53
  catch {
50
54
  throw new ReallyMeCryptoError("invalid-input");
package/dist/p521Ecdh.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { p521 } from "@noble/curves/nist.js";
5
5
  import { ReallyMeCryptoError } from "./errors.js";
6
+ import { bestEffortClear } from "./memory.js";
6
7
  /**
7
8
  * P-521 ECDH backed by @noble/curves.
8
9
  *
@@ -44,7 +45,10 @@ export const ReallyMeP521Ecdh = {
44
45
  throw new ReallyMeCryptoError("invalid-input");
45
46
  }
46
47
  try {
47
- return p521.getSharedSecret(secretKey, publicKey, false).slice(1, 67);
48
+ const uncompressed = p521.getSharedSecret(secretKey, publicKey, false);
49
+ const sharedSecret = uncompressed.slice(1, 67);
50
+ bestEffortClear(uncompressed);
51
+ return sharedSecret;
48
52
  }
49
53
  catch {
50
54
  throw new ReallyMeCryptoError("invalid-input");
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 = 1;
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
- * The primitive does not enforce these (it must remain able to verify
10
- * externally created hashes), so callers creating new key material SHOULD
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; lower counts
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; lower counts
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
  };
package/dist/pbkdf2.js CHANGED
@@ -6,27 +6,27 @@ import { sha256, sha512 } from "@noble/hashes/sha2.js";
6
6
  import { ReallyMeCryptoError } from "./errors.js";
7
7
  export const PBKDF2_MIN_INPUT_LENGTH = 1;
8
8
  export const PBKDF2_MAX_INPUT_LENGTH = 4096;
9
- // The primitive accepts any count >= 1 so it can verify PBKDF2 hashes created
10
- // elsewhere at their stored iteration count (a required interop use), matching
11
- // the Rust lane. It is NOT a security floor; see the recommended minimums
12
- // below for deriving *new* password keys.
13
- export const PBKDF2_MIN_ITERATIONS = 1;
9
+ // Public derivation routes enforce a modern baseline. Historical hashes with a
10
+ // lower work factor require an explicit migration tool, not a silent downgrade
11
+ // in the v0.3 API.
12
+ export const PBKDF2_MIN_ITERATIONS = 100_000;
13
+ // Bound hostile work-factor inputs before the provider begins CPU-expensive
14
+ // derivation. This matches the Rust, Swift, and Kotlin public lanes.
15
+ export const PBKDF2_MAX_ITERATIONS = 10_000_000;
14
16
  export const PBKDF2_MIN_OUTPUT_LENGTH = 1;
15
17
  export const PBKDF2_MAX_OUTPUT_LENGTH = 4096;
16
18
  /**
17
19
  * OWASP Password Storage Cheat Sheet minimum iteration counts for deriving a
18
20
  * *new* password key.
19
- * The primitive does not enforce these (it must remain able to verify
20
- * externally created hashes), so callers creating new key material SHOULD
21
- * pass at least the value for their PRF.
21
+ * These stronger profile recommendations remain above the enforced public
22
+ * baseline and should be selected when protocol latency budgets permit.
22
23
  */
23
24
  export const PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA256 = 600_000;
24
25
  export const PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA512 = 220_000;
25
26
  export const ReallyMePbkdf2 = {
26
27
  /**
27
28
  * PBKDF2-HMAC-SHA-256. For a new password key pass at least
28
- * {@link PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA256} iterations; lower counts
29
- * are accepted only to verify hashes created elsewhere.
29
+ * {@link PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA256} iterations.
30
30
  */
31
31
  deriveHmacSha256(password, salt, iterations, outputLength) {
32
32
  validate(password, salt, iterations, outputLength);
@@ -34,8 +34,7 @@ export const ReallyMePbkdf2 = {
34
34
  },
35
35
  /**
36
36
  * PBKDF2-HMAC-SHA-512. For a new password key pass at least
37
- * {@link PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA512} iterations; lower counts
38
- * are accepted only to verify hashes created elsewhere.
37
+ * {@link PBKDF2_RECOMMENDED_MIN_ITERATIONS_SHA512} iterations.
39
38
  */
40
39
  deriveHmacSha512(password, salt, iterations, outputLength) {
41
40
  validate(password, salt, iterations, outputLength);
@@ -49,6 +48,7 @@ function validate(password, salt, iterations, outputLength) {
49
48
  salt.length > PBKDF2_MAX_INPUT_LENGTH ||
50
49
  !Number.isInteger(iterations) ||
51
50
  iterations < PBKDF2_MIN_ITERATIONS ||
51
+ iterations > PBKDF2_MAX_ITERATIONS ||
52
52
  !Number.isInteger(outputLength) ||
53
53
  outputLength < PBKDF2_MIN_OUTPUT_LENGTH ||
54
54
  outputLength > PBKDF2_MAX_OUTPUT_LENGTH) {