@raytio/core 11.4.1 → 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.
- package/CHANGELOG.md +6 -0
- package/README.md +1714 -213
- package/dist/accessApplication/api/legacy/convertRelationships.d.ts +3 -5
- package/dist/accessApplication/api/legacy/convertRelationships.js +3 -3
- package/dist/crypto/cognitoAttributes.d.ts +3 -0
- package/dist/crypto/cognitoAttributes.js +15 -4
- package/dist/crypto/getAADecryptor.d.ts +1 -1
- package/dist/crypto/getAADecryptor.js +1 -3
- package/dist/crypto/index.d.ts +3 -0
- package/dist/crypto/index.js +6 -0
- package/dist/crypto/kdf/argon2.d.ts +67 -0
- package/dist/crypto/kdf/argon2.js +99 -0
- package/dist/crypto/kdf/index.d.ts +43 -0
- package/dist/crypto/kdf/index.js +106 -0
- package/dist/crypto/kdf/pbkdf2.d.ts +16 -0
- package/dist/crypto/kdf/pbkdf2.js +45 -0
- package/dist/crypto/kdf/twoSecretKdf.d.ts +37 -0
- package/dist/crypto/kdf/twoSecretKdf.js +66 -0
- package/dist/crypto/kdf/types.d.ts +65 -0
- package/dist/crypto/kdf/types.js +50 -0
- package/dist/crypto/kdf/utils.d.ts +59 -0
- package/dist/crypto/kdf/utils.js +110 -0
- package/dist/crypto/localSecret/format.d.ts +48 -0
- package/dist/crypto/localSecret/format.js +157 -0
- package/dist/crypto/localSecret/generator.d.ts +23 -0
- package/dist/crypto/localSecret/generator.js +53 -0
- package/dist/crypto/localSecret/index.d.ts +12 -0
- package/dist/crypto/localSecret/index.js +46 -0
- package/dist/crypto/localSecret/storage.d.ts +53 -0
- package/dist/crypto/localSecret/storage.js +207 -0
- package/dist/crypto/localSecret/types.d.ts +68 -0
- package/dist/crypto/localSecret/types.js +31 -0
- package/dist/crypto/pgpKey/encryption.d.ts +49 -0
- package/dist/crypto/pgpKey/encryption.js +104 -0
- package/dist/crypto/pgpKey/export.d.ts +59 -0
- package/dist/crypto/pgpKey/export.js +322 -0
- package/dist/crypto/pgpKey/format.d.ts +61 -0
- package/dist/crypto/pgpKey/format.js +143 -0
- package/dist/crypto/pgpKey/generator.d.ts +20 -0
- package/dist/crypto/pgpKey/generator.js +76 -0
- package/dist/crypto/pgpKey/import.d.ts +69 -0
- package/dist/crypto/pgpKey/import.js +239 -0
- package/dist/crypto/pgpKey/index.d.ts +19 -0
- package/dist/crypto/pgpKey/index.js +67 -0
- package/dist/crypto/pgpKey/signing.d.ts +44 -0
- package/dist/crypto/pgpKey/signing.js +71 -0
- package/dist/crypto/pgpKey/storage.d.ts +43 -0
- package/dist/crypto/pgpKey/storage.js +141 -0
- package/dist/crypto/pgpKey/types.d.ts +86 -0
- package/dist/crypto/pgpKey/types.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/rules/calculateScore.d.ts +1 -1
- package/dist/rules/convertInstanceToRuleInput.js +99 -97
- package/dist/rules/evaluateBadge.d.ts +36 -0
- package/dist/rules/evaluateBadge.js +36 -0
- package/dist/rules/index.d.ts +1 -0
- package/dist/rules/index.js +1 -0
- package/dist/rules/types/config.d.ts +1 -1
- package/dist/rules/types/dataValueTypes.d.ts +4 -4
- package/dist/schema/expandSchema/constants.js +1 -1
- package/dist/schema/expandSchema/expandSchema.d.ts +3 -3
- package/dist/schema/expandSchema/expandSchema.js +4 -4
- package/dist/schema/expandSchema/i18n.d.ts +6 -1
- package/dist/schema/expandSchema/i18n.js +32 -4
- package/dist/schema/expandSchema/index.d.ts +1 -0
- package/dist/schema/expandSchema/index.js +1 -0
- package/dist/schema/expandSchema/maybeUseI18n.d.ts +2 -2
- package/dist/schema/expandSchema/maybeUseI18n.js +68 -11
- package/dist/schema/expandSchema/processSchema.js +14 -5
- package/dist/schema/expandSchema/removePrivateFields.d.ts +75 -22
- package/dist/schema/expandSchema/sortSchemaProperties.d.ts +4 -1
- package/dist/schema/expandSchema/sortSchemaProperties.js +24 -1
- package/dist/schema/labels.js +1 -2
- package/dist/util/canonicalJsonify.d.ts +7 -1
- package/dist/util/canonicalJsonify.js +11 -10
- package/dist/verifications/safeHarbour.js +5 -0
- package/dist/verifications/verifyCheck/getOwnRealVerifications.js +3 -0
- package/dist/verifications/verifyCheck/getSomeoneElsesRealVerifications.js +1 -0
- package/package.json +6 -4
- package/dist/__tests__/docs.test.d.ts +0 -1
- package/dist/__tests__/docs.test.js +0 -24
- package/dist/accessApplication/api/__tests__/fetchKeysForSubmission.test.d.ts +0 -1
- package/dist/accessApplication/api/__tests__/fetchKeysForSubmission.test.js +0 -28
- package/dist/accessApplication/api/__tests__/fetchPOsOrAOsForSubmission.test.d.ts +0 -1
- package/dist/accessApplication/api/__tests__/fetchPOsOrAOsForSubmission.test.js +0 -23
- package/dist/accessApplication/api/__tests__/fetchRelationshipsForSubmission.test.d.ts +0 -1
- package/dist/accessApplication/api/__tests__/fetchRelationshipsForSubmission.test.js +0 -27
- package/dist/accessApplication/api/__tests__/getMissingDataForInstance.test.d.ts +0 -1
- package/dist/accessApplication/api/__tests__/getMissingDataForInstance.test.js +0 -30
- package/dist/accessApplication/api/legacy/__tests__/convertRelationships.test.d.ts +0 -1
- package/dist/accessApplication/api/legacy/__tests__/convertRelationships.test.js +0 -37
- package/dist/rules/helpers/__tests__/addInfiniteThresholdBoundaries.test.d.ts +0 -1
- package/dist/rules/helpers/__tests__/addInfiniteThresholdBoundaries.test.js +0 -27
- package/dist/rules/helpers/__tests__/checkTypeofValue.test.d.ts +0 -1
- package/dist/rules/helpers/__tests__/checkTypeofValue.test.js +0 -49
- package/dist/rules/helpers/__tests__/getValuesFromPath.test.d.ts +0 -1
- package/dist/rules/helpers/__tests__/getValuesFromPath.test.js +0 -67
- package/dist/rules/helpers/__tests__/thresholds.test.d.ts +0 -1
- package/dist/rules/helpers/__tests__/thresholds.test.js +0 -32
- package/dist/rules/operators/__tests__/bool.test.d.ts +0 -1
- package/dist/rules/operators/__tests__/bool.test.js +0 -21
- package/dist/rules/operators/__tests__/date.test.d.ts +0 -1
- package/dist/rules/operators/__tests__/date.test.js +0 -81
- package/dist/rules/operators/__tests__/hfield.test.d.ts +0 -1
- package/dist/rules/operators/__tests__/hfield.test.js +0 -38
- package/dist/rules/operators/__tests__/hschema.test.d.ts +0 -1
- package/dist/rules/operators/__tests__/hschema.test.js +0 -24
- package/dist/rules/operators/__tests__/number.test.d.ts +0 -1
- package/dist/rules/operators/__tests__/number.test.js +0 -53
- package/dist/rules/operators/__tests__/string.test.d.ts +0 -1
- package/dist/rules/operators/__tests__/string.test.js +0 -74
- package/dist/schema/expandSchema/__tests__/addLoadingTimes.test.d.ts +0 -1
- package/dist/schema/expandSchema/__tests__/addLoadingTimes.test.js +0 -24
- package/dist/schema/expandSchema/__tests__/expandSchema.test.d.ts +0 -1
- package/dist/schema/expandSchema/__tests__/expandSchema.test.js +0 -96
- package/dist/schema/expandSchema/__tests__/i18n.test.d.ts +0 -1
- package/dist/schema/expandSchema/__tests__/i18n.test.js +0 -32
- package/dist/schema/expandSchema/__tests__/maybeUseI18n.test.d.ts +0 -1
- package/dist/schema/expandSchema/__tests__/maybeUseI18n.test.js +0 -98
- package/dist/schema/expandSchema/__tests__/processSchema.test.d.ts +0 -1
- package/dist/schema/expandSchema/__tests__/processSchema.test.js +0 -326
- package/dist/schema/expandSchema/__tests__/sortSchemaProperties.test.d.ts +0 -1
- package/dist/schema/expandSchema/__tests__/sortSchemaProperties.test.js +0 -182
- package/dist/schema/expandSchema/__tests__/util.test.d.ts +0 -1
- package/dist/schema/expandSchema/__tests__/util.test.js +0 -19
- package/dist/verifications/cleanInstance.d.ts +0 -9
- package/dist/verifications/cleanInstance.js +0 -15
- package/dist/verifications/verifyCheck/__tests__/getOwnRealVerifications.test.d.ts +0 -1
- package/dist/verifications/verifyCheck/__tests__/getOwnRealVerifications.test.js +0 -217
- package/dist/verifications/verifyCheck/__tests__/getSomeoneElsesRealVerifications.test.d.ts +0 -1
- package/dist/verifications/verifyCheck/__tests__/getSomeoneElsesRealVerifications.test.js +0 -205
- package/dist/verifications/verifyCheck/operations/__tests__/checkOwnVerification.test.d.ts +0 -1
- package/dist/verifications/verifyCheck/operations/__tests__/checkOwnVerification.test.js +0 -131
- package/dist/verifications/verifyCheck/operations/__tests__/checkSomeoneElsesVerifications.test.d.ts +0 -1
- package/dist/verifications/verifyCheck/operations/__tests__/checkSomeoneElsesVerifications.test.js +0 -49
- 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;
|