simple-hpke 0.3.1 → 0.3.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/meta.json CHANGED
@@ -702,7 +702,7 @@
702
702
  "format": "esm"
703
703
  },
704
704
  "src/index.ts": {
705
- "bytes": 14331,
705
+ "bytes": 15697,
706
706
  "imports": [
707
707
  {
708
708
  "path": "node_modules/uint8arrays/dist/src/index.js",
@@ -738,7 +738,7 @@
738
738
  "format": "esm"
739
739
  },
740
740
  "src/util.ts": {
741
- "bytes": 17940,
741
+ "bytes": 22056,
742
742
  "imports": [
743
743
  {
744
744
  "path": "node_modules/uint8arrays/dist/src/index.js",
@@ -785,7 +785,7 @@
785
785
  "imports": [],
786
786
  "exports": [],
787
787
  "inputs": {},
788
- "bytes": 121397
788
+ "bytes": 128024
789
789
  },
790
790
  "dist/constants.js": {
791
791
  "imports": [],
@@ -896,16 +896,16 @@
896
896
  "bytesInOutput": 215
897
897
  },
898
898
  "src/index.ts": {
899
- "bytesInOutput": 5995
899
+ "bytesInOutput": 6418
900
900
  },
901
901
  "src/util.ts": {
902
- "bytesInOutput": 7314
902
+ "bytesInOutput": 8581
903
903
  },
904
904
  "src/constants.ts": {
905
905
  "bytesInOutput": 551
906
906
  }
907
907
  },
908
- "bytes": 52563
908
+ "bytes": 54253
909
909
  },
910
910
  "dist/errors.js.map": {
911
911
  "imports": [],
@@ -930,7 +930,7 @@
930
930
  "imports": [],
931
931
  "exports": [],
932
932
  "inputs": {},
933
- "bytes": 122106
933
+ "bytes": 128969
934
934
  },
935
935
  "dist/index.js": {
936
936
  "imports": [],
@@ -941,7 +941,8 @@
941
941
  "encrypt",
942
942
  "encryptKey",
943
943
  "keyId",
944
- "open"
944
+ "open",
945
+ "verifyKeyId"
945
946
  ],
946
947
  "entryPoint": "src/index.ts",
947
948
  "inputs": {
@@ -1036,19 +1037,19 @@
1036
1037
  "bytesInOutput": 551
1037
1038
  },
1038
1039
  "src/util.ts": {
1039
- "bytesInOutput": 8110
1040
+ "bytesInOutput": 9693
1040
1041
  },
1041
1042
  "src/index.ts": {
1042
- "bytesInOutput": 6166
1043
+ "bytesInOutput": 6589
1043
1044
  }
1044
1045
  },
1045
- "bytes": 53408
1046
+ "bytes": 55429
1046
1047
  },
1047
1048
  "dist/util.js.map": {
1048
1049
  "imports": [],
1049
1050
  "exports": [],
1050
1051
  "inputs": {},
1051
- "bytes": 121973
1052
+ "bytes": 128835
1052
1053
  },
1053
1054
  "dist/util.js": {
1054
1055
  "imports": [],
@@ -1065,7 +1066,8 @@
1065
1066
  "keyId",
1066
1067
  "labeledExpand",
1067
1068
  "labeledExtract",
1068
- "resolveRecipientPublicKey"
1069
+ "resolveRecipientPublicKey",
1070
+ "verifyKeyId"
1069
1071
  ],
1070
1072
  "entryPoint": "src/util.ts",
1071
1073
  "inputs": {
@@ -1160,13 +1162,13 @@
1160
1162
  "bytesInOutput": 551
1161
1163
  },
1162
1164
  "src/index.ts": {
1163
- "bytesInOutput": 5995
1165
+ "bytesInOutput": 6418
1164
1166
  },
1165
1167
  "src/util.ts": {
1166
- "bytesInOutput": 8126
1168
+ "bytesInOutput": 9709
1167
1169
  }
1168
1170
  },
1169
- "bytes": 53353
1171
+ "bytes": 55374
1170
1172
  }
1171
1173
  }
1172
1174
  }
package/dist/util.cjs CHANGED
@@ -33,7 +33,8 @@ __export(util_exports, {
33
33
  keyId: () => keyId,
34
34
  labeledExpand: () => labeledExpand,
35
35
  labeledExtract: () => labeledExtract,
36
- resolveRecipientPublicKey: () => resolveRecipientPublicKey
36
+ resolveRecipientPublicKey: () => resolveRecipientPublicKey,
37
+ verifyKeyId: () => verifyKeyId
37
38
  });
38
39
  module.exports = __toCommonJS(util_exports);
39
40
 
@@ -1413,7 +1414,16 @@ async function decryptToString(keypair, message, opts) {
1413
1414
  }
1414
1415
  __name(decryptToString, "decryptToString");
1415
1416
  async function decryptFromString(keypair, message, opts) {
1416
- const bytes = fromString2(message, "base64url");
1417
+ let bytes;
1418
+ try {
1419
+ bytes = fromString2(message, "base64url");
1420
+ } catch (err) {
1421
+ throw new HpkeError(
1422
+ "ERR_MALFORMED_MESSAGE",
1423
+ "malformed message: not base64url",
1424
+ { cause: err }
1425
+ );
1426
+ }
1417
1427
  const plaintext = await decryptBytes(
1418
1428
  keypair,
1419
1429
  bytes,
@@ -1541,6 +1551,12 @@ async function encryptKey(recipient, aesKey, opts) {
1541
1551
  validateRawKeyBytes(aesKey);
1542
1552
  keyBytes = aesKey;
1543
1553
  } else if (aesKey) {
1554
+ if (aesKey.algorithm.name !== "AES-GCM") {
1555
+ throw new HpkeError(
1556
+ "ERR_INVALID_AES_KEY",
1557
+ "aesKey must be an AES-GCM key, got " + aesKey.algorithm.name
1558
+ );
1559
+ }
1544
1560
  keyBytes = await exportAesKeyBytes(aesKey);
1545
1561
  validateRawKeyBytes(keyBytes);
1546
1562
  } else {
@@ -1555,6 +1571,7 @@ async function encryptKey(recipient, aesKey, opts) {
1555
1571
  const { key, baseNonce } = await keySchedule(sharedSecret, info);
1556
1572
  const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
1557
1573
  const wrapped = concat2(enc, ciphertext);
1574
+ if (aesKey instanceof CryptoKey) return { wrapped, key: aesKey };
1558
1575
  const aesGcmKey = await importAesKey(
1559
1576
  keyBytes,
1560
1577
  opts?.extractable ?? false
@@ -1607,6 +1624,12 @@ async function aeadOpen(key, nonce, ciphertext) {
1607
1624
  }
1608
1625
  __name(aeadOpen, "aeadOpen");
1609
1626
  async function encryptBytes(recipient, message, aesKey, opts) {
1627
+ if (aesKey instanceof CryptoKey && !aesKey.usages.includes("encrypt")) {
1628
+ throw new HpkeError(
1629
+ "ERR_INVALID_AES_KEY",
1630
+ 'aesKey is missing the "encrypt" usage, which encrypt needs to encrypt the message'
1631
+ );
1632
+ }
1610
1633
  const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
1611
1634
  const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
1612
1635
  const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
@@ -1705,37 +1728,28 @@ async function resolveRecipientPublicKey(recipient) {
1705
1728
  return importRawPublic(recipient);
1706
1729
  }
1707
1730
  if (recipient instanceof CryptoKey) {
1708
- if (recipient.type !== "public") {
1709
- throw new HpkeError(
1710
- "ERR_INVALID_RECIPIENT_KEY",
1711
- "recipient CryptoKey must be a public key"
1712
- );
1713
- }
1714
- assertX25519Algorithm(
1715
- recipient,
1716
- "recipient CryptoKey",
1717
- "ERR_INVALID_RECIPIENT_KEY"
1718
- );
1719
- return recipient;
1731
+ return assertRecipientPublicKey(recipient, "recipient CryptoKey");
1720
1732
  }
1721
1733
  if (typeof recipient.publicKey === "string") {
1722
1734
  const { publicKey, encoding } = recipient;
1723
- return importRawPublic(fromString2(publicKey, encoding ?? "base64url"));
1724
- }
1725
- const pair = recipient;
1726
- if (pair.publicKey instanceof CryptoKey) {
1727
- if (pair.publicKey.type !== "public") {
1735
+ let decoded;
1736
+ try {
1737
+ decoded = fromString2(publicKey, encoding ?? "base64url");
1738
+ } catch (err) {
1728
1739
  throw new HpkeError(
1729
1740
  "ERR_INVALID_RECIPIENT_KEY",
1730
- "recipient keypair publicKey must be a public key"
1741
+ `recipient publicKey string does not decode as ${encoding ?? "base64url"}`,
1742
+ { cause: err }
1731
1743
  );
1732
1744
  }
1733
- assertX25519Algorithm(
1745
+ return importRawPublic(decoded);
1746
+ }
1747
+ const pair = recipient;
1748
+ if (pair.publicKey instanceof CryptoKey) {
1749
+ return assertRecipientPublicKey(
1734
1750
  pair.publicKey,
1735
- "recipient keypair publicKey",
1736
- "ERR_INVALID_RECIPIENT_KEY"
1751
+ "recipient keypair publicKey"
1737
1752
  );
1738
- return pair.publicKey;
1739
1753
  }
1740
1754
  throw new HpkeError(
1741
1755
  "ERR_INVALID_RECIPIENT_KEY",
@@ -1743,6 +1757,23 @@ async function resolveRecipientPublicKey(recipient) {
1743
1757
  );
1744
1758
  }
1745
1759
  __name(resolveRecipientPublicKey, "resolveRecipientPublicKey");
1760
+ function assertRecipientPublicKey(key, context) {
1761
+ if (key.type !== "public") {
1762
+ throw new HpkeError(
1763
+ "ERR_INVALID_RECIPIENT_KEY",
1764
+ `${context} must be a public key`
1765
+ );
1766
+ }
1767
+ assertX25519Algorithm(key, context, "ERR_INVALID_RECIPIENT_KEY");
1768
+ if (!key.extractable) {
1769
+ throw new HpkeError(
1770
+ "ERR_INVALID_RECIPIENT_KEY",
1771
+ `${context} must be extractable (the KEM exports it)`
1772
+ );
1773
+ }
1774
+ return key;
1775
+ }
1776
+ __name(assertRecipientPublicKey, "assertRecipientPublicKey");
1746
1777
  function assertX25519Algorithm(key, context, code2) {
1747
1778
  if (key.algorithm.name !== "X25519") {
1748
1779
  throw new HpkeError(
@@ -1753,11 +1784,23 @@ function assertX25519Algorithm(key, context, code2) {
1753
1784
  }
1754
1785
  __name(assertX25519Algorithm, "assertX25519Algorithm");
1755
1786
  async function dh(priv, pub) {
1756
- const bits = await subtle2.deriveBits(
1757
- { name: "X25519", public: pub },
1758
- priv,
1759
- 256
1760
- );
1787
+ let bits;
1788
+ try {
1789
+ bits = await subtle2.deriveBits(
1790
+ { name: "X25519", public: pub },
1791
+ priv,
1792
+ 256
1793
+ );
1794
+ } catch (err) {
1795
+ if (err?.name === "OperationError") {
1796
+ throw new HpkeError(
1797
+ "ERR_SMALL_ORDER_KEY",
1798
+ "X25519 deriveBits rejected a small-order public key",
1799
+ { cause: err }
1800
+ );
1801
+ }
1802
+ throw err;
1803
+ }
1761
1804
  const bytes = new Uint8Array(bits);
1762
1805
  if (bytes.every((b) => b === 0)) {
1763
1806
  throw new HpkeError(
@@ -1810,6 +1853,23 @@ async function decap(enc, keypair) {
1810
1853
  "keypair.privateKey",
1811
1854
  "ERR_INVALID_KEYPAIR"
1812
1855
  );
1856
+ if (!keypair.privateKey.usages.includes("deriveBits")) {
1857
+ throw new HpkeError(
1858
+ "ERR_INVALID_KEYPAIR",
1859
+ "keypair.privateKey must have the deriveBits usage"
1860
+ );
1861
+ }
1862
+ if (keypair.publicKey.type !== "public" || !keypair.publicKey.extractable) {
1863
+ throw new HpkeError(
1864
+ "ERR_INVALID_KEYPAIR",
1865
+ "keypair.publicKey must be an extractable public key"
1866
+ );
1867
+ }
1868
+ assertX25519Algorithm(
1869
+ keypair.publicKey,
1870
+ "keypair.publicKey",
1871
+ "ERR_INVALID_KEYPAIR"
1872
+ );
1813
1873
  const pkE = await subtle2.importKey(
1814
1874
  "raw",
1815
1875
  enc,
@@ -1852,6 +1912,16 @@ async function keyId(key) {
1852
1912
  return toString2(new Uint8Array(digest2), "base64url");
1853
1913
  }
1854
1914
  __name(keyId, "keyId");
1915
+ async function verifyKeyId(key, expected) {
1916
+ const actual = await keyId(key);
1917
+ if (actual.length !== expected.length) return false;
1918
+ let diff = 0;
1919
+ for (let i = 0; i < actual.length; i++) {
1920
+ diff |= actual.charCodeAt(i) ^ expected.charCodeAt(i);
1921
+ }
1922
+ return diff === 0;
1923
+ }
1924
+ __name(verifyKeyId, "verifyKeyId");
1855
1925
  function i2osp(n, len) {
1856
1926
  const out = new Uint8Array(len);
1857
1927
  let v = n;