simple-hpke 0.2.0 → 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 +207 -27
- package/dist/constants.cjs +188 -47
- package/dist/constants.cjs.map +4 -4
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +188 -47
- package/dist/constants.js.map +4 -4
- package/dist/constants.min.cjs +1 -1
- package/dist/constants.min.cjs.map +4 -4
- package/dist/constants.min.js +1 -1
- package/dist/constants.min.js.map +4 -4
- package/dist/errors.cjs +38 -0
- package/dist/errors.cjs.map +7 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +7 -0
- package/dist/errors.min.cjs +2 -0
- package/dist/errors.min.cjs.map +7 -0
- package/dist/errors.min.js +2 -0
- package/dist/errors.min.js.map +7 -0
- package/dist/index.cjs +231 -50
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +34 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +231 -50
- package/dist/index.js.map +4 -4
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.cjs.map +4 -4
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/meta.json +81 -20
- package/dist/util.cjs +228 -48
- package/dist/util.cjs.map +4 -4
- package/dist/util.d.ts +56 -3
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +228 -48
- package/dist/util.js.map +4 -4
- package/dist/util.min.cjs +1 -1
- package/dist/util.min.cjs.map +4 -4
- package/dist/util.min.js +1 -1
- package/dist/util.min.js.map +4 -4
- package/package.json +6 -5
package/dist/constants.d.ts
CHANGED
|
@@ -11,4 +11,6 @@ export declare const HPKE_V1: Uint8Array<ArrayBuffer>;
|
|
|
11
11
|
export declare const WRAPPED_LEN_PREFIX = 2;
|
|
12
12
|
export declare const KEM_SUITE_ID: Uint8Array<ArrayBufferLike>;
|
|
13
13
|
export declare const HPKE_SUITE_ID: Uint8Array<ArrayBufferLike>;
|
|
14
|
+
export declare const KEY_ID_LABEL: Uint8Array<ArrayBuffer>;
|
|
15
|
+
export declare const KEY_ID_NONCE_LENGTH = 12;
|
|
14
16
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,MAAM,KAAS,CAAA;AAC5B,eAAO,MAAM,MAAM,IAAS,CAAA;AAC5B,eAAO,MAAM,OAAO,IAAS,CAAA;AAI7B,eAAO,MAAM,OAAO,KAAK,CAAA;AACzB,eAAO,MAAM,EAAE,KAAK,CAAA;AACpB,eAAO,MAAM,EAAE,KAAK,CAAA;AACpB,eAAO,MAAM,UAAU,KAAK,CAAA;AAC5B,eAAO,MAAM,eAAe,KAAK,CAAA;AAGjC,eAAO,MAAM,SAAS,IAAO,CAAA;AAE7B,eAAO,MAAM,OAAO,yBAAsC,CAAA;AAK1D,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAGnC,eAAO,MAAM,YAAY,6BAGxB,CAAA;AAID,eAAO,MAAM,aAAa,6BAKzB,CAAA"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,MAAM,KAAS,CAAA;AAC5B,eAAO,MAAM,MAAM,IAAS,CAAA;AAC5B,eAAO,MAAM,OAAO,IAAS,CAAA;AAI7B,eAAO,MAAM,OAAO,KAAK,CAAA;AACzB,eAAO,MAAM,EAAE,KAAK,CAAA;AACpB,eAAO,MAAM,EAAE,KAAK,CAAA;AACpB,eAAO,MAAM,UAAU,KAAK,CAAA;AAC5B,eAAO,MAAM,eAAe,KAAK,CAAA;AAGjC,eAAO,MAAM,SAAS,IAAO,CAAA;AAE7B,eAAO,MAAM,OAAO,yBAAsC,CAAA;AAK1D,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAGnC,eAAO,MAAM,YAAY,6BAGxB,CAAA;AAID,eAAO,MAAM,aAAa,6BAKzB,CAAA;AAUD,eAAO,MAAM,YAAY,yBACU,CAAA;AAMnC,eAAO,MAAM,mBAAmB,KAAK,CAAA"}
|
package/dist/constants.js
CHANGED
|
@@ -1283,6 +1283,19 @@ function toString2(array, encoding = "utf8") {
|
|
|
1283
1283
|
}
|
|
1284
1284
|
__name(toString2, "toString");
|
|
1285
1285
|
|
|
1286
|
+
// src/errors.ts
|
|
1287
|
+
var HpkeError = class extends Error {
|
|
1288
|
+
static {
|
|
1289
|
+
__name(this, "HpkeError");
|
|
1290
|
+
}
|
|
1291
|
+
code;
|
|
1292
|
+
constructor(code2, message, opts) {
|
|
1293
|
+
super(message, opts);
|
|
1294
|
+
this.name = "HpkeError";
|
|
1295
|
+
this.code = code2;
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1286
1299
|
// src/index.ts
|
|
1287
1300
|
var subtle = globalThis.crypto.subtle;
|
|
1288
1301
|
var open = Object.assign(openBytes, {
|
|
@@ -1297,13 +1310,19 @@ var decrypt = Object.assign(decryptBytes, {
|
|
|
1297
1310
|
});
|
|
1298
1311
|
async function decryptBytes(keypair, message, opts) {
|
|
1299
1312
|
if (message.byteLength < WRAPPED_LEN_PREFIX) {
|
|
1300
|
-
throw new
|
|
1313
|
+
throw new HpkeError(
|
|
1314
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1315
|
+
"malformed message: too short"
|
|
1316
|
+
);
|
|
1301
1317
|
}
|
|
1302
1318
|
const wrappedLen = message[0] << 8 | message[1];
|
|
1303
1319
|
const ivStart = WRAPPED_LEN_PREFIX + wrappedLen;
|
|
1304
1320
|
const ctStart = ivStart + NN;
|
|
1305
1321
|
if (message.byteLength < ctStart + AEAD_TAG_LENGTH) {
|
|
1306
|
-
throw new
|
|
1322
|
+
throw new HpkeError(
|
|
1323
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1324
|
+
"malformed message: too short"
|
|
1325
|
+
);
|
|
1307
1326
|
}
|
|
1308
1327
|
const wrapped = message.slice(WRAPPED_LEN_PREFIX, ivStart);
|
|
1309
1328
|
const iv = message.slice(ivStart, ctStart);
|
|
@@ -1314,11 +1333,20 @@ async function decryptBytes(keypair, message, opts) {
|
|
|
1314
1333
|
opts?.info !== void 0 ? { info: opts.info } : void 0
|
|
1315
1334
|
);
|
|
1316
1335
|
const key = await importAesKeyDecryptOnly(keyBytes);
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1336
|
+
let pt;
|
|
1337
|
+
try {
|
|
1338
|
+
pt = await subtle.decrypt(
|
|
1339
|
+
{ name: "AES-GCM", iv },
|
|
1340
|
+
key,
|
|
1341
|
+
ciphertext
|
|
1342
|
+
);
|
|
1343
|
+
} catch (err) {
|
|
1344
|
+
throw new HpkeError(
|
|
1345
|
+
"ERR_DECRYPT_FAILED",
|
|
1346
|
+
"failed to decrypt the message: tampered ciphertext",
|
|
1347
|
+
{ cause: err }
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1322
1350
|
return new Uint8Array(pt);
|
|
1323
1351
|
}
|
|
1324
1352
|
__name(decryptBytes, "decryptBytes");
|
|
@@ -1328,7 +1356,16 @@ async function decryptToString(keypair, message, opts) {
|
|
|
1328
1356
|
}
|
|
1329
1357
|
__name(decryptToString, "decryptToString");
|
|
1330
1358
|
async function decryptFromString(keypair, message, opts) {
|
|
1331
|
-
|
|
1359
|
+
let bytes;
|
|
1360
|
+
try {
|
|
1361
|
+
bytes = fromString2(message, "base64url");
|
|
1362
|
+
} catch (err) {
|
|
1363
|
+
throw new HpkeError(
|
|
1364
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1365
|
+
"malformed message: not base64url",
|
|
1366
|
+
{ cause: err }
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1332
1369
|
const plaintext = await decryptBytes(
|
|
1333
1370
|
keypair,
|
|
1334
1371
|
bytes,
|
|
@@ -1339,7 +1376,8 @@ async function decryptFromString(keypair, message, opts) {
|
|
|
1339
1376
|
__name(decryptFromString, "decryptFromString");
|
|
1340
1377
|
function validateKeysize(keysize) {
|
|
1341
1378
|
if (keysize !== 128 && keysize !== 256) {
|
|
1342
|
-
throw new
|
|
1379
|
+
throw new HpkeError(
|
|
1380
|
+
"ERR_INVALID_KEYSIZE",
|
|
1343
1381
|
`invalid keysize: ${keysize} (expected 128 or 256)`
|
|
1344
1382
|
);
|
|
1345
1383
|
}
|
|
@@ -1347,7 +1385,8 @@ function validateKeysize(keysize) {
|
|
|
1347
1385
|
__name(validateKeysize, "validateKeysize");
|
|
1348
1386
|
function validateRawKeyBytes(raw) {
|
|
1349
1387
|
if (raw.length !== 16 && raw.length !== 32) {
|
|
1350
|
-
throw new
|
|
1388
|
+
throw new HpkeError(
|
|
1389
|
+
"ERR_INVALID_AES_KEY",
|
|
1351
1390
|
`invalid aesKey length: ${raw.length} bytes (expected 16 or 32)`
|
|
1352
1391
|
);
|
|
1353
1392
|
}
|
|
@@ -1361,19 +1400,20 @@ function normalizeInfo(info) {
|
|
|
1361
1400
|
__name(normalizeInfo, "normalizeInfo");
|
|
1362
1401
|
async function exportAesKeyBytes(key) {
|
|
1363
1402
|
if (!key.extractable) {
|
|
1364
|
-
throw new
|
|
1403
|
+
throw new HpkeError(
|
|
1404
|
+
"ERR_INVALID_AES_KEY",
|
|
1365
1405
|
"aesKey must be extractable: its raw bytes are what get sealed"
|
|
1366
1406
|
);
|
|
1367
1407
|
}
|
|
1368
1408
|
return new Uint8Array(await subtle.exportKey("raw", key));
|
|
1369
1409
|
}
|
|
1370
1410
|
__name(exportAesKeyBytes, "exportAesKeyBytes");
|
|
1371
|
-
async function importAesKey(raw) {
|
|
1411
|
+
async function importAesKey(raw, extractable) {
|
|
1372
1412
|
return subtle.importKey(
|
|
1373
1413
|
"raw",
|
|
1374
1414
|
raw,
|
|
1375
1415
|
{ name: "AES-GCM" },
|
|
1376
|
-
|
|
1416
|
+
extractable,
|
|
1377
1417
|
["encrypt", "decrypt"]
|
|
1378
1418
|
);
|
|
1379
1419
|
}
|
|
@@ -1428,7 +1468,10 @@ async function keySchedule(sharedSecret, info) {
|
|
|
1428
1468
|
__name(keySchedule, "keySchedule");
|
|
1429
1469
|
async function openRawBytes(keypair, wrapped, opts) {
|
|
1430
1470
|
if (wrapped.byteLength < ENC_LENGTH + AEAD_TAG_LENGTH) {
|
|
1431
|
-
throw new
|
|
1471
|
+
throw new HpkeError(
|
|
1472
|
+
"ERR_MALFORMED_ENVELOPE",
|
|
1473
|
+
"malformed envelope: too short"
|
|
1474
|
+
);
|
|
1432
1475
|
}
|
|
1433
1476
|
const info = normalizeInfo(opts?.info);
|
|
1434
1477
|
const enc = wrapped.slice(0, ENC_LENGTH);
|
|
@@ -1440,7 +1483,7 @@ async function openRawBytes(keypair, wrapped, opts) {
|
|
|
1440
1483
|
__name(openRawBytes, "openRawBytes");
|
|
1441
1484
|
async function openBytes(keypair, wrapped, opts) {
|
|
1442
1485
|
const keyBytes = await openRawBytes(keypair, wrapped, opts);
|
|
1443
|
-
return importAesKey(keyBytes);
|
|
1486
|
+
return importAesKey(keyBytes, opts?.extractable ?? false);
|
|
1444
1487
|
}
|
|
1445
1488
|
__name(openBytes, "openBytes");
|
|
1446
1489
|
async function encryptKey(recipient, aesKey, opts) {
|
|
@@ -1450,6 +1493,12 @@ async function encryptKey(recipient, aesKey, opts) {
|
|
|
1450
1493
|
validateRawKeyBytes(aesKey);
|
|
1451
1494
|
keyBytes = aesKey;
|
|
1452
1495
|
} else if (aesKey) {
|
|
1496
|
+
if (aesKey.algorithm.name !== "AES-GCM") {
|
|
1497
|
+
throw new HpkeError(
|
|
1498
|
+
"ERR_INVALID_AES_KEY",
|
|
1499
|
+
"aesKey must be an AES-GCM key, got " + aesKey.algorithm.name
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1453
1502
|
keyBytes = await exportAesKeyBytes(aesKey);
|
|
1454
1503
|
validateRawKeyBytes(keyBytes);
|
|
1455
1504
|
} else {
|
|
@@ -1464,8 +1513,12 @@ async function encryptKey(recipient, aesKey, opts) {
|
|
|
1464
1513
|
const { key, baseNonce } = await keySchedule(sharedSecret, info);
|
|
1465
1514
|
const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
|
|
1466
1515
|
const wrapped = concat2(enc, ciphertext);
|
|
1467
|
-
|
|
1468
|
-
|
|
1516
|
+
if (aesKey instanceof CryptoKey) return { wrapped, key: aesKey };
|
|
1517
|
+
const aesGcmKey = await importAesKey(
|
|
1518
|
+
keyBytes,
|
|
1519
|
+
opts?.extractable ?? false
|
|
1520
|
+
);
|
|
1521
|
+
return { wrapped, key: aesGcmKey };
|
|
1469
1522
|
}
|
|
1470
1523
|
__name(encryptKey, "encryptKey");
|
|
1471
1524
|
|
|
@@ -1495,24 +1548,44 @@ async function aeadOpen(key, nonce, ciphertext) {
|
|
|
1495
1548
|
false,
|
|
1496
1549
|
["decrypt"]
|
|
1497
1550
|
);
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1551
|
+
let pt;
|
|
1552
|
+
try {
|
|
1553
|
+
pt = await subtle2.decrypt(
|
|
1554
|
+
{ name: "AES-GCM", iv: nonce },
|
|
1555
|
+
k,
|
|
1556
|
+
ciphertext
|
|
1557
|
+
);
|
|
1558
|
+
} catch (err) {
|
|
1559
|
+
throw new HpkeError(
|
|
1560
|
+
"ERR_DECRYPT_FAILED",
|
|
1561
|
+
"failed to decrypt the wrapped key: wrong keypair, wrong info, or a tampered envelope",
|
|
1562
|
+
{ cause: err }
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1503
1565
|
return new Uint8Array(pt);
|
|
1504
1566
|
}
|
|
1505
1567
|
__name(aeadOpen, "aeadOpen");
|
|
1506
1568
|
async function encryptBytes(recipient, message, aesKey, opts) {
|
|
1569
|
+
if (aesKey instanceof CryptoKey && !aesKey.usages.includes("encrypt")) {
|
|
1570
|
+
throw new HpkeError(
|
|
1571
|
+
"ERR_INVALID_AES_KEY",
|
|
1572
|
+
'aesKey is missing the "encrypt" usage, which encrypt needs to encrypt the message'
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1507
1575
|
const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
1508
|
-
const {
|
|
1576
|
+
const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
|
|
1509
1577
|
const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
|
|
1510
1578
|
const ct = new Uint8Array(await subtle2.encrypt(
|
|
1511
1579
|
{ name: "AES-GCM", iv },
|
|
1512
1580
|
key,
|
|
1513
1581
|
plaintext
|
|
1514
1582
|
));
|
|
1515
|
-
return concat2(
|
|
1583
|
+
return concat2(
|
|
1584
|
+
i2osp(wrapped.length, WRAPPED_LEN_PREFIX),
|
|
1585
|
+
wrapped,
|
|
1586
|
+
iv,
|
|
1587
|
+
ct
|
|
1588
|
+
);
|
|
1516
1589
|
}
|
|
1517
1590
|
__name(encryptBytes, "encryptBytes");
|
|
1518
1591
|
async function encryptToString(recipient, message, aesKey, opts) {
|
|
@@ -1578,7 +1651,8 @@ async function exportRawPublic(key) {
|
|
|
1578
1651
|
__name(exportRawPublic, "exportRawPublic");
|
|
1579
1652
|
async function importRawPublic(raw) {
|
|
1580
1653
|
if (raw.length !== ENC_LENGTH) {
|
|
1581
|
-
throw new
|
|
1654
|
+
throw new HpkeError(
|
|
1655
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1582
1656
|
`invalid public key length: ${raw.length} bytes (expected ${ENC_LENGTH})`
|
|
1583
1657
|
);
|
|
1584
1658
|
}
|
|
@@ -1596,44 +1670,83 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1596
1670
|
return importRawPublic(recipient);
|
|
1597
1671
|
}
|
|
1598
1672
|
if (recipient instanceof CryptoKey) {
|
|
1599
|
-
|
|
1600
|
-
throw new Error("recipient CryptoKey must be a public key");
|
|
1601
|
-
}
|
|
1602
|
-
assertX25519Algorithm(recipient, "recipient CryptoKey");
|
|
1603
|
-
return recipient;
|
|
1673
|
+
return assertRecipientPublicKey(recipient, "recipient CryptoKey");
|
|
1604
1674
|
}
|
|
1605
1675
|
if (typeof recipient.publicKey === "string") {
|
|
1606
1676
|
const { publicKey, encoding } = recipient;
|
|
1607
|
-
|
|
1677
|
+
let decoded;
|
|
1678
|
+
try {
|
|
1679
|
+
decoded = fromString2(publicKey, encoding ?? "base64url");
|
|
1680
|
+
} catch (err) {
|
|
1681
|
+
throw new HpkeError(
|
|
1682
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1683
|
+
`recipient publicKey string does not decode as ${encoding ?? "base64url"}`,
|
|
1684
|
+
{ cause: err }
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
return importRawPublic(decoded);
|
|
1608
1688
|
}
|
|
1609
1689
|
const pair = recipient;
|
|
1610
1690
|
if (pair.publicKey instanceof CryptoKey) {
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
return pair.publicKey;
|
|
1691
|
+
return assertRecipientPublicKey(
|
|
1692
|
+
pair.publicKey,
|
|
1693
|
+
"recipient keypair publicKey"
|
|
1694
|
+
);
|
|
1616
1695
|
}
|
|
1617
|
-
throw new
|
|
1696
|
+
throw new HpkeError(
|
|
1697
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1698
|
+
"unrecognized recipient key form"
|
|
1699
|
+
);
|
|
1618
1700
|
}
|
|
1619
1701
|
__name(resolveRecipientPublicKey, "resolveRecipientPublicKey");
|
|
1620
|
-
function
|
|
1702
|
+
function assertRecipientPublicKey(key, context) {
|
|
1703
|
+
if (key.type !== "public") {
|
|
1704
|
+
throw new HpkeError(
|
|
1705
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1706
|
+
`${context} must be a public key`
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1709
|
+
assertX25519Algorithm(key, context, "ERR_INVALID_RECIPIENT_KEY");
|
|
1710
|
+
if (!key.extractable) {
|
|
1711
|
+
throw new HpkeError(
|
|
1712
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1713
|
+
`${context} must be extractable (the KEM exports it)`
|
|
1714
|
+
);
|
|
1715
|
+
}
|
|
1716
|
+
return key;
|
|
1717
|
+
}
|
|
1718
|
+
__name(assertRecipientPublicKey, "assertRecipientPublicKey");
|
|
1719
|
+
function assertX25519Algorithm(key, context, code2) {
|
|
1621
1720
|
if (key.algorithm.name !== "X25519") {
|
|
1622
|
-
throw new
|
|
1721
|
+
throw new HpkeError(
|
|
1722
|
+
code2,
|
|
1623
1723
|
`${context} must be an X25519 key (got ${key.algorithm.name})`
|
|
1624
1724
|
);
|
|
1625
1725
|
}
|
|
1626
1726
|
}
|
|
1627
1727
|
__name(assertX25519Algorithm, "assertX25519Algorithm");
|
|
1628
1728
|
async function dh(priv, pub) {
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1729
|
+
let bits;
|
|
1730
|
+
try {
|
|
1731
|
+
bits = await subtle2.deriveBits(
|
|
1732
|
+
{ name: "X25519", public: pub },
|
|
1733
|
+
priv,
|
|
1734
|
+
256
|
|
1735
|
+
);
|
|
1736
|
+
} catch (err) {
|
|
1737
|
+
if (err?.name === "OperationError") {
|
|
1738
|
+
throw new HpkeError(
|
|
1739
|
+
"ERR_SMALL_ORDER_KEY",
|
|
1740
|
+
"X25519 deriveBits rejected a small-order public key",
|
|
1741
|
+
{ cause: err }
|
|
1742
|
+
);
|
|
1743
|
+
}
|
|
1744
|
+
throw err;
|
|
1745
|
+
}
|
|
1634
1746
|
const bytes = new Uint8Array(bits);
|
|
1635
1747
|
if (bytes.every((b) => b === 0)) {
|
|
1636
|
-
throw new
|
|
1748
|
+
throw new HpkeError(
|
|
1749
|
+
"ERR_SMALL_ORDER_KEY",
|
|
1637
1750
|
"X25519 shared secret is all-zero (small-order public key?)"
|
|
1638
1751
|
);
|
|
1639
1752
|
}
|
|
@@ -1672,9 +1785,33 @@ async function encap(pkR) {
|
|
|
1672
1785
|
__name(encap, "encap");
|
|
1673
1786
|
async function decap(enc, keypair) {
|
|
1674
1787
|
if (keypair.privateKey.type !== "private") {
|
|
1675
|
-
throw new
|
|
1788
|
+
throw new HpkeError(
|
|
1789
|
+
"ERR_INVALID_KEYPAIR",
|
|
1790
|
+
"keypair.privateKey must be a private key"
|
|
1791
|
+
);
|
|
1676
1792
|
}
|
|
1677
|
-
assertX25519Algorithm(
|
|
1793
|
+
assertX25519Algorithm(
|
|
1794
|
+
keypair.privateKey,
|
|
1795
|
+
"keypair.privateKey",
|
|
1796
|
+
"ERR_INVALID_KEYPAIR"
|
|
1797
|
+
);
|
|
1798
|
+
if (!keypair.privateKey.usages.includes("deriveBits")) {
|
|
1799
|
+
throw new HpkeError(
|
|
1800
|
+
"ERR_INVALID_KEYPAIR",
|
|
1801
|
+
"keypair.privateKey must have the deriveBits usage"
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
if (keypair.publicKey.type !== "public" || !keypair.publicKey.extractable) {
|
|
1805
|
+
throw new HpkeError(
|
|
1806
|
+
"ERR_INVALID_KEYPAIR",
|
|
1807
|
+
"keypair.publicKey must be an extractable public key"
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1810
|
+
assertX25519Algorithm(
|
|
1811
|
+
keypair.publicKey,
|
|
1812
|
+
"keypair.publicKey",
|
|
1813
|
+
"ERR_INVALID_KEYPAIR"
|
|
1814
|
+
);
|
|
1678
1815
|
const pkE = await subtle2.importKey(
|
|
1679
1816
|
"raw",
|
|
1680
1817
|
enc,
|
|
@@ -1733,6 +1870,8 @@ var HPKE_SUITE_ID = concat2(
|
|
|
1733
1870
|
i2osp(KDF_ID, 2),
|
|
1734
1871
|
i2osp(AEAD_ID, 2)
|
|
1735
1872
|
);
|
|
1873
|
+
var KEY_ID_LABEL = new TextEncoder().encode("simple-hpke/keyId/v1");
|
|
1874
|
+
var KEY_ID_NONCE_LENGTH = 12;
|
|
1736
1875
|
export {
|
|
1737
1876
|
AEAD_ID,
|
|
1738
1877
|
AEAD_TAG_LENGTH,
|
|
@@ -1742,6 +1881,8 @@ export {
|
|
|
1742
1881
|
KDF_ID,
|
|
1743
1882
|
KEM_ID,
|
|
1744
1883
|
KEM_SUITE_ID,
|
|
1884
|
+
KEY_ID_LABEL,
|
|
1885
|
+
KEY_ID_NONCE_LENGTH,
|
|
1745
1886
|
MODE_BASE,
|
|
1746
1887
|
NK,
|
|
1747
1888
|
NN,
|