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

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.3 - 2025-06-18 - 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.3',
1721
1720
  /**
1722
1721
  * @memberof module:config
1723
1722
  * @property {String} commentString A comment string to be included in armored messages
@@ -6957,17 +6956,11 @@ async function generate$a(algo) {
6957
6956
  * @async
6958
6957
  */
6959
6958
  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
6959
  switch (algo) {
6967
6960
  case enums.publicKey.ed25519:
6968
6961
  try {
6969
6962
  const webCrypto = util.getWebCrypto();
6970
- const jwk = privateKeyToJWK(algo, publicKey, privateKey);
6963
+ const jwk = privateKeyToJWK$1(algo, publicKey, privateKey);
6971
6964
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['sign']);
6972
6965
 
6973
6966
  const signature = new Uint8Array(
@@ -7007,17 +7000,11 @@ async function sign$9(algo, hashAlgo, message, publicKey, privateKey, hashed) {
7007
7000
  * @async
7008
7001
  */
7009
7002
  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
7003
  switch (algo) {
7017
7004
  case enums.publicKey.ed25519:
7018
7005
  try {
7019
7006
  const webCrypto = util.getWebCrypto();
7020
- const jwk = publicKeyToJWK(algo, publicKey);
7007
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7021
7008
  const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['verify']);
7022
7009
  const verified = await webCrypto.verify('Ed25519', key, RS, hashed);
7023
7010
  return verified;
@@ -7092,7 +7079,7 @@ function getPreferredHashAlgo$2(algo) {
7092
7079
  }
7093
7080
  }
7094
7081
 
7095
- const publicKeyToJWK = (algo, publicKey) => {
7082
+ const publicKeyToJWK$1 = (algo, publicKey) => {
7096
7083
  switch (algo) {
7097
7084
  case enums.publicKey.ed25519: {
7098
7085
  const jwk = {
@@ -7108,10 +7095,10 @@ const publicKeyToJWK = (algo, publicKey) => {
7108
7095
  }
7109
7096
  };
7110
7097
 
7111
- const privateKeyToJWK = (algo, publicKey, privateKey) => {
7098
+ const privateKeyToJWK$1 = (algo, publicKey, privateKey) => {
7112
7099
  switch (algo) {
7113
7100
  case enums.publicKey.ed25519: {
7114
- const jwk = publicKeyToJWK(algo, publicKey);
7101
+ const jwk = publicKeyToJWK$1(algo, publicKey);
7115
7102
  jwk.d = uint8ArrayToB64(privateKey);
7116
7103
  return jwk;
7117
7104
  }
@@ -8351,12 +8338,27 @@ const HKDF_INFO = {
8351
8338
  */
8352
8339
  async function generate$9(algo) {
8353
8340
  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
- }
8341
+ case enums.publicKey.x25519:
8342
+ try {
8343
+ const webCrypto = util.getWebCrypto();
8344
+ const webCryptoKey = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits']);
8345
+
8346
+ const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
8347
+ const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
8348
+
8349
+ return {
8350
+ A: new Uint8Array(b64ToUint8Array(publicKey.x)),
8351
+ k: b64ToUint8Array(privateKey.d)
8352
+ };
8353
+ } catch (err) {
8354
+ if (err.name !== 'NotSupportedError') {
8355
+ throw err;
8356
+ }
8357
+ // k stays in little-endian, unlike legacy ECDH over curve25519
8358
+ const k = getRandomBytes(32);
8359
+ const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k);
8360
+ return { A, k };
8361
+ }
8360
8362
 
8361
8363
  case enums.publicKey.x448: {
8362
8364
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
@@ -8498,13 +8500,32 @@ function getPayloadSize(algo) {
8498
8500
  */
8499
8501
  async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8500
8502
  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
- }
8503
+ case enums.publicKey.x25519:
8504
+ try {
8505
+ const webCrypto = util.getWebCrypto();
8506
+ const jwk = publicKeyToJWK(algo, recipientA);
8507
+ const ephemeralKeyPair = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits']);
8508
+ const recipientPublicKey = await webCrypto.importKey('jwk', jwk, 'X25519', false, []);
8509
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8510
+ { name: 'X25519', public: recipientPublicKey },
8511
+ ephemeralKeyPair.privateKey,
8512
+ getPayloadSize(algo) * 8 // in bits
8513
+ );
8514
+ const ephemeralPublicKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.publicKey);
8515
+ return {
8516
+ sharedSecret: new Uint8Array(sharedSecretBuffer),
8517
+ ephemeralPublicKey: new Uint8Array(b64ToUint8Array(ephemeralPublicKeyJwt.x))
8518
+ };
8519
+ } catch (err) {
8520
+ if (err.name !== 'NotSupportedError') {
8521
+ throw err;
8522
+ }
8523
+ const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo));
8524
+ const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA);
8525
+ assertNonZeroArray(sharedSecret);
8526
+ const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey);
8527
+ return { ephemeralPublicKey, sharedSecret };
8528
+ }
8508
8529
  case enums.publicKey.x448: {
8509
8530
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8510
8531
  const ephemeralSecretKey = x448.utils.randomPrivateKey();
@@ -8520,11 +8541,27 @@ async function generateEphemeralEncryptionMaterial(algo, recipientA) {
8520
8541
 
8521
8542
  async function recomputeSharedSecret(algo, ephemeralPublicKey, A, k) {
8522
8543
  switch (algo) {
8523
- case enums.publicKey.x25519: {
8524
- const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8525
- assertNonZeroArray(sharedSecret);
8526
- return sharedSecret;
8527
- }
8544
+ case enums.publicKey.x25519:
8545
+ try {
8546
+ const webCrypto = util.getWebCrypto();
8547
+ const privateKeyJWK = privateKeyToJWK(algo, A, k);
8548
+ const ephemeralPublicKeyJWK = publicKeyToJWK(algo, ephemeralPublicKey);
8549
+ const privateKey = await webCrypto.importKey('jwk', privateKeyJWK, 'X25519', false, ['deriveKey', 'deriveBits']);
8550
+ const ephemeralPublicKeyReference = await webCrypto.importKey('jwk', ephemeralPublicKeyJWK, 'X25519', false, []);
8551
+ const sharedSecretBuffer = await webCrypto.deriveBits(
8552
+ { name: 'X25519', public: ephemeralPublicKeyReference },
8553
+ privateKey,
8554
+ getPayloadSize(algo) * 8 // in bits
8555
+ );
8556
+ return new Uint8Array(sharedSecretBuffer);
8557
+ } catch (err) {
8558
+ if (err.name !== 'NotSupportedError') {
8559
+ throw err;
8560
+ }
8561
+ const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey);
8562
+ assertNonZeroArray(sharedSecret);
8563
+ return sharedSecret;
8564
+ }
8528
8565
  case enums.publicKey.x448: {
8529
8566
  const x448 = await util.getNobleCurve(enums.publicKey.x448);
8530
8567
  const sharedSecret = x448.getSharedSecret(k, ephemeralPublicKey);
@@ -8552,6 +8589,35 @@ function assertNonZeroArray(sharedSecret) {
8552
8589
  }
8553
8590
  }
8554
8591
 
8592
+
8593
+ function publicKeyToJWK(algo, publicKey) {
8594
+ switch (algo) {
8595
+ case enums.publicKey.x25519: {
8596
+ const jwk = {
8597
+ kty: 'OKP',
8598
+ crv: 'X25519',
8599
+ x: uint8ArrayToB64(publicKey),
8600
+ ext: true
8601
+ };
8602
+ return jwk;
8603
+ }
8604
+ default:
8605
+ throw new Error('Unsupported ECDH algorithm');
8606
+ }
8607
+ }
8608
+
8609
+ function privateKeyToJWK(algo, publicKey, privateKey) {
8610
+ switch (algo) {
8611
+ case enums.publicKey.x25519: {
8612
+ const jwk = publicKeyToJWK(algo, publicKey);
8613
+ jwk.d = uint8ArrayToB64(privateKey);
8614
+ return jwk;
8615
+ }
8616
+ default:
8617
+ throw new Error('Unsupported ECDH algorithm');
8618
+ }
8619
+ }
8620
+
8555
8621
  var ecdh_x = /*#__PURE__*/Object.freeze({
8556
8622
  __proto__: null,
8557
8623
  decrypt: decrypt$4,
@@ -9259,12 +9325,6 @@ var ecdsa = /*#__PURE__*/Object.freeze({
9259
9325
  async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9260
9326
  const curve = new CurveWithOID(oid);
9261
9327
  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
9328
  const { RS: signature } = await sign$9(enums.publicKey.ed25519, hashAlgo, message, publicKey.subarray(1), privateKey, hashed);
9269
9329
  // EdDSA signature params are returned in little-endian format
9270
9330
  return {
@@ -9288,12 +9348,6 @@ async function sign$7(oid, hashAlgo, message, publicKey, privateKey, hashed) {
9288
9348
  async function verify$7(oid, hashAlgo, { r, s }, m, publicKey, hashed) {
9289
9349
  const curve = new CurveWithOID(oid);
9290
9350
  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
9351
  const RS = util.concatUint8Array([r, s]);
9298
9352
  return verify$9(enums.publicKey.ed25519, hashAlgo, { RS }, m, publicKey.subarray(1), hashed);
9299
9353
  }
@@ -10151,7 +10205,7 @@ async function generate$4(algo) {
10151
10205
  async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10152
10206
  const { eccKeyShare, eccCipherText } = await encaps$1(algo, eccPublicKey);
10153
10207
  const { mlkemKeyShare, mlkemCipherText } = await encaps(algo, mlkemPublicKey);
10154
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10208
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10155
10209
  const wrappedKey = await wrap(enums.symmetric.aes256, kek, sessioneKeyData); // C
10156
10210
  return { eccCipherText, mlkemCipherText, wrappedKey };
10157
10211
  }
@@ -10159,25 +10213,24 @@ async function encrypt$2(algo, eccPublicKey, mlkemPublicKey, sessioneKeyData) {
10159
10213
  async function decrypt$2(algo, eccCipherText, mlkemCipherText, eccSecretKey, eccPublicKey, mlkemSecretKey, mlkemPublicKey, encryptedSessionKeyData) {
10160
10214
  const eccKeyShare = await decaps$1(algo, eccCipherText, eccSecretKey, eccPublicKey);
10161
10215
  const mlkemKeyShare = await decaps(algo, mlkemCipherText, mlkemSecretKey);
10162
- const kek = await multiKeyCombine(algo, eccKeyShare, eccCipherText, eccPublicKey, mlkemKeyShare, mlkemCipherText, mlkemPublicKey);
10216
+ const kek = await multiKeyCombine(algo, mlkemKeyShare, eccKeyShare, eccCipherText, eccPublicKey);
10163
10217
  const sessionKey = await unwrap(enums.symmetric.aes256, kek, encryptedSessionKeyData);
10164
10218
  return sessionKey;
10165
10219
  }
10166
10220
 
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
10221
+ /**
10222
+ * KEM key combiner
10223
+ */
10224
+ async function multiKeyCombine(algo, mlkemKeyShare, ecdhKeyShare, ecdhCipherText, ecdhPublicKey) {
10225
+ const domSep = util.encodeUTF8('OpenPGPCompositeKDFv1');
10171
10226
  const encData = util.concatUint8Array([
10172
10227
  mlkemKeyShare,
10173
10228
  ecdhKeyShare,
10174
10229
  ecdhCipherText,
10175
10230
  ecdhPublicKey,
10176
- // domSep
10177
- mlkemCipherText,
10178
- mlkemPublicKey,
10179
10231
  new Uint8Array([algo]),
10180
- util.encodeUTF8('OpenPGPCompositeKDFv1')
10232
+ domSep,
10233
+ new Uint8Array([domSep.length])
10181
10234
  ]);
10182
10235
 
10183
10236
  const kek = await computeDigest(enums.hash.sha3_256, encData);
@@ -10314,12 +10367,6 @@ async function generate$1(algo) {
10314
10367
  }
10315
10368
 
10316
10369
  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
10370
  switch (signatureAlgo) {
10324
10371
  case enums.publicKey.pqc_mldsa_ed25519: {
10325
10372
  const { eccSignature } = await sign$3(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest);
@@ -10333,12 +10380,6 @@ async function sign$2(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsa
10333
10380
  }
10334
10381
 
10335
10382
  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
10383
  switch (signatureAlgo) {
10343
10384
  case enums.publicKey.pqc_mldsa_ed25519: {
10344
10385
  const eccVerifiedPromise = verify$3(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature);
@@ -10351,11 +10392,12 @@ async function verify$2(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, d
10351
10392
  }
10352
10393
  }
10353
10394
 
10354
- function getRequiredHashAlgo(signatureAlgo) {
10355
- // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc#section-5.2.1.
10395
+ function isCompatibleHashAlgo(signatureAlgo, hashAlgo) {
10396
+ // The signature hash algo MUST have digest larger than 256 bits
10397
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
10356
10398
  switch (signatureAlgo) {
10357
10399
  case enums.publicKey.pqc_mldsa_ed25519:
10358
- return enums.hash.sha3_256;
10400
+ return getHashByteLength(hashAlgo) >= 32;
10359
10401
  default:
10360
10402
  throw new Error('Unsupported signature algorithm');
10361
10403
  }
@@ -12478,6 +12520,12 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12478
12520
  return verify$8(oid, hashAlgo, { r, s }, data, Q, hashed);
12479
12521
  }
12480
12522
  case enums.publicKey.eddsaLegacy: {
12523
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12524
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12525
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12526
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12527
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12528
+ }
12481
12529
  const { oid, Q } = publicParams;
12482
12530
  const curveSize = new CurveWithOID(oid).payloadSize;
12483
12531
  // When dealing little-endian MPI data, we always need to left-pad it, as done with big-endian values:
@@ -12488,6 +12536,13 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12488
12536
  }
12489
12537
  case enums.publicKey.ed25519:
12490
12538
  case enums.publicKey.ed448: {
12539
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12540
+ // Enforce digest sizes:
12541
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12542
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12543
+ throw new Error('Hash algorithm too weak for EdDSA.');
12544
+ }
12545
+
12491
12546
  const { A } = publicParams;
12492
12547
  return verify$9(algo, hashAlgo, signature, data, A, hashed);
12493
12548
  }
@@ -12500,6 +12555,11 @@ async function verify$1(algo, hashAlgo, signature, publicParams, privateParams,
12500
12555
  return verify$5(algo.getValue(), keyMaterial, signature.mac.data, hashed);
12501
12556
  }
12502
12557
  case enums.publicKey.pqc_mldsa_ed25519: {
12558
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12559
+ // The signature hash algo MUST have digest larger than 256 bits
12560
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12561
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12562
+ }
12503
12563
  const { eccPublicKey, mldsaPublicKey } = publicParams;
12504
12564
  return verify$2(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
12505
12565
  }
@@ -12548,12 +12608,24 @@ async function sign$1(algo, hashAlgo, publicKeyParams, privateKeyParams, data, h
12548
12608
  return sign$8(oid, hashAlgo, data, Q, d, hashed);
12549
12609
  }
12550
12610
  case enums.publicKey.eddsaLegacy: {
12611
+ if (getHashByteLength(hashAlgo) < getHashByteLength(enums.hash.sha256)) {
12612
+ // Enforce digest sizes, since the constraint was already present in RFC4880bis:
12613
+ // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
12614
+ // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
12615
+ throw new Error('Hash algorithm too weak for EdDSALegacy.');
12616
+ }
12551
12617
  const { oid, Q } = publicKeyParams;
12552
12618
  const { seed } = privateKeyParams;
12553
12619
  return sign$7(oid, hashAlgo, data, Q, seed, hashed);
12554
12620
  }
12555
12621
  case enums.publicKey.ed25519:
12556
12622
  case enums.publicKey.ed448: {
12623
+ if (getHashByteLength(hashAlgo) < getHashByteLength(getPreferredHashAlgo$2(algo))) {
12624
+ // Enforce digest sizes:
12625
+ // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
12626
+ // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
12627
+ throw new Error('Hash algorithm too weak for EdDSA.');
12628
+ }
12557
12629
  const { A } = publicKeyParams;
12558
12630
  const { seed } = privateKeyParams;
12559
12631
  return sign$9(algo, hashAlgo, data, A, seed, hashed);
@@ -12565,6 +12637,11 @@ async function sign$1(algo, hashAlgo, publicKeyParams, privateKeyParams, data, h
12565
12637
  return { mac: new ShortByteString(mac) };
12566
12638
  }
12567
12639
  case enums.publicKey.pqc_mldsa_ed25519: {
12640
+ if (!isCompatibleHashAlgo(algo, hashAlgo)) {
12641
+ // The signature hash algo MUST have digest larger than 256 bits
12642
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4
12643
+ throw new Error('Unexpected hash algorithm for PQC signature: digest size too short');
12644
+ }
12568
12645
  const { eccPublicKey } = publicKeyParams;
12569
12646
  const { eccSecretKey, mldsaSecretKey } = privateKeyParams;
12570
12647
  return sign$2(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
@@ -16833,12 +16910,8 @@ class PublicKeyPacket {
16833
16910
  throw new Error('Legacy curve25519 cannot be used with v6 keys');
16834
16911
  }
16835
16912
  // 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');
16913
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
16914
+ throw new Error('Unexpected key version: ML-DSA algorithms can only be used with v6 keys');
16842
16915
  }
16843
16916
  this.publicParams = publicParams;
16844
16917
  pos += read;
@@ -17838,11 +17911,8 @@ class SecretKeyPacket extends PublicKeyPacket {
17838
17911
  )) {
17839
17912
  throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`);
17840
17913
  }
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`);
17914
+ if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) {
17915
+ throw new Error(`Cannot generate v${this.version} signing keys of type 'pqc'. Generate a v6 key instead`);
17846
17916
  }
17847
17917
  const { privateParams, publicParams } = await generateParams(this.algorithm, bits, curve, symmetric);
17848
17918
  this.privateParams = privateParams;
@@ -18346,12 +18416,6 @@ async function createBindingSignature(subkey, primaryKey, options, config) {
18346
18416
  * @async
18347
18417
  */
18348
18418
  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
18419
  /**
18356
18420
  * If `preferredSenderAlgo` appears in the prefs of all recipients, we pick it; otherwise, we use the
18357
18421
  * strongest supported algo (`defaultAlgo` is always implicitly supported by all keys).
@@ -18399,6 +18463,10 @@ async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Dat
18399
18463
  enums.publicKey.ed448
18400
18464
  ]);
18401
18465
 
18466
+ const pqcAlgos = new Set([
18467
+ enums.publicKey.pqc_mldsa_ed25519
18468
+ ]);
18469
+
18402
18470
  if (eccAlgos.has(signingKeyPacket.algorithm)) {
18403
18471
  // For ECC, the returned hash algo MUST be at least as strong as `preferredCurveHashAlgo`, see:
18404
18472
  // - ECDSA: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.2-5
@@ -18421,6 +18489,21 @@ async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Dat
18421
18489
  strongestSupportedAlgo :
18422
18490
  preferredCurveAlgo;
18423
18491
  }
18492
+ } else if (pqcAlgos.has(signingKeyPacket.algorithm)) {
18493
+ // For PQC, the returned hash algo MUST be at least 256 bit long, see:
18494
+ // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-10.html#section-9.4 .
18495
+ // Hence, we return the `preferredHashAlgo` as long as it's supported and long enough;
18496
+ // Otherwise, we look at the strongest supported algo, and ultimately fallback the default algo (SHA-256).
18497
+ const preferredSenderAlgoIsSupported = isSupportedHashAlgo(preferredSenderAlgo) && isCompatibleHashAlgo(signingKeyPacket.algorithm, preferredSenderAlgo);
18498
+
18499
+ if (preferredSenderAlgoIsSupported) {
18500
+ return preferredSenderAlgo;
18501
+ } else {
18502
+ const strongestSupportedAlgo = getStrongestSupportedHashAlgo();
18503
+ return isCompatibleHashAlgo(signingKeyPacket.algorithm, strongestSupportedAlgo) ?
18504
+ strongestSupportedAlgo :
18505
+ defaultAlgo;
18506
+ }
18424
18507
  }
18425
18508
 
18426
18509
  // `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.3",
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 {