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/README.md +82 -14
- package/dist/constants.cjs +87 -28
- package/dist/constants.cjs.map +2 -2
- package/dist/constants.js +87 -28
- package/dist/constants.js.map +2 -2
- package/dist/constants.min.cjs +1 -1
- package/dist/constants.min.cjs.map +3 -3
- package/dist/constants.min.js +1 -1
- package/dist/constants.min.js.map +3 -3
- package/dist/index.cjs +99 -29
- package/dist/index.cjs.map +2 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +99 -29
- package/dist/index.js.map +2 -2
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.cjs.map +3 -3
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +3 -3
- package/dist/meta.json +18 -16
- package/dist/util.cjs +99 -29
- package/dist/util.cjs.map +2 -2
- package/dist/util.d.ts +24 -2
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +99 -29
- package/dist/util.js.map +2 -2
- package/dist/util.min.cjs +1 -1
- package/dist/util.min.cjs.map +3 -3
- package/dist/util.min.js +1 -1
- package/dist/util.min.js.map +3 -3
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -27,7 +27,8 @@ __export(index_exports, {
|
|
|
27
27
|
encrypt: () => encrypt,
|
|
28
28
|
encryptKey: () => encryptKey,
|
|
29
29
|
keyId: () => keyId,
|
|
30
|
-
open: () => open
|
|
30
|
+
open: () => open,
|
|
31
|
+
verifyKeyId: () => verifyKeyId
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(index_exports);
|
|
33
34
|
|
|
@@ -1391,6 +1392,12 @@ async function aeadOpen(key, nonce, ciphertext) {
|
|
|
1391
1392
|
}
|
|
1392
1393
|
__name(aeadOpen, "aeadOpen");
|
|
1393
1394
|
async function encryptBytes(recipient, message, aesKey, opts) {
|
|
1395
|
+
if (aesKey instanceof CryptoKey && !aesKey.usages.includes("encrypt")) {
|
|
1396
|
+
throw new HpkeError(
|
|
1397
|
+
"ERR_INVALID_AES_KEY",
|
|
1398
|
+
'aesKey is missing the "encrypt" usage, which encrypt needs to encrypt the message'
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1394
1401
|
const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
1395
1402
|
const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
|
|
1396
1403
|
const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
|
|
@@ -1489,37 +1496,28 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1489
1496
|
return importRawPublic(recipient);
|
|
1490
1497
|
}
|
|
1491
1498
|
if (recipient instanceof CryptoKey) {
|
|
1492
|
-
|
|
1493
|
-
throw new HpkeError(
|
|
1494
|
-
"ERR_INVALID_RECIPIENT_KEY",
|
|
1495
|
-
"recipient CryptoKey must be a public key"
|
|
1496
|
-
);
|
|
1497
|
-
}
|
|
1498
|
-
assertX25519Algorithm(
|
|
1499
|
-
recipient,
|
|
1500
|
-
"recipient CryptoKey",
|
|
1501
|
-
"ERR_INVALID_RECIPIENT_KEY"
|
|
1502
|
-
);
|
|
1503
|
-
return recipient;
|
|
1499
|
+
return assertRecipientPublicKey(recipient, "recipient CryptoKey");
|
|
1504
1500
|
}
|
|
1505
1501
|
if (typeof recipient.publicKey === "string") {
|
|
1506
1502
|
const { publicKey, encoding } = recipient;
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
if (pair.publicKey.type !== "public") {
|
|
1503
|
+
let decoded;
|
|
1504
|
+
try {
|
|
1505
|
+
decoded = fromString2(publicKey, encoding ?? "base64url");
|
|
1506
|
+
} catch (err) {
|
|
1512
1507
|
throw new HpkeError(
|
|
1513
1508
|
"ERR_INVALID_RECIPIENT_KEY",
|
|
1514
|
-
|
|
1509
|
+
`recipient publicKey string does not decode as ${encoding ?? "base64url"}`,
|
|
1510
|
+
{ cause: err }
|
|
1515
1511
|
);
|
|
1516
1512
|
}
|
|
1517
|
-
|
|
1513
|
+
return importRawPublic(decoded);
|
|
1514
|
+
}
|
|
1515
|
+
const pair = recipient;
|
|
1516
|
+
if (pair.publicKey instanceof CryptoKey) {
|
|
1517
|
+
return assertRecipientPublicKey(
|
|
1518
1518
|
pair.publicKey,
|
|
1519
|
-
"recipient keypair publicKey"
|
|
1520
|
-
"ERR_INVALID_RECIPIENT_KEY"
|
|
1519
|
+
"recipient keypair publicKey"
|
|
1521
1520
|
);
|
|
1522
|
-
return pair.publicKey;
|
|
1523
1521
|
}
|
|
1524
1522
|
throw new HpkeError(
|
|
1525
1523
|
"ERR_INVALID_RECIPIENT_KEY",
|
|
@@ -1527,6 +1525,23 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1527
1525
|
);
|
|
1528
1526
|
}
|
|
1529
1527
|
__name(resolveRecipientPublicKey, "resolveRecipientPublicKey");
|
|
1528
|
+
function assertRecipientPublicKey(key, context) {
|
|
1529
|
+
if (key.type !== "public") {
|
|
1530
|
+
throw new HpkeError(
|
|
1531
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1532
|
+
`${context} must be a public key`
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
assertX25519Algorithm(key, context, "ERR_INVALID_RECIPIENT_KEY");
|
|
1536
|
+
if (!key.extractable) {
|
|
1537
|
+
throw new HpkeError(
|
|
1538
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1539
|
+
`${context} must be extractable (the KEM exports it)`
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
return key;
|
|
1543
|
+
}
|
|
1544
|
+
__name(assertRecipientPublicKey, "assertRecipientPublicKey");
|
|
1530
1545
|
function assertX25519Algorithm(key, context, code2) {
|
|
1531
1546
|
if (key.algorithm.name !== "X25519") {
|
|
1532
1547
|
throw new HpkeError(
|
|
@@ -1537,11 +1552,23 @@ function assertX25519Algorithm(key, context, code2) {
|
|
|
1537
1552
|
}
|
|
1538
1553
|
__name(assertX25519Algorithm, "assertX25519Algorithm");
|
|
1539
1554
|
async function dh(priv, pub) {
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1555
|
+
let bits;
|
|
1556
|
+
try {
|
|
1557
|
+
bits = await subtle.deriveBits(
|
|
1558
|
+
{ name: "X25519", public: pub },
|
|
1559
|
+
priv,
|
|
1560
|
+
256
|
|
1561
|
+
);
|
|
1562
|
+
} catch (err) {
|
|
1563
|
+
if (err?.name === "OperationError") {
|
|
1564
|
+
throw new HpkeError(
|
|
1565
|
+
"ERR_SMALL_ORDER_KEY",
|
|
1566
|
+
"X25519 deriveBits rejected a small-order public key",
|
|
1567
|
+
{ cause: err }
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
1570
|
+
throw err;
|
|
1571
|
+
}
|
|
1545
1572
|
const bytes = new Uint8Array(bits);
|
|
1546
1573
|
if (bytes.every((b) => b === 0)) {
|
|
1547
1574
|
throw new HpkeError(
|
|
@@ -1594,6 +1621,23 @@ async function decap(enc, keypair) {
|
|
|
1594
1621
|
"keypair.privateKey",
|
|
1595
1622
|
"ERR_INVALID_KEYPAIR"
|
|
1596
1623
|
);
|
|
1624
|
+
if (!keypair.privateKey.usages.includes("deriveBits")) {
|
|
1625
|
+
throw new HpkeError(
|
|
1626
|
+
"ERR_INVALID_KEYPAIR",
|
|
1627
|
+
"keypair.privateKey must have the deriveBits usage"
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
if (keypair.publicKey.type !== "public" || !keypair.publicKey.extractable) {
|
|
1631
|
+
throw new HpkeError(
|
|
1632
|
+
"ERR_INVALID_KEYPAIR",
|
|
1633
|
+
"keypair.publicKey must be an extractable public key"
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1636
|
+
assertX25519Algorithm(
|
|
1637
|
+
keypair.publicKey,
|
|
1638
|
+
"keypair.publicKey",
|
|
1639
|
+
"ERR_INVALID_KEYPAIR"
|
|
1640
|
+
);
|
|
1597
1641
|
const pkE = await subtle.importKey(
|
|
1598
1642
|
"raw",
|
|
1599
1643
|
enc,
|
|
@@ -1636,6 +1680,16 @@ async function keyId(key) {
|
|
|
1636
1680
|
return toString2(new Uint8Array(digest2), "base64url");
|
|
1637
1681
|
}
|
|
1638
1682
|
__name(keyId, "keyId");
|
|
1683
|
+
async function verifyKeyId(key, expected) {
|
|
1684
|
+
const actual = await keyId(key);
|
|
1685
|
+
if (actual.length !== expected.length) return false;
|
|
1686
|
+
let diff = 0;
|
|
1687
|
+
for (let i = 0; i < actual.length; i++) {
|
|
1688
|
+
diff |= actual.charCodeAt(i) ^ expected.charCodeAt(i);
|
|
1689
|
+
}
|
|
1690
|
+
return diff === 0;
|
|
1691
|
+
}
|
|
1692
|
+
__name(verifyKeyId, "verifyKeyId");
|
|
1639
1693
|
function i2osp(n, len) {
|
|
1640
1694
|
const out = new Uint8Array(len);
|
|
1641
1695
|
let v = n;
|
|
@@ -1724,7 +1778,16 @@ async function decryptToString(keypair, message, opts) {
|
|
|
1724
1778
|
}
|
|
1725
1779
|
__name(decryptToString, "decryptToString");
|
|
1726
1780
|
async function decryptFromString(keypair, message, opts) {
|
|
1727
|
-
|
|
1781
|
+
let bytes;
|
|
1782
|
+
try {
|
|
1783
|
+
bytes = fromString2(message, "base64url");
|
|
1784
|
+
} catch (err) {
|
|
1785
|
+
throw new HpkeError(
|
|
1786
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1787
|
+
"malformed message: not base64url",
|
|
1788
|
+
{ cause: err }
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1728
1791
|
const plaintext = await decryptBytes(
|
|
1729
1792
|
keypair,
|
|
1730
1793
|
bytes,
|
|
@@ -1852,6 +1915,12 @@ async function encryptKey(recipient, aesKey, opts) {
|
|
|
1852
1915
|
validateRawKeyBytes(aesKey);
|
|
1853
1916
|
keyBytes = aesKey;
|
|
1854
1917
|
} else if (aesKey) {
|
|
1918
|
+
if (aesKey.algorithm.name !== "AES-GCM") {
|
|
1919
|
+
throw new HpkeError(
|
|
1920
|
+
"ERR_INVALID_AES_KEY",
|
|
1921
|
+
"aesKey must be an AES-GCM key, got " + aesKey.algorithm.name
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1855
1924
|
keyBytes = await exportAesKeyBytes(aesKey);
|
|
1856
1925
|
validateRawKeyBytes(keyBytes);
|
|
1857
1926
|
} else {
|
|
@@ -1866,6 +1935,7 @@ async function encryptKey(recipient, aesKey, opts) {
|
|
|
1866
1935
|
const { key, baseNonce } = await keySchedule(sharedSecret, info);
|
|
1867
1936
|
const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
|
|
1868
1937
|
const wrapped = concat2(enc, ciphertext);
|
|
1938
|
+
if (aesKey instanceof CryptoKey) return { wrapped, key: aesKey };
|
|
1869
1939
|
const aesGcmKey = await importAesKey(
|
|
1870
1940
|
keyBytes,
|
|
1871
1941
|
opts?.extractable ?? false
|