@happyvertical/encryption 0.74.8

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.
@@ -0,0 +1,916 @@
1
+ import * as openpgp from "openpgp";
2
+ import { BaseEncryption, KeyError, EncryptError, InvalidKeyError, VerificationError, DecryptError, PassphraseError, SignatureError } from "../index.js";
3
+ class PGPEncryption extends BaseEncryption {
4
+ options;
5
+ publicKeys;
6
+ privateKeys;
7
+ constructor(options) {
8
+ super("pgp", options.debug);
9
+ this.options = options;
10
+ }
11
+ /**
12
+ * Initialize keys from options
13
+ */
14
+ async initializeKeys() {
15
+ if (this.publicKeys || this.privateKeys) {
16
+ return;
17
+ }
18
+ try {
19
+ if (this.options.publicKey) {
20
+ const key = await openpgp.readKey({
21
+ armoredKey: this.options.publicKey
22
+ });
23
+ this.publicKeys = [key];
24
+ } else if (this.options.publicKeys) {
25
+ this.publicKeys = await Promise.all(
26
+ this.options.publicKeys.map(
27
+ (k) => openpgp.readKey({ armoredKey: k })
28
+ )
29
+ );
30
+ }
31
+ if (this.options.privateKey) {
32
+ const key = await openpgp.readPrivateKey({
33
+ armoredKey: this.options.privateKey
34
+ });
35
+ if (this.options.passphrase) {
36
+ const decryptedKey = await openpgp.decryptKey({
37
+ privateKey: key,
38
+ passphrase: this.options.passphrase
39
+ });
40
+ this.privateKeys = [decryptedKey];
41
+ } else {
42
+ this.privateKeys = [key];
43
+ }
44
+ } else if (this.options.privateKeys) {
45
+ this.privateKeys = await Promise.all(
46
+ this.options.privateKeys.map(async (k) => {
47
+ const key = await openpgp.readPrivateKey({ armoredKey: k });
48
+ if (this.options.passphrase) {
49
+ return openpgp.decryptKey({
50
+ privateKey: key,
51
+ passphrase: this.options.passphrase
52
+ });
53
+ }
54
+ return key;
55
+ })
56
+ );
57
+ }
58
+ } catch (error) {
59
+ throw new KeyError(
60
+ `Failed to initialize PGP keys: ${error.message}`,
61
+ this.adapterType,
62
+ error
63
+ );
64
+ }
65
+ }
66
+ /**
67
+ * Get public keys for encryption
68
+ */
69
+ async getPublicKeys(options) {
70
+ await this.initializeKeys();
71
+ if (options?.publicKeys) {
72
+ return Promise.all(
73
+ options.publicKeys.map((k) => openpgp.readKey({ armoredKey: k }))
74
+ );
75
+ }
76
+ if (!this.publicKeys || this.publicKeys.length === 0) {
77
+ throw new KeyError(
78
+ "No public keys available for encryption",
79
+ this.adapterType
80
+ );
81
+ }
82
+ return this.publicKeys;
83
+ }
84
+ /**
85
+ * Get private keys for decryption
86
+ */
87
+ async getPrivateKeys(options) {
88
+ await this.initializeKeys();
89
+ if (options?.privateKey) {
90
+ const key = await openpgp.readPrivateKey({
91
+ armoredKey: options.privateKey
92
+ });
93
+ if (options.passphrase) {
94
+ const decryptedKey = await openpgp.decryptKey({
95
+ privateKey: key,
96
+ passphrase: options.passphrase
97
+ });
98
+ return [decryptedKey];
99
+ }
100
+ return [key];
101
+ }
102
+ if (!this.privateKeys || this.privateKeys.length === 0) {
103
+ throw new KeyError(
104
+ "No private keys available for decryption",
105
+ this.adapterType
106
+ );
107
+ }
108
+ return this.privateKeys;
109
+ }
110
+ async encryptText(text, options) {
111
+ try {
112
+ this.log("Encrypting text", { length: text.length });
113
+ this.validateEncryptOptions(options);
114
+ const publicKeys = await this.getPublicKeys(options);
115
+ const format = options?.armor !== false ? "armored" : "binary";
116
+ const encryptConfig = {
117
+ message: await openpgp.createMessage({ text }),
118
+ encryptionKeys: publicKeys,
119
+ format
120
+ };
121
+ if (options?.sign && options?.privateKey) {
122
+ const signingKey = await openpgp.readPrivateKey({
123
+ armoredKey: options.privateKey
124
+ });
125
+ if (this.options.passphrase) {
126
+ encryptConfig.signingKeys = await openpgp.decryptKey({
127
+ privateKey: signingKey,
128
+ passphrase: this.options.passphrase
129
+ });
130
+ } else {
131
+ encryptConfig.signingKeys = signingKey;
132
+ }
133
+ } else if (options?.sign && this.privateKeys) {
134
+ encryptConfig.signingKeys = this.privateKeys;
135
+ }
136
+ const encrypted = await openpgp.encrypt(encryptConfig);
137
+ this.log("Text encrypted successfully");
138
+ if (format === "binary" && encrypted instanceof Uint8Array) {
139
+ return Buffer.from(encrypted).toString("base64");
140
+ }
141
+ return encrypted;
142
+ } catch (error) {
143
+ throw new EncryptError(
144
+ `Failed to encrypt text: ${error.message}`,
145
+ this.adapterType,
146
+ error
147
+ );
148
+ }
149
+ }
150
+ async decryptText(encrypted, options) {
151
+ try {
152
+ this.log("Decrypting text", { length: encrypted.length });
153
+ this.validateDecryptOptions(options);
154
+ const privateKeys = await this.getPrivateKeys(options);
155
+ let message;
156
+ try {
157
+ message = await openpgp.readMessage({
158
+ armoredMessage: encrypted
159
+ });
160
+ } catch {
161
+ try {
162
+ message = await openpgp.readMessage({
163
+ binaryMessage: Buffer.from(encrypted, "base64")
164
+ });
165
+ } catch {
166
+ message = await openpgp.readMessage({
167
+ binaryMessage: Buffer.from(encrypted, "binary")
168
+ });
169
+ }
170
+ }
171
+ const decryptConfig = {
172
+ message,
173
+ decryptionKeys: privateKeys,
174
+ format: "utf8"
175
+ };
176
+ if (options?.verify && options?.publicKey) {
177
+ if (typeof options.publicKey === "string") {
178
+ decryptConfig.verificationKeys = await openpgp.readKey({
179
+ armoredKey: options.publicKey
180
+ });
181
+ } else {
182
+ throw new InvalidKeyError(
183
+ "PGP verification requires armored public key string",
184
+ this.adapterType
185
+ );
186
+ }
187
+ }
188
+ const result = await openpgp.decrypt(decryptConfig);
189
+ if (options?.verify && result.signatures) {
190
+ try {
191
+ await result.signatures[0].verified;
192
+ this.log("Signature verified successfully");
193
+ } catch (error) {
194
+ throw new VerificationError(
195
+ "Signature verification failed",
196
+ this.adapterType,
197
+ error
198
+ );
199
+ }
200
+ }
201
+ this.log("Text decrypted successfully");
202
+ return result.data;
203
+ } catch (error) {
204
+ if (error instanceof VerificationError) {
205
+ throw error;
206
+ }
207
+ throw new DecryptError(
208
+ `Failed to decrypt text: ${error.message}`,
209
+ this.adapterType,
210
+ error
211
+ );
212
+ }
213
+ }
214
+ async encryptBuffer(buffer, options) {
215
+ try {
216
+ this.log("Encrypting buffer", { size: buffer.length });
217
+ this.validateEncryptOptions(options);
218
+ const publicKeys = await this.getPublicKeys(options);
219
+ const format = options?.armor === true ? "armored" : "binary";
220
+ const encryptConfig = {
221
+ message: await openpgp.createMessage({ binary: buffer }),
222
+ encryptionKeys: publicKeys,
223
+ format
224
+ };
225
+ if (options?.sign && options?.privateKey) {
226
+ const signingKey = await openpgp.readPrivateKey({
227
+ armoredKey: options.privateKey
228
+ });
229
+ if (this.options.passphrase) {
230
+ encryptConfig.signingKeys = await openpgp.decryptKey({
231
+ privateKey: signingKey,
232
+ passphrase: this.options.passphrase
233
+ });
234
+ } else {
235
+ encryptConfig.signingKeys = signingKey;
236
+ }
237
+ } else if (options?.sign && this.privateKeys) {
238
+ encryptConfig.signingKeys = this.privateKeys;
239
+ }
240
+ const encrypted = await openpgp.encrypt(encryptConfig);
241
+ this.log("Buffer encrypted successfully");
242
+ if (typeof encrypted === "string") {
243
+ return Buffer.from(encrypted);
244
+ }
245
+ if (encrypted instanceof Uint8Array) {
246
+ return Buffer.from(encrypted);
247
+ }
248
+ return Buffer.from(encrypted);
249
+ } catch (error) {
250
+ throw new EncryptError(
251
+ `Failed to encrypt buffer: ${error.message}`,
252
+ this.adapterType,
253
+ error
254
+ );
255
+ }
256
+ }
257
+ async decryptBuffer(buffer, options) {
258
+ try {
259
+ this.log("Decrypting buffer", { size: buffer.length });
260
+ this.validateDecryptOptions(options);
261
+ const privateKeys = await this.getPrivateKeys(options);
262
+ let message;
263
+ try {
264
+ message = await openpgp.readMessage({
265
+ armoredMessage: buffer.toString("utf8")
266
+ });
267
+ } catch {
268
+ message = await openpgp.readMessage({
269
+ binaryMessage: buffer
270
+ });
271
+ }
272
+ const decryptConfig = {
273
+ message,
274
+ decryptionKeys: privateKeys,
275
+ format: "binary"
276
+ };
277
+ if (options?.verify && options?.publicKey) {
278
+ if (typeof options.publicKey === "string") {
279
+ decryptConfig.verificationKeys = await openpgp.readKey({
280
+ armoredKey: options.publicKey
281
+ });
282
+ } else {
283
+ throw new InvalidKeyError(
284
+ "PGP verification requires armored public key string",
285
+ this.adapterType
286
+ );
287
+ }
288
+ }
289
+ const result = await openpgp.decrypt(decryptConfig);
290
+ if (options?.verify && result.signatures) {
291
+ try {
292
+ await result.signatures[0].verified;
293
+ this.log("Signature verified successfully");
294
+ } catch (error) {
295
+ throw new VerificationError(
296
+ "Signature verification failed",
297
+ this.adapterType,
298
+ error
299
+ );
300
+ }
301
+ }
302
+ this.log("Buffer decrypted successfully");
303
+ return Buffer.from(result.data);
304
+ } catch (error) {
305
+ if (error instanceof VerificationError) {
306
+ throw error;
307
+ }
308
+ throw new DecryptError(
309
+ `Failed to decrypt buffer: ${error.message}`,
310
+ this.adapterType,
311
+ error
312
+ );
313
+ }
314
+ }
315
+ async generateKeyPair(options) {
316
+ try {
317
+ this.log("Generating PGP key pair", options);
318
+ const keyType = options?.type || "rsa";
319
+ const keySize = options?.keySize || 4096;
320
+ let generateOptions;
321
+ if (keyType === "rsa") {
322
+ generateOptions = {
323
+ type: "rsa",
324
+ rsaBits: keySize,
325
+ userIDs: [
326
+ {
327
+ name: options?.name || "",
328
+ email: options?.email || ""
329
+ }
330
+ ],
331
+ passphrase: options?.passphrase,
332
+ format: "object"
333
+ };
334
+ } else if (keyType === "ecc" || keyType === "ecdh") {
335
+ generateOptions = {
336
+ type: "ecc",
337
+ curve: options?.curve || "curve25519",
338
+ userIDs: [
339
+ {
340
+ name: options?.name || "",
341
+ email: options?.email || ""
342
+ }
343
+ ],
344
+ passphrase: options?.passphrase,
345
+ format: "object"
346
+ };
347
+ } else {
348
+ throw new KeyError(
349
+ `Unsupported key type for PGP: ${keyType}`,
350
+ this.adapterType
351
+ );
352
+ }
353
+ const { privateKey: privKeyObj, publicKey: pubKeyObj } = await openpgp.generateKey(generateOptions);
354
+ const publicKey = pubKeyObj.armor();
355
+ const privateKey = privKeyObj.armor();
356
+ const fingerprint = pubKeyObj.getFingerprint();
357
+ const keyId = pubKeyObj.getKeyID().toHex();
358
+ this.log("Key pair generated successfully", { keyId, fingerprint });
359
+ return {
360
+ publicKey,
361
+ privateKey,
362
+ fingerprint,
363
+ keyId
364
+ };
365
+ } catch (error) {
366
+ throw new KeyError(
367
+ `Failed to generate PGP key pair: ${error.message}`,
368
+ this.adapterType,
369
+ error
370
+ );
371
+ }
372
+ }
373
+ async importKey(key, options) {
374
+ try {
375
+ this.log("Importing PGP key");
376
+ const armoredKey = typeof key === "string" ? key : key.toString("utf8");
377
+ const keyType = options?.type || "public";
378
+ if (keyType === "public") {
379
+ const pubKey = await openpgp.readKey({ armoredKey });
380
+ const expirationTime2 = await pubKey.getExpirationTime();
381
+ return {
382
+ type: "public",
383
+ format: "armored",
384
+ data: armoredKey,
385
+ fingerprint: pubKey.getFingerprint(),
386
+ keyId: pubKey.getKeyID().toHex(),
387
+ algorithm: pubKey.getAlgorithmInfo().algorithm,
388
+ created: pubKey.getCreationTime(),
389
+ expires: expirationTime2 instanceof Date ? expirationTime2 : void 0,
390
+ userIds: pubKey.getUserIDs().map((uid) => {
391
+ const match = uid.match(/^(.*?)\s*<(.+?)>$/);
392
+ if (match) {
393
+ return { name: match[1], email: match[2] };
394
+ }
395
+ return { email: uid };
396
+ })
397
+ };
398
+ }
399
+ const privKey = await openpgp.readPrivateKey({ armoredKey });
400
+ let decryptedKey = privKey;
401
+ if (options?.passphrase) {
402
+ try {
403
+ decryptedKey = await openpgp.decryptKey({
404
+ privateKey: privKey,
405
+ passphrase: options.passphrase
406
+ });
407
+ } catch (error) {
408
+ throw new PassphraseError(
409
+ "Invalid passphrase for private key",
410
+ this.adapterType,
411
+ error
412
+ );
413
+ }
414
+ }
415
+ this.log("Key imported successfully");
416
+ const expirationTime = await decryptedKey.getExpirationTime();
417
+ return {
418
+ type: "private",
419
+ format: "armored",
420
+ data: armoredKey,
421
+ fingerprint: decryptedKey.getFingerprint(),
422
+ keyId: decryptedKey.getKeyID().toHex(),
423
+ algorithm: decryptedKey.getAlgorithmInfo().algorithm,
424
+ created: decryptedKey.getCreationTime(),
425
+ expires: expirationTime instanceof Date ? expirationTime : void 0,
426
+ userIds: decryptedKey.getUserIDs().map((uid) => {
427
+ const match = uid.match(/^(.*?)\s*<(.+?)>$/);
428
+ if (match) {
429
+ return { name: match[1], email: match[2] };
430
+ }
431
+ return { email: uid };
432
+ })
433
+ };
434
+ } catch (error) {
435
+ if (error instanceof PassphraseError) {
436
+ throw error;
437
+ }
438
+ throw new InvalidKeyError(
439
+ `Failed to import PGP key: ${error.message}`,
440
+ this.adapterType,
441
+ error
442
+ );
443
+ }
444
+ }
445
+ async exportKey(key, options) {
446
+ try {
447
+ this.log("Exporting PGP key");
448
+ if (typeof key.data === "string") {
449
+ if (options?.format === "binary") {
450
+ return Buffer.from(key.data);
451
+ }
452
+ return key.data;
453
+ }
454
+ if (Buffer.isBuffer(key.data)) {
455
+ if (options?.format === "armored" || options?.armor) {
456
+ return key.data.toString("utf8");
457
+ }
458
+ return key.data;
459
+ }
460
+ throw new InvalidKeyError(
461
+ "Invalid key data format for export",
462
+ this.adapterType
463
+ );
464
+ } catch (error) {
465
+ throw new KeyError(
466
+ `Failed to export PGP key: ${error.message}`,
467
+ this.adapterType,
468
+ error
469
+ );
470
+ }
471
+ }
472
+ /**
473
+ * Sign data with private key
474
+ */
475
+ async sign(data, options) {
476
+ try {
477
+ this.log("Signing data");
478
+ await this.initializeKeys();
479
+ let signingKey;
480
+ if (options?.privateKey) {
481
+ signingKey = await openpgp.readPrivateKey({
482
+ armoredKey: options.privateKey
483
+ });
484
+ if (options.passphrase) {
485
+ signingKey = await openpgp.decryptKey({
486
+ privateKey: signingKey,
487
+ passphrase: options.passphrase
488
+ });
489
+ }
490
+ } else if (this.privateKeys && this.privateKeys.length > 0) {
491
+ signingKey = this.privateKeys[0];
492
+ } else {
493
+ throw new KeyError(
494
+ "No private key available for signing",
495
+ this.adapterType
496
+ );
497
+ }
498
+ const message = typeof data === "string" ? await openpgp.createMessage({ text: data }) : await openpgp.createMessage({ binary: data });
499
+ const format = options?.armor !== false ? "armored" : "binary";
500
+ const signed = await openpgp.sign({
501
+ message,
502
+ signingKeys: signingKey,
503
+ detached: options?.detached,
504
+ format
505
+ });
506
+ this.log("Data signed successfully");
507
+ if (format === "armored") {
508
+ return signed;
509
+ } else {
510
+ if (signed instanceof Uint8Array) {
511
+ return Buffer.from(signed);
512
+ }
513
+ return Buffer.from(signed);
514
+ }
515
+ } catch (error) {
516
+ throw new SignatureError(
517
+ `Failed to sign data: ${error.message}`,
518
+ this.adapterType,
519
+ error
520
+ );
521
+ }
522
+ }
523
+ /**
524
+ * Verify signature
525
+ */
526
+ async verify(data, signature, options) {
527
+ try {
528
+ this.log("Verifying signature");
529
+ if (!options?.publicKey) {
530
+ throw new KeyError(
531
+ "Public key required for signature verification",
532
+ this.adapterType
533
+ );
534
+ }
535
+ const verificationKey = await openpgp.readKey({
536
+ armoredKey: options.publicKey
537
+ });
538
+ const signatureStr = typeof signature === "string" ? signature : signature.toString("utf8");
539
+ const isDetached = signatureStr.includes("-----BEGIN PGP SIGNATURE-----");
540
+ if (isDetached) {
541
+ const message = typeof data === "string" ? await openpgp.createMessage({ text: data }) : await openpgp.createMessage({ binary: data });
542
+ const sig = await openpgp.readSignature({
543
+ armoredSignature: signatureStr
544
+ });
545
+ const result = await openpgp.verify({
546
+ message,
547
+ signature: sig,
548
+ verificationKeys: verificationKey
549
+ });
550
+ await result.signatures[0].verified;
551
+ } else {
552
+ let message;
553
+ if (signatureStr.includes("-----BEGIN PGP SIGNED MESSAGE-----")) {
554
+ message = await openpgp.readCleartextMessage({
555
+ cleartextMessage: signatureStr
556
+ });
557
+ } else {
558
+ try {
559
+ message = await openpgp.readMessage({
560
+ armoredMessage: signatureStr
561
+ });
562
+ } catch {
563
+ message = await openpgp.readMessage({
564
+ binaryMessage: typeof signature === "string" ? Buffer.from(signature, "binary") : signature
565
+ });
566
+ }
567
+ }
568
+ const result = await openpgp.verify({
569
+ message,
570
+ verificationKeys: verificationKey
571
+ });
572
+ await result.signatures[0].verified;
573
+ }
574
+ this.log("Signature verified successfully");
575
+ return true;
576
+ } catch (error) {
577
+ if (error instanceof KeyError) {
578
+ throw error;
579
+ }
580
+ this.log("Signature verification failed", error);
581
+ return false;
582
+ }
583
+ }
584
+ /**
585
+ * Encrypt email message in PGP/MIME format
586
+ */
587
+ async encryptEmail(message, options) {
588
+ try {
589
+ this.log("Encrypting email message");
590
+ const emailContent = this.serializeEmailContent(message, options);
591
+ const publicKeys = await this.getPublicKeys({
592
+ publicKeys: options?.publicKeys
593
+ });
594
+ let signingKey;
595
+ if (options?.sign) {
596
+ await this.initializeKeys();
597
+ if (options.privateKey) {
598
+ const key = await openpgp.readPrivateKey({
599
+ armoredKey: options.privateKey
600
+ });
601
+ if (options.passphrase) {
602
+ signingKey = await openpgp.decryptKey({
603
+ privateKey: key,
604
+ passphrase: options.passphrase
605
+ });
606
+ } else {
607
+ signingKey = key;
608
+ }
609
+ } else if (this.privateKeys && this.privateKeys.length > 0) {
610
+ signingKey = this.privateKeys[0];
611
+ }
612
+ }
613
+ const encryptConfig = {
614
+ message: await openpgp.createMessage({ text: emailContent }),
615
+ encryptionKeys: publicKeys,
616
+ format: options?.armor !== false ? "armored" : "binary"
617
+ };
618
+ if (signingKey) {
619
+ encryptConfig.signingKeys = signingKey;
620
+ }
621
+ const encrypted = await openpgp.encrypt(encryptConfig);
622
+ this.log("Email encrypted successfully");
623
+ return {
624
+ ...message,
625
+ text: encrypted,
626
+ html: void 0,
627
+ // Clear HTML when encrypted
628
+ contentType: 'multipart/encrypted; protocol="application/pgp-encrypted"',
629
+ headers: {
630
+ ...message.headers || {},
631
+ "Content-Type": 'multipart/encrypted; protocol="application/pgp-encrypted"'
632
+ }
633
+ };
634
+ } catch (error) {
635
+ throw new EncryptError(
636
+ `Failed to encrypt email: ${error.message}`,
637
+ this.adapterType,
638
+ error
639
+ );
640
+ }
641
+ }
642
+ /**
643
+ * Decrypt PGP/MIME email message
644
+ */
645
+ async decryptEmail(message, options) {
646
+ try {
647
+ this.log("Decrypting email message");
648
+ if (!message.text) {
649
+ throw new DecryptError(
650
+ "Email message has no text content to decrypt",
651
+ this.adapterType
652
+ );
653
+ }
654
+ const privateKeys = await this.getPrivateKeys({
655
+ privateKey: options?.privateKey,
656
+ passphrase: options?.passphrase
657
+ });
658
+ let encryptedMessage;
659
+ try {
660
+ encryptedMessage = await openpgp.readMessage({
661
+ armoredMessage: message.text
662
+ });
663
+ } catch {
664
+ encryptedMessage = await openpgp.readMessage({
665
+ binaryMessage: Buffer.from(message.text, "base64")
666
+ });
667
+ }
668
+ const decryptConfig = {
669
+ message: encryptedMessage,
670
+ decryptionKeys: privateKeys,
671
+ format: "utf8"
672
+ };
673
+ if (options?.verify) {
674
+ const verificationKey = options.publicKey || this.options.publicKey;
675
+ if (verificationKey) {
676
+ decryptConfig.verificationKeys = await openpgp.readKey({
677
+ armoredKey: verificationKey
678
+ });
679
+ }
680
+ }
681
+ const result = await openpgp.decrypt(decryptConfig);
682
+ const decryptedContent = result.data;
683
+ let verified = false;
684
+ let verificationError;
685
+ let signerKeyId;
686
+ let signerFingerprint;
687
+ if (options?.verify) {
688
+ const verificationKey = options.publicKey || this.options.publicKey;
689
+ if (!verificationKey) {
690
+ verificationError = "Public key required for signature verification";
691
+ this.log("Email signature verification skipped: no public key");
692
+ } else if (!result.signatures || result.signatures.length === 0) {
693
+ verificationError = "No signature found in message";
694
+ this.log("Email signature verification skipped: no signatures");
695
+ } else {
696
+ try {
697
+ await result.signatures[0].verified;
698
+ verified = true;
699
+ signerKeyId = result.signatures[0].keyID.toHex();
700
+ const pubKey = await openpgp.readKey({
701
+ armoredKey: verificationKey
702
+ });
703
+ signerFingerprint = pubKey.getFingerprint();
704
+ this.log("Email signature verified successfully");
705
+ } catch (error) {
706
+ verificationError = error instanceof Error ? error.message : String(error);
707
+ this.log("Email signature verification failed", error);
708
+ }
709
+ }
710
+ }
711
+ const deserializedMessage = this.deserializeEmailContent(
712
+ decryptedContent,
713
+ message
714
+ );
715
+ this.log("Email decrypted successfully");
716
+ return {
717
+ ...deserializedMessage,
718
+ encrypted: true,
719
+ signed: result.signatures && result.signatures.length > 0,
720
+ verified: options?.verify ? verified : void 0,
721
+ verificationError,
722
+ signerKeyId,
723
+ signerFingerprint,
724
+ encryptionAlgorithm: "pgp"
725
+ };
726
+ } catch (error) {
727
+ if (error instanceof DecryptError) {
728
+ throw error;
729
+ }
730
+ throw new DecryptError(
731
+ `Failed to decrypt email: ${error.message}`,
732
+ this.adapterType,
733
+ error
734
+ );
735
+ }
736
+ }
737
+ /**
738
+ * Sign email message
739
+ */
740
+ async signEmail(message, options) {
741
+ try {
742
+ this.log("Signing email message");
743
+ const emailContent = this.serializeEmailContent(message);
744
+ await this.initializeKeys();
745
+ let signingKey;
746
+ if (options?.privateKey) {
747
+ const key = await openpgp.readPrivateKey({
748
+ armoredKey: options.privateKey
749
+ });
750
+ if (options.passphrase) {
751
+ signingKey = await openpgp.decryptKey({
752
+ privateKey: key,
753
+ passphrase: options.passphrase
754
+ });
755
+ } else {
756
+ signingKey = key;
757
+ }
758
+ } else if (this.privateKeys && this.privateKeys.length > 0) {
759
+ signingKey = this.privateKeys[0];
760
+ } else {
761
+ throw new KeyError(
762
+ "No private key available for signing email",
763
+ this.adapterType
764
+ );
765
+ }
766
+ const signedMessageObj = await openpgp.sign({
767
+ message: await openpgp.createCleartextMessage({ text: emailContent }),
768
+ signingKeys: signingKey,
769
+ format: "object"
770
+ });
771
+ const signedMessage = signedMessageObj.armor();
772
+ this.log("Email signed successfully");
773
+ return {
774
+ ...message,
775
+ text: signedMessage,
776
+ contentType: "text/plain; charset=utf-8",
777
+ headers: {
778
+ ...message.headers || {},
779
+ "Content-Type": "text/plain; charset=utf-8"
780
+ }
781
+ };
782
+ } catch (error) {
783
+ throw new SignatureError(
784
+ `Failed to sign email: ${error.message}`,
785
+ this.adapterType,
786
+ error
787
+ );
788
+ }
789
+ }
790
+ /**
791
+ * Verify email signature
792
+ */
793
+ async verifyEmail(message, options) {
794
+ try {
795
+ this.log("Verifying email signature");
796
+ if (!message.text) {
797
+ return {
798
+ valid: false,
799
+ message: "Email message has no text content to verify"
800
+ };
801
+ }
802
+ if (!options?.publicKey) {
803
+ return {
804
+ valid: false,
805
+ message: "Public key required for email signature verification"
806
+ };
807
+ }
808
+ const verificationKey = await openpgp.readKey({
809
+ armoredKey: options.publicKey
810
+ });
811
+ const signedMessage = await openpgp.readCleartextMessage({
812
+ cleartextMessage: message.text
813
+ });
814
+ const result = await openpgp.verify({
815
+ message: signedMessage,
816
+ verificationKeys: verificationKey
817
+ });
818
+ await result.signatures[0].verified;
819
+ const keyId = result.signatures[0].keyID.toHex();
820
+ const fingerprint = verificationKey.getFingerprint();
821
+ this.log("Email signature verified successfully");
822
+ return {
823
+ valid: true,
824
+ keyId,
825
+ keyFingerprint: fingerprint,
826
+ timestamp: /* @__PURE__ */ new Date(),
827
+ algorithm: "pgp"
828
+ };
829
+ } catch (error) {
830
+ this.log("Email signature verification failed", error);
831
+ return {
832
+ valid: false,
833
+ message: error.message
834
+ };
835
+ }
836
+ }
837
+ /**
838
+ * Serialize email content for encryption/signing
839
+ */
840
+ serializeEmailContent(message, options) {
841
+ const parts = [];
842
+ if (options?.encryptSubject) {
843
+ parts.push(`Subject: ${message.subject}`);
844
+ }
845
+ if (message.text) {
846
+ parts.push(`
847
+ ${message.text}`);
848
+ }
849
+ if (message.html) {
850
+ parts.push(`
851
+ --- HTML Content ---
852
+ ${message.html}`);
853
+ }
854
+ if (message.attachments && message.attachments.length > 0) {
855
+ parts.push("\n--- Attachments ---");
856
+ for (const attachment of message.attachments) {
857
+ parts.push(
858
+ `- ${attachment.filename || "unnamed"} (${attachment.contentType}, ${attachment.size} bytes)`
859
+ );
860
+ }
861
+ }
862
+ return parts.join("\n");
863
+ }
864
+ /**
865
+ * Deserialize email content after decryption
866
+ */
867
+ deserializeEmailContent(content, originalMessage) {
868
+ const lines = content.split("\n");
869
+ let subject = originalMessage.subject;
870
+ let text = "";
871
+ let html;
872
+ let currentSection = "text";
873
+ for (const line of lines) {
874
+ if (line.startsWith("Subject: ")) {
875
+ subject = line.substring(9);
876
+ } else if (line === "--- HTML Content ---") {
877
+ currentSection = "html";
878
+ } else if (line === "--- Attachments ---") {
879
+ break;
880
+ } else if (currentSection === "text" && line.trim()) {
881
+ text += `${line}
882
+ `;
883
+ } else if (currentSection === "html") {
884
+ if (!html) html = "";
885
+ html += `${line}
886
+ `;
887
+ }
888
+ }
889
+ return {
890
+ ...originalMessage,
891
+ subject,
892
+ text: text.trim(),
893
+ html: html?.trim()
894
+ };
895
+ }
896
+ async getCapabilities() {
897
+ return {
898
+ textEncryption: true,
899
+ fileEncryption: true,
900
+ bufferEncryption: true,
901
+ streamEncryption: true,
902
+ emailEncryption: true,
903
+ signing: true,
904
+ verification: true,
905
+ keyGeneration: true,
906
+ keyManagement: true,
907
+ multipleRecipients: true,
908
+ symmetricEncryption: false,
909
+ asymmetricEncryption: true
910
+ };
911
+ }
912
+ }
913
+ export {
914
+ PGPEncryption
915
+ };
916
+ //# sourceMappingURL=pgp-BIhtvrNo.js.map