@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.mjs 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
  const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
3
3
 
4
4
  function _mergeNamespaces(n, m) {
@@ -1062,11 +1062,10 @@ var enums = {
1062
1062
  ed25519: 27,
1063
1063
  /** Ed448 (Sign only) */
1064
1064
  ed448: 28,
1065
- /** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
1066
- pqc_mlkem_x25519: 105,
1067
1065
  /** Post-quantum ML-DSA-64 + Ed25519 (Sign only) */
1068
- pqc_mldsa_ed25519: 107,
1069
-
1066
+ pqc_mldsa_ed25519: 30,
1067
+ /** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
1068
+ pqc_mlkem_x25519: 35,
1070
1069
  /** Persistent symmetric keys: encryption algorithm */
1071
1070
  aead: 100,
1072
1071
  /** Persistent symmetric keys: authentication algorithm */
@@ -1717,7 +1716,7 @@ var config = {
1717
1716
  * @memberof module:config
1718
1717
  * @property {String} versionString A version string to be included in armored messages
1719
1718
  */
1720
- versionString: 'OpenPGP.js 6.1.1-patch.2',
1719
+ versionString: 'OpenPGP.js 6.1.1-patch.4',
1721
1720
  /**
1722
1721
  * @memberof module:config
1723
1722
  * @property {String} commentString A comment string to be included in armored messages
@@ -6914,7 +6913,15 @@ async function generate$a(algo) {
6914
6913
  case enums.publicKey.ed25519:
6915
6914
  try {
6916
6915
  const webCrypto = util.getWebCrypto();
6917
- const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify']);
6916
+ const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify'])
6917
+ .catch(err => {
6918
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
6919
+ const newErr = new Error('Unexpected key generation issue');
6920
+ newErr.name = 'NotSupportedError';
6921
+ throw newErr;
6922
+ }
6923
+ throw err;
6924
+ });
6918
6925
 
6919
6926
  const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
6920
6927
  const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
@@ -6924,7 +6931,7 @@ async function generate$a(algo) {
6924
6931
  seed: b64ToUint8Array(privateKey.d, true)
6925
6932
  };
6926
6933
  } catch (err) {
6927
- if (err.name !== 'NotSupportedError' && err.name !== 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
6934
+ if (err.name !== 'NotSupportedError') {
6928
6935
  throw err;
6929
6936
  }
6930
6937
  const seed = getRandomBytes(getPayloadSize$1(algo));
@@ -6957,17 +6964,11 @@ async function generate$a(algo) {
6957
6964
  * @async
6958
6965
  */
6959
6966
  async function sign$9(algo, hashAlgo, message, publicKey, privateKey, hashed) {
6960
- if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
6961
- // Enforce digest sizes:
6962
- // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
6963
- // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
6964
- throw new Error('Hash algorithm too weak for EdDSA.');
6965
- }
6966
6967
  switch (algo) {
6967
6968
  case enums.publicKey.ed25519:
6968
6969
  try {
6969
6970
  const webCrypto = util.getWebCrypto();
6970
- const jwk = privateKeyToJWK(algo, publicKey, privateKey);
6971
+ const jwk = privateKeyToJWK$1(algo, publicKey, privateKey);
6971
6972
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['sign']);
6972
6973
 
6973
6974
  const signature = new Uint8Array(
@@ -7007,17 +7008,11 @@ async function sign$9(algo, hashAlgo, message, publicKey, privateKey, hashed) {
7007
7008
  * @async
7008
7009
  */
7009
7010
  async function verify$9(algo, hashAlgo, { RS }, m, publicKey, hashed) {
7010
- if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
7011
- // Enforce digest sizes:
7012
- // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
7013
- // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
7014
- throw new Error('Hash algorithm too weak for EdDSA.');
7015
- }
7016
7011
  switch (algo) {
7017
7012
  case enums.publicKey.ed25519:
7018
7013
  try {
7019
7014
  const webCrypto = util.getWebCrypto();
7020
- const jwk = publicKeyToJWK(algo, publicKey);
7015
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7021
7016
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['verify']);
7022
7017
  const verified = await webCrypto.verify('Ed25519', key, RS, hashed);
7023
7018
  return verified;
@@ -7092,7 +7087,7 @@ function getPreferredHashAlgo$2(algo) {
7092
7087
  }
7093
7088
  }
7094
7089
 
7095
- const publicKeyToJWK = (algo, publicKey) => {
7090
+ const publicKeyToJWK$1 = (algo, publicKey) => {
7096
7091
  switch (algo) {
7097
7092
  case enums.publicKey.ed25519: {
7098
7093
  const jwk = {
@@ -7108,10 +7103,10 @@ const publicKeyToJWK = (algo, publicKey) => {
7108
7103
  }
7109
7104
  };
7110
7105
 
7111
- const privateKeyToJWK = (algo, publicKey, privateKey) => {
7106
+ const privateKeyToJWK$1 = (algo, publicKey, privateKey) => {
7112
7107
  switch (algo) {
7113
7108
  case enums.publicKey.ed25519: {
7114
- const jwk = publicKeyToJWK(algo, publicKey);
7109
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7115
7110
  jwk.d = uint8ArrayToB64(privateKey);
7116
7111
  return jwk;
7117
7112
  }
@@ -8351,12 +8346,41 @@ const HKDF_INFO = {
8351
8346
  */
8352
8347
  async function generate$9(algo) {
8353
8348
  switch (algo) {
8354
- case enums.publicKey.x25519: {
8355
- // k stays in little-endian, unlike legacy ECDH over curve25519
8356
- const k = getRandomBytes(32);
8357
- const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8358
- return { A, k };
8359
- }
8349
+ case enums.publicKey.x25519:
8350
+ try {
8351
+ const webCrypto = util.getWebCrypto();
8352
+ const webCryptoKey = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
8353
+ .catch(err => {
8354
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
8355
+ const newErr = new Error('Unexpected key generation issue');
8356
+ newErr.name = 'NotSupportedError';
8357
+ throw newErr;
8358
+ }
8359
+ throw err;
8360
+ });
8361
+
8362
+ const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
8363
+ const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
8364
+
8365
+ if (privateKey.x !== publicKey.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
8366
+ const err = new Error('Unexpected mismatching public point');
8367
+ err.name = 'NotSupportedError';
8368
+ throw err;
8369
+ }
8370
+
8371
+ return {
8372
+ A: new Uint8Array(b64ToUint8Array(publicKey.x)),
8373
+ k: b64ToUint8Array(privateKey.d)
8374
+ };
8375
+ } catch (err) {
8376
+ if (err.name !== 'NotSupportedError') {
8377
+ throw err;
8378
+ }
8379
+ // k stays in little-endian, unlike legacy ECDH over curve25519
8380
+ const k = getRandomBytes(32);
8381
+ const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8382
+ return { A, k };
8383
+ }
8360
8384
 
8361
8385
  case enums.publicKey.x448: {
8362
8386
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
@@ -8498,13 +8522,46 @@ function getPayloadSize(algo) {
8498
8522
  */
8499
8523
  async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8500
8524
  switch (algo) {
8501
- case enums.publicKey.x25519: {
8502
- const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8503
- const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8504
- assertNonZeroArray(sharedSecret);
8505
- const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8506
- return { ephemeralPublicKey, sharedSecret };
8507
- }
8525
+ case enums.publicKey.x25519:
8526
+ try {
8527
+ const webCrypto = util.getWebCrypto();
8528
+ const ephemeralKeyPair = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
8529
+ .catch(err => {
8530
+ if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
8531
+ const newErr = new Error('Unexpected key generation issue');
8532
+ newErr.name = 'NotSupportedError';
8533
+ throw newErr;
8534
+ }
8535
+ throw err;
8536
+ });
8537
+ const ephemeralPublicKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.publicKey);
8538
+ const ephemeralPrivateKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.privateKey);
8539
+ if (ephemeralPrivateKeyJwt.x !== ephemeralPublicKeyJwt.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
8540
+ const err = new Error('Unexpected mismatching public point');
8541
+ err.name = 'NotSupportedError';
8542
+ throw err;
8543
+ }
8544
+ const jwk = publicKeyToJWK(algo, recipientA);
8545
+ const recipientPublicKey = await webCrypto.importKey('jwk', jwk, 'X25519', false, []);
8546
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8547
+ { name: 'X25519', public: recipientPublicKey },
8548
+ ephemeralKeyPair.privateKey,
8549
+ getPayloadSize(algo) * 8 // in bits
8550
+ );
8551
+ return {
8552
+ sharedSecret: new Uint8Array(sharedSecretBuffer),
8553
+ ephemeralPublicKey: new Uint8Array(b64ToUint8Array(ephemeralPublicKeyJwt.x))
8554
+ };
8555
+ } catch (err) {
8556
+ if (err.name !== 'NotSupportedError') {
8557
+ throw err;
8558
+ }
8559
+ const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8560
+ const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8561
+ assertNonZeroArray(sharedSecret);
8562
+ const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8563
+ return { ephemeralPublicKey, sharedSecret };
8564
+ }
8508
8565
  case enums.publicKey.x448: {
8509
8566
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8510
8567
  const ephemeralSecretKey = x448.utils.randomPrivateKey();
@@ -8520,11 +8577,27 @@ async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8520
8577
 
8521
8578
  async function recomputeSharedSecret(algo, ephemeralPublicKey, A, k) {
8522
8579
  switch (algo) {
8523
- case enums.publicKey.x25519: {
8524
- const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8525
- assertNonZeroArray(sharedSecret);
8526
- return sharedSecret;
8527
- }
8580
+ case enums.publicKey.x25519:
8581
+ try {
8582
+ const webCrypto = util.getWebCrypto();
8583
+ const privateKeyJWK = privateKeyToJWK(algo, A, k);
8584
+ const ephemeralPublicKeyJWK = publicKeyToJWK(algo, ephemeralPublicKey);
8585
+ const privateKey = await webCrypto.importKey('jwk', privateKeyJWK, 'X25519', false, ['deriveKey', 'deriveBits']);
8586
+ const ephemeralPublicKeyReference = await webCrypto.importKey('jwk', ephemeralPublicKeyJWK, 'X25519', false, []);
8587
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8588
+ { name: 'X25519', public: ephemeralPublicKeyReference },
8589
+ privateKey,
8590
+ getPayloadSize(algo) * 8 // in bits
8591
+ );
8592
+ return new Uint8Array(sharedSecretBuffer);
8593
+ } catch (err) {
8594
+ if (err.name !== 'NotSupportedError') {
8595
+ throw err;
8596
+ }
8597
+ const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8598
+ assertNonZeroArray(sharedSecret);
8599
+ return sharedSecret;
8600
+ }
8528
8601
  case enums.publicKey.x448: {
8529
8602
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8530
8603
  const sharedSecret = x448.getSharedSecret(k, ephemeralPublicKey);
@@ -8552,6 +8625,35 @@ function assertNonZeroArray(sharedSecret) {
8552
8625
  }
8553
8626
  }
8554
8627
 
8628
+
8629
+ function publicKeyToJWK(algo, publicKey) {
8630
+ switch (algo) {
8631
+ case enums.publicKey.x25519: {
8632
+ const jwk = {
8633
+ kty: 'OKP',
8634
+ crv: 'X25519',
8635
+ x: uint8ArrayToB64(publicKey),
8636
+ ext: true
8637
+ };
8638
+ return jwk;
8639
+ }
8640
+ default:
8641
+ throw new Error('Unsupported ECDH algorithm');
8642
+ }
8643
+ }
8644
+
8645
+ function privateKeyToJWK(algo, publicKey, privateKey) {
8646
+ switch (algo) {
8647
+ case enums.publicKey.x25519: {
8648
+ const jwk = publicKeyToJWK(algo, publicKey);
8649
+ jwk.d = uint8ArrayToB64(privateKey);
8650
+ return jwk;
8651
+ }
8652
+ default:
8653
+ throw new Error('Unsupported ECDH algorithm');
8654
+ }
8655
+ }
8656
+
8555
8657
  var ecdh_x = /*#__PURE__*/Object.freeze({
8556
8658
  __proto__: null,
8557
8659
  decrypt: decrypt$4,
@@ -9259,12 +9361,6 @@ var ecdsa = /*#__PURE__*/Object.freeze({
9259
9361
  async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9260
9362
  const curve = new CurveWithOID(oid);
9261
9363
  checkPublicPointEnconding(curve, publicKey);
9262
- if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
9263
- // Enforce digest sizes, since the constraint was already present in RFC4880bis:
9264
- // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
9265
- // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
9266
- throw new Error('Hash algorithm too weak for EdDSA.');
9267
- }
9268
9364
  const { RS: signature } = await sign$9(enums.publicKey.ed25519, hashAlgo, message, publicKey.subarray(1), privateKey, hashed);
9269
9365
  // EdDSA signature params are returned in little-endian format
9270
9366
  return {
@@ -9288,12 +9384,6 @@ async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9288
9384
  async function verify$7(oid, hashAlgo, { r, s }, m, publicKey, hashed) {
9289
9385
  const curve = new CurveWithOID(oid);
9290
9386
  checkPublicPointEnconding(curve, publicKey);
9291
- if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
9292
- // Enforce digest sizes, since the constraint was already present in RFC4880bis:
9293
- // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
9294
- // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
9295
- throw new Error('Hash algorithm too weak for EdDSA.');
9296
- }
9297
9387
  const RS = util.concatUint8Array([r, s]);
9298
9388
  return verify$9(enums.publicKey.ed25519, hashAlgo, { RS }, m, publicKey.subarray(1), hashed);
9299
9389
  }
@@ -10151,7 +10241,7 @@ async function generate$4(algo) {
10151
10241
  async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10152
10242
  const { eccKeyShare, eccCipherText } = await encaps$1(algo, eccPublicKey);
10153
10243
  const { mlkemKeyShare, mlkemCipherText } = await encaps(algo, mlkemPublicKey);
10154
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10244
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10155
10245
  const wrappedKey = await wrap(enums.symmetric.aes256, kek, sessioneKeyData); // C
10156
10246
  return { eccCipherText, mlkemCipherText, wrappedKey };
10157
10247
  }
@@ -10159,25 +10249,24 @@ async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10159
10249
  async function decrypt$2(algo, eccCipherText, mlkemCipherText, eccSecretKey, eccPublicKey, mlkemSecretKey, mlkemPublicKey, encryptedSessionKeyData) {
10160
10250
  const eccKeyShare = await decaps$1(algo, eccCipherText, eccSecretKey, eccPublicKey);
10161
10251
  const mlkemKeyShare = await decaps(algo, mlkemCipherText, mlkemSecretKey);
10162
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10252
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10163
10253
  const sessionKey = await unwrap(enums.symmetric.aes256, kek, encryptedSessionKeyData);
10164
10254
  return sessionKey;
10165
10255
  }
10166
10256
 
10167
- async function multiKeyCombine(algo, ecdhKeyShare, ecdhCipherText, ecdhPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey) {
10168
- // LAMPS-aligned and NIST compatible combiner, proposed in: https://mailarchive.ietf.org/arch/msg/openpgp/NMTCy707LICtxIhP3Xt1U5C8MF0/
10169
- // 2a. KDF(mlkemSS || tradSS || tradCT || tradPK || Domain)
10170
- // where Domain is "Domain" for LAMPS, and "mlkemCT || mlkemPK || algId || const" for OpenPGP
10257
+ /**
10258
+ * KEM key combiner
10259
+ */
10260
+ async function multiKeyCombine(algo, mlkemKeyShare, ecdhKeyShare, ecdhCipherText, ecdhPublicKey) {
10261
+ const domSep = util.encodeUTF8('OpenPGPCompositeKDFv1');
10171
10262
  const encData = util.concatUint8Array([
10172
10263
  mlkemKeyShare,
10173
10264
  ecdhKeyShare,
10174
10265
  ecdhCipherText,
10175
10266
  ecdhPublicKey,
10176
- // domSep
10177
- mlkemCipherText,
10178
- mlkemPublicKey,
10179
10267
  new Uint8Array([algo]),
10180
- util.encodeUTF8('OpenPGPCompositeKDFv1')
10268
+ domSep,
10269
+ new Uint8Array([domSep.length])
10181
10270
  ]);
10182
10271
 
10183
10272
  const kek = await computeDigest(enums.hash.sha3_256, encData);
@@ -10314,12 +10403,6 @@ async function generate$1(algo) {
10314
10403
  }
10315
10404
 
10316
10405
  async function sign$2(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, dataDigest) {
10317
- if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
10318
- // The signature hash algo MUST be set to the specified algorithm, see
10319
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10320
- throw new Error('Unexpected hash algorithm for PQC signature');
10321
- }
10322
-
10323
10406
  switch (signatureAlgo) {
10324
10407
  case enums.publicKey.pqc_mldsa_ed25519: {
10325
10408
  const { eccSignature } = await sign$3(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest);
@@ -10333,12 +10416,6 @@ async function sign$2(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsa
10333
10416
  }
10334
10417
 
10335
10418
  async function verify$2(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, dataDigest, { eccSignature, mldsaSignature }) {
10336
- if (hashAlgo !== getRequiredHashAlgo(signatureAlgo)) {
10337
- // The signature hash algo MUST be set to the specified algorithm, see
10338
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10339
- throw new Error('Unexpected hash algorithm for PQC signature');
10340
- }
10341
-
10342
10419
  switch (signatureAlgo) {
10343
10420
  case enums.publicKey.pqc_mldsa_ed25519: {
10344
10421
  const eccVerifiedPromise = verify$3(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature);
@@ -10351,11 +10428,12 @@ async function verify$2(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, d
10351
10428
  }
10352
10429
  }
10353
10430
 
10354
- function getRequiredHashAlgo(signatureAlgo) {
10355
- // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10431
+ function isCompatibleHashAlgo(signatureAlgo, hashAlgo) {
10432
+ // The signature hash algo MUST have digest larger than 256 bits
10433
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
10356
10434
  switch (signatureAlgo) {
10357
10435
  case enums.publicKey.pqc_mldsa_ed25519:
10358
- return enums.hash.sha3_256;
10436
+ return getHashByteLength(hashAlgo) >= 32;
10359
10437
  default:
10360
10438
  throw new Error('Unsupported signature algorithm');
10361
10439
  }
@@ -12478,6 +12556,12 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12478
12556
  return verify$8(oid, hashAlgo, { r, s }, data, Q, hashed);
12479
12557
  }
12480
12558
  case enums.publicKey.eddsaLegacy: {
12559
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12560
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12561
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12562
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12563
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12564
+ }
12481
12565
  const { oid, Q } = publicParams;
12482
12566
  const curveSize = new CurveWithOID(oid).payloadSize;
12483
12567
  // When dealing little-endian MPI data, we always need to left-pad it, as done with big-endian values:
@@ -12488,6 +12572,13 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12488
12572
  }
12489
12573
  case enums.publicKey.ed25519:
12490
12574
  case enums.publicKey.ed448: {
12575
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12576
+ // Enforce digest sizes:
12577
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12578
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12579
+ throw new Error('Hash algorithm too weak for EdDSA.');
12580
+ }
12581
+
12491
12582
  const { A } = publicParams;
12492
12583
  return verify$9(algo, hashAlgo, signature, data, A, hashed);
12493
12584
  }
@@ -12500,6 +12591,11 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12500
12591
  return verify$5(algo.getValue(), keyMaterial, signature.mac.data, hashed);
12501
12592
  }
12502
12593
  case enums.publicKey.pqc_mldsa_ed25519: {
12594
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12595
+ // The signature hash algo MUST have digest larger than 256 bits
12596
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12597
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12598
+ }
12503
12599
  const { eccPublicKey, mldsaPublicKey } = publicParams;
12504
12600
  return verify$2(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
12505
12601
  }
@@ -12548,12 +12644,24 @@ async function sign$1(algo, hashAlgo, publicKeyParams, privateKeyParams, data, h
12548
12644
  return sign$8(oid, hashAlgo, data, Q, d, hashed);
12549
12645
  }
12550
12646
  case enums.publicKey.eddsaLegacy: {
12647
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12648
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12649
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12650
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12651
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12652
+ }
12551
12653
  const { oid, Q } = publicKeyParams;
12552
12654
  const { seed } = privateKeyParams;
12553
12655
  return sign$7(oid, hashAlgo, data, Q, seed, hashed);
12554
12656
  }
12555
12657
  case enums.publicKey.ed25519:
12556
12658
  case enums.publicKey.ed448: {
12659
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12660
+ // Enforce digest sizes:
12661
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12662
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12663
+ throw new Error('Hash algorithm too weak for EdDSA.');
12664
+ }
12557
12665
  const { A } = publicKeyParams;
12558
12666
  const { seed } = privateKeyParams;
12559
12667
  return sign$9(algo, hashAlgo, data, A, seed, hashed);
@@ -12565,6 +12673,11 @@ async function sign$1(algo, hashAlgo, publicKeyParams, privateKeyParams, data, h
12565
12673
  return { mac: new ShortByteString(mac) };
12566
12674
  }
12567
12675
  case enums.publicKey.pqc_mldsa_ed25519: {
12676
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12677
+ // The signature hash algo MUST have digest larger than 256 bits
12678
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12679
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12680
+ }
12568
12681
  const { eccPublicKey } = publicKeyParams;
12569
12682
  const { eccSecretKey, mldsaSecretKey } = privateKeyParams;
12570
12683
  return sign$2(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
@@ -16833,12 +16946,8 @@ class PublicKeyPacket {
16833
16946
  throw new Error('Legacy curve25519 cannot be used with v6 keys');
16834
16947
  }
16835
16948
  // The composite ML-DSA + EdDSA schemes MUST be used only with v6 keys.
16836
- // The composite ML-KEM + ECDH schemes MUST be used only with v6 keys.
16837
- if (this.version !== 6 && (
16838
- this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
16839
- this.algorithm === enums.publicKey.pqc_mlkem_x25519
16840
- )) {
16841
- throw new Error('Unexpected key version: ML-DSA and ML-KEM algorithms can only be used with v6 keys');
16949
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
16950
+ throw new Error('Unexpected key version: ML-DSA algorithms can only be used with v6 keys');
16842
16951
  }
16843
16952
  this.publicParams = publicParams;
16844
16953
  pos += read;
@@ -17838,11 +17947,8 @@ class SecretKeyPacket extends PublicKeyPacket {
17838
17947
  )) {
17839
17948
  throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`);
17840
17949
  }
17841
- if (this.version !== 6 && (
17842
- this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
17843
- this.algorithm === enums.publicKey.pqc_mlkem_x25519
17844
- )) {
17845
- throw new Error(`Cannot generate v${this.version} keys of type 'pqc'. Generate a v6 key instead`);
17950
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
17951
+ throw new Error(`Cannot generate v${this.version} signing keys of type 'pqc'. Generate a v6 key instead`);
17846
17952
  }
17847
17953
  const { privateParams, publicParams } = await generateParams(this.algorithm, bits, curve, symmetric);
17848
17954
  this.privateParams = privateParams;
@@ -18346,12 +18452,6 @@ async function createBindingSignature(subkey, primaryKey, options, config) {
18346
18452
  * @async
18347
18453
  */
18348
18454
  async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Date(), targetUserIDs = [], config) {
18349
- if (signingKeyPacket.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
18350
- // For PQC, the returned hash algo MUST be set to the specified algorithm, see
18351
- // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
18352
- return getRequiredHashAlgo(signingKeyPacket.algorithm);
18353
- }
18354
-
18355
18455
  /**
18356
18456
  * If `preferredSenderAlgo` appears in the prefs of all recipients, we pick it; otherwise, we use the
18357
18457
  * strongest supported algo (`defaultAlgo` is always implicitly supported by all keys).
@@ -18399,6 +18499,10 @@ async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Dat
18399
18499
  enums.publicKey.ed448
18400
18500
  ]);
18401
18501
 
18502
+ const pqcAlgos = new Set([
18503
+ enums.publicKey.pqc_mldsa_ed25519
18504
+ ]);
18505
+
18402
18506
  if (eccAlgos.has(signingKeyPacket.algorithm)) {
18403
18507
  // For ECC, the returned hash algo MUST be at least as strong as `preferredCurveHashAlgo`, see:
18404
18508
  // - ECDSA: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.2-5
@@ -18421,6 +18525,21 @@ async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Dat
18421
18525
  strongestSupportedAlgo :
18422
18526
  preferredCurveAlgo;
18423
18527
  }
18528
+ } else if (pqcAlgos.has(signingKeyPacket.algorithm)) {
18529
+ // For PQC, the returned hash algo MUST be at least 256 bit long, see:
18530
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4 .
18531
+ // Hence, we return the `preferredHashAlgo` as long as it's supported and long enough;
18532
+ // Otherwise, we look at the strongest supported algo, and ultimately fallback the default algo (SHA-256).
18533
+ const preferredSenderAlgoIsSupported = isSupportedHashAlgo(preferredSenderAlgo) && isCompatibleHashAlgo(signingKeyPacket.algorithm, preferredSenderAlgo);
18534
+
18535
+ if (preferredSenderAlgoIsSupported) {
18536
+ return preferredSenderAlgo;
18537
+ } else {
18538
+ const strongestSupportedAlgo = getStrongestSupportedHashAlgo();
18539
+ return isCompatibleHashAlgo(signingKeyPacket.algorithm, strongestSupportedAlgo) ?
18540
+ strongestSupportedAlgo :
18541
+ defaultAlgo;
18542
+ }
18424
18543
  }
18425
18544
 
18426
18545
  // `preferredSenderAlgo` may be weaker than the default, but we do not guard against this,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@protontech/openpgp",
3
3
  "description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.",
4
- "version": "6.1.1-patch.2",
4
+ "version": "6.1.1-patch.4",
5
5
  "license": "LGPL-3.0+",
6
6
  "homepage": "https://openpgpjs.org/",
7
7
  "engines": {
package/src/enums.d.ts CHANGED
@@ -89,8 +89,8 @@ declare namespace enums {
89
89
  x448 = 26,
90
90
  ed25519 = 27,
91
91
  ed448 = 28,
92
- pqc_mlkem_x25519 = 105,
93
- pqc_mldsa_ed25519 = 107
92
+ pqc_mldsa_ed25519 = 30,
93
+ pqc_mlkem_x25519 = 35
94
94
  }
95
95
 
96
96
  export enum curve {