@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.
package/dist/openpgp.js CHANGED
@@ -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
  var openpgp = (function (exports) {
3
3
  'use strict';
4
4
 
@@ -1065,11 +1065,10 @@ var openpgp = (function (exports) {
1065
1065
  ed25519: 27,
1066
1066
  /** Ed448 (Sign only) */
1067
1067
  ed448: 28,
1068
- /** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
1069
- pqc_mlkem_x25519: 105,
1070
1068
  /** Post-quantum ML-DSA-64 + Ed25519 (Sign only) */
1071
- pqc_mldsa_ed25519: 107,
1072
-
1069
+ pqc_mldsa_ed25519: 30,
1070
+ /** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
1071
+ pqc_mlkem_x25519: 35,
1073
1072
  /** Persistent symmetric keys: encryption algorithm */
1074
1073
  aead: 100,
1075
1074
  /** Persistent symmetric keys: authentication algorithm */
@@ -1720,7 +1719,7 @@ var openpgp = (function (exports) {
1720
1719
  * @memberof module:config
1721
1720
  * @property {String} versionString A version string to be included in armored messages
1722
1721
  */
1723
- versionString: 'OpenPGP.js 6.1.1-patch.2',
1722
+ versionString: 'OpenPGP.js 6.1.1-patch.4',
1724
1723
  /**
1725
1724
  * @memberof module:config
1726
1725
  * @property {String} commentString A comment string to be included in armored messages
@@ -6917,7 +6916,15 @@ var openpgp = (function (exports) {
6917
6916
  case enums.publicKey.ed25519:
6918
6917
  try {
6919
6918
  const webCrypto = util.getWebCrypto();
6920
- const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify']);
6919
+ const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify'])
6920
+ .catch(err => {
6921
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
6922
+ const newErr = new Error('Unexpected key generation issue');
6923
+ newErr.name = 'NotSupportedError';
6924
+ throw newErr;
6925
+ }
6926
+ throw err;
6927
+ });
6921
6928
 
6922
6929
  const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
6923
6930
  const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
@@ -6927,7 +6934,7 @@ var openpgp = (function (exports) {
6927
6934
  seed: b64ToUint8Array(privateKey.d, true)
6928
6935
  };
6929
6936
  } catch (err) {
6930
- if (err.name !== 'NotSupportedError' && err.name !== 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
6937
+ if (err.name !== 'NotSupportedError') {
6931
6938
  throw err;
6932
6939
  }
6933
6940
  const seed = getRandomBytes(getPayloadSize$1(algo));
@@ -6960,17 +6967,11 @@ var openpgp = (function (exports) {
6960
6967
  * @async
6961
6968
  */
6962
6969
  async function sign$9(algo, hashAlgo, message, publicKey, privateKey, hashed) {
6963
- if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
6964
- // Enforce digest sizes:
6965
- // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
6966
- // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
6967
- throw new Error('Hash algorithm too weak for EdDSA.');
6968
- }
6969
6970
  switch (algo) {
6970
6971
  case enums.publicKey.ed25519:
6971
6972
  try {
6972
6973
  const webCrypto = util.getWebCrypto();
6973
- const jwk = privateKeyToJWK(algo, publicKey, privateKey);
6974
+ const jwk = privateKeyToJWK$1(algo, publicKey, privateKey);
6974
6975
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['sign']);
6975
6976
 
6976
6977
  const signature = new Uint8Array(
@@ -7010,17 +7011,11 @@ var openpgp = (function (exports) {
7010
7011
  * @async
7011
7012
  */
7012
7013
  async function verify$9(algo, hashAlgo, { RS }, m, publicKey, hashed) {
7013
- if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
7014
- // Enforce digest sizes:
7015
- // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
7016
- // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
7017
- throw new Error('Hash algorithm too weak for EdDSA.');
7018
- }
7019
7014
  switch (algo) {
7020
7015
  case enums.publicKey.ed25519:
7021
7016
  try {
7022
7017
  const webCrypto = util.getWebCrypto();
7023
- const jwk = publicKeyToJWK(algo, publicKey);
7018
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7024
7019
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['verify']);
7025
7020
  const verified = await webCrypto.verify('Ed25519', key, RS, hashed);
7026
7021
  return verified;
@@ -7095,7 +7090,7 @@ var openpgp = (function (exports) {
7095
7090
  }
7096
7091
  }
7097
7092
 
7098
- const publicKeyToJWK = (algo, publicKey) => {
7093
+ const publicKeyToJWK$1 = (algo, publicKey) => {
7099
7094
  switch (algo) {
7100
7095
  case enums.publicKey.ed25519: {
7101
7096
  const jwk = {
@@ -7111,10 +7106,10 @@ var openpgp = (function (exports) {
7111
7106
  }
7112
7107
  };
7113
7108
 
7114
- const privateKeyToJWK = (algo, publicKey, privateKey) => {
7109
+ const privateKeyToJWK$1 = (algo, publicKey, privateKey) => {
7115
7110
  switch (algo) {
7116
7111
  case enums.publicKey.ed25519: {
7117
- const jwk = publicKeyToJWK(algo, publicKey);
7112
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7118
7113
  jwk.d = uint8ArrayToB64(privateKey);
7119
7114
  return jwk;
7120
7115
  }
@@ -8354,12 +8349,41 @@ var openpgp = (function (exports) {
8354
8349
  */
8355
8350
  async function generate$9(algo) {
8356
8351
  switch (algo) {
8357
- case enums.publicKey.x25519: {
8358
- // k stays in little-endian, unlike legacy ECDH over curve25519
8359
- const k = getRandomBytes(32);
8360
- const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8361
- return { A, k };
8362
- }
8352
+ case enums.publicKey.x25519:
8353
+ try {
8354
+ const webCrypto = util.getWebCrypto();
8355
+ const webCryptoKey = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
8356
+ .catch(err => {
8357
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
8358
+ const newErr = new Error('Unexpected key generation issue');
8359
+ newErr.name = 'NotSupportedError';
8360
+ throw newErr;
8361
+ }
8362
+ throw err;
8363
+ });
8364
+
8365
+ const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
8366
+ const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
8367
+
8368
+ if (privateKey.x !== publicKey.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
8369
+ const err = new Error('Unexpected mismatching public point');
8370
+ err.name = 'NotSupportedError';
8371
+ throw err;
8372
+ }
8373
+
8374
+ return {
8375
+ A: new Uint8Array(b64ToUint8Array(publicKey.x)),
8376
+ k: b64ToUint8Array(privateKey.d)
8377
+ };
8378
+ } catch (err) {
8379
+ if (err.name !== 'NotSupportedError') {
8380
+ throw err;
8381
+ }
8382
+ // k stays in little-endian, unlike legacy ECDH over curve25519
8383
+ const k = getRandomBytes(32);
8384
+ const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8385
+ return { A, k };
8386
+ }
8363
8387
 
8364
8388
  case enums.publicKey.x448: {
8365
8389
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
@@ -8501,13 +8525,46 @@ var openpgp = (function (exports) {
8501
8525
  */
8502
8526
  async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8503
8527
  switch (algo) {
8504
- case enums.publicKey.x25519: {
8505
- const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8506
- const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8507
- assertNonZeroArray(sharedSecret);
8508
- const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8509
- return { ephemeralPublicKey, sharedSecret };
8510
- }
8528
+ case enums.publicKey.x25519:
8529
+ try {
8530
+ const webCrypto = util.getWebCrypto();
8531
+ const ephemeralKeyPair = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
8532
+ .catch(err => {
8533
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
8534
+ const newErr = new Error('Unexpected key generation issue');
8535
+ newErr.name = 'NotSupportedError';
8536
+ throw newErr;
8537
+ }
8538
+ throw err;
8539
+ });
8540
+ const ephemeralPublicKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.publicKey);
8541
+ const ephemeralPrivateKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.privateKey);
8542
+ if (ephemeralPrivateKeyJwt.x !== ephemeralPublicKeyJwt.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
8543
+ const err = new Error('Unexpected mismatching public point');
8544
+ err.name = 'NotSupportedError';
8545
+ throw err;
8546
+ }
8547
+ const jwk = publicKeyToJWK(algo, recipientA);
8548
+ const recipientPublicKey = await webCrypto.importKey('jwk', jwk, 'X25519', false, []);
8549
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8550
+ { name: 'X25519', public: recipientPublicKey },
8551
+ ephemeralKeyPair.privateKey,
8552
+ getPayloadSize(algo) * 8 // in bits
8553
+ );
8554
+ return {
8555
+ sharedSecret: new Uint8Array(sharedSecretBuffer),
8556
+ ephemeralPublicKey: new Uint8Array(b64ToUint8Array(ephemeralPublicKeyJwt.x))
8557
+ };
8558
+ } catch (err) {
8559
+ if (err.name !== 'NotSupportedError') {
8560
+ throw err;
8561
+ }
8562
+ const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8563
+ const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8564
+ assertNonZeroArray(sharedSecret);
8565
+ const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8566
+ return { ephemeralPublicKey, sharedSecret };
8567
+ }
8511
8568
  case enums.publicKey.x448: {
8512
8569
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8513
8570
  const ephemeralSecretKey = x448.utils.randomPrivateKey();
@@ -8523,11 +8580,27 @@ var openpgp = (function (exports) {
8523
8580
 
8524
8581
  async function recomputeSharedSecret(algo, ephemeralPublicKey, A, k) {
8525
8582
  switch (algo) {
8526
- case enums.publicKey.x25519: {
8527
- const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8528
- assertNonZeroArray(sharedSecret);
8529
- return sharedSecret;
8530
- }
8583
+ case enums.publicKey.x25519:
8584
+ try {
8585
+ const webCrypto = util.getWebCrypto();
8586
+ const privateKeyJWK = privateKeyToJWK(algo, A, k);
8587
+ const ephemeralPublicKeyJWK = publicKeyToJWK(algo, ephemeralPublicKey);
8588
+ const privateKey = await webCrypto.importKey('jwk', privateKeyJWK, 'X25519', false, ['deriveKey', 'deriveBits']);
8589
+ const ephemeralPublicKeyReference = await webCrypto.importKey('jwk', ephemeralPublicKeyJWK, 'X25519', false, []);
8590
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8591
+ { name: 'X25519', public: ephemeralPublicKeyReference },
8592
+ privateKey,
8593
+ getPayloadSize(algo) * 8 // in bits
8594
+ );
8595
+ return new Uint8Array(sharedSecretBuffer);
8596
+ } catch (err) {
8597
+ if (err.name !== 'NotSupportedError') {
8598
+ throw err;
8599
+ }
8600
+ const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8601
+ assertNonZeroArray(sharedSecret);
8602
+ return sharedSecret;
8603
+ }
8531
8604
  case enums.publicKey.x448: {
8532
8605
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8533
8606
  const sharedSecret = x448.getSharedSecret(k, ephemeralPublicKey);
@@ -8555,6 +8628,35 @@ var openpgp = (function (exports) {
8555
8628
  }
8556
8629
  }
8557
8630
 
8631
+
8632
+ function publicKeyToJWK(algo, publicKey) {
8633
+ switch (algo) {
8634
+ case enums.publicKey.x25519: {
8635
+ const jwk = {
8636
+ kty: 'OKP',
8637
+ crv: 'X25519',
8638
+ x: uint8ArrayToB64(publicKey),
8639
+ ext: true
8640
+ };
8641
+ return jwk;
8642
+ }
8643
+ default:
8644
+ throw new Error('Unsupported ECDH algorithm');
8645
+ }
8646
+ }
8647
+
8648
+ function privateKeyToJWK(algo, publicKey, privateKey) {
8649
+ switch (algo) {
8650
+ case enums.publicKey.x25519: {
8651
+ const jwk = publicKeyToJWK(algo, publicKey);
8652
+ jwk.d = uint8ArrayToB64(privateKey);
8653
+ return jwk;
8654
+ }
8655
+ default:
8656
+ throw new Error('Unsupported ECDH algorithm');
8657
+ }
8658
+ }
8659
+
8558
8660
  var ecdh_x = /*#__PURE__*/Object.freeze({
8559
8661
  __proto__: null,
8560
8662
  decrypt: decrypt$4,
@@ -9262,12 +9364,6 @@ var openpgp = (function (exports) {
9262
9364
  async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9263
9365
  const curve = new CurveWithOID(oid);
9264
9366
  checkPublicPointEnconding(curve, publicKey);
9265
- if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
9266
- // Enforce digest sizes, since the constraint was already present in RFC4880bis:
9267
- // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
9268
- // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
9269
- throw new Error('Hash algorithm too weak for EdDSA.');
9270
- }
9271
9367
  const { RS: signature } = await sign$9(enums.publicKey.ed25519, hashAlgo, message, publicKey.subarray(1), privateKey, hashed);
9272
9368
  // EdDSA signature params are returned in little-endian format
9273
9369
  return {
@@ -9291,12 +9387,6 @@ var openpgp = (function (exports) {
9291
9387
  async function verify$7(oid, hashAlgo, { r, s }, m, publicKey, hashed) {
9292
9388
  const curve = new CurveWithOID(oid);
9293
9389
  checkPublicPointEnconding(curve, publicKey);
9294
- if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
9295
- // Enforce digest sizes, since the constraint was already present in RFC4880bis:
9296
- // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
9297
- // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
9298
- throw new Error('Hash algorithm too weak for EdDSA.');
9299
- }
9300
9390
  const RS = util.concatUint8Array([r, s]);
9301
9391
  return verify$9(enums.publicKey.ed25519, hashAlgo, { RS }, m, publicKey.subarray(1), hashed);
9302
9392
  }
@@ -10154,7 +10244,7 @@ var openpgp = (function (exports) {
10154
10244
  async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10155
10245
  const { eccKeyShare, eccCipherText } = await encaps$1(algo, eccPublicKey);
10156
10246
  const { mlkemKeyShare, mlkemCipherText } = await encaps(algo, mlkemPublicKey);
10157
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10247
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10158
10248
  const wrappedKey = await wrap(enums.symmetric.aes256, kek, sessioneKeyData); // C
10159
10249
  return { eccCipherText, mlkemCipherText, wrappedKey };
10160
10250
  }
@@ -10162,25 +10252,24 @@ var openpgp = (function (exports) {
10162
10252
  async function decrypt$2(algo, eccCipherText, mlkemCipherText, eccSecretKey, eccPublicKey, mlkemSecretKey, mlkemPublicKey, encryptedSessionKeyData) {
10163
10253
  const eccKeyShare = await decaps$1(algo, eccCipherText, eccSecretKey, eccPublicKey);
10164
10254
  const mlkemKeyShare = await decaps(algo, mlkemCipherText, mlkemSecretKey);
10165
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10255
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10166
10256
  const sessionKey = await unwrap(enums.symmetric.aes256, kek, encryptedSessionKeyData);
10167
10257
  return sessionKey;
10168
10258
  }
10169
10259
 
10170
- async function multiKeyCombine(algo, ecdhKeyShare, ecdhCipherText, ecdhPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey) {
10171
- // LAMPS-aligned and NIST compatible combiner, proposed in: https://mailarchive.ietf.org/arch/msg/openpgp/NMTCy707LICtxIhP3Xt1U5C8MF0/
10172
- // 2a. KDF(mlkemSS || tradSS || tradCT || tradPK || Domain)
10173
- // where Domain is "Domain" for LAMPS, and "mlkemCT || mlkemPK || algId || const" for OpenPGP
10260
+ /**
10261
+ * KEM key combiner
10262
+ */
10263
+ async function multiKeyCombine(algo, mlkemKeyShare, ecdhKeyShare, ecdhCipherText, ecdhPublicKey) {
10264
+ const domSep = util.encodeUTF8('OpenPGPCompositeKDFv1');
10174
10265
  const encData = util.concatUint8Array([
10175
10266
  mlkemKeyShare,
10176
10267
  ecdhKeyShare,
10177
10268
  ecdhCipherText,
10178
10269
  ecdhPublicKey,
10179
- // domSep
10180
- mlkemCipherText,
10181
- mlkemPublicKey,
10182
10270
  new Uint8Array([algo]),
10183
- util.encodeUTF8('OpenPGPCompositeKDFv1')
10271
+ domSep,
10272
+ new Uint8Array([domSep.length])
10184
10273
  ]);
10185
10274
 
10186
10275
  const kek = await computeDigest(enums.hash.sha3_256, encData);
@@ -10317,12 +10406,6 @@ var openpgp = (function (exports) {
10317
10406
  }
10318
10407
 
10319
10408
  async function sign$2(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, dataDigest) {
10320
- if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
10321
- // The signature hash algo MUST be set to the specified algorithm, see
10322
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10323
- throw new Error('Unexpected hash algorithm for PQC signature');
10324
- }
10325
-
10326
10409
  switch (signatureAlgo) {
10327
10410
  case enums.publicKey.pqc_mldsa_ed25519: {
10328
10411
  const { eccSignature } = await sign$3(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest);
@@ -10336,12 +10419,6 @@ var openpgp = (function (exports) {
10336
10419
  }
10337
10420
 
10338
10421
  async function verify$2(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, dataDigest, { eccSignature, mldsaSignature }) {
10339
- if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
10340
- // The signature hash algo MUST be set to the specified algorithm, see
10341
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10342
- throw new Error('Unexpected hash algorithm for PQC signature');
10343
- }
10344
-
10345
10422
  switch (signatureAlgo) {
10346
10423
  case enums.publicKey.pqc_mldsa_ed25519: {
10347
10424
  const eccVerifiedPromise = verify$3(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature);
@@ -10354,11 +10431,12 @@ var openpgp = (function (exports) {
10354
10431
  }
10355
10432
  }
10356
10433
 
10357
- function getRequiredHashAlgo(signatureAlgo) {
10358
- // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10434
+ function isCompatibleHashAlgo(signatureAlgo, hashAlgo) {
10435
+ // The signature hash algo MUST have digest larger than 256 bits
10436
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
10359
10437
  switch (signatureAlgo) {
10360
10438
  case enums.publicKey.pqc_mldsa_ed25519:
10361
- return enums.hash.sha3_256;
10439
+ return getHashByteLength(hashAlgo) >= 32;
10362
10440
  default:
10363
10441
  throw new Error('Unsupported signature algorithm');
10364
10442
  }
@@ -12481,6 +12559,12 @@ var openpgp = (function (exports) {
12481
12559
  return verify$8(oid, hashAlgo, { r, s }, data, Q, hashed);
12482
12560
  }
12483
12561
  case enums.publicKey.eddsaLegacy: {
12562
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12563
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12564
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12565
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12566
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12567
+ }
12484
12568
  const { oid, Q } = publicParams;
12485
12569
  const curveSize = new CurveWithOID(oid).payloadSize;
12486
12570
  // When dealing little-endian MPI data, we always need to left-pad it, as done with big-endian values:
@@ -12491,6 +12575,13 @@ var openpgp = (function (exports) {
12491
12575
  }
12492
12576
  case enums.publicKey.ed25519:
12493
12577
  case enums.publicKey.ed448: {
12578
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12579
+ // Enforce digest sizes:
12580
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12581
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12582
+ throw new Error('Hash algorithm too weak for EdDSA.');
12583
+ }
12584
+
12494
12585
  const { A } = publicParams;
12495
12586
  return verify$9(algo, hashAlgo, signature, data, A, hashed);
12496
12587
  }
@@ -12503,6 +12594,11 @@ var openpgp = (function (exports) {
12503
12594
  return verify$5(algo.getValue(), keyMaterial, signature.mac.data, hashed);
12504
12595
  }
12505
12596
  case enums.publicKey.pqc_mldsa_ed25519: {
12597
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12598
+ // The signature hash algo MUST have digest larger than 256 bits
12599
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12600
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12601
+ }
12506
12602
  const { eccPublicKey, mldsaPublicKey } = publicParams;
12507
12603
  return verify$2(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
12508
12604
  }
@@ -12551,12 +12647,24 @@ var openpgp = (function (exports) {
12551
12647
  return sign$8(oid, hashAlgo, data, Q, d, hashed);
12552
12648
  }
12553
12649
  case enums.publicKey.eddsaLegacy: {
12650
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12651
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12652
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12653
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12654
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12655
+ }
12554
12656
  const { oid, Q } = publicKeyParams;
12555
12657
  const { seed } = privateKeyParams;
12556
12658
  return sign$7(oid, hashAlgo, data, Q, seed, hashed);
12557
12659
  }
12558
12660
  case enums.publicKey.ed25519:
12559
12661
  case enums.publicKey.ed448: {
12662
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12663
+ // Enforce digest sizes:
12664
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12665
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12666
+ throw new Error('Hash algorithm too weak for EdDSA.');
12667
+ }
12560
12668
  const { A } = publicKeyParams;
12561
12669
  const { seed } = privateKeyParams;
12562
12670
  return sign$9(algo, hashAlgo, data, A, seed, hashed);
@@ -12568,6 +12676,11 @@ var openpgp = (function (exports) {
12568
12676
  return { mac: new ShortByteString(mac) };
12569
12677
  }
12570
12678
  case enums.publicKey.pqc_mldsa_ed25519: {
12679
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12680
+ // The signature hash algo MUST have digest larger than 256 bits
12681
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12682
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12683
+ }
12571
12684
  const { eccPublicKey } = publicKeyParams;
12572
12685
  const { eccSecretKey, mldsaSecretKey } = privateKeyParams;
12573
12686
  return sign$2(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
@@ -16836,12 +16949,8 @@ var openpgp = (function (exports) {
16836
16949
  throw new Error('Legacy curve25519 cannot be used with v6 keys');
16837
16950
  }
16838
16951
  // The composite ML-DSA + EdDSA schemes MUST be used only with v6 keys.
16839
- // The composite ML-KEM + ECDH schemes MUST be used only with v6 keys.
16840
- if (this.version !== 6 && (
16841
- this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
16842
- this.algorithm === enums.publicKey.pqc_mlkem_x25519
16843
- )) {
16844
- throw new Error('Unexpected key version: ML-DSA and ML-KEM algorithms can only be used with v6 keys');
16952
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
16953
+ throw new Error('Unexpected key version: ML-DSA algorithms can only be used with v6 keys');
16845
16954
  }
16846
16955
  this.publicParams = publicParams;
16847
16956
  pos += read;
@@ -17841,11 +17950,8 @@ var openpgp = (function (exports) {
17841
17950
  )) {
17842
17951
  throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`);
17843
17952
  }
17844
- if (this.version !== 6 && (
17845
- this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
17846
- this.algorithm === enums.publicKey.pqc_mlkem_x25519
17847
- )) {
17848
- throw new Error(`Cannot generate v${this.version} keys of type 'pqc'. Generate a v6 key instead`);
17953
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
17954
+ throw new Error(`Cannot generate v${this.version} signing keys of type 'pqc'. Generate a v6 key instead`);
17849
17955
  }
17850
17956
  const { privateParams, publicParams } = await generateParams(this.algorithm, bits, curve, symmetric);
17851
17957
  this.privateParams = privateParams;
@@ -18349,12 +18455,6 @@ var openpgp = (function (exports) {
18349
18455
  * @async
18350
18456
  */
18351
18457
  async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Date(), targetUserIDs = [], config) {
18352
- if (signingKeyPacket.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
18353
- // For PQC, the returned hash algo MUST be set to the specified algorithm, see
18354
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
18355
- return getRequiredHashAlgo(signingKeyPacket.algorithm);
18356
- }
18357
-
18358
18458
  /**
18359
18459
  * If `preferredSenderAlgo` appears in the prefs of all recipients, we pick it; otherwise, we use the
18360
18460
  * strongest supported algo (`defaultAlgo` is always implicitly supported by all keys).
@@ -18402,6 +18502,10 @@ var openpgp = (function (exports) {
18402
18502
  enums.publicKey.ed448
18403
18503
  ]);
18404
18504
 
18505
+ const pqcAlgos = new Set([
18506
+ enums.publicKey.pqc_mldsa_ed25519
18507
+ ]);
18508
+
18405
18509
  if (eccAlgos.has(signingKeyPacket.algorithm)) {
18406
18510
  // For ECC, the returned hash algo MUST be at least as strong as `preferredCurveHashAlgo`, see:
18407
18511
  // - ECDSA: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.2-5
@@ -18424,6 +18528,21 @@ var openpgp = (function (exports) {
18424
18528
  strongestSupportedAlgo :
18425
18529
  preferredCurveAlgo;
18426
18530
  }
18531
+ } else if (pqcAlgos.has(signingKeyPacket.algorithm)) {
18532
+ // For PQC, the returned hash algo MUST be at least 256 bit long, see:
18533
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4 .
18534
+ // Hence, we return the `preferredHashAlgo` as long as it's supported and long enough;
18535
+ // Otherwise, we look at the strongest supported algo, and ultimately fallback the default algo (SHA-256).
18536
+ const preferredSenderAlgoIsSupported = isSupportedHashAlgo(preferredSenderAlgo) && isCompatibleHashAlgo(signingKeyPacket.algorithm, preferredSenderAlgo);
18537
+
18538
+ if (preferredSenderAlgoIsSupported) {
18539
+ return preferredSenderAlgo;
18540
+ } else {
18541
+ const strongestSupportedAlgo = getStrongestSupportedHashAlgo();
18542
+ return isCompatibleHashAlgo(signingKeyPacket.algorithm, strongestSupportedAlgo) ?
18543
+ strongestSupportedAlgo :
18544
+ defaultAlgo;
18545
+ }
18427
18546
  }
18428
18547
 
18429
18548
  // `preferredSenderAlgo` may be weaker than the default, but we do not guard against this,