@protontech/openpgp 6.1.1-patch.2 → 6.1.1-patch.4

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.
@@ -1,4 +1,4 @@
1
- /*! OpenPGP.js v6.1.1-patch.2 - 2025-05-14 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
1
+ /*! OpenPGP.js v6.1.1-patch.4 - 2025-07-14 - 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 : {};
@@ -1089,11 +1089,10 @@ var enums = {
1089
1089
  ed25519: 27,
1090
1090
  /** Ed448 (Sign only) */
1091
1091
  ed448: 28,
1092
- /** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
1093
- pqc_mlkem_x25519: 105,
1094
1092
  /** Post-quantum ML-DSA-64 + Ed25519 (Sign only) */
1095
- pqc_mldsa_ed25519: 107,
1096
-
1093
+ pqc_mldsa_ed25519: 30,
1094
+ /** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
1095
+ pqc_mlkem_x25519: 35,
1097
1096
  /** Persistent symmetric keys: encryption algorithm */
1098
1097
  aead: 100,
1099
1098
  /** Persistent symmetric keys: authentication algorithm */
@@ -1744,7 +1743,7 @@ var config = {
1744
1743
  * @memberof module:config
1745
1744
  * @property {String} versionString A version string to be included in armored messages
1746
1745
  */
1747
- versionString: 'OpenPGP.js 6.1.1-patch.2',
1746
+ versionString: 'OpenPGP.js 6.1.1-patch.4',
1748
1747
  /**
1749
1748
  * @memberof module:config
1750
1749
  * @property {String} commentString A comment string to be included in armored messages
@@ -6947,7 +6946,15 @@ async function generate$a(algo) {
6947
6946
  case enums.publicKey.ed25519:
6948
6947
  try {
6949
6948
  const webCrypto = util.getWebCrypto();
6950
- const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify']);
6949
+ const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify'])
6950
+ .catch(err => {
6951
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
6952
+ const newErr = new Error('Unexpected key generation issue');
6953
+ newErr.name = 'NotSupportedError';
6954
+ throw newErr;
6955
+ }
6956
+ throw err;
6957
+ });
6951
6958
 
6952
6959
  const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
6953
6960
  const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
@@ -6957,7 +6964,7 @@ async function generate$a(algo) {
6957
6964
  seed: b64ToUint8Array(privateKey.d, true)
6958
6965
  };
6959
6966
  } catch (err) {
6960
- if (err.name !== 'NotSupportedError' && err.name !== 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
6967
+ if (err.name !== 'NotSupportedError') {
6961
6968
  throw err;
6962
6969
  }
6963
6970
  const seed = getRandomBytes(getPayloadSize$1(algo));
@@ -6990,17 +6997,11 @@ async function generate$a(algo) {
6990
6997
  * @async
6991
6998
  */
6992
6999
  async function sign$9(algo, hashAlgo, message, publicKey, privateKey, hashed) {
6993
- if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
6994
- // Enforce digest sizes:
6995
- // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
6996
- // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
6997
- throw new Error('Hash algorithm too weak for EdDSA.');
6998
- }
6999
7000
  switch (algo) {
7000
7001
  case enums.publicKey.ed25519:
7001
7002
  try {
7002
7003
  const webCrypto = util.getWebCrypto();
7003
- const jwk = privateKeyToJWK(algo, publicKey, privateKey);
7004
+ const jwk = privateKeyToJWK$1(algo, publicKey, privateKey);
7004
7005
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['sign']);
7005
7006
 
7006
7007
  const signature = new Uint8Array(
@@ -7040,17 +7041,11 @@ async function sign$9(algo, hashAlgo, message, publicKey, privateKey, hashed) {
7040
7041
  * @async
7041
7042
  */
7042
7043
  async function verify$9(algo, hashAlgo, { RS }, m, publicKey, hashed) {
7043
- if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
7044
- // Enforce digest sizes:
7045
- // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
7046
- // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
7047
- throw new Error('Hash algorithm too weak for EdDSA.');
7048
- }
7049
7044
  switch (algo) {
7050
7045
  case enums.publicKey.ed25519:
7051
7046
  try {
7052
7047
  const webCrypto = util.getWebCrypto();
7053
- const jwk = publicKeyToJWK(algo, publicKey);
7048
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7054
7049
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['verify']);
7055
7050
  const verified = await webCrypto.verify('Ed25519', key, RS, hashed);
7056
7051
  return verified;
@@ -7125,7 +7120,7 @@ function getPreferredHashAlgo$2(algo) {
7125
7120
  }
7126
7121
  }
7127
7122
 
7128
- const publicKeyToJWK = (algo, publicKey) => {
7123
+ const publicKeyToJWK$1 = (algo, publicKey) => {
7129
7124
  switch (algo) {
7130
7125
  case enums.publicKey.ed25519: {
7131
7126
  const jwk = {
@@ -7141,10 +7136,10 @@ const publicKeyToJWK = (algo, publicKey) => {
7141
7136
  }
7142
7137
  };
7143
7138
 
7144
- const privateKeyToJWK = (algo, publicKey, privateKey) => {
7139
+ const privateKeyToJWK$1 = (algo, publicKey, privateKey) => {
7145
7140
  switch (algo) {
7146
7141
  case enums.publicKey.ed25519: {
7147
- const jwk = publicKeyToJWK(algo, publicKey);
7142
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7148
7143
  jwk.d = uint8ArrayToB64(privateKey);
7149
7144
  return jwk;
7150
7145
  }
@@ -8384,12 +8379,41 @@ const HKDF_INFO = {
8384
8379
  */
8385
8380
  async function generate$9(algo) {
8386
8381
  switch (algo) {
8387
- case enums.publicKey.x25519: {
8388
- // k stays in little-endian, unlike legacy ECDH over curve25519
8389
- const k = getRandomBytes(32);
8390
- const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8391
- return { A, k };
8392
- }
8382
+ case enums.publicKey.x25519:
8383
+ try {
8384
+ const webCrypto = util.getWebCrypto();
8385
+ const webCryptoKey = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
8386
+ .catch(err => {
8387
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
8388
+ const newErr = new Error('Unexpected key generation issue');
8389
+ newErr.name = 'NotSupportedError';
8390
+ throw newErr;
8391
+ }
8392
+ throw err;
8393
+ });
8394
+
8395
+ const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
8396
+ const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
8397
+
8398
+ if (privateKey.x !== publicKey.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
8399
+ const err = new Error('Unexpected mismatching public point');
8400
+ err.name = 'NotSupportedError';
8401
+ throw err;
8402
+ }
8403
+
8404
+ return {
8405
+ A: new Uint8Array(b64ToUint8Array(publicKey.x)),
8406
+ k: b64ToUint8Array(privateKey.d)
8407
+ };
8408
+ } catch (err) {
8409
+ if (err.name !== 'NotSupportedError') {
8410
+ throw err;
8411
+ }
8412
+ // k stays in little-endian, unlike legacy ECDH over curve25519
8413
+ const k = getRandomBytes(32);
8414
+ const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8415
+ return { A, k };
8416
+ }
8393
8417
 
8394
8418
  case enums.publicKey.x448: {
8395
8419
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
@@ -8531,13 +8555,46 @@ function getPayloadSize(algo) {
8531
8555
  */
8532
8556
  async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8533
8557
  switch (algo) {
8534
- case enums.publicKey.x25519: {
8535
- const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8536
- const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8537
- assertNonZeroArray(sharedSecret);
8538
- const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8539
- return { ephemeralPublicKey, sharedSecret };
8540
- }
8558
+ case enums.publicKey.x25519:
8559
+ try {
8560
+ const webCrypto = util.getWebCrypto();
8561
+ const ephemeralKeyPair = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
8562
+ .catch(err => {
8563
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
8564
+ const newErr = new Error('Unexpected key generation issue');
8565
+ newErr.name = 'NotSupportedError';
8566
+ throw newErr;
8567
+ }
8568
+ throw err;
8569
+ });
8570
+ const ephemeralPublicKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.publicKey);
8571
+ const ephemeralPrivateKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.privateKey);
8572
+ if (ephemeralPrivateKeyJwt.x !== ephemeralPublicKeyJwt.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
8573
+ const err = new Error('Unexpected mismatching public point');
8574
+ err.name = 'NotSupportedError';
8575
+ throw err;
8576
+ }
8577
+ const jwk = publicKeyToJWK(algo, recipientA);
8578
+ const recipientPublicKey = await webCrypto.importKey('jwk', jwk, 'X25519', false, []);
8579
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8580
+ { name: 'X25519', public: recipientPublicKey },
8581
+ ephemeralKeyPair.privateKey,
8582
+ getPayloadSize(algo) * 8 // in bits
8583
+ );
8584
+ return {
8585
+ sharedSecret: new Uint8Array(sharedSecretBuffer),
8586
+ ephemeralPublicKey: new Uint8Array(b64ToUint8Array(ephemeralPublicKeyJwt.x))
8587
+ };
8588
+ } catch (err) {
8589
+ if (err.name !== 'NotSupportedError') {
8590
+ throw err;
8591
+ }
8592
+ const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8593
+ const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8594
+ assertNonZeroArray(sharedSecret);
8595
+ const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8596
+ return { ephemeralPublicKey, sharedSecret };
8597
+ }
8541
8598
  case enums.publicKey.x448: {
8542
8599
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8543
8600
  const ephemeralSecretKey = x448.utils.randomPrivateKey();
@@ -8553,11 +8610,27 @@ async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8553
8610
 
8554
8611
  async function recomputeSharedSecret(algo, ephemeralPublicKey, A, k) {
8555
8612
  switch (algo) {
8556
- case enums.publicKey.x25519: {
8557
- const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8558
- assertNonZeroArray(sharedSecret);
8559
- return sharedSecret;
8560
- }
8613
+ case enums.publicKey.x25519:
8614
+ try {
8615
+ const webCrypto = util.getWebCrypto();
8616
+ const privateKeyJWK = privateKeyToJWK(algo, A, k);
8617
+ const ephemeralPublicKeyJWK = publicKeyToJWK(algo, ephemeralPublicKey);
8618
+ const privateKey = await webCrypto.importKey('jwk', privateKeyJWK, 'X25519', false, ['deriveKey', 'deriveBits']);
8619
+ const ephemeralPublicKeyReference = await webCrypto.importKey('jwk', ephemeralPublicKeyJWK, 'X25519', false, []);
8620
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8621
+ { name: 'X25519', public: ephemeralPublicKeyReference },
8622
+ privateKey,
8623
+ getPayloadSize(algo) * 8 // in bits
8624
+ );
8625
+ return new Uint8Array(sharedSecretBuffer);
8626
+ } catch (err) {
8627
+ if (err.name !== 'NotSupportedError') {
8628
+ throw err;
8629
+ }
8630
+ const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8631
+ assertNonZeroArray(sharedSecret);
8632
+ return sharedSecret;
8633
+ }
8561
8634
  case enums.publicKey.x448: {
8562
8635
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8563
8636
  const sharedSecret = x448.getSharedSecret(k, ephemeralPublicKey);
@@ -8585,6 +8658,35 @@ function assertNonZeroArray(sharedSecret) {
8585
8658
  }
8586
8659
  }
8587
8660
 
8661
+
8662
+ function publicKeyToJWK(algo, publicKey) {
8663
+ switch (algo) {
8664
+ case enums.publicKey.x25519: {
8665
+ const jwk = {
8666
+ kty: 'OKP',
8667
+ crv: 'X25519',
8668
+ x: uint8ArrayToB64(publicKey),
8669
+ ext: true
8670
+ };
8671
+ return jwk;
8672
+ }
8673
+ default:
8674
+ throw new Error('Unsupported ECDH algorithm');
8675
+ }
8676
+ }
8677
+
8678
+ function privateKeyToJWK(algo, publicKey, privateKey) {
8679
+ switch (algo) {
8680
+ case enums.publicKey.x25519: {
8681
+ const jwk = publicKeyToJWK(algo, publicKey);
8682
+ jwk.d = uint8ArrayToB64(privateKey);
8683
+ return jwk;
8684
+ }
8685
+ default:
8686
+ throw new Error('Unsupported ECDH algorithm');
8687
+ }
8688
+ }
8689
+
8588
8690
  var ecdh_x = /*#__PURE__*/Object.freeze({
8589
8691
  __proto__: null,
8590
8692
  decrypt: decrypt$4,
@@ -9292,12 +9394,6 @@ var ecdsa = /*#__PURE__*/Object.freeze({
9292
9394
  async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9293
9395
  const curve = new CurveWithOID(oid);
9294
9396
  checkPublicPointEnconding(curve, publicKey);
9295
- if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
9296
- // Enforce digest sizes, since the constraint was already present in RFC4880bis:
9297
- // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
9298
- // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
9299
- throw new Error('Hash algorithm too weak for EdDSA.');
9300
- }
9301
9397
  const { RS: signature } = await sign$9(enums.publicKey.ed25519, hashAlgo, message, publicKey.subarray(1), privateKey, hashed);
9302
9398
  // EdDSA signature params are returned in little-endian format
9303
9399
  return {
@@ -9321,12 +9417,6 @@ async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9321
9417
  async function verify$7(oid, hashAlgo, { r, s }, m, publicKey, hashed) {
9322
9418
  const curve = new CurveWithOID(oid);
9323
9419
  checkPublicPointEnconding(curve, publicKey);
9324
- if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
9325
- // Enforce digest sizes, since the constraint was already present in RFC4880bis:
9326
- // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
9327
- // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
9328
- throw new Error('Hash algorithm too weak for EdDSA.');
9329
- }
9330
9420
  const RS = util.concatUint8Array([r, s]);
9331
9421
  return verify$9(enums.publicKey.ed25519, hashAlgo, { RS }, m, publicKey.subarray(1), hashed);
9332
9422
  }
@@ -10184,7 +10274,7 @@ async function generate$4(algo) {
10184
10274
  async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10185
10275
  const { eccKeyShare, eccCipherText } = await encaps$1(algo, eccPublicKey);
10186
10276
  const { mlkemKeyShare, mlkemCipherText } = await encaps(algo, mlkemPublicKey);
10187
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10277
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10188
10278
  const wrappedKey = await wrap(enums.symmetric.aes256, kek, sessioneKeyData); // C
10189
10279
  return { eccCipherText, mlkemCipherText, wrappedKey };
10190
10280
  }
@@ -10192,25 +10282,24 @@ async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10192
10282
  async function decrypt$2(algo, eccCipherText, mlkemCipherText, eccSecretKey, eccPublicKey, mlkemSecretKey, mlkemPublicKey, encryptedSessionKeyData) {
10193
10283
  const eccKeyShare = await decaps$1(algo, eccCipherText, eccSecretKey, eccPublicKey);
10194
10284
  const mlkemKeyShare = await decaps(algo, mlkemCipherText, mlkemSecretKey);
10195
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10285
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10196
10286
  const sessionKey = await unwrap(enums.symmetric.aes256, kek, encryptedSessionKeyData);
10197
10287
  return sessionKey;
10198
10288
  }
10199
10289
 
10200
- async function multiKeyCombine(algo, ecdhKeyShare, ecdhCipherText, ecdhPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey) {
10201
- // LAMPS-aligned and NIST compatible combiner, proposed in: https://mailarchive.ietf.org/arch/msg/openpgp/NMTCy707LICtxIhP3Xt1U5C8MF0/
10202
- // 2a. KDF(mlkemSS || tradSS || tradCT || tradPK || Domain)
10203
- // where Domain is "Domain" for LAMPS, and "mlkemCT || mlkemPK || algId || const" for OpenPGP
10290
+ /**
10291
+ * KEM key combiner
10292
+ */
10293
+ async function multiKeyCombine(algo, mlkemKeyShare, ecdhKeyShare, ecdhCipherText, ecdhPublicKey) {
10294
+ const domSep = util.encodeUTF8('OpenPGPCompositeKDFv1');
10204
10295
  const encData = util.concatUint8Array([
10205
10296
  mlkemKeyShare,
10206
10297
  ecdhKeyShare,
10207
10298
  ecdhCipherText,
10208
10299
  ecdhPublicKey,
10209
- // domSep
10210
- mlkemCipherText,
10211
- mlkemPublicKey,
10212
10300
  new Uint8Array([algo]),
10213
- util.encodeUTF8('OpenPGPCompositeKDFv1')
10301
+ domSep,
10302
+ new Uint8Array([domSep.length])
10214
10303
  ]);
10215
10304
 
10216
10305
  const kek = await computeDigest(enums.hash.sha3_256, encData);
@@ -10347,12 +10436,6 @@ async function generate$1(algo) {
10347
10436
  }
10348
10437
 
10349
10438
  async function sign$2(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, dataDigest) {
10350
- if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
10351
- // The signature hash algo MUST be set to the specified algorithm, see
10352
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10353
- throw new Error('Unexpected hash algorithm for PQC signature');
10354
- }
10355
-
10356
10439
  switch (signatureAlgo) {
10357
10440
  case enums.publicKey.pqc_mldsa_ed25519: {
10358
10441
  const { eccSignature } = await sign$3(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest);
@@ -10366,12 +10449,6 @@ async function sign$2(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsa
10366
10449
  }
10367
10450
 
10368
10451
  async function verify$2(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, dataDigest, { eccSignature, mldsaSignature }) {
10369
- if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
10370
- // The signature hash algo MUST be set to the specified algorithm, see
10371
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10372
- throw new Error('Unexpected hash algorithm for PQC signature');
10373
- }
10374
-
10375
10452
  switch (signatureAlgo) {
10376
10453
  case enums.publicKey.pqc_mldsa_ed25519: {
10377
10454
  const eccVerifiedPromise = verify$3(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature);
@@ -10384,11 +10461,12 @@ async function verify$2(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, d
10384
10461
  }
10385
10462
  }
10386
10463
 
10387
- function getRequiredHashAlgo(signatureAlgo) {
10388
- // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10464
+ function isCompatibleHashAlgo(signatureAlgo, hashAlgo) {
10465
+ // The signature hash algo MUST have digest larger than 256 bits
10466
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
10389
10467
  switch (signatureAlgo) {
10390
10468
  case enums.publicKey.pqc_mldsa_ed25519:
10391
- return enums.hash.sha3_256;
10469
+ return getHashByteLength(hashAlgo) >= 32;
10392
10470
  default:
10393
10471
  throw new Error('Unsupported signature algorithm');
10394
10472
  }
@@ -12511,6 +12589,12 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12511
12589
  return verify$8(oid, hashAlgo, { r, s }, data, Q, hashed);
12512
12590
  }
12513
12591
  case enums.publicKey.eddsaLegacy: {
12592
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12593
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12594
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12595
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12596
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12597
+ }
12514
12598
  const { oid, Q } = publicParams;
12515
12599
  const curveSize = new CurveWithOID(oid).payloadSize;
12516
12600
  // When dealing little-endian MPI data, we always need to left-pad it, as done with big-endian values:
@@ -12521,6 +12605,13 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12521
12605
  }
12522
12606
  case enums.publicKey.ed25519:
12523
12607
  case enums.publicKey.ed448: {
12608
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12609
+ // Enforce digest sizes:
12610
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12611
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12612
+ throw new Error('Hash algorithm too weak for EdDSA.');
12613
+ }
12614
+
12524
12615
  const { A } = publicParams;
12525
12616
  return verify$9(algo, hashAlgo, signature, data, A, hashed);
12526
12617
  }
@@ -12533,6 +12624,11 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12533
12624
  return verify$5(algo.getValue(), keyMaterial, signature.mac.data, hashed);
12534
12625
  }
12535
12626
  case enums.publicKey.pqc_mldsa_ed25519: {
12627
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12628
+ // The signature hash algo MUST have digest larger than 256 bits
12629
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12630
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12631
+ }
12536
12632
  const { eccPublicKey, mldsaPublicKey } = publicParams;
12537
12633
  return verify$2(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
12538
12634
  }
@@ -12581,12 +12677,24 @@ async function sign$1(algo, hashAlgo, publicKeyParams, privateKeyParams, data, h
12581
12677
  return sign$8(oid, hashAlgo, data, Q, d, hashed);
12582
12678
  }
12583
12679
  case enums.publicKey.eddsaLegacy: {
12680
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12681
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12682
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12683
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12684
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12685
+ }
12584
12686
  const { oid, Q } = publicKeyParams;
12585
12687
  const { seed } = privateKeyParams;
12586
12688
  return sign$7(oid, hashAlgo, data, Q, seed, hashed);
12587
12689
  }
12588
12690
  case enums.publicKey.ed25519:
12589
12691
  case enums.publicKey.ed448: {
12692
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12693
+ // Enforce digest sizes:
12694
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12695
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12696
+ throw new Error('Hash algorithm too weak for EdDSA.');
12697
+ }
12590
12698
  const { A } = publicKeyParams;
12591
12699
  const { seed } = privateKeyParams;
12592
12700
  return sign$9(algo, hashAlgo, data, A, seed, hashed);
@@ -12598,6 +12706,11 @@ async function sign$1(algo, hashAlgo, publicKeyParams, privateKeyParams, data, h
12598
12706
  return { mac: new ShortByteString(mac) };
12599
12707
  }
12600
12708
  case enums.publicKey.pqc_mldsa_ed25519: {
12709
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12710
+ // The signature hash algo MUST have digest larger than 256 bits
12711
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12712
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12713
+ }
12601
12714
  const { eccPublicKey } = publicKeyParams;
12602
12715
  const { eccSecretKey, mldsaSecretKey } = privateKeyParams;
12603
12716
  return sign$2(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
@@ -16874,12 +16987,8 @@ class PublicKeyPacket {
16874
16987
  throw new Error('Legacy curve25519 cannot be used with v6 keys');
16875
16988
  }
16876
16989
  // The composite ML-DSA + EdDSA schemes MUST be used only with v6 keys.
16877
- // The composite ML-KEM + ECDH schemes MUST be used only with v6 keys.
16878
- if (this.version !== 6 && (
16879
- this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
16880
- this.algorithm === enums.publicKey.pqc_mlkem_x25519
16881
- )) {
16882
- throw new Error('Unexpected key version: ML-DSA and ML-KEM algorithms can only be used with v6 keys');
16990
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
16991
+ throw new Error('Unexpected key version: ML-DSA algorithms can only be used with v6 keys');
16883
16992
  }
16884
16993
  this.publicParams = publicParams;
16885
16994
  pos += read;
@@ -17879,11 +17988,8 @@ class SecretKeyPacket extends PublicKeyPacket {
17879
17988
  )) {
17880
17989
  throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`);
17881
17990
  }
17882
- if (this.version !== 6 && (
17883
- this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
17884
- this.algorithm === enums.publicKey.pqc_mlkem_x25519
17885
- )) {
17886
- throw new Error(`Cannot generate v${this.version} keys of type 'pqc'. Generate a v6 key instead`);
17991
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
17992
+ throw new Error(`Cannot generate v${this.version} signing keys of type 'pqc'. Generate a v6 key instead`);
17887
17993
  }
17888
17994
  const { privateParams, publicParams } = await generateParams(this.algorithm, bits, curve, symmetric);
17889
17995
  this.privateParams = privateParams;
@@ -18387,12 +18493,6 @@ async function createBindingSignature(subkey, primaryKey, options, config) {
18387
18493
  * @async
18388
18494
  */
18389
18495
  async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Date(), targetUserIDs = [], config) {
18390
- if (signingKeyPacket.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
18391
- // For PQC, the returned hash algo MUST be set to the specified algorithm, see
18392
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
18393
- return getRequiredHashAlgo(signingKeyPacket.algorithm);
18394
- }
18395
-
18396
18496
  /**
18397
18497
  * If `preferredSenderAlgo` appears in the prefs of all recipients, we pick it; otherwise, we use the
18398
18498
  * strongest supported algo (`defaultAlgo` is always implicitly supported by all keys).
@@ -18440,6 +18540,10 @@ async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Dat
18440
18540
  enums.publicKey.ed448
18441
18541
  ]);
18442
18542
 
18543
+ const pqcAlgos = new Set([
18544
+ enums.publicKey.pqc_mldsa_ed25519
18545
+ ]);
18546
+
18443
18547
  if (eccAlgos.has(signingKeyPacket.algorithm)) {
18444
18548
  // For ECC, the returned hash algo MUST be at least as strong as `preferredCurveHashAlgo`, see:
18445
18549
  // - ECDSA: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.2-5
@@ -18462,6 +18566,21 @@ async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Dat
18462
18566
  strongestSupportedAlgo :
18463
18567
  preferredCurveAlgo;
18464
18568
  }
18569
+ } else if (pqcAlgos.has(signingKeyPacket.algorithm)) {
18570
+ // For PQC, the returned hash algo MUST be at least 256 bit long, see:
18571
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4 .
18572
+ // Hence, we return the `preferredHashAlgo` as long as it's supported and long enough;
18573
+ // Otherwise, we look at the strongest supported algo, and ultimately fallback the default algo (SHA-256).
18574
+ const preferredSenderAlgoIsSupported = isSupportedHashAlgo(preferredSenderAlgo) && isCompatibleHashAlgo(signingKeyPacket.algorithm, preferredSenderAlgo);
18575
+
18576
+ if (preferredSenderAlgoIsSupported) {
18577
+ return preferredSenderAlgo;
18578
+ } else {
18579
+ const strongestSupportedAlgo = getStrongestSupportedHashAlgo();
18580
+ return isCompatibleHashAlgo(signingKeyPacket.algorithm, strongestSupportedAlgo) ?
18581
+ strongestSupportedAlgo :
18582
+ defaultAlgo;
18583
+ }
18465
18584
  }
18466
18585
 
18467
18586
  // `preferredSenderAlgo` may be weaker than the default, but we do not guard against this,