@raytio/core 11.5.0 → 11.6.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 (133) hide show
  1. package/README.md +1708 -217
  2. package/dist/accessApplication/api/legacy/convertRelationships.d.ts +3 -5
  3. package/dist/accessApplication/api/legacy/convertRelationships.js +3 -3
  4. package/dist/crypto/cognitoAttributes.d.ts +3 -0
  5. package/dist/crypto/cognitoAttributes.js +15 -4
  6. package/dist/crypto/getAADecryptor.d.ts +1 -1
  7. package/dist/crypto/getAADecryptor.js +1 -3
  8. package/dist/crypto/index.d.ts +3 -0
  9. package/dist/crypto/index.js +6 -0
  10. package/dist/crypto/kdf/argon2.d.ts +67 -0
  11. package/dist/crypto/kdf/argon2.js +99 -0
  12. package/dist/crypto/kdf/index.d.ts +43 -0
  13. package/dist/crypto/kdf/index.js +106 -0
  14. package/dist/crypto/kdf/pbkdf2.d.ts +16 -0
  15. package/dist/crypto/kdf/pbkdf2.js +45 -0
  16. package/dist/crypto/kdf/twoSecretKdf.d.ts +37 -0
  17. package/dist/crypto/kdf/twoSecretKdf.js +66 -0
  18. package/dist/crypto/kdf/types.d.ts +65 -0
  19. package/dist/crypto/kdf/types.js +50 -0
  20. package/dist/crypto/kdf/utils.d.ts +59 -0
  21. package/dist/crypto/kdf/utils.js +110 -0
  22. package/dist/crypto/localSecret/format.d.ts +48 -0
  23. package/dist/crypto/localSecret/format.js +157 -0
  24. package/dist/crypto/localSecret/generator.d.ts +23 -0
  25. package/dist/crypto/localSecret/generator.js +53 -0
  26. package/dist/crypto/localSecret/index.d.ts +12 -0
  27. package/dist/crypto/localSecret/index.js +46 -0
  28. package/dist/crypto/localSecret/storage.d.ts +53 -0
  29. package/dist/crypto/localSecret/storage.js +207 -0
  30. package/dist/crypto/localSecret/types.d.ts +68 -0
  31. package/dist/crypto/localSecret/types.js +31 -0
  32. package/dist/crypto/pgpKey/encryption.d.ts +49 -0
  33. package/dist/crypto/pgpKey/encryption.js +104 -0
  34. package/dist/crypto/pgpKey/export.d.ts +59 -0
  35. package/dist/crypto/pgpKey/export.js +322 -0
  36. package/dist/crypto/pgpKey/format.d.ts +61 -0
  37. package/dist/crypto/pgpKey/format.js +143 -0
  38. package/dist/crypto/pgpKey/generator.d.ts +20 -0
  39. package/dist/crypto/pgpKey/generator.js +76 -0
  40. package/dist/crypto/pgpKey/import.d.ts +69 -0
  41. package/dist/crypto/pgpKey/import.js +239 -0
  42. package/dist/crypto/pgpKey/index.d.ts +19 -0
  43. package/dist/crypto/pgpKey/index.js +67 -0
  44. package/dist/crypto/pgpKey/signing.d.ts +44 -0
  45. package/dist/crypto/pgpKey/signing.js +71 -0
  46. package/dist/crypto/pgpKey/storage.d.ts +43 -0
  47. package/dist/crypto/pgpKey/storage.js +141 -0
  48. package/dist/crypto/pgpKey/types.d.ts +86 -0
  49. package/dist/crypto/pgpKey/types.js +25 -0
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.js +1 -0
  52. package/dist/rules/calculateScore.d.ts +1 -1
  53. package/dist/rules/convertInstanceToRuleInput.js +99 -97
  54. package/dist/rules/evaluateBadge.d.ts +36 -0
  55. package/dist/rules/evaluateBadge.js +36 -0
  56. package/dist/rules/index.d.ts +1 -0
  57. package/dist/rules/index.js +1 -0
  58. package/dist/rules/types/config.d.ts +1 -1
  59. package/dist/rules/types/dataValueTypes.d.ts +4 -4
  60. package/dist/schema/expandSchema/constants.js +1 -1
  61. package/dist/schema/expandSchema/expandSchema.d.ts +3 -3
  62. package/dist/schema/expandSchema/expandSchema.js +4 -4
  63. package/dist/schema/expandSchema/i18n.d.ts +6 -1
  64. package/dist/schema/expandSchema/i18n.js +32 -4
  65. package/dist/schema/expandSchema/maybeUseI18n.d.ts +2 -2
  66. package/dist/schema/expandSchema/maybeUseI18n.js +68 -11
  67. package/dist/schema/expandSchema/processSchema.js +14 -5
  68. package/dist/schema/expandSchema/removePrivateFields.d.ts +75 -22
  69. package/dist/schema/expandSchema/sortSchemaProperties.d.ts +4 -1
  70. package/dist/schema/expandSchema/sortSchemaProperties.js +24 -1
  71. package/dist/schema/labels.js +1 -2
  72. package/dist/util/canonicalJsonify.d.ts +7 -1
  73. package/dist/util/canonicalJsonify.js +3 -2
  74. package/dist/verifications/safeHarbour.js +5 -0
  75. package/dist/verifications/verifyCheck/getOwnRealVerifications.js +2 -0
  76. package/package.json +6 -4
  77. package/dist/__tests__/docs.test.d.ts +0 -1
  78. package/dist/__tests__/docs.test.js +0 -24
  79. package/dist/accessApplication/api/__tests__/fetchKeysForSubmission.test.d.ts +0 -1
  80. package/dist/accessApplication/api/__tests__/fetchKeysForSubmission.test.js +0 -28
  81. package/dist/accessApplication/api/__tests__/fetchPOsOrAOsForSubmission.test.d.ts +0 -1
  82. package/dist/accessApplication/api/__tests__/fetchPOsOrAOsForSubmission.test.js +0 -23
  83. package/dist/accessApplication/api/__tests__/fetchRelationshipsForSubmission.test.d.ts +0 -1
  84. package/dist/accessApplication/api/__tests__/fetchRelationshipsForSubmission.test.js +0 -27
  85. package/dist/accessApplication/api/__tests__/getMissingDataForInstance.test.d.ts +0 -1
  86. package/dist/accessApplication/api/__tests__/getMissingDataForInstance.test.js +0 -30
  87. package/dist/accessApplication/api/legacy/__tests__/convertRelationships.test.d.ts +0 -1
  88. package/dist/accessApplication/api/legacy/__tests__/convertRelationships.test.js +0 -37
  89. package/dist/rules/helpers/__tests__/addInfiniteThresholdBoundaries.test.d.ts +0 -1
  90. package/dist/rules/helpers/__tests__/addInfiniteThresholdBoundaries.test.js +0 -27
  91. package/dist/rules/helpers/__tests__/checkTypeofValue.test.d.ts +0 -1
  92. package/dist/rules/helpers/__tests__/checkTypeofValue.test.js +0 -49
  93. package/dist/rules/helpers/__tests__/getValuesFromPath.test.d.ts +0 -1
  94. package/dist/rules/helpers/__tests__/getValuesFromPath.test.js +0 -67
  95. package/dist/rules/helpers/__tests__/thresholds.test.d.ts +0 -1
  96. package/dist/rules/helpers/__tests__/thresholds.test.js +0 -32
  97. package/dist/rules/operators/__tests__/bool.test.d.ts +0 -1
  98. package/dist/rules/operators/__tests__/bool.test.js +0 -21
  99. package/dist/rules/operators/__tests__/date.test.d.ts +0 -1
  100. package/dist/rules/operators/__tests__/date.test.js +0 -81
  101. package/dist/rules/operators/__tests__/hfield.test.d.ts +0 -1
  102. package/dist/rules/operators/__tests__/hfield.test.js +0 -38
  103. package/dist/rules/operators/__tests__/hschema.test.d.ts +0 -1
  104. package/dist/rules/operators/__tests__/hschema.test.js +0 -24
  105. package/dist/rules/operators/__tests__/number.test.d.ts +0 -1
  106. package/dist/rules/operators/__tests__/number.test.js +0 -53
  107. package/dist/rules/operators/__tests__/string.test.d.ts +0 -1
  108. package/dist/rules/operators/__tests__/string.test.js +0 -74
  109. package/dist/schema/expandSchema/__tests__/addLoadingTimes.test.d.ts +0 -1
  110. package/dist/schema/expandSchema/__tests__/addLoadingTimes.test.js +0 -24
  111. package/dist/schema/expandSchema/__tests__/expandSchema.test.d.ts +0 -1
  112. package/dist/schema/expandSchema/__tests__/expandSchema.test.js +0 -96
  113. package/dist/schema/expandSchema/__tests__/i18n.test.d.ts +0 -1
  114. package/dist/schema/expandSchema/__tests__/i18n.test.js +0 -32
  115. package/dist/schema/expandSchema/__tests__/maybeUseI18n.test.d.ts +0 -1
  116. package/dist/schema/expandSchema/__tests__/maybeUseI18n.test.js +0 -98
  117. package/dist/schema/expandSchema/__tests__/processSchema.test.d.ts +0 -1
  118. package/dist/schema/expandSchema/__tests__/processSchema.test.js +0 -326
  119. package/dist/schema/expandSchema/__tests__/sortSchemaProperties.test.d.ts +0 -1
  120. package/dist/schema/expandSchema/__tests__/sortSchemaProperties.test.js +0 -182
  121. package/dist/schema/expandSchema/__tests__/util.test.d.ts +0 -1
  122. package/dist/schema/expandSchema/__tests__/util.test.js +0 -19
  123. package/dist/verifications/cleanInstance.d.ts +0 -9
  124. package/dist/verifications/cleanInstance.js +0 -15
  125. package/dist/verifications/verifyCheck/__tests__/getOwnRealVerifications.test.d.ts +0 -1
  126. package/dist/verifications/verifyCheck/__tests__/getOwnRealVerifications.test.js +0 -221
  127. package/dist/verifications/verifyCheck/__tests__/getSomeoneElsesRealVerifications.test.d.ts +0 -1
  128. package/dist/verifications/verifyCheck/__tests__/getSomeoneElsesRealVerifications.test.js +0 -206
  129. package/dist/verifications/verifyCheck/operations/__tests__/checkOwnVerification.test.d.ts +0 -1
  130. package/dist/verifications/verifyCheck/operations/__tests__/checkOwnVerification.test.js +0 -138
  131. package/dist/verifications/verifyCheck/operations/__tests__/checkSomeoneElsesVerifications.test.d.ts +0 -1
  132. package/dist/verifications/verifyCheck/operations/__tests__/checkSomeoneElsesVerifications.test.js +0 -49
  133. package/dist/verifications/verifyCheck/operations/__tests__/sampleBundle.json +0 -44
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /* eslint-disable max-classes-per-file -- Error classes grouped in types module */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.DEFAULT_ARGON2ID_PARAMS = exports.UnknownKdfAlgorithmError = exports.LocalSecretRequiredError = void 0;
5
+ exports.isPbkdf2Config = isPbkdf2Config;
6
+ exports.isArgon2idConfig = isArgon2idConfig;
7
+ /**
8
+ * Error thrown when LocalSecret is required but not provided
9
+ */
10
+ // eslint-disable-next-line fp/no-class -- Custom error class for specific error handling
11
+ class LocalSecretRequiredError extends Error {
12
+ constructor(message = "This account requires a Local Secret for decryption") {
13
+ super(message);
14
+ // eslint-disable-next-line fp/no-this, fp/no-mutation -- Required for Error subclassing
15
+ this.name = "LocalSecretRequiredError";
16
+ }
17
+ }
18
+ exports.LocalSecretRequiredError = LocalSecretRequiredError;
19
+ /**
20
+ * Error thrown when KDF algorithm is unknown
21
+ */
22
+ // eslint-disable-next-line fp/no-class -- Custom error class for specific error handling
23
+ class UnknownKdfAlgorithmError extends Error {
24
+ constructor(algorithm) {
25
+ super(`Unknown KDF algorithm: ${algorithm}`);
26
+ // eslint-disable-next-line fp/no-this, fp/no-mutation -- Required for Error subclassing
27
+ this.name = "UnknownKdfAlgorithmError";
28
+ }
29
+ }
30
+ exports.UnknownKdfAlgorithmError = UnknownKdfAlgorithmError;
31
+ /**
32
+ * Default Argon2id parameters (matching Bitwarden recommendations)
33
+ */
34
+ exports.DEFAULT_ARGON2ID_PARAMS = {
35
+ memory: 65536, // 64 MiB
36
+ iterations: 3,
37
+ parallelism: 4,
38
+ };
39
+ /**
40
+ * Type guard for PBKDF2 config
41
+ */
42
+ function isPbkdf2Config(config) {
43
+ return config.algorithm_name === "PBKDF2";
44
+ }
45
+ /**
46
+ * Type guard for Argon2id config
47
+ */
48
+ function isArgon2idConfig(config) {
49
+ return config.algorithm_name === "Argon2id";
50
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * KDF Utility Functions
3
+ *
4
+ * Encoding, decoding, and cryptographic utilities for KDF operations.
5
+ * Issue #1649
6
+ */
7
+ /**
8
+ * Convert a base64 string to Uint8Array
9
+ */
10
+ export declare function base64ToUint8Array(base64: string): Uint8Array;
11
+ /**
12
+ * Convert Uint8Array to base64 string
13
+ */
14
+ export declare function uint8ArrayToBase64(bytes: Uint8Array): string;
15
+ /**
16
+ * XOR two byte arrays of equal length
17
+ *
18
+ * Used for combining password-derived key with LocalSecret in 2SKD.
19
+ * This follows the 1Password approach of XOR combination.
20
+ *
21
+ * @param a - First byte array
22
+ * @param b - Second byte array
23
+ * @returns XOR result
24
+ * @throws Error if arrays are not the same length
25
+ */
26
+ export declare function xorBytes(a: Uint8Array, b: Uint8Array): Uint8Array;
27
+ /**
28
+ * Normalize password for key derivation
29
+ *
30
+ * Applies NFKD normalization and trims whitespace.
31
+ * This ensures consistent key derivation across platforms.
32
+ *
33
+ * @param password - Raw password input
34
+ * @returns Normalized password string
35
+ */
36
+ export declare function normalizePassword(password: string): string;
37
+ /**
38
+ * Generate cryptographically secure random bytes
39
+ *
40
+ * @param length - Number of bytes to generate
41
+ * @returns Random bytes
42
+ */
43
+ export declare function generateRandomBytes(length: number): Uint8Array;
44
+ /**
45
+ * Generate a random salt for key derivation
46
+ *
47
+ * @returns 16-byte random salt as base64 string
48
+ */
49
+ export declare function generateSalt(): string;
50
+ /**
51
+ * Constant-time comparison of two byte arrays
52
+ *
53
+ * Prevents timing attacks when comparing secrets.
54
+ *
55
+ * @param a - First byte array
56
+ * @param b - Second byte array
57
+ * @returns true if arrays are equal
58
+ */
59
+ export declare function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /* eslint-disable fp/no-mutation, no-plusplus, no-bitwise, @typescript-eslint/prefer-for-of -- Low-level byte manipulation utilities */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.base64ToUint8Array = base64ToUint8Array;
5
+ exports.uint8ArrayToBase64 = uint8ArrayToBase64;
6
+ exports.xorBytes = xorBytes;
7
+ exports.normalizePassword = normalizePassword;
8
+ exports.generateRandomBytes = generateRandomBytes;
9
+ exports.generateSalt = generateSalt;
10
+ exports.constantTimeEqual = constantTimeEqual;
11
+ /**
12
+ * KDF Utility Functions
13
+ *
14
+ * Encoding, decoding, and cryptographic utilities for KDF operations.
15
+ * Issue #1649
16
+ */
17
+ /**
18
+ * Convert a base64 string to Uint8Array
19
+ */
20
+ function base64ToUint8Array(base64) {
21
+ const binaryString = atob(base64);
22
+ const bytes = new Uint8Array(binaryString.length);
23
+ for (let i = 0; i < binaryString.length; i++) {
24
+ bytes[i] = binaryString.charCodeAt(i);
25
+ }
26
+ return bytes;
27
+ }
28
+ /**
29
+ * Convert Uint8Array to base64 string
30
+ */
31
+ function uint8ArrayToBase64(bytes) {
32
+ let binaryString = "";
33
+ for (let i = 0; i < bytes.length; i++) {
34
+ binaryString += String.fromCharCode(bytes[i]);
35
+ }
36
+ return btoa(binaryString);
37
+ }
38
+ /**
39
+ * XOR two byte arrays of equal length
40
+ *
41
+ * Used for combining password-derived key with LocalSecret in 2SKD.
42
+ * This follows the 1Password approach of XOR combination.
43
+ *
44
+ * @param a - First byte array
45
+ * @param b - Second byte array
46
+ * @returns XOR result
47
+ * @throws Error if arrays are not the same length
48
+ */
49
+ function xorBytes(a, b) {
50
+ if (a.length !== b.length) {
51
+ throw new Error(`XOR inputs must be same length (got ${a.length} and ${b.length})`);
52
+ }
53
+ const result = new Uint8Array(a.length);
54
+ for (let i = 0; i < a.length; i++) {
55
+ result[i] = a[i] ^ b[i];
56
+ }
57
+ return result;
58
+ }
59
+ /**
60
+ * Normalize password for key derivation
61
+ *
62
+ * Applies NFKD normalization and trims whitespace.
63
+ * This ensures consistent key derivation across platforms.
64
+ *
65
+ * @param password - Raw password input
66
+ * @returns Normalized password string
67
+ */
68
+ function normalizePassword(password) {
69
+ // Trim leading/trailing whitespace
70
+ const trimmed = password.trim();
71
+ // Apply NFKD normalization for Unicode consistency
72
+ return trimmed.normalize("NFKD");
73
+ }
74
+ /**
75
+ * Generate cryptographically secure random bytes
76
+ *
77
+ * @param length - Number of bytes to generate
78
+ * @returns Random bytes
79
+ */
80
+ function generateRandomBytes(length) {
81
+ return crypto.getRandomValues(new Uint8Array(length));
82
+ }
83
+ /**
84
+ * Generate a random salt for key derivation
85
+ *
86
+ * @returns 16-byte random salt as base64 string
87
+ */
88
+ function generateSalt() {
89
+ const salt = generateRandomBytes(16);
90
+ return uint8ArrayToBase64(salt);
91
+ }
92
+ /**
93
+ * Constant-time comparison of two byte arrays
94
+ *
95
+ * Prevents timing attacks when comparing secrets.
96
+ *
97
+ * @param a - First byte array
98
+ * @param b - Second byte array
99
+ * @returns true if arrays are equal
100
+ */
101
+ function constantTimeEqual(a, b) {
102
+ if (a.length !== b.length) {
103
+ return false;
104
+ }
105
+ let result = 0;
106
+ for (let i = 0; i < a.length; i++) {
107
+ result |= a[i] ^ b[i];
108
+ }
109
+ return result === 0;
110
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * LocalSecret Formatter
3
+ *
4
+ * Formats LocalSecrets for human-readable display and parses them back.
5
+ * Uses an unambiguous character set (no 0, 1, I, O) for easy transcription.
6
+ *
7
+ * Format: A7K2M9-X4P8N3-B5J1L6-Q9W2R8-T3Y7U0-V6Z4C1-...
8
+ *
9
+ * Issue #1649
10
+ */
11
+ import type { FormattedLocalSecret } from "./types";
12
+ /**
13
+ * Format a LocalSecret for human-readable display
14
+ *
15
+ * @param secret - The 32-byte LocalSecret
16
+ * @returns Formatted LocalSecret with grouped characters
17
+ */
18
+ export declare function formatLocalSecret(secret: Uint8Array): FormattedLocalSecret;
19
+ /**
20
+ * Parse a formatted LocalSecret back to bytes
21
+ *
22
+ * Handles various input formats:
23
+ * - With dashes: A7K2M9-X4P8N3-...
24
+ * - Without dashes: A7K2M9X4P8N3...
25
+ * - With spaces: A7K2M9 X4P8N3 ...
26
+ * - Lowercase: a7k2m9-x4p8n3-...
27
+ *
28
+ * @param formatted - The formatted LocalSecret string
29
+ * @returns The 32-byte LocalSecret
30
+ */
31
+ export declare function parseLocalSecret(formatted: string): Uint8Array;
32
+ /**
33
+ * Validate a formatted LocalSecret string
34
+ *
35
+ * @param formatted - The formatted LocalSecret string
36
+ * @returns true if valid, false otherwise
37
+ */
38
+ export declare function isValidFormattedLocalSecret(formatted: string): boolean;
39
+ /**
40
+ * Mask a LocalSecret for partial display
41
+ *
42
+ * Shows only the first and last groups, masking the middle.
43
+ * Example: A7K2M9-******-******-******-******-V6Z4C1
44
+ *
45
+ * @param formatted - The formatted LocalSecret
46
+ * @returns Masked version for display
47
+ */
48
+ export declare function maskLocalSecret(formatted: string): string;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ /**
3
+ * LocalSecret Formatter
4
+ *
5
+ * Formats LocalSecrets for human-readable display and parses them back.
6
+ * Uses an unambiguous character set (no 0, 1, I, O) for easy transcription.
7
+ *
8
+ * Format: A7K2M9-X4P8N3-B5J1L6-Q9W2R8-T3Y7U0-V6Z4C1-...
9
+ *
10
+ * Issue #1649
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.formatLocalSecret = formatLocalSecret;
14
+ exports.parseLocalSecret = parseLocalSecret;
15
+ exports.isValidFormattedLocalSecret = isValidFormattedLocalSecret;
16
+ exports.maskLocalSecret = maskLocalSecret;
17
+ const types_1 = require("./types");
18
+ /**
19
+ * Encode bytes to the LocalSecret character set
20
+ *
21
+ * Uses base32-like encoding with our custom character set.
22
+ * Note: This function uses mutation for performance in BigInt arithmetic.
23
+ */
24
+ function encodeToCharset(bytes) {
25
+ const charset = types_1.LOCAL_SECRET_CHARSET;
26
+ const base = BigInt(charset.length); // 32
27
+ // Convert bytes to a big integer using reduce (functional approach)
28
+ const value = bytes.reduce(
29
+ // eslint-disable-next-line no-bitwise -- Required for byte-to-BigInt conversion
30
+ (acc, byte) => (acc << BigInt(8)) | BigInt(byte), BigInt(0));
31
+ // Convert to base-32 string using recursive approach
32
+ const toChars = (v, acc) => {
33
+ if (v <= BigInt(0))
34
+ return acc;
35
+ const remainder = Number(v % base);
36
+ return toChars(v / base, [charset[remainder], ...acc]);
37
+ };
38
+ const chars = toChars(value, []);
39
+ // Pad to consistent length (ceil(256 bits / 5 bits per char) = 52 chars)
40
+ const expectedLength = Math.ceil((types_1.LOCAL_SECRET_SIZE * 8) / 5);
41
+ const padding = Array.from({
42
+ length: Math.max(0, expectedLength - chars.length),
43
+ }).fill(charset[0]);
44
+ return [...padding, ...chars].join("");
45
+ }
46
+ /**
47
+ * Decode a string from the LocalSecret character set to bytes
48
+ * Note: This function uses mutation for performance in BigInt arithmetic.
49
+ */
50
+ function decodeFromCharset(encoded) {
51
+ const charset = types_1.LOCAL_SECRET_CHARSET;
52
+ const base = BigInt(charset.length); // 32
53
+ // Convert string to big integer using reduce (functional approach)
54
+ const value = [...encoded.toUpperCase()].reduce((acc, char) => {
55
+ const index = charset.indexOf(char);
56
+ if (index === -1) {
57
+ throw new Error(`Invalid character in LocalSecret: ${char}`);
58
+ }
59
+ return acc * base + BigInt(index);
60
+ }, BigInt(0));
61
+ // Convert big integer to bytes using recursive approach
62
+ const toBytes = (v, count, acc) => {
63
+ if (count <= 0)
64
+ return acc;
65
+ /* eslint-disable no-bitwise -- Required for BigInt-to-byte conversion */
66
+ return toBytes(v >> BigInt(8), count - 1, [
67
+ Number(v & BigInt(0xff)),
68
+ ...acc,
69
+ ]);
70
+ /* eslint-enable no-bitwise */
71
+ };
72
+ return new Uint8Array(toBytes(value, types_1.LOCAL_SECRET_SIZE, []));
73
+ }
74
+ /**
75
+ * Format a LocalSecret for human-readable display
76
+ *
77
+ * @param secret - The 32-byte LocalSecret
78
+ * @returns Formatted LocalSecret with grouped characters
79
+ */
80
+ function formatLocalSecret(secret) {
81
+ if (secret.length !== types_1.LOCAL_SECRET_SIZE) {
82
+ throw new Error(`LocalSecret must be ${types_1.LOCAL_SECRET_SIZE} bytes (got ${secret.length})`);
83
+ }
84
+ const encoded = encodeToCharset(secret);
85
+ // Split into groups using functional approach
86
+ const numGroups = Math.ceil(encoded.length / types_1.LOCAL_SECRET_GROUP_SIZE);
87
+ const groups = Array.from({ length: numGroups }, (_, i) => encoded.slice(i * types_1.LOCAL_SECRET_GROUP_SIZE, (i + 1) * types_1.LOCAL_SECRET_GROUP_SIZE));
88
+ return {
89
+ formatted: groups.join("-"),
90
+ groups,
91
+ };
92
+ }
93
+ /**
94
+ * Parse a formatted LocalSecret back to bytes
95
+ *
96
+ * Handles various input formats:
97
+ * - With dashes: A7K2M9-X4P8N3-...
98
+ * - Without dashes: A7K2M9X4P8N3...
99
+ * - With spaces: A7K2M9 X4P8N3 ...
100
+ * - Lowercase: a7k2m9-x4p8n3-...
101
+ *
102
+ * @param formatted - The formatted LocalSecret string
103
+ * @returns The 32-byte LocalSecret
104
+ */
105
+ function parseLocalSecret(formatted) {
106
+ // Remove dashes, spaces, and convert to uppercase
107
+ const cleaned = formatted.replace(/[-\s]/g, "").toUpperCase();
108
+ // Validate characters
109
+ for (const char of cleaned) {
110
+ if (!types_1.LOCAL_SECRET_CHARSET.includes(char)) {
111
+ throw new Error(`Invalid character in LocalSecret: ${char}`);
112
+ }
113
+ }
114
+ // Expected length after encoding
115
+ const expectedLength = Math.ceil((types_1.LOCAL_SECRET_SIZE * 8) / 5);
116
+ if (cleaned.length !== expectedLength) {
117
+ throw new Error(`Invalid LocalSecret length: expected ${expectedLength} characters, got ${cleaned.length}`);
118
+ }
119
+ return decodeFromCharset(cleaned);
120
+ }
121
+ /**
122
+ * Validate a formatted LocalSecret string
123
+ *
124
+ * @param formatted - The formatted LocalSecret string
125
+ * @returns true if valid, false otherwise
126
+ */
127
+ function isValidFormattedLocalSecret(formatted) {
128
+ try {
129
+ parseLocalSecret(formatted);
130
+ return true;
131
+ }
132
+ catch (_a) {
133
+ return false;
134
+ }
135
+ }
136
+ /**
137
+ * Mask a LocalSecret for partial display
138
+ *
139
+ * Shows only the first and last groups, masking the middle.
140
+ * Example: A7K2M9-******-******-******-******-V6Z4C1
141
+ *
142
+ * @param formatted - The formatted LocalSecret
143
+ * @returns Masked version for display
144
+ */
145
+ function maskLocalSecret(formatted) {
146
+ const groups = formatted.split("-");
147
+ if (groups.length <= 2) {
148
+ return formatted;
149
+ }
150
+ const masked = groups.map((group, index) => {
151
+ if (index === 0 || index === groups.length - 1) {
152
+ return group;
153
+ }
154
+ return "*".repeat(group.length);
155
+ });
156
+ return masked.join("-");
157
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * LocalSecret Generator
3
+ *
4
+ * Generates cryptographically secure LocalSecrets.
5
+ * Issue #1649
6
+ */
7
+ /**
8
+ * Generate a new LocalSecret
9
+ *
10
+ * Uses the Web Crypto API's getRandomValues for cryptographically
11
+ * secure random number generation.
12
+ *
13
+ * @returns 32-byte (256-bit) random LocalSecret
14
+ */
15
+ export declare function generateLocalSecret(): Uint8Array;
16
+ /**
17
+ * Generate a unique device ID
18
+ *
19
+ * Used to identify devices for LocalSecret management.
20
+ *
21
+ * @returns UUID v4 string
22
+ */
23
+ export declare function generateDeviceId(): string;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * LocalSecret Generator
4
+ *
5
+ * Generates cryptographically secure LocalSecrets.
6
+ * Issue #1649
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.generateLocalSecret = generateLocalSecret;
10
+ exports.generateDeviceId = generateDeviceId;
11
+ const types_1 = require("./types");
12
+ /**
13
+ * Generate a new LocalSecret
14
+ *
15
+ * Uses the Web Crypto API's getRandomValues for cryptographically
16
+ * secure random number generation.
17
+ *
18
+ * @returns 32-byte (256-bit) random LocalSecret
19
+ */
20
+ function generateLocalSecret() {
21
+ return crypto.getRandomValues(new Uint8Array(types_1.LOCAL_SECRET_SIZE));
22
+ }
23
+ /**
24
+ * Generate a unique device ID
25
+ *
26
+ * Used to identify devices for LocalSecret management.
27
+ *
28
+ * @returns UUID v4 string
29
+ */
30
+ function generateDeviceId() {
31
+ // Use crypto.randomUUID if available (modern browsers)
32
+ if (typeof crypto.randomUUID === "function") {
33
+ return crypto.randomUUID();
34
+ }
35
+ // Fallback for older browsers
36
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
37
+ // Set version (4) and variant (8, 9, A, or B)
38
+ // eslint-disable-next-line no-bitwise, fp/no-mutation -- UUID v4 specification requires bitwise operations
39
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
40
+ // eslint-disable-next-line no-bitwise, fp/no-mutation -- UUID v4 specification requires bitwise operations
41
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
42
+ // Convert to hex string with dashes
43
+ const hex = Array.from(bytes)
44
+ .map(b => b.toString(16).padStart(2, "0"))
45
+ .join("");
46
+ return [
47
+ hex.slice(0, 8),
48
+ hex.slice(8, 12),
49
+ hex.slice(12, 16),
50
+ hex.slice(16, 20),
51
+ hex.slice(20, 32),
52
+ ].join("-");
53
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * LocalSecret Module
3
+ *
4
+ * Provides generation, formatting, storage, and management of
5
+ * device-bound LocalSecrets for Two-Secret Key Derivation (2SKD).
6
+ *
7
+ * Issue #1649
8
+ */
9
+ export * from "./types";
10
+ export { generateLocalSecret, generateDeviceId } from "./generator";
11
+ export { formatLocalSecret, parseLocalSecret, isValidFormattedLocalSecret, maskLocalSecret, } from "./format";
12
+ export { storeLocalSecret, getLocalSecret, deleteLocalSecret, hasLocalSecret, getLocalSecretRecord, getOrCreateDeviceId, createIndexedDBStorage, } from "./storage";
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * LocalSecret Module
4
+ *
5
+ * Provides generation, formatting, storage, and management of
6
+ * device-bound LocalSecrets for Two-Secret Key Derivation (2SKD).
7
+ *
8
+ * Issue #1649
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
22
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.createIndexedDBStorage = exports.getOrCreateDeviceId = exports.getLocalSecretRecord = exports.hasLocalSecret = exports.deleteLocalSecret = exports.getLocalSecret = exports.storeLocalSecret = exports.maskLocalSecret = exports.isValidFormattedLocalSecret = exports.parseLocalSecret = exports.formatLocalSecret = exports.generateDeviceId = exports.generateLocalSecret = void 0;
26
+ // Re-export types
27
+ __exportStar(require("./types"), exports);
28
+ // Re-export generator
29
+ var generator_1 = require("./generator");
30
+ Object.defineProperty(exports, "generateLocalSecret", { enumerable: true, get: function () { return generator_1.generateLocalSecret; } });
31
+ Object.defineProperty(exports, "generateDeviceId", { enumerable: true, get: function () { return generator_1.generateDeviceId; } });
32
+ // Re-export formatter
33
+ var format_1 = require("./format");
34
+ Object.defineProperty(exports, "formatLocalSecret", { enumerable: true, get: function () { return format_1.formatLocalSecret; } });
35
+ Object.defineProperty(exports, "parseLocalSecret", { enumerable: true, get: function () { return format_1.parseLocalSecret; } });
36
+ Object.defineProperty(exports, "isValidFormattedLocalSecret", { enumerable: true, get: function () { return format_1.isValidFormattedLocalSecret; } });
37
+ Object.defineProperty(exports, "maskLocalSecret", { enumerable: true, get: function () { return format_1.maskLocalSecret; } });
38
+ // Re-export storage
39
+ var storage_1 = require("./storage");
40
+ Object.defineProperty(exports, "storeLocalSecret", { enumerable: true, get: function () { return storage_1.storeLocalSecret; } });
41
+ Object.defineProperty(exports, "getLocalSecret", { enumerable: true, get: function () { return storage_1.getLocalSecret; } });
42
+ Object.defineProperty(exports, "deleteLocalSecret", { enumerable: true, get: function () { return storage_1.deleteLocalSecret; } });
43
+ Object.defineProperty(exports, "hasLocalSecret", { enumerable: true, get: function () { return storage_1.hasLocalSecret; } });
44
+ Object.defineProperty(exports, "getLocalSecretRecord", { enumerable: true, get: function () { return storage_1.getLocalSecretRecord; } });
45
+ Object.defineProperty(exports, "getOrCreateDeviceId", { enumerable: true, get: function () { return storage_1.getOrCreateDeviceId; } });
46
+ Object.defineProperty(exports, "createIndexedDBStorage", { enumerable: true, get: function () { return storage_1.createIndexedDBStorage; } });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * LocalSecret Storage
3
+ *
4
+ * IndexedDB-based storage for LocalSecrets.
5
+ * The LocalSecret is stored locally on the device and never sent to servers.
6
+ *
7
+ * Issue #1649
8
+ */
9
+ import type { LocalSecretStorage, StoredLocalSecret } from "./types";
10
+ /**
11
+ * Get or create a unique device ID
12
+ *
13
+ * The device ID is stored in localStorage for persistence.
14
+ */
15
+ export declare function getOrCreateDeviceId(): Promise<string>;
16
+ /**
17
+ * Store a LocalSecret in IndexedDB
18
+ *
19
+ * @param userId - User's Cognito sub
20
+ * @param secret - The 32-byte LocalSecret
21
+ */
22
+ export declare function storeLocalSecret(userId: string, secret: Uint8Array): Promise<void>;
23
+ /**
24
+ * Retrieve a LocalSecret from IndexedDB
25
+ *
26
+ * @param userId - User's Cognito sub
27
+ * @returns The LocalSecret or null if not found
28
+ */
29
+ export declare function getLocalSecret(userId: string): Promise<Uint8Array | null>;
30
+ /**
31
+ * Delete a LocalSecret from IndexedDB
32
+ *
33
+ * @param userId - User's Cognito sub
34
+ */
35
+ export declare function deleteLocalSecret(userId: string): Promise<void>;
36
+ /**
37
+ * Check if a LocalSecret exists for a user
38
+ *
39
+ * @param userId - User's Cognito sub
40
+ * @returns true if a LocalSecret exists
41
+ */
42
+ export declare function hasLocalSecret(userId: string): Promise<boolean>;
43
+ /**
44
+ * Get the stored LocalSecret record (including metadata)
45
+ *
46
+ * @param userId - User's Cognito sub
47
+ * @returns The full storage record or null
48
+ */
49
+ export declare function getLocalSecretRecord(userId: string): Promise<StoredLocalSecret | null>;
50
+ /**
51
+ * Create a LocalSecretStorage implementation using IndexedDB
52
+ */
53
+ export declare function createIndexedDBStorage(): LocalSecretStorage;