@raytio/core 11.5.0 → 11.7.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,207 @@
1
+ "use strict";
2
+ /**
3
+ * LocalSecret Storage
4
+ *
5
+ * IndexedDB-based storage for LocalSecrets.
6
+ * The LocalSecret is stored locally on the device and never sent to servers.
7
+ *
8
+ * Issue #1649
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.getOrCreateDeviceId = getOrCreateDeviceId;
12
+ exports.storeLocalSecret = storeLocalSecret;
13
+ exports.getLocalSecret = getLocalSecret;
14
+ exports.deleteLocalSecret = deleteLocalSecret;
15
+ exports.hasLocalSecret = hasLocalSecret;
16
+ exports.getLocalSecretRecord = getLocalSecretRecord;
17
+ exports.createIndexedDBStorage = createIndexedDBStorage;
18
+ const types_1 = require("./types");
19
+ const generator_1 = require("./generator");
20
+ // Cached device ID
21
+ let deviceId = null;
22
+ /**
23
+ * Get or create a unique device ID
24
+ *
25
+ * The device ID is stored in localStorage for persistence.
26
+ */
27
+ async function getOrCreateDeviceId() {
28
+ if (deviceId) {
29
+ return deviceId;
30
+ }
31
+ const storageKey = "raytio-device-id";
32
+ try {
33
+ const stored = localStorage.getItem(storageKey);
34
+ if (stored) {
35
+ // eslint-disable-next-line fp/no-mutation -- Cache assignment
36
+ deviceId = stored;
37
+ return deviceId;
38
+ }
39
+ }
40
+ catch (_a) {
41
+ // localStorage not available
42
+ }
43
+ // Generate new device ID
44
+ const newDeviceId = (0, generator_1.generateDeviceId)();
45
+ // eslint-disable-next-line fp/no-mutation -- Cache assignment
46
+ deviceId = newDeviceId;
47
+ try {
48
+ localStorage.setItem(storageKey, newDeviceId);
49
+ }
50
+ catch (_b) {
51
+ // localStorage not available, device ID will be regenerated on next session
52
+ }
53
+ return newDeviceId;
54
+ }
55
+ /**
56
+ * Open the IndexedDB database for LocalSecret storage
57
+ */
58
+ function openDatabase() {
59
+ return new Promise((resolve, reject) => {
60
+ const request = indexedDB.open(types_1.LOCAL_SECRET_DB_CONFIG.name, types_1.LOCAL_SECRET_DB_CONFIG.version);
61
+ /* eslint-disable fp/no-mutation -- IndexedDB API requires callback assignment */
62
+ request.onerror = () => {
63
+ reject(new Error("Failed to open LocalSecret database"));
64
+ };
65
+ request.onsuccess = () => {
66
+ resolve(request.result);
67
+ };
68
+ request.onupgradeneeded = event => {
69
+ const db = event.target.result;
70
+ // Create object store if it doesn't exist
71
+ if (!db.objectStoreNames.contains(types_1.LOCAL_SECRET_DB_CONFIG.storeName)) {
72
+ db.createObjectStore(types_1.LOCAL_SECRET_DB_CONFIG.storeName);
73
+ }
74
+ };
75
+ /* eslint-enable fp/no-mutation */
76
+ });
77
+ }
78
+ /**
79
+ * Store a LocalSecret in IndexedDB
80
+ *
81
+ * @param userId - User's Cognito sub
82
+ * @param secret - The 32-byte LocalSecret
83
+ */
84
+ async function storeLocalSecret(userId, secret) {
85
+ const db = await openDatabase();
86
+ const deviceIdValue = await getOrCreateDeviceId();
87
+ const record = {
88
+ secret,
89
+ userId,
90
+ createdAt: new Date().toISOString(),
91
+ deviceId: deviceIdValue,
92
+ };
93
+ return new Promise((resolve, reject) => {
94
+ const transaction = db.transaction(types_1.LOCAL_SECRET_DB_CONFIG.storeName, "readwrite");
95
+ const store = transaction.objectStore(types_1.LOCAL_SECRET_DB_CONFIG.storeName);
96
+ const request = store.put(record, `local-secret-${userId}`);
97
+ /* eslint-disable fp/no-mutation -- IndexedDB API requires callback assignment */
98
+ request.onerror = () => {
99
+ reject(new Error("Failed to store LocalSecret"));
100
+ };
101
+ request.onsuccess = () => {
102
+ resolve();
103
+ };
104
+ transaction.oncomplete = () => {
105
+ db.close();
106
+ };
107
+ /* eslint-enable fp/no-mutation */
108
+ });
109
+ }
110
+ /**
111
+ * Retrieve a LocalSecret from IndexedDB
112
+ *
113
+ * @param userId - User's Cognito sub
114
+ * @returns The LocalSecret or null if not found
115
+ */
116
+ async function getLocalSecret(userId) {
117
+ const db = await openDatabase();
118
+ return new Promise((resolve, reject) => {
119
+ const transaction = db.transaction(types_1.LOCAL_SECRET_DB_CONFIG.storeName, "readonly");
120
+ const store = transaction.objectStore(types_1.LOCAL_SECRET_DB_CONFIG.storeName);
121
+ const request = store.get(`local-secret-${userId}`);
122
+ /* eslint-disable fp/no-mutation -- IndexedDB API requires callback assignment */
123
+ request.onerror = () => {
124
+ reject(new Error("Failed to retrieve LocalSecret"));
125
+ };
126
+ request.onsuccess = () => {
127
+ var _a;
128
+ const record = request.result;
129
+ resolve((_a = record === null || record === void 0 ? void 0 : record.secret) !== null && _a !== void 0 ? _a : null);
130
+ };
131
+ transaction.oncomplete = () => {
132
+ db.close();
133
+ };
134
+ /* eslint-enable fp/no-mutation */
135
+ });
136
+ }
137
+ /**
138
+ * Delete a LocalSecret from IndexedDB
139
+ *
140
+ * @param userId - User's Cognito sub
141
+ */
142
+ async function deleteLocalSecret(userId) {
143
+ const db = await openDatabase();
144
+ return new Promise((resolve, reject) => {
145
+ const transaction = db.transaction(types_1.LOCAL_SECRET_DB_CONFIG.storeName, "readwrite");
146
+ const store = transaction.objectStore(types_1.LOCAL_SECRET_DB_CONFIG.storeName);
147
+ const request = store.delete(`local-secret-${userId}`);
148
+ /* eslint-disable fp/no-mutation -- IndexedDB API requires callback assignment */
149
+ request.onerror = () => {
150
+ reject(new Error("Failed to delete LocalSecret"));
151
+ };
152
+ request.onsuccess = () => {
153
+ resolve();
154
+ };
155
+ transaction.oncomplete = () => {
156
+ db.close();
157
+ };
158
+ /* eslint-enable fp/no-mutation */
159
+ });
160
+ }
161
+ /**
162
+ * Check if a LocalSecret exists for a user
163
+ *
164
+ * @param userId - User's Cognito sub
165
+ * @returns true if a LocalSecret exists
166
+ */
167
+ async function hasLocalSecret(userId) {
168
+ const secret = await getLocalSecret(userId);
169
+ return secret !== null;
170
+ }
171
+ /**
172
+ * Get the stored LocalSecret record (including metadata)
173
+ *
174
+ * @param userId - User's Cognito sub
175
+ * @returns The full storage record or null
176
+ */
177
+ async function getLocalSecretRecord(userId) {
178
+ const db = await openDatabase();
179
+ return new Promise((resolve, reject) => {
180
+ const transaction = db.transaction(types_1.LOCAL_SECRET_DB_CONFIG.storeName, "readonly");
181
+ const store = transaction.objectStore(types_1.LOCAL_SECRET_DB_CONFIG.storeName);
182
+ const request = store.get(`local-secret-${userId}`);
183
+ /* eslint-disable fp/no-mutation -- IndexedDB API requires callback assignment */
184
+ request.onerror = () => {
185
+ reject(new Error("Failed to retrieve LocalSecret record"));
186
+ };
187
+ request.onsuccess = () => {
188
+ var _a;
189
+ resolve((_a = request.result) !== null && _a !== void 0 ? _a : null);
190
+ };
191
+ transaction.oncomplete = () => {
192
+ db.close();
193
+ };
194
+ /* eslint-enable fp/no-mutation */
195
+ });
196
+ }
197
+ /**
198
+ * Create a LocalSecretStorage implementation using IndexedDB
199
+ */
200
+ function createIndexedDBStorage() {
201
+ return {
202
+ store: storeLocalSecret,
203
+ get: getLocalSecret,
204
+ delete: deleteLocalSecret,
205
+ exists: hasLocalSecret,
206
+ };
207
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * LocalSecret Types
3
+ *
4
+ * Types for the device-bound LocalSecret used in 2SKD.
5
+ * Issue #1649
6
+ */
7
+ /**
8
+ * LocalSecret storage record
9
+ */
10
+ export interface StoredLocalSecret {
11
+ /** The 32-byte LocalSecret */
12
+ secret: Uint8Array;
13
+ /** User's Cognito sub (user ID) */
14
+ userId: string;
15
+ /** When the LocalSecret was created */
16
+ createdAt: string;
17
+ /** Unique identifier for this device */
18
+ deviceId: string;
19
+ }
20
+ /**
21
+ * LocalSecret display format
22
+ *
23
+ * Formatted for human readability with dashes between groups.
24
+ * Uses unambiguous character set (no 0, 1, I, O).
25
+ */
26
+ export interface FormattedLocalSecret {
27
+ /** Full formatted string (e.g., "A7K2M9-X4P8N3-...") */
28
+ formatted: string;
29
+ /** Individual groups for display */
30
+ groups: string[];
31
+ }
32
+ /**
33
+ * LocalSecret storage interface
34
+ *
35
+ * Abstraction for storing LocalSecrets (IndexedDB, Keychain, etc.)
36
+ */
37
+ export interface LocalSecretStorage {
38
+ /** Store a LocalSecret for a user */
39
+ store(userId: string, secret: Uint8Array): Promise<void>;
40
+ /** Retrieve a LocalSecret for a user */
41
+ get(userId: string): Promise<Uint8Array | null>;
42
+ /** Delete a LocalSecret for a user */
43
+ delete(userId: string): Promise<void>;
44
+ /** Check if a LocalSecret exists for a user */
45
+ exists(userId: string): Promise<boolean>;
46
+ }
47
+ /**
48
+ * IndexedDB configuration for LocalSecret storage
49
+ */
50
+ export declare const LOCAL_SECRET_DB_CONFIG: {
51
+ readonly name: "raytio-secrets";
52
+ readonly version: 1;
53
+ readonly storeName: "local-secrets";
54
+ };
55
+ /**
56
+ * LocalSecret size in bytes (256 bits)
57
+ */
58
+ export declare const LOCAL_SECRET_SIZE = 32;
59
+ /**
60
+ * Character set for LocalSecret display format
61
+ *
62
+ * Excludes ambiguous characters: 0, 1, I, O
63
+ */
64
+ export declare const LOCAL_SECRET_CHARSET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
65
+ /**
66
+ * Number of characters per group in formatted display
67
+ */
68
+ export declare const LOCAL_SECRET_GROUP_SIZE = 6;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * LocalSecret Types
4
+ *
5
+ * Types for the device-bound LocalSecret used in 2SKD.
6
+ * Issue #1649
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.LOCAL_SECRET_GROUP_SIZE = exports.LOCAL_SECRET_CHARSET = exports.LOCAL_SECRET_SIZE = exports.LOCAL_SECRET_DB_CONFIG = void 0;
10
+ /**
11
+ * IndexedDB configuration for LocalSecret storage
12
+ */
13
+ exports.LOCAL_SECRET_DB_CONFIG = {
14
+ name: "raytio-secrets",
15
+ version: 1,
16
+ storeName: "local-secrets",
17
+ };
18
+ /**
19
+ * LocalSecret size in bytes (256 bits)
20
+ */
21
+ exports.LOCAL_SECRET_SIZE = 32;
22
+ /**
23
+ * Character set for LocalSecret display format
24
+ *
25
+ * Excludes ambiguous characters: 0, 1, I, O
26
+ */
27
+ exports.LOCAL_SECRET_CHARSET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
28
+ /**
29
+ * Number of characters per group in formatted display
30
+ */
31
+ exports.LOCAL_SECRET_GROUP_SIZE = 6;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * PGP Private Key Encryption
3
+ *
4
+ * Encrypts private keys using AES-GCM with the user's 2SKD KEK.
5
+ * Issue #1374
6
+ */
7
+ /**
8
+ * Encryption result containing ciphertext and IV
9
+ */
10
+ export interface EncryptedPrivateKey {
11
+ /** AES-GCM encrypted private key bytes */
12
+ encryptedPrivateKey: Uint8Array;
13
+ /** 12-byte initialization vector for decryption */
14
+ iv: Uint8Array;
15
+ }
16
+ /**
17
+ * Encrypt private key bytes with the user's KEK using AES-GCM
18
+ *
19
+ * Uses a random 12-byte IV for each encryption operation.
20
+ *
21
+ * @param privateKeyBytes - Raw private key bytes (PKCS8 format)
22
+ * @param kek - 32-byte Key Encryption Key from 2SKD
23
+ * @returns Encrypted private key and IV
24
+ */
25
+ export declare function encryptPrivateKey(privateKeyBytes: Uint8Array, kek: Uint8Array): Promise<EncryptedPrivateKey>;
26
+ /**
27
+ * Decrypt private key bytes with the user's KEK using AES-GCM
28
+ *
29
+ * @param encryptedPrivateKey - AES-GCM encrypted private key bytes
30
+ * @param iv - 12-byte initialization vector used during encryption
31
+ * @param kek - 32-byte Key Encryption Key from 2SKD
32
+ * @returns Decrypted private key bytes (PKCS8 format)
33
+ * @throws Error if decryption fails (wrong key or tampered data)
34
+ */
35
+ export declare function decryptPrivateKey(encryptedPrivateKey: Uint8Array, iv: Uint8Array, kek: Uint8Array): Promise<Uint8Array>;
36
+ /**
37
+ * Import private key bytes as a CryptoKey for RSA-PSS signing
38
+ *
39
+ * @param privateKeyBytes - Private key in PKCS8 format
40
+ * @returns CryptoKey configured for RSA-PSS signing with SHA-256
41
+ */
42
+ export declare function importPrivateKey(privateKeyBytes: Uint8Array): Promise<CryptoKey>;
43
+ /**
44
+ * Import a PEM-encoded public key as a CryptoKey for RSA-PSS verification
45
+ *
46
+ * @param publicKeyPem - Public key in PEM format (SPKI)
47
+ * @returns CryptoKey configured for RSA-PSS verification with SHA-256
48
+ */
49
+ export declare function importPublicKey(publicKeyPem: string): Promise<CryptoKey>;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * PGP Private Key Encryption
4
+ *
5
+ * Encrypts private keys using AES-GCM with the user's 2SKD KEK.
6
+ * Issue #1374
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.encryptPrivateKey = encryptPrivateKey;
10
+ exports.decryptPrivateKey = decryptPrivateKey;
11
+ exports.importPrivateKey = importPrivateKey;
12
+ exports.importPublicKey = importPublicKey;
13
+ const types_1 = require("./types");
14
+ /**
15
+ * Import a raw KEK as a CryptoKey for AES-GCM operations
16
+ *
17
+ * @param kek - 32-byte KEK (Key Encryption Key)
18
+ * @returns CryptoKey for encryption/decryption
19
+ */
20
+ async function importKek(kek) {
21
+ return crypto.subtle.importKey("raw", kek, { name: "AES-GCM" }, false, [
22
+ "encrypt",
23
+ "decrypt",
24
+ ]);
25
+ }
26
+ /**
27
+ * Encrypt private key bytes with the user's KEK using AES-GCM
28
+ *
29
+ * Uses a random 12-byte IV for each encryption operation.
30
+ *
31
+ * @param privateKeyBytes - Raw private key bytes (PKCS8 format)
32
+ * @param kek - 32-byte Key Encryption Key from 2SKD
33
+ * @returns Encrypted private key and IV
34
+ */
35
+ async function encryptPrivateKey(privateKeyBytes, kek) {
36
+ if (kek.length !== 32) {
37
+ throw new Error("KEK must be 32 bytes for AES-256");
38
+ }
39
+ if (privateKeyBytes.length === 0) {
40
+ throw new Error("Private key cannot be empty");
41
+ }
42
+ // Generate random IV
43
+ const iv = new Uint8Array(types_1.AES_GCM_IV_SIZE);
44
+ crypto.getRandomValues(iv);
45
+ // Import KEK as CryptoKey
46
+ const cryptoKey = await importKek(kek);
47
+ // Encrypt with AES-GCM
48
+ const encryptedBuffer = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, cryptoKey, privateKeyBytes);
49
+ return {
50
+ encryptedPrivateKey: new Uint8Array(encryptedBuffer),
51
+ iv,
52
+ };
53
+ }
54
+ /**
55
+ * Decrypt private key bytes with the user's KEK using AES-GCM
56
+ *
57
+ * @param encryptedPrivateKey - AES-GCM encrypted private key bytes
58
+ * @param iv - 12-byte initialization vector used during encryption
59
+ * @param kek - 32-byte Key Encryption Key from 2SKD
60
+ * @returns Decrypted private key bytes (PKCS8 format)
61
+ * @throws Error if decryption fails (wrong key or tampered data)
62
+ */
63
+ async function decryptPrivateKey(encryptedPrivateKey, iv, kek) {
64
+ if (kek.length !== 32) {
65
+ throw new Error("KEK must be 32 bytes for AES-256");
66
+ }
67
+ if (iv.length !== types_1.AES_GCM_IV_SIZE) {
68
+ throw new Error("IV must be 12 bytes");
69
+ }
70
+ if (encryptedPrivateKey.length === 0) {
71
+ throw new Error("Encrypted private key cannot be empty");
72
+ }
73
+ // Import KEK as CryptoKey
74
+ const cryptoKey = await importKek(kek);
75
+ // Decrypt with AES-GCM
76
+ const decryptedBuffer = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, cryptoKey, encryptedPrivateKey);
77
+ return new Uint8Array(decryptedBuffer);
78
+ }
79
+ /**
80
+ * Import private key bytes as a CryptoKey for RSA-PSS signing
81
+ *
82
+ * @param privateKeyBytes - Private key in PKCS8 format
83
+ * @returns CryptoKey configured for RSA-PSS signing with SHA-256
84
+ */
85
+ async function importPrivateKey(privateKeyBytes) {
86
+ return crypto.subtle.importKey("pkcs8", privateKeyBytes, { name: "RSA-PSS", hash: "SHA-256" }, false, ["sign"]);
87
+ }
88
+ /**
89
+ * Import a PEM-encoded public key as a CryptoKey for RSA-PSS verification
90
+ *
91
+ * @param publicKeyPem - Public key in PEM format (SPKI)
92
+ * @returns CryptoKey configured for RSA-PSS verification with SHA-256
93
+ */
94
+ async function importPublicKey(publicKeyPem) {
95
+ // Extract base64 content from PEM
96
+ const pemContents = publicKeyPem
97
+ .replace(/-----BEGIN PUBLIC KEY-----/, "")
98
+ .replace(/-----END PUBLIC KEY-----/, "")
99
+ .replace(/\s/g, "");
100
+ // Decode base64 to bytes using functional approach
101
+ const binaryString = atob(pemContents);
102
+ const bytes = Uint8Array.from(binaryString, c => c.charCodeAt(0));
103
+ return crypto.subtle.importKey("spki", bytes, { name: "RSA-PSS", hash: "SHA-256" }, false, ["verify"]);
104
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * PGP Key Export
3
+ *
4
+ * Export PKCS8 private keys to OpenPGP armored format.
5
+ * Compatible with GPG and GitHub.
6
+ *
7
+ * This module converts existing PKCS8 key material to OpenPGP format,
8
+ * preserving the original cryptographic material rather than generating
9
+ * new keys.
10
+ *
11
+ * Issue #1374
12
+ */
13
+ /**
14
+ * Options for exporting a PGP key
15
+ */
16
+ export interface ExportPGPKeyOptions {
17
+ /** Optional passphrase to encrypt the exported key */
18
+ passphrase?: string;
19
+ /** User IDs to associate with the key */
20
+ userIds?: Array<{
21
+ name?: string;
22
+ email?: string;
23
+ }>;
24
+ /** Key creation date (defaults to current time) */
25
+ date?: Date;
26
+ }
27
+ /**
28
+ * Result of exporting a PGP key
29
+ */
30
+ export interface ExportedPGPKey {
31
+ /** Armored private key in OpenPGP format */
32
+ armoredPrivateKey: string;
33
+ /** Armored public key in OpenPGP format */
34
+ armoredPublicKey: string;
35
+ /** Whether the exported private key is encrypted */
36
+ isEncrypted: boolean;
37
+ /** Key fingerprint (SHA-256 of SPKI bytes, first 40 hex chars) */
38
+ fingerprint: string;
39
+ }
40
+ /**
41
+ * Error thrown when key export fails
42
+ */
43
+ export declare class PGPKeyExportError extends Error {
44
+ readonly code: string;
45
+ constructor(message: string, code: string);
46
+ }
47
+ /**
48
+ * Export a PKCS8 private key to OpenPGP armored format
49
+ *
50
+ * This function converts existing PKCS8 RSA key material to OpenPGP format,
51
+ * preserving the original cryptographic material. The exported key can be
52
+ * used with GPG, GitHub, and other OpenPGP-compatible tools.
53
+ *
54
+ * @param privateKeyBytes - PKCS8 encoded private key bytes
55
+ * @param options - Export options (passphrase, userIds, date)
56
+ * @returns Armored private and public keys with fingerprint
57
+ * @throws PGPKeyExportError if export fails
58
+ */
59
+ export declare function exportPGPKeyToArmored(privateKeyBytes: Uint8Array, options?: ExportPGPKeyOptions): Promise<ExportedPGPKey>;