@protontech/openpgp 6.0.0-alpha.1.patch.1 → 6.0.0-beta.0.patch.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 (36) hide show
  1. package/dist/lightweight/argon2id.min.mjs +1 -1
  2. package/dist/lightweight/argon2id.mjs +1 -1
  3. package/dist/lightweight/bn.interface.min.mjs +2 -2
  4. package/dist/lightweight/bn.interface.min.mjs.map +1 -1
  5. package/dist/lightweight/bn.interface.mjs +1 -1
  6. package/dist/lightweight/interface.min.mjs +1 -1
  7. package/dist/lightweight/interface.mjs +1 -1
  8. package/dist/lightweight/legacy_ciphers.min.mjs +1 -1
  9. package/dist/lightweight/legacy_ciphers.mjs +1 -1
  10. package/dist/lightweight/native.interface.min.mjs +1 -1
  11. package/dist/lightweight/native.interface.mjs +1 -1
  12. package/dist/lightweight/noble_curves.min.mjs +3 -3
  13. package/dist/lightweight/noble_curves.min.mjs.map +1 -1
  14. package/dist/lightweight/noble_curves.mjs +1 -1
  15. package/dist/lightweight/noble_hashes.min.mjs +1 -1
  16. package/dist/lightweight/noble_hashes.mjs +1 -1
  17. package/dist/lightweight/openpgp.min.mjs +2 -2
  18. package/dist/lightweight/openpgp.min.mjs.map +1 -1
  19. package/dist/lightweight/openpgp.mjs +211 -83
  20. package/dist/lightweight/sha3.min.mjs +2 -2
  21. package/dist/lightweight/sha3.min.mjs.map +1 -1
  22. package/dist/lightweight/sha3.mjs +1 -1
  23. package/dist/node/openpgp.cjs +211 -83
  24. package/dist/node/openpgp.min.cjs +11 -11
  25. package/dist/node/openpgp.min.cjs.map +1 -1
  26. package/dist/node/openpgp.min.mjs +11 -11
  27. package/dist/node/openpgp.min.mjs.map +1 -1
  28. package/dist/node/openpgp.mjs +211 -83
  29. package/dist/openpgp.js +211 -83
  30. package/dist/openpgp.min.js +11 -11
  31. package/dist/openpgp.min.js.map +1 -1
  32. package/dist/openpgp.min.mjs +11 -11
  33. package/dist/openpgp.min.mjs.map +1 -1
  34. package/dist/openpgp.mjs +211 -83
  35. package/openpgp.d.ts +5 -3
  36. package/package.json +9 -9
@@ -1,4 +1,4 @@
1
- /*! OpenPGP.js v6.0.0-alpha.1.patch.1 - 2024-03-11 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
1
+ /*! OpenPGP.js v6.0.0-beta.0.patch.0 - 2024-04-19 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
2
2
  const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
3
3
 
4
4
  import { createRequire } from 'module';
@@ -1468,6 +1468,14 @@ var config = {
1468
1468
  * @property {Boolean} aeadProtect
1469
1469
  */
1470
1470
  aeadProtect: false,
1471
+ /**
1472
+ * Whether to disable encrypton using SEIPDv2 even if the encryption keys include the SEIPDv2 feature flag.
1473
+ * If true, SEIPDv1 (i.e. no AEAD) packets are always used instead.
1474
+ * SEIPDv2 is a more secure and faster choice, but it is not necessarily compatible with other libs and our mobile apps.
1475
+ * @memberof module:config
1476
+ * @property {Boolean} ignoreSEIPDv2FeatureFlag
1477
+ */
1478
+ ignoreSEIPDv2FeatureFlag: false,
1471
1479
  /**
1472
1480
  * When reading OpenPGP v4 private keys (e.g. those generated in OpenPGP.js when not setting `config.v5Keys = true`)
1473
1481
  * which were encrypted by OpenPGP.js v5 (or older) using `config.aeadProtect = true`,
@@ -1580,11 +1588,6 @@ var config = {
1580
1588
  * @property {Boolean} passwordCollisionCheck
1581
1589
  */
1582
1590
  passwordCollisionCheck: false,
1583
- /**
1584
- * @memberof module:config
1585
- * @property {Boolean} revocationsExpire If true, expired revocation signatures are ignored
1586
- */
1587
- revocationsExpire: false,
1588
1591
  /**
1589
1592
  * Allow decryption using RSA keys without `encrypt` flag.
1590
1593
  * This setting is potentially insecure, but it is needed to get around an old openpgpjs bug
@@ -1660,7 +1663,7 @@ var config = {
1660
1663
  * @memberof module:config
1661
1664
  * @property {String} versionString A version string to be included in armored messages
1662
1665
  */
1663
- versionString: 'OpenPGP.js 6.0.0-alpha.1.patch.1',
1666
+ versionString: 'OpenPGP.js 6.0.0-beta.0.patch.0',
1664
1667
  /**
1665
1668
  * @memberof module:config
1666
1669
  * @property {String} commentString A comment string to be included in armored messages
@@ -1680,6 +1683,14 @@ var config = {
1680
1683
  * @property {Array} knownNotations
1681
1684
  */
1682
1685
  knownNotations: [],
1686
+ /**
1687
+ * If true, a salt notation is used to randomize signatures generated by v4 and v5 keys (v6 signatures are always non-deterministic, by design).
1688
+ * This protects EdDSA signatures from potentially leaking the secret key in case of faults (i.e. bitflips) which, in principle, could occur
1689
+ * during the signing computation. It is added to signatures of any algo for simplicity, and as it may also serve as protection in case of
1690
+ * weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks.
1691
+ * NOTE: the notation is interoperable, but will reveal that the signature has been generated using OpenPGP.js, which may not be desirable in some cases.
1692
+ */
1693
+ nonDeterministicSignaturesViaNotation: true,
1683
1694
  /**
1684
1695
  * Whether to use the the noble-curves library for curves (other than Curve25519) that are not supported by the available native crypto API.
1685
1696
  * When false, certain standard curves will not be supported (depending on the platform).
@@ -1710,14 +1721,7 @@ var config = {
1710
1721
  * @memberof module:config
1711
1722
  * @property {Set<String>} rejectCurves {@link module:enums.curve}
1712
1723
  */
1713
- rejectCurves: new Set([enums.curve.secp256k1]),
1714
- /**
1715
- * Whether to validate generated EdDSA signatures before returning them, to ensure they are not faulty signatures.
1716
- * This check will make signing 2-3 times slower.
1717
- * Faulty signatures may be generated (in principle) if random bitflips occur at specific points in the signature
1718
- * computation, and could be used to recover the signer's secret key given a second signature over the same data.
1719
- */
1720
- checkEdDSAFaultySignatures: true
1724
+ rejectCurves: new Set([enums.curve.secp256k1])
1721
1725
  };
1722
1726
 
1723
1727
  /**
@@ -2202,16 +2206,19 @@ const util = {
2202
2206
  },
2203
2207
 
2204
2208
  /**
2205
- * Test email format based on W3C HTML5 specification.
2206
- * This check is not exaustive, and does not match RFC 5322 exactly
2207
- * (see https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)),
2208
- * but is commonly used for email address validation.
2209
+ * Test email format to ensure basic compliance:
2210
+ * - must include a single @
2211
+ * - no control or space unicode chars allowed
2212
+ * - no backslash and square brackets (as the latter can mess with the userID parsing)
2213
+ * - cannot end with a punctuation char
2214
+ * These checks are not meant to be exhaustive; applications are strongly encouraged to implement stricter validation,
2215
+ * e.g. based on the W3C HTML spec (https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)).
2209
2216
  */
2210
2217
  isEmailAddress: function(data) {
2211
2218
  if (!util.isString(data)) {
2212
2219
  return false;
2213
2220
  }
2214
- const re = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
2221
+ const re = /^[^\p{C}\p{Z}@<>\\]+@[^\p{C}\p{Z}@<>\\]+[^\p{C}\p{Z}\p{P}]$/u;
2215
2222
  return re.test(data);
2216
2223
  },
2217
2224
 
@@ -2617,6 +2624,78 @@ function addheader(customComment, config) {
2617
2624
  return result;
2618
2625
  }
2619
2626
 
2627
+ /**
2628
+ * Calculates a checksum over the given data and returns it base64 encoded
2629
+ * @param {String | ReadableStream<String>} data - Data to create a CRC-24 checksum for
2630
+ * @returns {String | ReadableStream<String>} Base64 encoded checksum.
2631
+ * @private
2632
+ */
2633
+ function getCheckSum(data) {
2634
+ const crc = createcrc24(data);
2635
+ return encode$1(crc);
2636
+ }
2637
+
2638
+ // https://create.stephan-brumme.com/crc32/#slicing-by-8-overview
2639
+
2640
+ const crc_table = [
2641
+ new Array(0xFF),
2642
+ new Array(0xFF),
2643
+ new Array(0xFF),
2644
+ new Array(0xFF)
2645
+ ];
2646
+
2647
+ for (let i = 0; i <= 0xFF; i++) {
2648
+ let crc = i << 16;
2649
+ for (let j = 0; j < 8; j++) {
2650
+ crc = (crc << 1) ^ ((crc & 0x800000) !== 0 ? 0x864CFB : 0);
2651
+ }
2652
+ crc_table[0][i] =
2653
+ ((crc & 0xFF0000) >> 16) |
2654
+ (crc & 0x00FF00) |
2655
+ ((crc & 0x0000FF) << 16);
2656
+ }
2657
+ for (let i = 0; i <= 0xFF; i++) {
2658
+ crc_table[1][i] = (crc_table[0][i] >> 8) ^ crc_table[0][crc_table[0][i] & 0xFF];
2659
+ }
2660
+ for (let i = 0; i <= 0xFF; i++) {
2661
+ crc_table[2][i] = (crc_table[1][i] >> 8) ^ crc_table[0][crc_table[1][i] & 0xFF];
2662
+ }
2663
+ for (let i = 0; i <= 0xFF; i++) {
2664
+ crc_table[3][i] = (crc_table[2][i] >> 8) ^ crc_table[0][crc_table[2][i] & 0xFF];
2665
+ }
2666
+
2667
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness
2668
+ const isLittleEndian$1 = (function() {
2669
+ const buffer = new ArrayBuffer(2);
2670
+ new DataView(buffer).setInt16(0, 0xFF, true /* littleEndian */);
2671
+ // Int16Array uses the platform's endianness.
2672
+ return new Int16Array(buffer)[0] === 0xFF;
2673
+ }());
2674
+
2675
+ /**
2676
+ * Internal function to calculate a CRC-24 checksum over a given string (data)
2677
+ * @param {String | ReadableStream<String>} input - Data to create a CRC-24 checksum for
2678
+ * @returns {Uint8Array | ReadableStream<Uint8Array>} The CRC-24 checksum.
2679
+ * @private
2680
+ */
2681
+ function createcrc24(input) {
2682
+ let crc = 0xCE04B7;
2683
+ return transform(input, value => {
2684
+ const len32 = isLittleEndian$1 ? Math.floor(value.length / 4) : 0;
2685
+ const arr32 = new Uint32Array(value.buffer, value.byteOffset, len32);
2686
+ for (let i = 0; i < len32; i++) {
2687
+ crc ^= arr32[i];
2688
+ crc =
2689
+ crc_table[0][(crc >> 24) & 0xFF] ^
2690
+ crc_table[1][(crc >> 16) & 0xFF] ^
2691
+ crc_table[2][(crc >> 8) & 0xFF] ^
2692
+ crc_table[3][(crc >> 0) & 0xFF];
2693
+ }
2694
+ for (let i = len32 * 4; i < value.length; i++) {
2695
+ crc = (crc >> 8) ^ crc_table[0][(crc & 0xFF) ^ value[i]];
2696
+ }
2697
+ }, () => new Uint8Array([crc, crc >> 8, crc >> 16]));
2698
+ }
2620
2699
 
2621
2700
  /**
2622
2701
  * Verify armored headers. crypto-refresh-06, section 6.2:
@@ -2772,10 +2851,13 @@ function unarmor(input) {
2772
2851
  * @param {Integer} [partIndex]
2773
2852
  * @param {Integer} [partTotal]
2774
2853
  * @param {String} [customComment] - Additional comment to add to the armored string
2854
+ * @param {Boolean} [emitChecksum] - Whether to compute and include the CRC checksum
2855
+ * (NB: some types of data must not include it, but compliance is left as responsibility of the caller: this function does not carry out any checks)
2856
+ * @param {Object} [config] - Full configuration, defaults to openpgp.config
2775
2857
  * @returns {String | ReadableStream<String>} Armored text.
2776
2858
  * @static
2777
2859
  */
2778
- function armor(messageType, body, partIndex, partTotal, customComment, config$1 = config) {
2860
+ function armor(messageType, body, partIndex, partTotal, customComment, emitChecksum = false, config$1 = config) {
2779
2861
  let text;
2780
2862
  let hash;
2781
2863
  if (messageType === enums.armor.signed) {
@@ -2783,18 +2865,24 @@ function armor(messageType, body, partIndex, partTotal, customComment, config$1
2783
2865
  hash = body.hash;
2784
2866
  body = body.data;
2785
2867
  }
2868
+ // unless explicitly forbidden by the spec, we need to include the checksum to work around a GnuPG bug
2869
+ // where data fails to be decoded if the base64 ends with no padding chars (=) (see https://dev.gnupg.org/T7071)
2870
+ const maybeBodyClone = emitChecksum && passiveClone(body);
2871
+
2786
2872
  const result = [];
2787
2873
  switch (messageType) {
2788
2874
  case enums.armor.multipartSection:
2789
2875
  result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n');
2790
2876
  result.push(addheader(customComment, config$1));
2791
2877
  result.push(encode$1(body));
2878
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2792
2879
  result.push('-----END PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n');
2793
2880
  break;
2794
2881
  case enums.armor.multipartLast:
2795
2882
  result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '-----\n');
2796
2883
  result.push(addheader(customComment, config$1));
2797
2884
  result.push(encode$1(body));
2885
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2798
2886
  result.push('-----END PGP MESSAGE, PART ' + partIndex + '-----\n');
2799
2887
  break;
2800
2888
  case enums.armor.signed:
@@ -2804,30 +2892,35 @@ function armor(messageType, body, partIndex, partTotal, customComment, config$1
2804
2892
  result.push('\n-----BEGIN PGP SIGNATURE-----\n');
2805
2893
  result.push(addheader(customComment, config$1));
2806
2894
  result.push(encode$1(body));
2895
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2807
2896
  result.push('-----END PGP SIGNATURE-----\n');
2808
2897
  break;
2809
2898
  case enums.armor.message:
2810
2899
  result.push('-----BEGIN PGP MESSAGE-----\n');
2811
2900
  result.push(addheader(customComment, config$1));
2812
2901
  result.push(encode$1(body));
2902
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2813
2903
  result.push('-----END PGP MESSAGE-----\n');
2814
2904
  break;
2815
2905
  case enums.armor.publicKey:
2816
2906
  result.push('-----BEGIN PGP PUBLIC KEY BLOCK-----\n');
2817
2907
  result.push(addheader(customComment, config$1));
2818
2908
  result.push(encode$1(body));
2909
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2819
2910
  result.push('-----END PGP PUBLIC KEY BLOCK-----\n');
2820
2911
  break;
2821
2912
  case enums.armor.privateKey:
2822
2913
  result.push('-----BEGIN PGP PRIVATE KEY BLOCK-----\n');
2823
2914
  result.push(addheader(customComment, config$1));
2824
2915
  result.push(encode$1(body));
2916
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2825
2917
  result.push('-----END PGP PRIVATE KEY BLOCK-----\n');
2826
2918
  break;
2827
2919
  case enums.armor.signature:
2828
2920
  result.push('-----BEGIN PGP SIGNATURE-----\n');
2829
2921
  result.push(addheader(customComment, config$1));
2830
2922
  result.push(encode$1(body));
2923
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2831
2924
  result.push('-----END PGP SIGNATURE-----\n');
2832
2925
  break;
2833
2926
  }
@@ -9355,20 +9448,6 @@ async function sign$5(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9355
9448
  }
9356
9449
  const secretKey = util.concatUint8Array([privateKey, publicKey.subarray(1)]);
9357
9450
  const signature = nacl.sign.detached(hashed, secretKey);
9358
- if (config.checkEdDSAFaultySignatures && !nacl.sign.detached.verify(hashed, signature, publicKey.subarray(1))) {
9359
- /**
9360
- * Detect faulty signatures caused by random bitflips during `crypto_sign` which could lead to private key extraction
9361
- * if two signatures over the same message are obtained.
9362
- * See https://github.com/jedisct1/libsodium/issues/170.
9363
- * If the input data is not deterministic, e.g. thanks to the random salt in v6 OpenPGP signatures (not yet implemented),
9364
- * then the generated signature is always safe, and the verification step is skipped.
9365
- * Otherwise, we need to verify the generated to ensure that no bitflip occured:
9366
- * - in M between the computation of `r` and `h`.
9367
- * - in the public key before computing `h`
9368
- * The verification step is almost 2-3 times as slow as signing, but it's faster than re-signing + re-deriving the public key for separate checks.
9369
- */
9370
- throw new Error('Transient signing failure');
9371
- }
9372
9451
  // EdDSA signature params are returned in little-endian format
9373
9452
  return {
9374
9453
  r: signature.subarray(0, 32),
@@ -9489,20 +9568,6 @@ async function sign$4(algo, hashAlgo, message, publicKey, privateKey, hashed) {
9489
9568
  case enums.publicKey.ed25519: {
9490
9569
  const secretKey = util.concatUint8Array([privateKey, publicKey]);
9491
9570
  const signature = nacl.sign.detached(hashed, secretKey);
9492
- if (config.checkEdDSAFaultySignatures && !nacl.sign.detached.verify(hashed, signature, publicKey)) {
9493
- /**
9494
- * Detect faulty signatures caused by random bitflips during `crypto_sign` which could lead to private key extraction
9495
- * if two signatures over the same message are obtained.
9496
- * See https://github.com/jedisct1/libsodium/issues/170.
9497
- * If the input data is not deterministic, e.g. thanks to the random salt in v6 OpenPGP signatures (not yet implemented),
9498
- * then the generated signature is always safe, and the verification step is skipped.
9499
- * Otherwise, we need to verify the generated to ensure that no bitflip occured:
9500
- * - in M between the computation of `r` and `h`.
9501
- * - in the public key before computing `h`
9502
- * The verification step is almost 2-3 times as slow as signing, but it's faster than re-signing + re-deriving the public key for separate checks.
9503
- */
9504
- throw new Error('Transient signing failure');
9505
- }
9506
9571
  return { RS: signature };
9507
9572
  }
9508
9573
  case enums.publicKey.ed448: {
@@ -11235,7 +11300,7 @@ class ECDHXSymmetricKey {
11235
11300
  * Encrypts data using specified algorithm and public key parameters.
11236
11301
  * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
11237
11302
  * @param {module:enums.publicKey} keyAlgo - Public key algorithm
11238
- * @param {module:enums.symmetric} symmetricAlgo - Cipher algorithm
11303
+ * @param {module:enums.symmetric|null} symmetricAlgo - Cipher algorithm (v3 only)
11239
11304
  * @param {Object} publicParams - Algorithm-specific public key parameters
11240
11305
  * @param {Object} privateParams - Algorithm-specific private key parameters
11241
11306
  * @param {Uint8Array} data - Data to be encrypted
@@ -11263,7 +11328,7 @@ async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, privatePar
11263
11328
  }
11264
11329
  case enums.publicKey.x25519:
11265
11330
  case enums.publicKey.x448: {
11266
- if (!util.isAES(symmetricAlgo)) {
11331
+ if (symmetricAlgo && !util.isAES(symmetricAlgo)) {
11267
11332
  // see https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/276
11268
11333
  throw new Error('X25519 and X448 keys can only encrypt AES session keys');
11269
11334
  }
@@ -11895,9 +11960,26 @@ class Argon2OutOfMemoryError extends Error {
11895
11960
  let loadArgonWasmModule;
11896
11961
  let argon2Promise;
11897
11962
  // reload wasm module above this treshold, to deallocated used memory
11898
- const ARGON2_WASM_MEMORY_THRESHOLD_RELOAD = 2 << 19;
11963
+ // (cannot be declared as a simple `static` field as its not supported by Safari 14)
11964
+ let ARGON2_WASM_MEMORY_THRESHOLD_RELOAD = 2 << 19;
11899
11965
 
11900
11966
  class Argon2S2K {
11967
+ static get ARGON2_WASM_MEMORY_THRESHOLD_RELOAD() {
11968
+ return ARGON2_WASM_MEMORY_THRESHOLD_RELOAD;
11969
+ }
11970
+
11971
+ static set ARGON2_WASM_MEMORY_THRESHOLD_RELOAD(memoryThreshold) {
11972
+ ARGON2_WASM_MEMORY_THRESHOLD_RELOAD = memoryThreshold;
11973
+ }
11974
+
11975
+ static reloadWasmModule() {
11976
+ if (!loadArgonWasmModule) return;
11977
+
11978
+ // it will be awaited if needed at the next `produceKey` invocation
11979
+ argon2Promise = loadArgonWasmModule();
11980
+ argon2Promise.catch(() => {});
11981
+ }
11982
+
11901
11983
  /**
11902
11984
  * @param {Object} [config] - Full configuration, defaults to openpgp.config
11903
11985
  */
@@ -11985,10 +12067,8 @@ class Argon2S2K {
11985
12067
  });
11986
12068
 
11987
12069
  // a lot of memory was used, reload to deallocate
11988
- if (decodedM > ARGON2_WASM_MEMORY_THRESHOLD_RELOAD) {
11989
- // it will be awaited if needed at the next `produceKey` invocation
11990
- argon2Promise = loadArgonWasmModule();
11991
- argon2Promise.catch(() => {});
12070
+ if (decodedM > Argon2S2K.ARGON2_WASM_MEMORY_THRESHOLD_RELOAD) {
12071
+ Argon2S2K.reloadWasmModule();
11992
12072
  }
11993
12073
  return hash;
11994
12074
  } catch (e) {
@@ -14237,6 +14317,14 @@ class KeyID {
14237
14317
  // Symbol to store cryptographic validity of the signature, to avoid recomputing multiple times on verification.
14238
14318
  const verified = Symbol('verified');
14239
14319
 
14320
+ // A salt notation is used to randomize signatures.
14321
+ // This is to protect EdDSA signatures in particular, which are known to be vulnerable to fault attacks
14322
+ // leading to secret key extraction if two signatures over the same data can be collected (see https://github.com/jedisct1/libsodium/issues/170).
14323
+ // For simplicity, we add the salt to all algos, as it may also serve as protection in case of weaknesses in the hash algo, potentially hindering e.g.
14324
+ // some chosen-prefix attacks.
14325
+ // v6 signatures do not need to rely on this notation, as they already include a separate, built-in salt.
14326
+ const SALT_NOTATION_NAME = 'salt@notations.openpgpjs.org';
14327
+
14240
14328
  // GPG puts the Issuer and Signature subpackets in the unhashed area.
14241
14329
  // Tampering with those invalidates the signature, so we still trust them and parse them.
14242
14330
  // All other unhashed subpackets are ignored.
@@ -14406,7 +14494,7 @@ class SignaturePacket {
14406
14494
  * @throws {Error} if signing failed
14407
14495
  * @async
14408
14496
  */
14409
- async sign(key, data, date = new Date(), detached = false) {
14497
+ async sign(key, data, date = new Date(), detached = false, config) {
14410
14498
  this.version = key.version;
14411
14499
 
14412
14500
  this.created = util.normalizeDate(date);
@@ -14416,6 +14504,31 @@ class SignaturePacket {
14416
14504
 
14417
14505
  const arr = [new Uint8Array([this.version, this.signatureType, this.publicKeyAlgorithm, this.hashAlgorithm])];
14418
14506
 
14507
+ // add randomness to the signature
14508
+ if (this.version === 6) {
14509
+ const saltLength = saltLengthForHash(this.hashAlgorithm);
14510
+ if (this.salt === null) {
14511
+ this.salt = mod$1.random.getRandomBytes(saltLength);
14512
+ } else if (saltLength !== this.salt.length) {
14513
+ throw new Error('Provided salt does not have the required length');
14514
+ }
14515
+ } else if (config.nonDeterministicSignaturesViaNotation) {
14516
+ const saltNotations = this.rawNotations.filter(({ name }) => (name === SALT_NOTATION_NAME));
14517
+ // since re-signing the same object is not supported, it's not expected to have multiple salt notations,
14518
+ // but we guard against it as a sanity check
14519
+ if (saltNotations.length === 0) {
14520
+ const saltValue = mod$1.random.getRandomBytes(saltLengthForHash(this.hashAlgorithm));
14521
+ this.rawNotations.push({
14522
+ name: SALT_NOTATION_NAME,
14523
+ value: saltValue,
14524
+ humanReadable: false,
14525
+ critical: false
14526
+ });
14527
+ } else {
14528
+ throw new Error('Unexpected existing salt notation');
14529
+ }
14530
+ }
14531
+
14419
14532
  // Add hashed subpackets
14420
14533
  arr.push(this.writeHashedSubPackets());
14421
14534
 
@@ -14426,14 +14539,6 @@ class SignaturePacket {
14426
14539
 
14427
14540
  this.signatureData = util.concat(arr);
14428
14541
 
14429
- if (this.version === 6) {
14430
- const saltLength = saltLengthForHash(this.hashAlgorithm);
14431
- if (this.salt === null) {
14432
- this.salt = mod$1.random.getRandomBytes(saltLength);
14433
- } else if (saltLength !== this.salt.length) {
14434
- throw new Error('Provided salt does not have the required length');
14435
- }
14436
- }
14437
14542
  const toHash = this.toHash(this.signatureType, data, detached);
14438
14543
  const hash = await this.hash(this.signatureType, data, toHash, detached);
14439
14544
 
@@ -16235,9 +16340,12 @@ class PublicKeyEncryptedSessionKeyPacket {
16235
16340
  }
16236
16341
  this.publicKeyAlgorithm = bytes[offset++];
16237
16342
  this.encrypted = mod$1.parseEncSessionKeyParams(this.publicKeyAlgorithm, bytes.subarray(offset));
16238
- if (this.version === 3 && (
16239
- this.publicKeyAlgorithm === enums.publicKey.x25519 || this.publicKeyAlgorithm === enums.publicKey.x448)) {
16240
- this.sessionKeyAlgorithm = enums.write(enums.symmetric, this.encrypted.C.algorithm);
16343
+ if (this.publicKeyAlgorithm === enums.publicKey.x25519 || this.publicKeyAlgorithm === enums.publicKey.x448) {
16344
+ if (this.version === 3) {
16345
+ this.sessionKeyAlgorithm = enums.write(enums.symmetric, this.encrypted.C.algorithm);
16346
+ } else if (this.encrypted.C.algorithm !== null) {
16347
+ throw new Error('Unexpected cleartext symmetric algorithm');
16348
+ }
16241
16349
  }
16242
16350
  }
16243
16351
 
@@ -16281,10 +16389,13 @@ class PublicKeyEncryptedSessionKeyPacket {
16281
16389
  */
16282
16390
  async encrypt(key) {
16283
16391
  const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
16284
- const encoded = encodeSessionKey(this.version, algo, this.sessionKeyAlgorithm, this.sessionKey);
16392
+ // No symmetric encryption algorithm identifier is passed to the public-key algorithm for a
16393
+ // v6 PKESK packet, as it is included in the v2 SEIPD packet.
16394
+ const sessionKeyAlgorithm = this.version === 3 ? this.sessionKeyAlgorithm : null;
16395
+ const encoded = encodeSessionKey(this.version, algo, sessionKeyAlgorithm, this.sessionKey);
16285
16396
  const privateParams = algo === enums.publicKey.aead ? key.privateParams : null;
16286
16397
  this.encrypted = await mod$1.publicKeyEncrypt(
16287
- algo, this.sessionKeyAlgorithm, key.publicParams, privateParams, encoded, key.getFingerprintBytes());
16398
+ algo, sessionKeyAlgorithm, key.publicParams, privateParams, encoded, key.getFingerprintBytes());
16288
16399
  }
16289
16400
 
16290
16401
  /**
@@ -16383,6 +16494,7 @@ function decodeSessionKey(version, keyAlgo, decryptedData, randomSessionKey) {
16383
16494
  case enums.publicKey.x25519:
16384
16495
  case enums.publicKey.x448:
16385
16496
  return {
16497
+ sessionKeyAlgorithm: null,
16386
16498
  sessionKey: decryptedData
16387
16499
  };
16388
16500
  default:
@@ -18070,7 +18182,9 @@ class Signature {
18070
18182
  * @returns {ReadableStream<String>} ASCII armor.
18071
18183
  */
18072
18184
  armor(config$1 = config) {
18073
- return armor(enums.armor.signature, this.write(), undefined, undefined, undefined, config$1);
18185
+ // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer.
18186
+ const emitChecksum = this.packets.some(packet => packet.constructor.tag === SignaturePacket.tag && packet.version !== 6);
18187
+ return armor(enums.armor.signature, this.write(), undefined, undefined, undefined, emitChecksum, config$1);
18074
18188
  }
18075
18189
 
18076
18190
  /**
@@ -18283,7 +18397,7 @@ async function getPreferredCompressionAlgo(keys = [], date = new Date(), userIDs
18283
18397
  async function getPreferredCipherSuite(keys = [], date = new Date(), userIDs = [], config$1 = config) {
18284
18398
  const selfSigs = await Promise.all(keys.map((key, i) => key.getPrimarySelfSignature(date, userIDs[i], config$1)));
18285
18399
  const withAEAD = keys.length ?
18286
- selfSigs.every(selfSig => selfSig.features[0] & enums.features.seipdv2) :
18400
+ !config$1.ignoreSEIPDv2FeatureFlag && selfSigs.every(selfSig => selfSig.features && (selfSig.features[0] & enums.features.seipdv2)) :
18287
18401
  config$1.aeadProtect;
18288
18402
 
18289
18403
  if (withAEAD) {
@@ -18330,8 +18444,8 @@ async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, s
18330
18444
  Object.assign(signaturePacket, signatureProperties);
18331
18445
  signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
18332
18446
  signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userID, config);
18333
- signaturePacket.rawNotations = notations;
18334
- await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached);
18447
+ signaturePacket.rawNotations = [...notations];
18448
+ await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached, config);
18335
18449
  return signaturePacket;
18336
18450
  }
18337
18451
 
@@ -18394,7 +18508,7 @@ async function isDataRevoked(primaryKey, signatureType, dataToVerify, revocation
18394
18508
  !signature || revocationSignature.issuerKeyID.equals(signature.issuerKeyID)
18395
18509
  ) {
18396
18510
  await revocationSignature.verify(
18397
- key, signatureType, dataToVerify, config.revocationsExpire ? date : null, false, config
18511
+ key, signatureType, dataToVerify, date, false, config
18398
18512
  );
18399
18513
 
18400
18514
  // TODO get an identifier of the revoked object instead
@@ -19664,7 +19778,9 @@ class Key {
19664
19778
  const revocationSignature = await getLatestValidSignature(this.revocationSignatures, this.keyPacket, enums.signature.keyRevocation, dataToVerify, date, config$1);
19665
19779
  const packetlist = new PacketList();
19666
19780
  packetlist.push(revocationSignature);
19667
- return armor(enums.armor.publicKey, packetlist.write(), null, null, 'This is a revocation certificate');
19781
+ // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer.
19782
+ const emitChecksum = this.keyPacket.version !== 6;
19783
+ return armor(enums.armor.publicKey, packetlist.write(), null, null, 'This is a revocation certificate', emitChecksum, config$1);
19668
19784
  }
19669
19785
 
19670
19786
  /**
@@ -19854,7 +19970,9 @@ class PublicKey extends Key {
19854
19970
  * @returns {ReadableStream<String>} ASCII armor.
19855
19971
  */
19856
19972
  armor(config$1 = config) {
19857
- return armor(enums.armor.publicKey, this.toPacketList().write(), undefined, undefined, undefined, config$1);
19973
+ // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer.
19974
+ const emitChecksum = this.keyPacket.version !== 6;
19975
+ return armor(enums.armor.publicKey, this.toPacketList().write(), undefined, undefined, undefined, emitChecksum, config$1);
19858
19976
  }
19859
19977
  }
19860
19978
 
@@ -19927,7 +20045,9 @@ class PrivateKey extends PublicKey {
19927
20045
  * @returns {ReadableStream<String>} ASCII armor.
19928
20046
  */
19929
20047
  armor(config$1 = config) {
19930
- return armor(enums.armor.privateKey, this.toPacketList().write(), undefined, undefined, undefined, config$1);
20048
+ // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer.
20049
+ const emitChecksum = this.keyPacket.version !== 6;
20050
+ return armor(enums.armor.privateKey, this.toPacketList().write(), undefined, undefined, undefined, emitChecksum, config$1);
19931
20051
  }
19932
20052
 
19933
20053
  /**
@@ -21246,7 +21366,13 @@ class Message {
21246
21366
  * @returns {ReadableStream<String>} ASCII armor.
21247
21367
  */
21248
21368
  armor(config$1 = config) {
21249
- return armor(enums.armor.message, this.write(), null, null, null, config$1);
21369
+ const trailingPacket = this.packets[this.packets.length - 1];
21370
+ // An ASCII-armored Encrypted Message packet sequence that ends in an v2 SEIPD packet MUST NOT contain a CRC24 footer.
21371
+ // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer.
21372
+ const emitChecksum = trailingPacket.constructor.tag === SymEncryptedIntegrityProtectedDataPacket.tag ?
21373
+ trailingPacket.version !== 2 :
21374
+ this.packets.some(packet => packet.constructor.tag === SignaturePacket.tag && packet.version !== 6);
21375
+ return armor(enums.armor.message, this.write(), null, null, null, emitChecksum, config$1);
21250
21376
  }
21251
21377
  }
21252
21378
 
@@ -21581,9 +21707,9 @@ class CleartextMessage {
21581
21707
  * @returns {String | ReadableStream<String>} ASCII armor.
21582
21708
  */
21583
21709
  armor(config$1 = config) {
21584
- // emit header if one of the signatures has a version not 6
21585
- const emitHeader = this.signature.packets.some(packet => packet.version !== 6);
21586
- const hash = emitHeader ?
21710
+ // emit header and checksum if one of the signatures has a version not 6
21711
+ const emitHeaderAndChecksum = this.signature.packets.some(packet => packet.version !== 6);
21712
+ const hash = emitHeaderAndChecksum ?
21587
21713
  Array.from(new Set(this.signature.packets.map(
21588
21714
  packet => enums.read(enums.hash, packet.hashAlgorithm).toUpperCase()
21589
21715
  ))).join() :
@@ -21594,7 +21720,9 @@ class CleartextMessage {
21594
21720
  text: this.text,
21595
21721
  data: this.signature.packets.write()
21596
21722
  };
21597
- return armor(enums.armor.signed, body, undefined, undefined, undefined, config$1);
21723
+
21724
+ // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer.
21725
+ return armor(enums.armor.signed, body, undefined, undefined, undefined, emitHeaderAndChecksum, config$1);
21598
21726
  }
21599
21727
  }
21600
21728