@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
  const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
3
3
 
4
4
  import { createRequire } from 'module';
@@ -1066,11 +1066,10 @@ var enums = {
1066
1066
  ed25519: 27,
1067
1067
  /** Ed448 (Sign only) */
1068
1068
  ed448: 28,
1069
- /** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
1070
- pqc_mlkem_x25519: 105,
1071
1069
  /** Post-quantum ML-DSA-64 + Ed25519 (Sign only) */
1072
- pqc_mldsa_ed25519: 107,
1073
-
1070
+ pqc_mldsa_ed25519: 30,
1071
+ /** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
1072
+ pqc_mlkem_x25519: 35,
1074
1073
  /** Persistent symmetric keys: encryption algorithm */
1075
1074
  aead: 100,
1076
1075
  /** Persistent symmetric keys: authentication algorithm */
@@ -1721,7 +1720,7 @@ var config = {
1721
1720
  * @memberof module:config
1722
1721
  * @property {String} versionString A version string to be included in armored messages
1723
1722
  */
1724
- versionString: 'OpenPGP.js 6.1.1-patch.2',
1723
+ versionString: 'OpenPGP.js 6.1.1-patch.4',
1725
1724
  /**
1726
1725
  * @memberof module:config
1727
1726
  * @property {String} commentString A comment string to be included in armored messages
@@ -6924,7 +6923,15 @@ async function generate$a(algo) {
6924
6923
  case enums.publicKey.ed25519:
6925
6924
  try {
6926
6925
  const webCrypto = util.getWebCrypto();
6927
- const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify']);
6926
+ const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify'])
6927
+ .catch(err => {
6928
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
6929
+ const newErr = new Error('Unexpected key generation issue');
6930
+ newErr.name = 'NotSupportedError';
6931
+ throw newErr;
6932
+ }
6933
+ throw err;
6934
+ });
6928
6935
 
6929
6936
  const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
6930
6937
  const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
@@ -6934,7 +6941,7 @@ async function generate$a(algo) {
6934
6941
  seed: b64ToUint8Array(privateKey.d, true)
6935
6942
  };
6936
6943
  } catch (err) {
6937
- if (err.name !== 'NotSupportedError' && err.name !== 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
6944
+ if (err.name !== 'NotSupportedError') {
6938
6945
  throw err;
6939
6946
  }
6940
6947
  const seed = getRandomBytes(getPayloadSize$1(algo));
@@ -6967,17 +6974,11 @@ async function generate$a(algo) {
6967
6974
  * @async
6968
6975
  */
6969
6976
  async function sign$9(algo, hashAlgo, message, publicKey, privateKey, hashed) {
6970
- if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
6971
- // Enforce digest sizes:
6972
- // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
6973
- // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
6974
- throw new Error('Hash algorithm too weak for EdDSA.');
6975
- }
6976
6977
  switch (algo) {
6977
6978
  case enums.publicKey.ed25519:
6978
6979
  try {
6979
6980
  const webCrypto = util.getWebCrypto();
6980
- const jwk = privateKeyToJWK(algo, publicKey, privateKey);
6981
+ const jwk = privateKeyToJWK$1(algo, publicKey, privateKey);
6981
6982
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['sign']);
6982
6983
 
6983
6984
  const signature = new Uint8Array(
@@ -7017,17 +7018,11 @@ async function sign$9(algo, hashAlgo, message, publicKey, privateKey, hashed) {
7017
7018
  * @async
7018
7019
  */
7019
7020
  async function verify$9(algo, hashAlgo, { RS }, m, publicKey, hashed) {
7020
- if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
7021
- // Enforce digest sizes:
7022
- // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
7023
- // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
7024
- throw new Error('Hash algorithm too weak for EdDSA.');
7025
- }
7026
7021
  switch (algo) {
7027
7022
  case enums.publicKey.ed25519:
7028
7023
  try {
7029
7024
  const webCrypto = util.getWebCrypto();
7030
- const jwk = publicKeyToJWK(algo, publicKey);
7025
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7031
7026
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['verify']);
7032
7027
  const verified = await webCrypto.verify('Ed25519', key, RS, hashed);
7033
7028
  return verified;
@@ -7102,7 +7097,7 @@ function getPreferredHashAlgo$2(algo) {
7102
7097
  }
7103
7098
  }
7104
7099
 
7105
- const publicKeyToJWK = (algo, publicKey) => {
7100
+ const publicKeyToJWK$1 = (algo, publicKey) => {
7106
7101
  switch (algo) {
7107
7102
  case enums.publicKey.ed25519: {
7108
7103
  const jwk = {
@@ -7118,10 +7113,10 @@ const publicKeyToJWK = (algo, publicKey) => {
7118
7113
  }
7119
7114
  };
7120
7115
 
7121
- const privateKeyToJWK = (algo, publicKey, privateKey) => {
7116
+ const privateKeyToJWK$1 = (algo, publicKey, privateKey) => {
7122
7117
  switch (algo) {
7123
7118
  case enums.publicKey.ed25519: {
7124
- const jwk = publicKeyToJWK(algo, publicKey);
7119
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7125
7120
  jwk.d = uint8ArrayToB64(privateKey);
7126
7121
  return jwk;
7127
7122
  }
@@ -8361,12 +8356,41 @@ const HKDF_INFO = {
8361
8356
  */
8362
8357
  async function generate$9(algo) {
8363
8358
  switch (algo) {
8364
- case enums.publicKey.x25519: {
8365
- // k stays in little-endian, unlike legacy ECDH over curve25519
8366
- const k = getRandomBytes(32);
8367
- const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8368
- return { A, k };
8369
- }
8359
+ case enums.publicKey.x25519:
8360
+ try {
8361
+ const webCrypto = util.getWebCrypto();
8362
+ const webCryptoKey = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
8363
+ .catch(err => {
8364
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
8365
+ const newErr = new Error('Unexpected key generation issue');
8366
+ newErr.name = 'NotSupportedError';
8367
+ throw newErr;
8368
+ }
8369
+ throw err;
8370
+ });
8371
+
8372
+ const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
8373
+ const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
8374
+
8375
+ if (privateKey.x !== publicKey.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
8376
+ const err = new Error('Unexpected mismatching public point');
8377
+ err.name = 'NotSupportedError';
8378
+ throw err;
8379
+ }
8380
+
8381
+ return {
8382
+ A: new Uint8Array(b64ToUint8Array(publicKey.x)),
8383
+ k: b64ToUint8Array(privateKey.d)
8384
+ };
8385
+ } catch (err) {
8386
+ if (err.name !== 'NotSupportedError') {
8387
+ throw err;
8388
+ }
8389
+ // k stays in little-endian, unlike legacy ECDH over curve25519
8390
+ const k = getRandomBytes(32);
8391
+ const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8392
+ return { A, k };
8393
+ }
8370
8394
 
8371
8395
  case enums.publicKey.x448: {
8372
8396
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
@@ -8508,13 +8532,46 @@ function getPayloadSize(algo) {
8508
8532
  */
8509
8533
  async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8510
8534
  switch (algo) {
8511
- case enums.publicKey.x25519: {
8512
- const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8513
- const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8514
- assertNonZeroArray(sharedSecret);
8515
- const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8516
- return { ephemeralPublicKey, sharedSecret };
8517
- }
8535
+ case enums.publicKey.x25519:
8536
+ try {
8537
+ const webCrypto = util.getWebCrypto();
8538
+ const ephemeralKeyPair = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
8539
+ .catch(err => {
8540
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
8541
+ const newErr = new Error('Unexpected key generation issue');
8542
+ newErr.name = 'NotSupportedError';
8543
+ throw newErr;
8544
+ }
8545
+ throw err;
8546
+ });
8547
+ const ephemeralPublicKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.publicKey);
8548
+ const ephemeralPrivateKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.privateKey);
8549
+ if (ephemeralPrivateKeyJwt.x !== ephemeralPublicKeyJwt.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
8550
+ const err = new Error('Unexpected mismatching public point');
8551
+ err.name = 'NotSupportedError';
8552
+ throw err;
8553
+ }
8554
+ const jwk = publicKeyToJWK(algo, recipientA);
8555
+ const recipientPublicKey = await webCrypto.importKey('jwk', jwk, 'X25519', false, []);
8556
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8557
+ { name: 'X25519', public: recipientPublicKey },
8558
+ ephemeralKeyPair.privateKey,
8559
+ getPayloadSize(algo) * 8 // in bits
8560
+ );
8561
+ return {
8562
+ sharedSecret: new Uint8Array(sharedSecretBuffer),
8563
+ ephemeralPublicKey: new Uint8Array(b64ToUint8Array(ephemeralPublicKeyJwt.x))
8564
+ };
8565
+ } catch (err) {
8566
+ if (err.name !== 'NotSupportedError') {
8567
+ throw err;
8568
+ }
8569
+ const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8570
+ const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8571
+ assertNonZeroArray(sharedSecret);
8572
+ const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8573
+ return { ephemeralPublicKey, sharedSecret };
8574
+ }
8518
8575
  case enums.publicKey.x448: {
8519
8576
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8520
8577
  const ephemeralSecretKey = x448.utils.randomPrivateKey();
@@ -8530,11 +8587,27 @@ async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8530
8587
 
8531
8588
  async function recomputeSharedSecret(algo, ephemeralPublicKey, A, k) {
8532
8589
  switch (algo) {
8533
- case enums.publicKey.x25519: {
8534
- const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8535
- assertNonZeroArray(sharedSecret);
8536
- return sharedSecret;
8537
- }
8590
+ case enums.publicKey.x25519:
8591
+ try {
8592
+ const webCrypto = util.getWebCrypto();
8593
+ const privateKeyJWK = privateKeyToJWK(algo, A, k);
8594
+ const ephemeralPublicKeyJWK = publicKeyToJWK(algo, ephemeralPublicKey);
8595
+ const privateKey = await webCrypto.importKey('jwk', privateKeyJWK, 'X25519', false, ['deriveKey', 'deriveBits']);
8596
+ const ephemeralPublicKeyReference = await webCrypto.importKey('jwk', ephemeralPublicKeyJWK, 'X25519', false, []);
8597
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8598
+ { name: 'X25519', public: ephemeralPublicKeyReference },
8599
+ privateKey,
8600
+ getPayloadSize(algo) * 8 // in bits
8601
+ );
8602
+ return new Uint8Array(sharedSecretBuffer);
8603
+ } catch (err) {
8604
+ if (err.name !== 'NotSupportedError') {
8605
+ throw err;
8606
+ }
8607
+ const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8608
+ assertNonZeroArray(sharedSecret);
8609
+ return sharedSecret;
8610
+ }
8538
8611
  case enums.publicKey.x448: {
8539
8612
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8540
8613
  const sharedSecret = x448.getSharedSecret(k, ephemeralPublicKey);
@@ -8562,6 +8635,35 @@ function assertNonZeroArray(sharedSecret) {
8562
8635
  }
8563
8636
  }
8564
8637
 
8638
+
8639
+ function publicKeyToJWK(algo, publicKey) {
8640
+ switch (algo) {
8641
+ case enums.publicKey.x25519: {
8642
+ const jwk = {
8643
+ kty: 'OKP',
8644
+ crv: 'X25519',
8645
+ x: uint8ArrayToB64(publicKey),
8646
+ ext: true
8647
+ };
8648
+ return jwk;
8649
+ }
8650
+ default:
8651
+ throw new Error('Unsupported ECDH algorithm');
8652
+ }
8653
+ }
8654
+
8655
+ function privateKeyToJWK(algo, publicKey, privateKey) {
8656
+ switch (algo) {
8657
+ case enums.publicKey.x25519: {
8658
+ const jwk = publicKeyToJWK(algo, publicKey);
8659
+ jwk.d = uint8ArrayToB64(privateKey);
8660
+ return jwk;
8661
+ }
8662
+ default:
8663
+ throw new Error('Unsupported ECDH algorithm');
8664
+ }
8665
+ }
8666
+
8565
8667
  var ecdh_x = /*#__PURE__*/Object.freeze({
8566
8668
  __proto__: null,
8567
8669
  decrypt: decrypt$4,
@@ -9269,12 +9371,6 @@ var ecdsa = /*#__PURE__*/Object.freeze({
9269
9371
  async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9270
9372
  const curve = new CurveWithOID(oid);
9271
9373
  checkPublicPointEnconding(curve, publicKey);
9272
- if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
9273
- // Enforce digest sizes, since the constraint was already present in RFC4880bis:
9274
- // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
9275
- // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
9276
- throw new Error('Hash algorithm too weak for EdDSA.');
9277
- }
9278
9374
  const { RS: signature } = await sign$9(enums.publicKey.ed25519, hashAlgo, message, publicKey.subarray(1), privateKey, hashed);
9279
9375
  // EdDSA signature params are returned in little-endian format
9280
9376
  return {
@@ -9298,12 +9394,6 @@ async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9298
9394
  async function verify$7(oid, hashAlgo, { r, s }, m, publicKey, hashed) {
9299
9395
  const curve = new CurveWithOID(oid);
9300
9396
  checkPublicPointEnconding(curve, publicKey);
9301
- if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
9302
- // Enforce digest sizes, since the constraint was already present in RFC4880bis:
9303
- // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
9304
- // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
9305
- throw new Error('Hash algorithm too weak for EdDSA.');
9306
- }
9307
9397
  const RS = util.concatUint8Array([r, s]);
9308
9398
  return verify$9(enums.publicKey.ed25519, hashAlgo, { RS }, m, publicKey.subarray(1), hashed);
9309
9399
  }
@@ -10161,7 +10251,7 @@ async function generate$4(algo) {
10161
10251
  async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10162
10252
  const { eccKeyShare, eccCipherText } = await encaps$1(algo, eccPublicKey);
10163
10253
  const { mlkemKeyShare, mlkemCipherText } = await encaps(algo, mlkemPublicKey);
10164
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10254
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10165
10255
  const wrappedKey = await wrap(enums.symmetric.aes256, kek, sessioneKeyData); // C
10166
10256
  return { eccCipherText, mlkemCipherText, wrappedKey };
10167
10257
  }
@@ -10169,25 +10259,24 @@ async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10169
10259
  async function decrypt$2(algo, eccCipherText, mlkemCipherText, eccSecretKey, eccPublicKey, mlkemSecretKey, mlkemPublicKey, encryptedSessionKeyData) {
10170
10260
  const eccKeyShare = await decaps$1(algo, eccCipherText, eccSecretKey, eccPublicKey);
10171
10261
  const mlkemKeyShare = await decaps(algo, mlkemCipherText, mlkemSecretKey);
10172
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10262
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10173
10263
  const sessionKey = await unwrap(enums.symmetric.aes256, kek, encryptedSessionKeyData);
10174
10264
  return sessionKey;
10175
10265
  }
10176
10266
 
10177
- async function multiKeyCombine(algo, ecdhKeyShare, ecdhCipherText, ecdhPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey) {
10178
- // LAMPS-aligned and NIST compatible combiner, proposed in: https://mailarchive.ietf.org/arch/msg/openpgp/NMTCy707LICtxIhP3Xt1U5C8MF0/
10179
- // 2a. KDF(mlkemSS || tradSS || tradCT || tradPK || Domain)
10180
- // where Domain is "Domain" for LAMPS, and "mlkemCT || mlkemPK || algId || const" for OpenPGP
10267
+ /**
10268
+ * KEM key combiner
10269
+ */
10270
+ async function multiKeyCombine(algo, mlkemKeyShare, ecdhKeyShare, ecdhCipherText, ecdhPublicKey) {
10271
+ const domSep = util.encodeUTF8('OpenPGPCompositeKDFv1');
10181
10272
  const encData = util.concatUint8Array([
10182
10273
  mlkemKeyShare,
10183
10274
  ecdhKeyShare,
10184
10275
  ecdhCipherText,
10185
10276
  ecdhPublicKey,
10186
- // domSep
10187
- mlkemCipherText,
10188
- mlkemPublicKey,
10189
10277
  new Uint8Array([algo]),
10190
- util.encodeUTF8('OpenPGPCompositeKDFv1')
10278
+ domSep,
10279
+ new Uint8Array([domSep.length])
10191
10280
  ]);
10192
10281
 
10193
10282
  const kek = await computeDigest(enums.hash.sha3_256, encData);
@@ -10324,12 +10413,6 @@ async function generate$1(algo) {
10324
10413
  }
10325
10414
 
10326
10415
  async function sign$2(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, dataDigest) {
10327
- if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
10328
- // The signature hash algo MUST be set to the specified algorithm, see
10329
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10330
- throw new Error('Unexpected hash algorithm for PQC signature');
10331
- }
10332
-
10333
10416
  switch (signatureAlgo) {
10334
10417
  case enums.publicKey.pqc_mldsa_ed25519: {
10335
10418
  const { eccSignature } = await sign$3(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest);
@@ -10343,12 +10426,6 @@ async function sign$2(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsa
10343
10426
  }
10344
10427
 
10345
10428
  async function verify$2(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, dataDigest, { eccSignature, mldsaSignature }) {
10346
- if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
10347
- // The signature hash algo MUST be set to the specified algorithm, see
10348
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10349
- throw new Error('Unexpected hash algorithm for PQC signature');
10350
- }
10351
-
10352
10429
  switch (signatureAlgo) {
10353
10430
  case enums.publicKey.pqc_mldsa_ed25519: {
10354
10431
  const eccVerifiedPromise = verify$3(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature);
@@ -10361,11 +10438,12 @@ async function verify$2(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, d
10361
10438
  }
10362
10439
  }
10363
10440
 
10364
- function getRequiredHashAlgo(signatureAlgo) {
10365
- // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10441
+ function isCompatibleHashAlgo(signatureAlgo, hashAlgo) {
10442
+ // The signature hash algo MUST have digest larger than 256 bits
10443
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
10366
10444
  switch (signatureAlgo) {
10367
10445
  case enums.publicKey.pqc_mldsa_ed25519:
10368
- return enums.hash.sha3_256;
10446
+ return getHashByteLength(hashAlgo) >= 32;
10369
10447
  default:
10370
10448
  throw new Error('Unsupported signature algorithm');
10371
10449
  }
@@ -12488,6 +12566,12 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12488
12566
  return verify$8(oid, hashAlgo, { r, s }, data, Q, hashed);
12489
12567
  }
12490
12568
  case enums.publicKey.eddsaLegacy: {
12569
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12570
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12571
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12572
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12573
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12574
+ }
12491
12575
  const { oid, Q } = publicParams;
12492
12576
  const curveSize = new CurveWithOID(oid).payloadSize;
12493
12577
  // When dealing little-endian MPI data, we always need to left-pad it, as done with big-endian values:
@@ -12498,6 +12582,13 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12498
12582
  }
12499
12583
  case enums.publicKey.ed25519:
12500
12584
  case enums.publicKey.ed448: {
12585
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12586
+ // Enforce digest sizes:
12587
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12588
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12589
+ throw new Error('Hash algorithm too weak for EdDSA.');
12590
+ }
12591
+
12501
12592
  const { A } = publicParams;
12502
12593
  return verify$9(algo, hashAlgo, signature, data, A, hashed);
12503
12594
  }
@@ -12510,6 +12601,11 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12510
12601
  return verify$5(algo.getValue(), keyMaterial, signature.mac.data, hashed);
12511
12602
  }
12512
12603
  case enums.publicKey.pqc_mldsa_ed25519: {
12604
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12605
+ // The signature hash algo MUST have digest larger than 256 bits
12606
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12607
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12608
+ }
12513
12609
  const { eccPublicKey, mldsaPublicKey } = publicParams;
12514
12610
  return verify$2(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
12515
12611
  }
@@ -12558,12 +12654,24 @@ async function sign$1(algo, hashAlgo, publicKeyParams, privateKeyParams, data, h
12558
12654
  return sign$8(oid, hashAlgo, data, Q, d, hashed);
12559
12655
  }
12560
12656
  case enums.publicKey.eddsaLegacy: {
12657
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12658
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12659
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12660
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12661
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12662
+ }
12561
12663
  const { oid, Q } = publicKeyParams;
12562
12664
  const { seed } = privateKeyParams;
12563
12665
  return sign$7(oid, hashAlgo, data, Q, seed, hashed);
12564
12666
  }
12565
12667
  case enums.publicKey.ed25519:
12566
12668
  case enums.publicKey.ed448: {
12669
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12670
+ // Enforce digest sizes:
12671
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12672
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12673
+ throw new Error('Hash algorithm too weak for EdDSA.');
12674
+ }
12567
12675
  const { A } = publicKeyParams;
12568
12676
  const { seed } = privateKeyParams;
12569
12677
  return sign$9(algo, hashAlgo, data, A, seed, hashed);
@@ -12575,6 +12683,11 @@ async function sign$1(algo, hashAlgo, publicKeyParams, privateKeyParams, data, h
12575
12683
  return { mac: new ShortByteString(mac) };
12576
12684
  }
12577
12685
  case enums.publicKey.pqc_mldsa_ed25519: {
12686
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12687
+ // The signature hash algo MUST have digest larger than 256 bits
12688
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12689
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12690
+ }
12578
12691
  const { eccPublicKey } = publicKeyParams;
12579
12692
  const { eccSecretKey, mldsaSecretKey } = privateKeyParams;
12580
12693
  return sign$2(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
@@ -16851,12 +16964,8 @@ class PublicKeyPacket {
16851
16964
  throw new Error('Legacy curve25519 cannot be used with v6 keys');
16852
16965
  }
16853
16966
  // The composite ML-DSA + EdDSA schemes MUST be used only with v6 keys.
16854
- // The composite ML-KEM + ECDH schemes MUST be used only with v6 keys.
16855
- if (this.version !== 6 && (
16856
- this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
16857
- this.algorithm === enums.publicKey.pqc_mlkem_x25519
16858
- )) {
16859
- throw new Error('Unexpected key version: ML-DSA and ML-KEM algorithms can only be used with v6 keys');
16967
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
16968
+ throw new Error('Unexpected key version: ML-DSA algorithms can only be used with v6 keys');
16860
16969
  }
16861
16970
  this.publicParams = publicParams;
16862
16971
  pos += read;
@@ -17856,11 +17965,8 @@ class SecretKeyPacket extends PublicKeyPacket {
17856
17965
  )) {
17857
17966
  throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`);
17858
17967
  }
17859
- if (this.version !== 6 && (
17860
- this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
17861
- this.algorithm === enums.publicKey.pqc_mlkem_x25519
17862
- )) {
17863
- throw new Error(`Cannot generate v${this.version} keys of type 'pqc'. Generate a v6 key instead`);
17968
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
17969
+ throw new Error(`Cannot generate v${this.version} signing keys of type 'pqc'. Generate a v6 key instead`);
17864
17970
  }
17865
17971
  const { privateParams, publicParams } = await generateParams(this.algorithm, bits, curve, symmetric);
17866
17972
  this.privateParams = privateParams;
@@ -18364,12 +18470,6 @@ async function createBindingSignature(subkey, primaryKey, options, config) {
18364
18470
  * @async
18365
18471
  */
18366
18472
  async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Date(), targetUserIDs = [], config) {
18367
- if (signingKeyPacket.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
18368
- // For PQC, the returned hash algo MUST be set to the specified algorithm, see
18369
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
18370
- return getRequiredHashAlgo(signingKeyPacket.algorithm);
18371
- }
18372
-
18373
18473
  /**
18374
18474
  * If `preferredSenderAlgo` appears in the prefs of all recipients, we pick it; otherwise, we use the
18375
18475
  * strongest supported algo (`defaultAlgo` is always implicitly supported by all keys).
@@ -18417,6 +18517,10 @@ async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Dat
18417
18517
  enums.publicKey.ed448
18418
18518
  ]);
18419
18519
 
18520
+ const pqcAlgos = new Set([
18521
+ enums.publicKey.pqc_mldsa_ed25519
18522
+ ]);
18523
+
18420
18524
  if (eccAlgos.has(signingKeyPacket.algorithm)) {
18421
18525
  // For ECC, the returned hash algo MUST be at least as strong as `preferredCurveHashAlgo`, see:
18422
18526
  // - ECDSA: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.2-5
@@ -18439,6 +18543,21 @@ async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Dat
18439
18543
  strongestSupportedAlgo :
18440
18544
  preferredCurveAlgo;
18441
18545
  }
18546
+ } else if (pqcAlgos.has(signingKeyPacket.algorithm)) {
18547
+ // For PQC, the returned hash algo MUST be at least 256 bit long, see:
18548
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4 .
18549
+ // Hence, we return the `preferredHashAlgo` as long as it's supported and long enough;
18550
+ // Otherwise, we look at the strongest supported algo, and ultimately fallback the default algo (SHA-256).
18551
+ const preferredSenderAlgoIsSupported = isSupportedHashAlgo(preferredSenderAlgo) && isCompatibleHashAlgo(signingKeyPacket.algorithm, preferredSenderAlgo);
18552
+
18553
+ if (preferredSenderAlgoIsSupported) {
18554
+ return preferredSenderAlgo;
18555
+ } else {
18556
+ const strongestSupportedAlgo = getStrongestSupportedHashAlgo();
18557
+ return isCompatibleHashAlgo(signingKeyPacket.algorithm, strongestSupportedAlgo) ?
18558
+ strongestSupportedAlgo :
18559
+ defaultAlgo;
18560
+ }
18442
18561
  }
18443
18562
 
18444
18563
  // `preferredSenderAlgo` may be weaker than the default, but we do not guard against this,