@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
  'use strict';
3
3
 
4
4
  const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
@@ -1490,6 +1490,14 @@ var config = {
1490
1490
  * @property {Boolean} aeadProtect
1491
1491
  */
1492
1492
  aeadProtect: false,
1493
+ /**
1494
+ * Whether to disable encrypton using SEIPDv2 even if the encryption keys include the SEIPDv2 feature flag.
1495
+ * If true, SEIPDv1 (i.e. no AEAD) packets are always used instead.
1496
+ * SEIPDv2 is a more secure and faster choice, but it is not necessarily compatible with other libs and our mobile apps.
1497
+ * @memberof module:config
1498
+ * @property {Boolean} ignoreSEIPDv2FeatureFlag
1499
+ */
1500
+ ignoreSEIPDv2FeatureFlag: false,
1493
1501
  /**
1494
1502
  * When reading OpenPGP v4 private keys (e.g. those generated in OpenPGP.js when not setting `config.v5Keys = true`)
1495
1503
  * which were encrypted by OpenPGP.js v5 (or older) using `config.aeadProtect = true`,
@@ -1602,11 +1610,6 @@ var config = {
1602
1610
  * @property {Boolean} passwordCollisionCheck
1603
1611
  */
1604
1612
  passwordCollisionCheck: false,
1605
- /**
1606
- * @memberof module:config
1607
- * @property {Boolean} revocationsExpire If true, expired revocation signatures are ignored
1608
- */
1609
- revocationsExpire: false,
1610
1613
  /**
1611
1614
  * Allow decryption using RSA keys without `encrypt` flag.
1612
1615
  * This setting is potentially insecure, but it is needed to get around an old openpgpjs bug
@@ -1682,7 +1685,7 @@ var config = {
1682
1685
  * @memberof module:config
1683
1686
  * @property {String} versionString A version string to be included in armored messages
1684
1687
  */
1685
- versionString: 'OpenPGP.js 6.0.0-alpha.1.patch.1',
1688
+ versionString: 'OpenPGP.js 6.0.0-beta.0.patch.0',
1686
1689
  /**
1687
1690
  * @memberof module:config
1688
1691
  * @property {String} commentString A comment string to be included in armored messages
@@ -1702,6 +1705,14 @@ var config = {
1702
1705
  * @property {Array} knownNotations
1703
1706
  */
1704
1707
  knownNotations: [],
1708
+ /**
1709
+ * If true, a salt notation is used to randomize signatures generated by v4 and v5 keys (v6 signatures are always non-deterministic, by design).
1710
+ * This protects EdDSA signatures from potentially leaking the secret key in case of faults (i.e. bitflips) which, in principle, could occur
1711
+ * 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
1712
+ * weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks.
1713
+ * 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.
1714
+ */
1715
+ nonDeterministicSignaturesViaNotation: true,
1705
1716
  /**
1706
1717
  * Whether to use the the noble-curves library for curves (other than Curve25519) that are not supported by the available native crypto API.
1707
1718
  * When false, certain standard curves will not be supported (depending on the platform).
@@ -1732,14 +1743,7 @@ var config = {
1732
1743
  * @memberof module:config
1733
1744
  * @property {Set<String>} rejectCurves {@link module:enums.curve}
1734
1745
  */
1735
- rejectCurves: new Set([enums.curve.secp256k1]),
1736
- /**
1737
- * Whether to validate generated EdDSA signatures before returning them, to ensure they are not faulty signatures.
1738
- * This check will make signing 2-3 times slower.
1739
- * Faulty signatures may be generated (in principle) if random bitflips occur at specific points in the signature
1740
- * computation, and could be used to recover the signer's secret key given a second signature over the same data.
1741
- */
1742
- checkEdDSAFaultySignatures: true
1746
+ rejectCurves: new Set([enums.curve.secp256k1])
1743
1747
  };
1744
1748
 
1745
1749
  /**
@@ -2224,16 +2228,19 @@ const util = {
2224
2228
  },
2225
2229
 
2226
2230
  /**
2227
- * Test email format based on W3C HTML5 specification.
2228
- * This check is not exaustive, and does not match RFC 5322 exactly
2229
- * (see https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)),
2230
- * but is commonly used for email address validation.
2231
+ * Test email format to ensure basic compliance:
2232
+ * - must include a single @
2233
+ * - no control or space unicode chars allowed
2234
+ * - no backslash and square brackets (as the latter can mess with the userID parsing)
2235
+ * - cannot end with a punctuation char
2236
+ * These checks are not meant to be exhaustive; applications are strongly encouraged to implement stricter validation,
2237
+ * e.g. based on the W3C HTML spec (https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)).
2231
2238
  */
2232
2239
  isEmailAddress: function(data) {
2233
2240
  if (!util.isString(data)) {
2234
2241
  return false;
2235
2242
  }
2236
- 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])?)*$/;
2243
+ const re = /^[^\p{C}\p{Z}@<>\\]+@[^\p{C}\p{Z}@<>\\]+[^\p{C}\p{Z}\p{P}]$/u;
2237
2244
  return re.test(data);
2238
2245
  },
2239
2246
 
@@ -2639,6 +2646,78 @@ function addheader(customComment, config) {
2639
2646
  return result;
2640
2647
  }
2641
2648
 
2649
+ /**
2650
+ * Calculates a checksum over the given data and returns it base64 encoded
2651
+ * @param {String | ReadableStream<String>} data - Data to create a CRC-24 checksum for
2652
+ * @returns {String | ReadableStream<String>} Base64 encoded checksum.
2653
+ * @private
2654
+ */
2655
+ function getCheckSum(data) {
2656
+ const crc = createcrc24(data);
2657
+ return encode$1(crc);
2658
+ }
2659
+
2660
+ // https://create.stephan-brumme.com/crc32/#slicing-by-8-overview
2661
+
2662
+ const crc_table = [
2663
+ new Array(0xFF),
2664
+ new Array(0xFF),
2665
+ new Array(0xFF),
2666
+ new Array(0xFF)
2667
+ ];
2668
+
2669
+ for (let i = 0; i <= 0xFF; i++) {
2670
+ let crc = i << 16;
2671
+ for (let j = 0; j < 8; j++) {
2672
+ crc = (crc << 1) ^ ((crc & 0x800000) !== 0 ? 0x864CFB : 0);
2673
+ }
2674
+ crc_table[0][i] =
2675
+ ((crc & 0xFF0000) >> 16) |
2676
+ (crc & 0x00FF00) |
2677
+ ((crc & 0x0000FF) << 16);
2678
+ }
2679
+ for (let i = 0; i <= 0xFF; i++) {
2680
+ crc_table[1][i] = (crc_table[0][i] >> 8) ^ crc_table[0][crc_table[0][i] & 0xFF];
2681
+ }
2682
+ for (let i = 0; i <= 0xFF; i++) {
2683
+ crc_table[2][i] = (crc_table[1][i] >> 8) ^ crc_table[0][crc_table[1][i] & 0xFF];
2684
+ }
2685
+ for (let i = 0; i <= 0xFF; i++) {
2686
+ crc_table[3][i] = (crc_table[2][i] >> 8) ^ crc_table[0][crc_table[2][i] & 0xFF];
2687
+ }
2688
+
2689
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness
2690
+ const isLittleEndian$1 = (function() {
2691
+ const buffer = new ArrayBuffer(2);
2692
+ new DataView(buffer).setInt16(0, 0xFF, true /* littleEndian */);
2693
+ // Int16Array uses the platform's endianness.
2694
+ return new Int16Array(buffer)[0] === 0xFF;
2695
+ }());
2696
+
2697
+ /**
2698
+ * Internal function to calculate a CRC-24 checksum over a given string (data)
2699
+ * @param {String | ReadableStream<String>} input - Data to create a CRC-24 checksum for
2700
+ * @returns {Uint8Array | ReadableStream<Uint8Array>} The CRC-24 checksum.
2701
+ * @private
2702
+ */
2703
+ function createcrc24(input) {
2704
+ let crc = 0xCE04B7;
2705
+ return transform(input, value => {
2706
+ const len32 = isLittleEndian$1 ? Math.floor(value.length / 4) : 0;
2707
+ const arr32 = new Uint32Array(value.buffer, value.byteOffset, len32);
2708
+ for (let i = 0; i < len32; i++) {
2709
+ crc ^= arr32[i];
2710
+ crc =
2711
+ crc_table[0][(crc >> 24) & 0xFF] ^
2712
+ crc_table[1][(crc >> 16) & 0xFF] ^
2713
+ crc_table[2][(crc >> 8) & 0xFF] ^
2714
+ crc_table[3][(crc >> 0) & 0xFF];
2715
+ }
2716
+ for (let i = len32 * 4; i < value.length; i++) {
2717
+ crc = (crc >> 8) ^ crc_table[0][(crc & 0xFF) ^ value[i]];
2718
+ }
2719
+ }, () => new Uint8Array([crc, crc >> 8, crc >> 16]));
2720
+ }
2642
2721
 
2643
2722
  /**
2644
2723
  * Verify armored headers. crypto-refresh-06, section 6.2:
@@ -2794,10 +2873,13 @@ function unarmor(input) {
2794
2873
  * @param {Integer} [partIndex]
2795
2874
  * @param {Integer} [partTotal]
2796
2875
  * @param {String} [customComment] - Additional comment to add to the armored string
2876
+ * @param {Boolean} [emitChecksum] - Whether to compute and include the CRC checksum
2877
+ * (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)
2878
+ * @param {Object} [config] - Full configuration, defaults to openpgp.config
2797
2879
  * @returns {String | ReadableStream<String>} Armored text.
2798
2880
  * @static
2799
2881
  */
2800
- function armor(messageType, body, partIndex, partTotal, customComment, config$1 = config) {
2882
+ function armor(messageType, body, partIndex, partTotal, customComment, emitChecksum = false, config$1 = config) {
2801
2883
  let text;
2802
2884
  let hash;
2803
2885
  if (messageType === enums.armor.signed) {
@@ -2805,18 +2887,24 @@ function armor(messageType, body, partIndex, partTotal, customComment, config$1
2805
2887
  hash = body.hash;
2806
2888
  body = body.data;
2807
2889
  }
2890
+ // unless explicitly forbidden by the spec, we need to include the checksum to work around a GnuPG bug
2891
+ // where data fails to be decoded if the base64 ends with no padding chars (=) (see https://dev.gnupg.org/T7071)
2892
+ const maybeBodyClone = emitChecksum && passiveClone(body);
2893
+
2808
2894
  const result = [];
2809
2895
  switch (messageType) {
2810
2896
  case enums.armor.multipartSection:
2811
2897
  result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n');
2812
2898
  result.push(addheader(customComment, config$1));
2813
2899
  result.push(encode$1(body));
2900
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2814
2901
  result.push('-----END PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n');
2815
2902
  break;
2816
2903
  case enums.armor.multipartLast:
2817
2904
  result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '-----\n');
2818
2905
  result.push(addheader(customComment, config$1));
2819
2906
  result.push(encode$1(body));
2907
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2820
2908
  result.push('-----END PGP MESSAGE, PART ' + partIndex + '-----\n');
2821
2909
  break;
2822
2910
  case enums.armor.signed:
@@ -2826,30 +2914,35 @@ function armor(messageType, body, partIndex, partTotal, customComment, config$1
2826
2914
  result.push('\n-----BEGIN PGP SIGNATURE-----\n');
2827
2915
  result.push(addheader(customComment, config$1));
2828
2916
  result.push(encode$1(body));
2917
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2829
2918
  result.push('-----END PGP SIGNATURE-----\n');
2830
2919
  break;
2831
2920
  case enums.armor.message:
2832
2921
  result.push('-----BEGIN PGP MESSAGE-----\n');
2833
2922
  result.push(addheader(customComment, config$1));
2834
2923
  result.push(encode$1(body));
2924
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2835
2925
  result.push('-----END PGP MESSAGE-----\n');
2836
2926
  break;
2837
2927
  case enums.armor.publicKey:
2838
2928
  result.push('-----BEGIN PGP PUBLIC KEY BLOCK-----\n');
2839
2929
  result.push(addheader(customComment, config$1));
2840
2930
  result.push(encode$1(body));
2931
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2841
2932
  result.push('-----END PGP PUBLIC KEY BLOCK-----\n');
2842
2933
  break;
2843
2934
  case enums.armor.privateKey:
2844
2935
  result.push('-----BEGIN PGP PRIVATE KEY BLOCK-----\n');
2845
2936
  result.push(addheader(customComment, config$1));
2846
2937
  result.push(encode$1(body));
2938
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2847
2939
  result.push('-----END PGP PRIVATE KEY BLOCK-----\n');
2848
2940
  break;
2849
2941
  case enums.armor.signature:
2850
2942
  result.push('-----BEGIN PGP SIGNATURE-----\n');
2851
2943
  result.push(addheader(customComment, config$1));
2852
2944
  result.push(encode$1(body));
2945
+ maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone));
2853
2946
  result.push('-----END PGP SIGNATURE-----\n');
2854
2947
  break;
2855
2948
  }
@@ -9377,20 +9470,6 @@ async function sign$5(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9377
9470
  }
9378
9471
  const secretKey = util.concatUint8Array([privateKey, publicKey.subarray(1)]);
9379
9472
  const signature = nacl.sign.detached(hashed, secretKey);
9380
- if (config.checkEdDSAFaultySignatures && !nacl.sign.detached.verify(hashed, signature, publicKey.subarray(1))) {
9381
- /**
9382
- * Detect faulty signatures caused by random bitflips during `crypto_sign` which could lead to private key extraction
9383
- * if two signatures over the same message are obtained.
9384
- * See https://github.com/jedisct1/libsodium/issues/170.
9385
- * If the input data is not deterministic, e.g. thanks to the random salt in v6 OpenPGP signatures (not yet implemented),
9386
- * then the generated signature is always safe, and the verification step is skipped.
9387
- * Otherwise, we need to verify the generated to ensure that no bitflip occured:
9388
- * - in M between the computation of `r` and `h`.
9389
- * - in the public key before computing `h`
9390
- * 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.
9391
- */
9392
- throw new Error('Transient signing failure');
9393
- }
9394
9473
  // EdDSA signature params are returned in little-endian format
9395
9474
  return {
9396
9475
  r: signature.subarray(0, 32),
@@ -9511,20 +9590,6 @@ async function sign$4(algo, hashAlgo, message, publicKey, privateKey, hashed) {
9511
9590
  case enums.publicKey.ed25519: {
9512
9591
  const secretKey = util.concatUint8Array([privateKey, publicKey]);
9513
9592
  const signature = nacl.sign.detached(hashed, secretKey);
9514
- if (config.checkEdDSAFaultySignatures && !nacl.sign.detached.verify(hashed, signature, publicKey)) {
9515
- /**
9516
- * Detect faulty signatures caused by random bitflips during `crypto_sign` which could lead to private key extraction
9517
- * if two signatures over the same message are obtained.
9518
- * See https://github.com/jedisct1/libsodium/issues/170.
9519
- * If the input data is not deterministic, e.g. thanks to the random salt in v6 OpenPGP signatures (not yet implemented),
9520
- * then the generated signature is always safe, and the verification step is skipped.
9521
- * Otherwise, we need to verify the generated to ensure that no bitflip occured:
9522
- * - in M between the computation of `r` and `h`.
9523
- * - in the public key before computing `h`
9524
- * 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.
9525
- */
9526
- throw new Error('Transient signing failure');
9527
- }
9528
9593
  return { RS: signature };
9529
9594
  }
9530
9595
  case enums.publicKey.ed448: {
@@ -11257,7 +11322,7 @@ class ECDHXSymmetricKey {
11257
11322
  * Encrypts data using specified algorithm and public key parameters.
11258
11323
  * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
11259
11324
  * @param {module:enums.publicKey} keyAlgo - Public key algorithm
11260
- * @param {module:enums.symmetric} symmetricAlgo - Cipher algorithm
11325
+ * @param {module:enums.symmetric|null} symmetricAlgo - Cipher algorithm (v3 only)
11261
11326
  * @param {Object} publicParams - Algorithm-specific public key parameters
11262
11327
  * @param {Object} privateParams - Algorithm-specific private key parameters
11263
11328
  * @param {Uint8Array} data - Data to be encrypted
@@ -11285,7 +11350,7 @@ async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, privatePar
11285
11350
  }
11286
11351
  case enums.publicKey.x25519:
11287
11352
  case enums.publicKey.x448: {
11288
- if (!util.isAES(symmetricAlgo)) {
11353
+ if (symmetricAlgo && !util.isAES(symmetricAlgo)) {
11289
11354
  // see https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/276
11290
11355
  throw new Error('X25519 and X448 keys can only encrypt AES session keys');
11291
11356
  }
@@ -11917,9 +11982,26 @@ class Argon2OutOfMemoryError extends Error {
11917
11982
  let loadArgonWasmModule;
11918
11983
  let argon2Promise;
11919
11984
  // reload wasm module above this treshold, to deallocated used memory
11920
- const ARGON2_WASM_MEMORY_THRESHOLD_RELOAD = 2 << 19;
11985
+ // (cannot be declared as a simple `static` field as its not supported by Safari 14)
11986
+ let ARGON2_WASM_MEMORY_THRESHOLD_RELOAD = 2 << 19;
11921
11987
 
11922
11988
  class Argon2S2K {
11989
+ static get ARGON2_WASM_MEMORY_THRESHOLD_RELOAD() {
11990
+ return ARGON2_WASM_MEMORY_THRESHOLD_RELOAD;
11991
+ }
11992
+
11993
+ static set ARGON2_WASM_MEMORY_THRESHOLD_RELOAD(memoryThreshold) {
11994
+ ARGON2_WASM_MEMORY_THRESHOLD_RELOAD = memoryThreshold;
11995
+ }
11996
+
11997
+ static reloadWasmModule() {
11998
+ if (!loadArgonWasmModule) return;
11999
+
12000
+ // it will be awaited if needed at the next `produceKey` invocation
12001
+ argon2Promise = loadArgonWasmModule();
12002
+ argon2Promise.catch(() => {});
12003
+ }
12004
+
11923
12005
  /**
11924
12006
  * @param {Object} [config] - Full configuration, defaults to openpgp.config
11925
12007
  */
@@ -12007,10 +12089,8 @@ class Argon2S2K {
12007
12089
  });
12008
12090
 
12009
12091
  // a lot of memory was used, reload to deallocate
12010
- if (decodedM > ARGON2_WASM_MEMORY_THRESHOLD_RELOAD) {
12011
- // it will be awaited if needed at the next `produceKey` invocation
12012
- argon2Promise = loadArgonWasmModule();
12013
- argon2Promise.catch(() => {});
12092
+ if (decodedM > Argon2S2K.ARGON2_WASM_MEMORY_THRESHOLD_RELOAD) {
12093
+ Argon2S2K.reloadWasmModule();
12014
12094
  }
12015
12095
  return hash;
12016
12096
  } catch (e) {
@@ -14259,6 +14339,14 @@ class KeyID {
14259
14339
  // Symbol to store cryptographic validity of the signature, to avoid recomputing multiple times on verification.
14260
14340
  const verified = Symbol('verified');
14261
14341
 
14342
+ // A salt notation is used to randomize signatures.
14343
+ // This is to protect EdDSA signatures in particular, which are known to be vulnerable to fault attacks
14344
+ // leading to secret key extraction if two signatures over the same data can be collected (see https://github.com/jedisct1/libsodium/issues/170).
14345
+ // 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.
14346
+ // some chosen-prefix attacks.
14347
+ // v6 signatures do not need to rely on this notation, as they already include a separate, built-in salt.
14348
+ const SALT_NOTATION_NAME = 'salt@notations.openpgpjs.org';
14349
+
14262
14350
  // GPG puts the Issuer and Signature subpackets in the unhashed area.
14263
14351
  // Tampering with those invalidates the signature, so we still trust them and parse them.
14264
14352
  // All other unhashed subpackets are ignored.
@@ -14428,7 +14516,7 @@ class SignaturePacket {
14428
14516
  * @throws {Error} if signing failed
14429
14517
  * @async
14430
14518
  */
14431
- async sign(key, data, date = new Date(), detached = false) {
14519
+ async sign(key, data, date = new Date(), detached = false, config) {
14432
14520
  this.version = key.version;
14433
14521
 
14434
14522
  this.created = util.normalizeDate(date);
@@ -14438,6 +14526,31 @@ class SignaturePacket {
14438
14526
 
14439
14527
  const arr = [new Uint8Array([this.version, this.signatureType, this.publicKeyAlgorithm, this.hashAlgorithm])];
14440
14528
 
14529
+ // add randomness to the signature
14530
+ if (this.version === 6) {
14531
+ const saltLength = saltLengthForHash(this.hashAlgorithm);
14532
+ if (this.salt === null) {
14533
+ this.salt = mod$1.random.getRandomBytes(saltLength);
14534
+ } else if (saltLength !== this.salt.length) {
14535
+ throw new Error('Provided salt does not have the required length');
14536
+ }
14537
+ } else if (config.nonDeterministicSignaturesViaNotation) {
14538
+ const saltNotations = this.rawNotations.filter(({ name }) => (name === SALT_NOTATION_NAME));
14539
+ // since re-signing the same object is not supported, it's not expected to have multiple salt notations,
14540
+ // but we guard against it as a sanity check
14541
+ if (saltNotations.length === 0) {
14542
+ const saltValue = mod$1.random.getRandomBytes(saltLengthForHash(this.hashAlgorithm));
14543
+ this.rawNotations.push({
14544
+ name: SALT_NOTATION_NAME,
14545
+ value: saltValue,
14546
+ humanReadable: false,
14547
+ critical: false
14548
+ });
14549
+ } else {
14550
+ throw new Error('Unexpected existing salt notation');
14551
+ }
14552
+ }
14553
+
14441
14554
  // Add hashed subpackets
14442
14555
  arr.push(this.writeHashedSubPackets());
14443
14556
 
@@ -14448,14 +14561,6 @@ class SignaturePacket {
14448
14561
 
14449
14562
  this.signatureData = util.concat(arr);
14450
14563
 
14451
- if (this.version === 6) {
14452
- const saltLength = saltLengthForHash(this.hashAlgorithm);
14453
- if (this.salt === null) {
14454
- this.salt = mod$1.random.getRandomBytes(saltLength);
14455
- } else if (saltLength !== this.salt.length) {
14456
- throw new Error('Provided salt does not have the required length');
14457
- }
14458
- }
14459
14564
  const toHash = this.toHash(this.signatureType, data, detached);
14460
14565
  const hash = await this.hash(this.signatureType, data, toHash, detached);
14461
14566
 
@@ -16257,9 +16362,12 @@ class PublicKeyEncryptedSessionKeyPacket {
16257
16362
  }
16258
16363
  this.publicKeyAlgorithm = bytes[offset++];
16259
16364
  this.encrypted = mod$1.parseEncSessionKeyParams(this.publicKeyAlgorithm, bytes.subarray(offset));
16260
- if (this.version === 3 && (
16261
- this.publicKeyAlgorithm === enums.publicKey.x25519 || this.publicKeyAlgorithm === enums.publicKey.x448)) {
16262
- this.sessionKeyAlgorithm = enums.write(enums.symmetric, this.encrypted.C.algorithm);
16365
+ if (this.publicKeyAlgorithm === enums.publicKey.x25519 || this.publicKeyAlgorithm === enums.publicKey.x448) {
16366
+ if (this.version === 3) {
16367
+ this.sessionKeyAlgorithm = enums.write(enums.symmetric, this.encrypted.C.algorithm);
16368
+ } else if (this.encrypted.C.algorithm !== null) {
16369
+ throw new Error('Unexpected cleartext symmetric algorithm');
16370
+ }
16263
16371
  }
16264
16372
  }
16265
16373
 
@@ -16303,10 +16411,13 @@ class PublicKeyEncryptedSessionKeyPacket {
16303
16411
  */
16304
16412
  async encrypt(key) {
16305
16413
  const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
16306
- const encoded = encodeSessionKey(this.version, algo, this.sessionKeyAlgorithm, this.sessionKey);
16414
+ // No symmetric encryption algorithm identifier is passed to the public-key algorithm for a
16415
+ // v6 PKESK packet, as it is included in the v2 SEIPD packet.
16416
+ const sessionKeyAlgorithm = this.version === 3 ? this.sessionKeyAlgorithm : null;
16417
+ const encoded = encodeSessionKey(this.version, algo, sessionKeyAlgorithm, this.sessionKey);
16307
16418
  const privateParams = algo === enums.publicKey.aead ? key.privateParams : null;
16308
16419
  this.encrypted = await mod$1.publicKeyEncrypt(
16309
- algo, this.sessionKeyAlgorithm, key.publicParams, privateParams, encoded, key.getFingerprintBytes());
16420
+ algo, sessionKeyAlgorithm, key.publicParams, privateParams, encoded, key.getFingerprintBytes());
16310
16421
  }
16311
16422
 
16312
16423
  /**
@@ -16405,6 +16516,7 @@ function decodeSessionKey(version, keyAlgo, decryptedData, randomSessionKey) {
16405
16516
  case enums.publicKey.x25519:
16406
16517
  case enums.publicKey.x448:
16407
16518
  return {
16519
+ sessionKeyAlgorithm: null,
16408
16520
  sessionKey: decryptedData
16409
16521
  };
16410
16522
  default:
@@ -18092,7 +18204,9 @@ class Signature {
18092
18204
  * @returns {ReadableStream<String>} ASCII armor.
18093
18205
  */
18094
18206
  armor(config$1 = config) {
18095
- return armor(enums.armor.signature, this.write(), undefined, undefined, undefined, config$1);
18207
+ // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer.
18208
+ const emitChecksum = this.packets.some(packet => packet.constructor.tag === SignaturePacket.tag && packet.version !== 6);
18209
+ return armor(enums.armor.signature, this.write(), undefined, undefined, undefined, emitChecksum, config$1);
18096
18210
  }
18097
18211
 
18098
18212
  /**
@@ -18305,7 +18419,7 @@ async function getPreferredCompressionAlgo(keys = [], date = new Date(), userIDs
18305
18419
  async function getPreferredCipherSuite(keys = [], date = new Date(), userIDs = [], config$1 = config) {
18306
18420
  const selfSigs = await Promise.all(keys.map((key, i) => key.getPrimarySelfSignature(date, userIDs[i], config$1)));
18307
18421
  const withAEAD = keys.length ?
18308
- selfSigs.every(selfSig => selfSig.features[0] & enums.features.seipdv2) :
18422
+ !config$1.ignoreSEIPDv2FeatureFlag && selfSigs.every(selfSig => selfSig.features && (selfSig.features[0] & enums.features.seipdv2)) :
18309
18423
  config$1.aeadProtect;
18310
18424
 
18311
18425
  if (withAEAD) {
@@ -18352,8 +18466,8 @@ async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, s
18352
18466
  Object.assign(signaturePacket, signatureProperties);
18353
18467
  signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
18354
18468
  signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userID, config);
18355
- signaturePacket.rawNotations = notations;
18356
- await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached);
18469
+ signaturePacket.rawNotations = [...notations];
18470
+ await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached, config);
18357
18471
  return signaturePacket;
18358
18472
  }
18359
18473
 
@@ -18416,7 +18530,7 @@ async function isDataRevoked(primaryKey, signatureType, dataToVerify, revocation
18416
18530
  !signature || revocationSignature.issuerKeyID.equals(signature.issuerKeyID)
18417
18531
  ) {
18418
18532
  await revocationSignature.verify(
18419
- key, signatureType, dataToVerify, config.revocationsExpire ? date : null, false, config
18533
+ key, signatureType, dataToVerify, date, false, config
18420
18534
  );
18421
18535
 
18422
18536
  // TODO get an identifier of the revoked object instead
@@ -19686,7 +19800,9 @@ class Key {
19686
19800
  const revocationSignature = await getLatestValidSignature(this.revocationSignatures, this.keyPacket, enums.signature.keyRevocation, dataToVerify, date, config$1);
19687
19801
  const packetlist = new PacketList();
19688
19802
  packetlist.push(revocationSignature);
19689
- return armor(enums.armor.publicKey, packetlist.write(), null, null, 'This is a revocation certificate');
19803
+ // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer.
19804
+ const emitChecksum = this.keyPacket.version !== 6;
19805
+ return armor(enums.armor.publicKey, packetlist.write(), null, null, 'This is a revocation certificate', emitChecksum, config$1);
19690
19806
  }
19691
19807
 
19692
19808
  /**
@@ -19876,7 +19992,9 @@ class PublicKey extends Key {
19876
19992
  * @returns {ReadableStream<String>} ASCII armor.
19877
19993
  */
19878
19994
  armor(config$1 = config) {
19879
- return armor(enums.armor.publicKey, this.toPacketList().write(), undefined, undefined, undefined, config$1);
19995
+ // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer.
19996
+ const emitChecksum = this.keyPacket.version !== 6;
19997
+ return armor(enums.armor.publicKey, this.toPacketList().write(), undefined, undefined, undefined, emitChecksum, config$1);
19880
19998
  }
19881
19999
  }
19882
20000
 
@@ -19949,7 +20067,9 @@ class PrivateKey extends PublicKey {
19949
20067
  * @returns {ReadableStream<String>} ASCII armor.
19950
20068
  */
19951
20069
  armor(config$1 = config) {
19952
- return armor(enums.armor.privateKey, this.toPacketList().write(), undefined, undefined, undefined, config$1);
20070
+ // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer.
20071
+ const emitChecksum = this.keyPacket.version !== 6;
20072
+ return armor(enums.armor.privateKey, this.toPacketList().write(), undefined, undefined, undefined, emitChecksum, config$1);
19953
20073
  }
19954
20074
 
19955
20075
  /**
@@ -21268,7 +21388,13 @@ class Message {
21268
21388
  * @returns {ReadableStream<String>} ASCII armor.
21269
21389
  */
21270
21390
  armor(config$1 = config) {
21271
- return armor(enums.armor.message, this.write(), null, null, null, config$1);
21391
+ const trailingPacket = this.packets[this.packets.length - 1];
21392
+ // An ASCII-armored Encrypted Message packet sequence that ends in an v2 SEIPD packet MUST NOT contain a CRC24 footer.
21393
+ // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer.
21394
+ const emitChecksum = trailingPacket.constructor.tag === SymEncryptedIntegrityProtectedDataPacket.tag ?
21395
+ trailingPacket.version !== 2 :
21396
+ this.packets.some(packet => packet.constructor.tag === SignaturePacket.tag && packet.version !== 6);
21397
+ return armor(enums.armor.message, this.write(), null, null, null, emitChecksum, config$1);
21272
21398
  }
21273
21399
  }
21274
21400
 
@@ -21603,9 +21729,9 @@ class CleartextMessage {
21603
21729
  * @returns {String | ReadableStream<String>} ASCII armor.
21604
21730
  */
21605
21731
  armor(config$1 = config) {
21606
- // emit header if one of the signatures has a version not 6
21607
- const emitHeader = this.signature.packets.some(packet => packet.version !== 6);
21608
- const hash = emitHeader ?
21732
+ // emit header and checksum if one of the signatures has a version not 6
21733
+ const emitHeaderAndChecksum = this.signature.packets.some(packet => packet.version !== 6);
21734
+ const hash = emitHeaderAndChecksum ?
21609
21735
  Array.from(new Set(this.signature.packets.map(
21610
21736
  packet => enums.read(enums.hash, packet.hashAlgorithm).toUpperCase()
21611
21737
  ))).join() :
@@ -21616,7 +21742,9 @@ class CleartextMessage {
21616
21742
  text: this.text,
21617
21743
  data: this.signature.packets.write()
21618
21744
  };
21619
- return armor(enums.armor.signed, body, undefined, undefined, undefined, config$1);
21745
+
21746
+ // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer.
21747
+ return armor(enums.armor.signed, body, undefined, undefined, undefined, emitHeaderAndChecksum, config$1);
21620
21748
  }
21621
21749
  }
21622
21750