@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,322 @@
1
+ "use strict";
2
+ /**
3
+ * PGP Key Export
4
+ *
5
+ * Export PKCS8 private keys to OpenPGP armored format.
6
+ * Compatible with GPG and GitHub.
7
+ *
8
+ * This module converts existing PKCS8 key material to OpenPGP format,
9
+ * preserving the original cryptographic material rather than generating
10
+ * new keys.
11
+ *
12
+ * Issue #1374
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.PGPKeyExportError = void 0;
39
+ exports.exportPGPKeyToArmored = exportPGPKeyToArmored;
40
+ const generator_1 = require("./generator");
41
+ /**
42
+ * Error thrown when key export fails
43
+ */
44
+ // eslint-disable-next-line fp/no-class -- Custom error class for specific error handling
45
+ class PGPKeyExportError extends Error {
46
+ constructor(message, code) {
47
+ super(message);
48
+ // eslint-disable-next-line fp/no-this, fp/no-mutation -- Required for Error subclassing
49
+ this.name = "PGPKeyExportError";
50
+ // eslint-disable-next-line fp/no-this, fp/no-mutation -- Required for Error subclassing
51
+ this.code = code;
52
+ }
53
+ }
54
+ exports.PGPKeyExportError = PGPKeyExportError;
55
+ // ============================================================================
56
+ // Helper functions
57
+ // ============================================================================
58
+ /**
59
+ * Convert base64url encoding to Uint8Array
60
+ *
61
+ * @param base64url - Base64url encoded string
62
+ * @returns Decoded bytes
63
+ */
64
+ function base64UrlToUint8Array(base64url) {
65
+ // Convert base64url to base64
66
+ const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
67
+ // Add padding if needed
68
+ const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), "=");
69
+ const binary = atob(paddedBase64);
70
+ const bytes = new Uint8Array(binary.length);
71
+ Array.from({ length: binary.length }).forEach((_, i) => {
72
+ // eslint-disable-next-line fp/no-mutation -- Building byte array element by element
73
+ bytes[i] = binary.charCodeAt(i);
74
+ });
75
+ return bytes;
76
+ }
77
+ /**
78
+ * Import PKCS8 private key bytes as an extractable CryptoKey
79
+ *
80
+ * @param privateKeyBytes - PKCS8 encoded private key
81
+ * @returns Extractable CryptoKey
82
+ */
83
+ async function importPrivateKeyExtractable(privateKeyBytes) {
84
+ return crypto.subtle.importKey("pkcs8", privateKeyBytes, { name: "RSA-PSS", hash: "SHA-256" }, true, // extractable
85
+ ["sign"]);
86
+ }
87
+ /**
88
+ * Extract RSA parameters from a CryptoKey as JWK
89
+ *
90
+ * @param privateKey - CryptoKey to extract parameters from
91
+ * @returns JWK with RSA parameters
92
+ */
93
+ async function extractRSAParams(privateKey) {
94
+ return crypto.subtle.exportKey("jwk", privateKey);
95
+ }
96
+ /**
97
+ * Compute modular inverse: u = q^-1 mod p
98
+ * Used for the 'u' parameter in OpenPGP RSA keys
99
+ */
100
+ function modInverse(a, modulus) {
101
+ // Extended Euclidean Algorithm
102
+ const m0 = modulus;
103
+ let x0 = BigInt(0);
104
+ let x1 = BigInt(1);
105
+ if (modulus === BigInt(1))
106
+ return BigInt(0);
107
+ let aVal = a;
108
+ let mVal = modulus;
109
+ while (aVal > BigInt(1)) {
110
+ const q = aVal / mVal;
111
+ let t = mVal;
112
+ // eslint-disable-next-line fp/no-mutation -- Algorithm requires mutation
113
+ mVal = aVal % mVal;
114
+ // eslint-disable-next-line fp/no-mutation -- Algorithm requires mutation
115
+ aVal = t;
116
+ // eslint-disable-next-line fp/no-mutation -- Algorithm requires mutation
117
+ t = x0;
118
+ // eslint-disable-next-line fp/no-mutation -- Algorithm requires mutation
119
+ x0 = x1 - q * x0;
120
+ // eslint-disable-next-line fp/no-mutation -- Algorithm requires mutation
121
+ x1 = t;
122
+ }
123
+ // Make x1 positive
124
+ if (x1 < BigInt(0)) {
125
+ // eslint-disable-next-line fp/no-mutation -- Ensuring positive result
126
+ x1 += m0;
127
+ }
128
+ return x1;
129
+ }
130
+ /**
131
+ * Convert BigInt to Uint8Array (big-endian)
132
+ */
133
+ function bigIntToUint8Array(num) {
134
+ const hex = num.toString(16);
135
+ // Ensure even number of hex digits
136
+ const paddedHex = hex.length % 2 === 0 ? hex : `0${hex}`;
137
+ const bytes = new Uint8Array(paddedHex.length / 2);
138
+ Array.from({ length: bytes.length }).forEach((_, i) => {
139
+ // eslint-disable-next-line fp/no-mutation -- Building byte array element by element
140
+ bytes[i] = parseInt(paddedHex.slice(i * 2, i * 2 + 2), 16);
141
+ });
142
+ return bytes;
143
+ }
144
+ /**
145
+ * Convert Uint8Array to BigInt (big-endian)
146
+ */
147
+ function uint8ArrayToBigInt(bytes) {
148
+ let result = BigInt(0);
149
+ for (const byte of bytes) {
150
+ // eslint-disable-next-line fp/no-mutation, no-bitwise -- Building bigint value
151
+ result = (result << BigInt(8)) + BigInt(byte);
152
+ }
153
+ return result;
154
+ }
155
+ // ============================================================================
156
+ // Public API
157
+ // ============================================================================
158
+ /**
159
+ * Export a PKCS8 private key to OpenPGP armored format
160
+ *
161
+ * This function converts existing PKCS8 RSA key material to OpenPGP format,
162
+ * preserving the original cryptographic material. The exported key can be
163
+ * used with GPG, GitHub, and other OpenPGP-compatible tools.
164
+ *
165
+ * @param privateKeyBytes - PKCS8 encoded private key bytes
166
+ * @param options - Export options (passphrase, userIds, date)
167
+ * @returns Armored private and public keys with fingerprint
168
+ * @throws PGPKeyExportError if export fails
169
+ */
170
+ async function exportPGPKeyToArmored(privateKeyBytes, options = {}) {
171
+ var _a;
172
+ // Dynamic import to avoid bundling openpgp for users who don't need export
173
+ const openpgp = await Promise.resolve().then(() => __importStar(require("openpgp")));
174
+ // Cast enums to internal type for access to features enum
175
+ const internalEnums = openpgp.enums;
176
+ // Import the PKCS8 key as extractable and get JWK
177
+ let jwk;
178
+ try {
179
+ const cryptoKey = await importPrivateKeyExtractable(privateKeyBytes);
180
+ // eslint-disable-next-line fp/no-mutation -- Assigning result to declared variable
181
+ jwk = await extractRSAParams(cryptoKey);
182
+ }
183
+ catch (_b) {
184
+ throw new PGPKeyExportError("Failed to import private key. Please check the key format.", "IMPORT_ERROR");
185
+ }
186
+ // Validate we have all required RSA parameters
187
+ if (!jwk.n || !jwk.e || !jwk.d || !jwk.p || !jwk.q) {
188
+ throw new PGPKeyExportError("Invalid RSA key: missing required parameters.", "INVALID_KEY");
189
+ }
190
+ // Compute SPKI-based fingerprint (consistent with generator and import flows)
191
+ const publicCryptoKey = await crypto.subtle.importKey("jwk", { kty: "RSA", n: jwk.n, e: jwk.e }, { name: "RSA-PSS", hash: "SHA-256" }, true, ["verify"]);
192
+ const spkiBuffer = await crypto.subtle.exportKey("spki", publicCryptoKey);
193
+ const fingerprint = await (0, generator_1.computeKeyFingerprint)(new Uint8Array(spkiBuffer));
194
+ // Convert JWK parameters to Uint8Arrays for OpenPGP
195
+ const n = base64UrlToUint8Array(jwk.n);
196
+ const e = base64UrlToUint8Array(jwk.e);
197
+ const d = base64UrlToUint8Array(jwk.d);
198
+ const p = base64UrlToUint8Array(jwk.p);
199
+ const q = base64UrlToUint8Array(jwk.q);
200
+ // Compute u = q^-1 mod p (required by OpenPGP format)
201
+ const pBigInt = uint8ArrayToBigInt(p);
202
+ const qBigInt = uint8ArrayToBigInt(q);
203
+ const uBigInt = modInverse(qBigInt, pBigInt);
204
+ const u = bigIntToUint8Array(uBigInt);
205
+ // Determine user IDs for the key
206
+ const userIds = options.userIds && options.userIds.length > 0
207
+ ? options.userIds
208
+ : [{ name: "Raytio User", email: "user@rayt.io" }];
209
+ const creationDate = (_a = options.date) !== null && _a !== void 0 ? _a : new Date();
210
+ try {
211
+ // Create SecretKeyPacket with our existing key material
212
+ // The constructor doesn't take date in the types, but does at runtime
213
+ const SecretKeyPacketClass = openpgp.SecretKeyPacket;
214
+ const secretKeyPacket = new SecretKeyPacketClass(creationDate);
215
+ // Set the algorithm to RSA Encrypt-Sign
216
+ // eslint-disable-next-line fp/no-mutation -- Required for packet construction
217
+ secretKeyPacket.algorithm = internalEnums.publicKey.rsaEncryptSign;
218
+ // Set the public parameters (n, e)
219
+ // eslint-disable-next-line fp/no-mutation -- Required for packet construction
220
+ secretKeyPacket.publicParams = { n, e };
221
+ // Set the private parameters (d, p, q, u)
222
+ // eslint-disable-next-line fp/no-mutation -- Required for packet construction
223
+ secretKeyPacket.privateParams = { d, p, q, u };
224
+ // Mark as not encrypted (we'll encrypt later if passphrase provided)
225
+ // eslint-disable-next-line fp/no-mutation -- Required for packet construction
226
+ secretKeyPacket.isEncrypted = false;
227
+ // Compute fingerprint and key ID
228
+ await secretKeyPacket.computeFingerprintAndKeyID();
229
+ // Create a UserIDPacket for each user ID
230
+ const userIdPackets = userIds.map(uid => {
231
+ var _a, _b;
232
+ return openpgp.UserIDPacket.fromObject({
233
+ name: (_a = uid.name) !== null && _a !== void 0 ? _a : "",
234
+ email: (_b = uid.email) !== null && _b !== void 0 ? _b : "",
235
+ });
236
+ });
237
+ // Create self-certification signature for each user ID
238
+ const signaturePackets = await Promise.all(userIdPackets.map(async (userIdPacket) => {
239
+ const SignaturePacketClass = openpgp.SignaturePacket;
240
+ const signaturePacket = new SignaturePacketClass();
241
+ // Set signature properties
242
+ // eslint-disable-next-line fp/no-mutation -- Required for signature construction
243
+ signaturePacket.signatureType = internalEnums.signature.certPositive;
244
+ // eslint-disable-next-line fp/no-mutation -- Required for signature construction
245
+ signaturePacket.publicKeyAlgorithm =
246
+ internalEnums.publicKey.rsaEncryptSign;
247
+ // eslint-disable-next-line fp/no-mutation -- Required for signature construction
248
+ signaturePacket.hashAlgorithm = internalEnums.hash.sha256;
249
+ // Set key flags for certification and signing
250
+ // eslint-disable-next-line fp/no-mutation -- Required for signature construction
251
+ signaturePacket.keyFlags = new Uint8Array([
252
+ // eslint-disable-next-line no-bitwise -- Bitwise OR for combining key flags
253
+ internalEnums.keyFlags.certifyKeys | internalEnums.keyFlags.signData,
254
+ ]);
255
+ // Set preferred algorithms
256
+ // eslint-disable-next-line fp/no-mutation -- Required for signature construction
257
+ signaturePacket.preferredSymmetricAlgorithms = [
258
+ internalEnums.symmetric.aes256,
259
+ internalEnums.symmetric.aes128,
260
+ ];
261
+ // eslint-disable-next-line fp/no-mutation -- Required for signature construction
262
+ signaturePacket.preferredHashAlgorithms = [
263
+ internalEnums.hash.sha512,
264
+ internalEnums.hash.sha256,
265
+ ];
266
+ // eslint-disable-next-line fp/no-mutation -- Required for signature construction
267
+ signaturePacket.preferredCompressionAlgorithms = [
268
+ internalEnums.compression.uncompressed,
269
+ internalEnums.compression.zlib,
270
+ internalEnums.compression.zip,
271
+ ];
272
+ // Set features (modification detection)
273
+ // eslint-disable-next-line fp/no-mutation -- Required for signature construction
274
+ signaturePacket.features = new Uint8Array([
275
+ internalEnums.features.modificationDetection,
276
+ ]);
277
+ // Sign the user ID with the secret key
278
+ // The sign method takes different arguments at runtime than in types
279
+ const dataToSign = {
280
+ userID: userIdPacket,
281
+ key: secretKeyPacket,
282
+ };
283
+ await signaturePacket.sign(secretKeyPacket, dataToSign, creationDate, false, openpgp.config);
284
+ return signaturePacket;
285
+ }));
286
+ // Build packet list: SecretKeyPacket, then (UserIDPacket, SignaturePacket) pairs
287
+ const packetList = new openpgp.PacketList();
288
+ // eslint-disable-next-line fp/no-mutating-methods -- Required for building packet list
289
+ packetList.push(secretKeyPacket);
290
+ userIdPackets.forEach((userIdPacket, index) => {
291
+ // eslint-disable-next-line fp/no-mutating-methods -- Required for building packet list
292
+ packetList.push(userIdPacket);
293
+ // eslint-disable-next-line fp/no-mutating-methods -- Required for building packet list
294
+ packetList.push(signaturePackets[index]);
295
+ });
296
+ // Create PrivateKey from packet list
297
+ // The constructor expects a generic PacketList<AnyPacket>
298
+ const privateKey = new openpgp.PrivateKey(packetList);
299
+ // Encrypt the key if passphrase is provided
300
+ let finalPrivateKey = privateKey;
301
+ if (options.passphrase) {
302
+ // eslint-disable-next-line fp/no-mutation -- Reassigning encrypted key
303
+ finalPrivateKey = await openpgp.encryptKey({
304
+ privateKey,
305
+ passphrase: options.passphrase,
306
+ });
307
+ }
308
+ // Get armored versions
309
+ const armoredPrivateKey = finalPrivateKey.armor();
310
+ const armoredPublicKey = finalPrivateKey.toPublic().armor();
311
+ return {
312
+ armoredPrivateKey,
313
+ armoredPublicKey,
314
+ isEncrypted: Boolean(options.passphrase),
315
+ fingerprint,
316
+ };
317
+ }
318
+ catch (error) {
319
+ const message = error instanceof Error ? error.message : "Unknown error occurred";
320
+ throw new PGPKeyExportError(`Failed to create OpenPGP key from PKCS8 material: ${message}`, "CONVERSION_ERROR");
321
+ }
322
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * PEM Format Utilities
3
+ *
4
+ * Utilities for working with PEM-formatted cryptographic keys.
5
+ * Issue #1374
6
+ */
7
+ /**
8
+ * Check if a string is in valid PEM format
9
+ *
10
+ * PEM format requires:
11
+ * - A BEGIN header with a type (e.g., "-----BEGIN PUBLIC KEY-----")
12
+ * - Base64-encoded content
13
+ * - An END footer with matching type (e.g., "-----END PUBLIC KEY-----")
14
+ *
15
+ * @param input - String to check
16
+ * @returns true if the string is valid PEM format, false otherwise
17
+ */
18
+ export declare function isPemFormat(input: string): boolean;
19
+ /**
20
+ * Extract the type from a PEM-formatted string
21
+ *
22
+ * For example, extracts "PUBLIC KEY" from:
23
+ * -----BEGIN PUBLIC KEY-----
24
+ * ...
25
+ * -----END PUBLIC KEY-----
26
+ *
27
+ * @param pem - PEM-formatted string
28
+ * @returns The type string (e.g., "PUBLIC KEY", "PRIVATE KEY"), or null if not valid PEM
29
+ */
30
+ export declare function extractPemType(pem: string): string | null;
31
+ /**
32
+ * Convert PEM-formatted string to raw bytes
33
+ *
34
+ * Extracts the base64 content from between the PEM headers and decodes it.
35
+ *
36
+ * @param pem - PEM-formatted string
37
+ * @returns Raw bytes as Uint8Array
38
+ * @throws Error if the input is not valid PEM format
39
+ */
40
+ export declare function pemToBytes(pem: string): Uint8Array;
41
+ /**
42
+ * Convert raw bytes to PEM format
43
+ *
44
+ * Encodes the bytes as base64 and wraps with PEM headers.
45
+ * Base64 content is wrapped at 64 characters per line per RFC 7468.
46
+ *
47
+ * @param bytes - Raw bytes to encode
48
+ * @param type - PEM type (e.g., "PUBLIC KEY", "PRIVATE KEY")
49
+ * @returns PEM-formatted string
50
+ */
51
+ export declare function bytesToPem(bytes: Uint8Array, type: string): string;
52
+ /**
53
+ * Format a key fingerprint for display
54
+ *
55
+ * Converts to uppercase and groups into 4-character blocks separated by spaces.
56
+ * For example: "abcd1234efgh5678" becomes "ABCD 1234 EFGH 5678"
57
+ *
58
+ * @param fingerprint - Raw fingerprint string (typically 40 hex characters)
59
+ * @returns Formatted fingerprint string
60
+ */
61
+ export declare function formatFingerprint(fingerprint: string): string;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * PEM Format Utilities
4
+ *
5
+ * Utilities for working with PEM-formatted cryptographic keys.
6
+ * Issue #1374
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isPemFormat = isPemFormat;
10
+ exports.extractPemType = extractPemType;
11
+ exports.pemToBytes = pemToBytes;
12
+ exports.bytesToPem = bytesToPem;
13
+ exports.formatFingerprint = formatFingerprint;
14
+ const utils_1 = require("../kdf/utils");
15
+ /**
16
+ * PEM header/footer pattern
17
+ * Matches: -----BEGIN <TYPE>----- and -----END <TYPE>-----
18
+ */
19
+ const PEM_HEADER_REGEX = /-----BEGIN ([A-Z0-9 ]+)-----/;
20
+ const PEM_FOOTER_REGEX = /-----END ([A-Z0-9 ]+)-----/;
21
+ /**
22
+ * Base64 character set for validation
23
+ */
24
+ const BASE64_CHARS = /^[A-Za-z0-9+/=\s]+$/;
25
+ /**
26
+ * Check if a string is in valid PEM format
27
+ *
28
+ * PEM format requires:
29
+ * - A BEGIN header with a type (e.g., "-----BEGIN PUBLIC KEY-----")
30
+ * - Base64-encoded content
31
+ * - An END footer with matching type (e.g., "-----END PUBLIC KEY-----")
32
+ *
33
+ * @param input - String to check
34
+ * @returns true if the string is valid PEM format, false otherwise
35
+ */
36
+ function isPemFormat(input) {
37
+ if (!input || typeof input !== "string") {
38
+ return false;
39
+ }
40
+ const headerMatch = input.match(PEM_HEADER_REGEX);
41
+ const footerMatch = input.match(PEM_FOOTER_REGEX);
42
+ if (!headerMatch || !footerMatch) {
43
+ return false;
44
+ }
45
+ // Types must match
46
+ const headerType = headerMatch[1];
47
+ const footerType = footerMatch[1];
48
+ if (headerType !== footerType) {
49
+ return false;
50
+ }
51
+ // Check that there's content between header and footer
52
+ const headerEnd = input.indexOf("-----", input.indexOf("BEGIN") + 5) + 5;
53
+ const footerStart = input.lastIndexOf("-----END");
54
+ if (headerEnd >= footerStart) {
55
+ return false;
56
+ }
57
+ const content = input.slice(headerEnd, footerStart).trim();
58
+ return content.length > 0 && BASE64_CHARS.test(content);
59
+ }
60
+ /**
61
+ * Extract the type from a PEM-formatted string
62
+ *
63
+ * For example, extracts "PUBLIC KEY" from:
64
+ * -----BEGIN PUBLIC KEY-----
65
+ * ...
66
+ * -----END PUBLIC KEY-----
67
+ *
68
+ * @param pem - PEM-formatted string
69
+ * @returns The type string (e.g., "PUBLIC KEY", "PRIVATE KEY"), or null if not valid PEM
70
+ */
71
+ function extractPemType(pem) {
72
+ if (!pem || typeof pem !== "string") {
73
+ return null;
74
+ }
75
+ const match = pem.match(PEM_HEADER_REGEX);
76
+ return match ? match[1] : null;
77
+ }
78
+ /**
79
+ * Convert PEM-formatted string to raw bytes
80
+ *
81
+ * Extracts the base64 content from between the PEM headers and decodes it.
82
+ *
83
+ * @param pem - PEM-formatted string
84
+ * @returns Raw bytes as Uint8Array
85
+ * @throws Error if the input is not valid PEM format
86
+ */
87
+ function pemToBytes(pem) {
88
+ if (!pem || typeof pem !== "string") {
89
+ throw new Error("Invalid PEM: input must be a non-empty string");
90
+ }
91
+ const headerMatch = pem.match(PEM_HEADER_REGEX);
92
+ const footerMatch = pem.match(PEM_FOOTER_REGEX);
93
+ if (!headerMatch || !footerMatch) {
94
+ throw new Error("Invalid PEM: missing header or footer");
95
+ }
96
+ // Extract content between header and footer
97
+ const headerEnd = pem.indexOf("-----", pem.indexOf("BEGIN") + 5) + 5;
98
+ const footerStart = pem.lastIndexOf("-----END");
99
+ const base64Content = pem.slice(headerEnd, footerStart).replace(/\s/g, ""); // Remove all whitespace
100
+ // Decode base64 to bytes using functional approach
101
+ const binaryString = atob(base64Content);
102
+ return Uint8Array.from(binaryString, c => c.charCodeAt(0));
103
+ }
104
+ /**
105
+ * Convert raw bytes to PEM format
106
+ *
107
+ * Encodes the bytes as base64 and wraps with PEM headers.
108
+ * Base64 content is wrapped at 64 characters per line per RFC 7468.
109
+ *
110
+ * @param bytes - Raw bytes to encode
111
+ * @param type - PEM type (e.g., "PUBLIC KEY", "PRIVATE KEY")
112
+ * @returns PEM-formatted string
113
+ */
114
+ function bytesToPem(bytes, type) {
115
+ var _a;
116
+ // Convert bytes to base64 using functional approach
117
+ const base64 = (0, utils_1.uint8ArrayToBase64)(bytes);
118
+ // Wrap at 64 characters per line using regex match
119
+ const lines = (_a = base64.match(/.{1,64}/g)) !== null && _a !== void 0 ? _a : [];
120
+ // Build PEM string
121
+ const header = `-----BEGIN ${type}-----`;
122
+ const footer = `-----END ${type}-----`;
123
+ return [header, ...lines, footer].join("\n");
124
+ }
125
+ /**
126
+ * Format a key fingerprint for display
127
+ *
128
+ * Converts to uppercase and groups into 4-character blocks separated by spaces.
129
+ * For example: "abcd1234efgh5678" becomes "ABCD 1234 EFGH 5678"
130
+ *
131
+ * @param fingerprint - Raw fingerprint string (typically 40 hex characters)
132
+ * @returns Formatted fingerprint string
133
+ */
134
+ function formatFingerprint(fingerprint) {
135
+ var _a;
136
+ if (!fingerprint) {
137
+ return "";
138
+ }
139
+ const upper = fingerprint.toUpperCase();
140
+ // Split into 4-char groups using regex match
141
+ const groups = (_a = upper.match(/.{1,4}/g)) !== null && _a !== void 0 ? _a : [];
142
+ return groups.join(" ");
143
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * PGP Key Generator
3
+ *
4
+ * Generates RSA 4096-bit key pairs using Web Crypto API.
5
+ * Issue #1374
6
+ */
7
+ import type { PGPKeyPair } from "./types";
8
+ /**
9
+ * Compute SHA-256 fingerprint of public key bytes
10
+ *
11
+ * @param publicKeyBytes - Raw public key bytes (SPKI format)
12
+ * @returns First 40 hex characters of SHA-256 hash
13
+ */
14
+ export declare function computeKeyFingerprint(publicKeyBytes: Uint8Array): Promise<string>;
15
+ /**
16
+ * Generate an RSA 4096-bit key pair for digital signatures
17
+ *
18
+ * @returns Generated key pair with PEM public key and raw private key bytes
19
+ */
20
+ export declare function generatePGPKeyPair(): Promise<PGPKeyPair>;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /**
3
+ * PGP Key Generator
4
+ *
5
+ * Generates RSA 4096-bit key pairs using Web Crypto API.
6
+ * Issue #1374
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.computeKeyFingerprint = computeKeyFingerprint;
10
+ exports.generatePGPKeyPair = generatePGPKeyPair;
11
+ const utils_1 = require("../kdf/utils");
12
+ const types_1 = require("./types");
13
+ /**
14
+ * RSA key generation parameters
15
+ */
16
+ const RSA_PARAMS = {
17
+ name: "RSA-PSS",
18
+ modulusLength: 4096,
19
+ publicExponent: new Uint8Array([1, 0, 1]), // 65537
20
+ hash: "SHA-256",
21
+ };
22
+ /**
23
+ * Compute SHA-256 fingerprint of public key bytes
24
+ *
25
+ * @param publicKeyBytes - Raw public key bytes (SPKI format)
26
+ * @returns First 40 hex characters of SHA-256 hash
27
+ */
28
+ async function computeKeyFingerprint(publicKeyBytes) {
29
+ const hashBuffer = await crypto.subtle.digest("SHA-256", publicKeyBytes);
30
+ const hashArray = new Uint8Array(hashBuffer);
31
+ // Convert to hex and take first 40 characters
32
+ const hexString = Array.from(hashArray)
33
+ .map(b => b.toString(16).padStart(2, "0"))
34
+ .join("");
35
+ return hexString.substring(0, types_1.KEY_FINGERPRINT_LENGTH);
36
+ }
37
+ /**
38
+ * Convert ArrayBuffer to PEM format
39
+ *
40
+ * @param buffer - Raw key bytes
41
+ * @param type - PEM type (PUBLIC KEY or PRIVATE KEY)
42
+ * @returns PEM formatted string
43
+ */
44
+ function arrayBufferToPem(buffer, type) {
45
+ var _a;
46
+ const base64 = (0, utils_1.uint8ArrayToBase64)(new Uint8Array(buffer));
47
+ const lines = (_a = base64.match(/.{1,64}/g)) !== null && _a !== void 0 ? _a : [];
48
+ return `-----BEGIN ${type}-----\n${lines.join("\n")}\n-----END ${type}-----`;
49
+ }
50
+ /**
51
+ * Generate an RSA 4096-bit key pair for digital signatures
52
+ *
53
+ * @returns Generated key pair with PEM public key and raw private key bytes
54
+ */
55
+ async function generatePGPKeyPair() {
56
+ // Generate the key pair
57
+ const keyPair = await crypto.subtle.generateKey(RSA_PARAMS, true, [
58
+ "sign",
59
+ "verify",
60
+ ]);
61
+ // Export public key as SPKI
62
+ const publicKeyBuffer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
63
+ const publicKeyPem = arrayBufferToPem(publicKeyBuffer, "PUBLIC KEY");
64
+ // Export private key as PKCS8
65
+ const privateKeyBuffer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
66
+ const privateKeyBytes = new Uint8Array(privateKeyBuffer);
67
+ // Compute fingerprint
68
+ const keyId = await computeKeyFingerprint(new Uint8Array(publicKeyBuffer));
69
+ const algorithm = "RSA-4096";
70
+ return {
71
+ publicKeyPem,
72
+ privateKeyBytes,
73
+ keyId,
74
+ algorithm,
75
+ };
76
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * PGP Key Import Parser
3
+ *
4
+ * Parse and validate armored PGP private keys for import.
5
+ * Converts to PKCS8 format compatible with Web Crypto API.
6
+ *
7
+ * Issue #1374
8
+ */
9
+ import type { PGPKeyAlgorithm } from "./types";
10
+ /**
11
+ * Result from parsing an armored PGP key
12
+ */
13
+ export interface ParsedPGPKey {
14
+ /** Private key in PKCS8 format (raw bytes) */
15
+ privateKeyBytes: Uint8Array;
16
+ /** Public key in PEM format (SPKI) */
17
+ publicKeyPem: string;
18
+ /** Key fingerprint (40 hex chars) */
19
+ keyId: string;
20
+ /** Key algorithm */
21
+ algorithm: PGPKeyAlgorithm;
22
+ /** Key size in bits */
23
+ keySize: number;
24
+ /** User IDs associated with the key */
25
+ userIds: string[];
26
+ /** When the key was created */
27
+ createdAt: string;
28
+ }
29
+ /**
30
+ * Validation result for imported key
31
+ */
32
+ export interface KeyValidationResult {
33
+ /** Whether the key is valid for import */
34
+ isValid: boolean;
35
+ /** Error message if invalid */
36
+ error?: string;
37
+ /** Warning message (e.g., key size < 4096) */
38
+ warning?: string;
39
+ }
40
+ /**
41
+ * Error thrown when key import fails
42
+ */
43
+ export declare class PGPKeyImportError extends Error {
44
+ readonly code: string;
45
+ constructor(message: string, code: string);
46
+ }
47
+ /**
48
+ * Check if a string looks like an armored PGP key
49
+ *
50
+ * @param input - String to check
51
+ * @returns true if it appears to be armored PGP format
52
+ */
53
+ export declare function isArmoredPGPKey(input: string): boolean;
54
+ /**
55
+ * Parse an armored PGP private key
56
+ *
57
+ * @param armoredKey - Armored PGP private key string
58
+ * @param passphrase - Optional passphrase if key is encrypted
59
+ * @returns Parsed key data
60
+ * @throws PGPKeyImportError if parsing fails
61
+ */
62
+ export declare function parseArmoredPGPKey(armoredKey: string, passphrase?: string): Promise<ParsedPGPKey>;
63
+ /**
64
+ * Validate an imported key
65
+ *
66
+ * @param parsedKey - Parsed key to validate
67
+ * @returns Validation result with any warnings
68
+ */
69
+ export declare function validateImportedKey(parsedKey: ParsedPGPKey): KeyValidationResult;