simple-hpke 0.2.0 → 0.3.1
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 +131 -19
- package/dist/constants.cjs +116 -34
- 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 +116 -34
- 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 +147 -36
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +31 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +147 -36
- 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 +77 -18
- package/dist/util.cjs +144 -34
- package/dist/util.cjs.map +4 -4
- package/dist/util.d.ts +32 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +144 -34
- 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 +2 -2
package/dist/meta.json
CHANGED
|
@@ -690,8 +690,19 @@
|
|
|
690
690
|
],
|
|
691
691
|
"format": "esm"
|
|
692
692
|
},
|
|
693
|
+
"src/errors.ts": {
|
|
694
|
+
"bytes": 975,
|
|
695
|
+
"imports": [
|
|
696
|
+
{
|
|
697
|
+
"path": "<runtime>",
|
|
698
|
+
"kind": "import-statement",
|
|
699
|
+
"external": true
|
|
700
|
+
}
|
|
701
|
+
],
|
|
702
|
+
"format": "esm"
|
|
703
|
+
},
|
|
693
704
|
"src/index.ts": {
|
|
694
|
-
"bytes":
|
|
705
|
+
"bytes": 14331,
|
|
695
706
|
"imports": [
|
|
696
707
|
{
|
|
697
708
|
"path": "node_modules/uint8arrays/dist/src/index.js",
|
|
@@ -703,11 +714,21 @@
|
|
|
703
714
|
"kind": "import-statement",
|
|
704
715
|
"original": "./util"
|
|
705
716
|
},
|
|
717
|
+
{
|
|
718
|
+
"path": "src/errors.ts",
|
|
719
|
+
"kind": "import-statement",
|
|
720
|
+
"original": "./errors"
|
|
721
|
+
},
|
|
706
722
|
{
|
|
707
723
|
"path": "src/constants.ts",
|
|
708
724
|
"kind": "import-statement",
|
|
709
725
|
"original": "./constants"
|
|
710
726
|
},
|
|
727
|
+
{
|
|
728
|
+
"path": "src/errors.ts",
|
|
729
|
+
"kind": "import-statement",
|
|
730
|
+
"original": "./errors"
|
|
731
|
+
},
|
|
711
732
|
{
|
|
712
733
|
"path": "<runtime>",
|
|
713
734
|
"kind": "import-statement",
|
|
@@ -717,7 +738,7 @@
|
|
|
717
738
|
"format": "esm"
|
|
718
739
|
},
|
|
719
740
|
"src/util.ts": {
|
|
720
|
-
"bytes":
|
|
741
|
+
"bytes": 17940,
|
|
721
742
|
"imports": [
|
|
722
743
|
{
|
|
723
744
|
"path": "node_modules/uint8arrays/dist/src/index.js",
|
|
@@ -729,6 +750,11 @@
|
|
|
729
750
|
"kind": "import-statement",
|
|
730
751
|
"original": "./index.js"
|
|
731
752
|
},
|
|
753
|
+
{
|
|
754
|
+
"path": "src/errors.ts",
|
|
755
|
+
"kind": "import-statement",
|
|
756
|
+
"original": "./errors.js"
|
|
757
|
+
},
|
|
732
758
|
{
|
|
733
759
|
"path": "src/constants.ts",
|
|
734
760
|
"kind": "import-statement",
|
|
@@ -743,7 +769,7 @@
|
|
|
743
769
|
"format": "esm"
|
|
744
770
|
},
|
|
745
771
|
"src/constants.ts": {
|
|
746
|
-
"bytes":
|
|
772
|
+
"bytes": 2125,
|
|
747
773
|
"imports": [
|
|
748
774
|
{
|
|
749
775
|
"path": "src/util.ts",
|
|
@@ -759,7 +785,7 @@
|
|
|
759
785
|
"imports": [],
|
|
760
786
|
"exports": [],
|
|
761
787
|
"inputs": {},
|
|
762
|
-
"bytes":
|
|
788
|
+
"bytes": 121397
|
|
763
789
|
},
|
|
764
790
|
"dist/constants.js": {
|
|
765
791
|
"imports": [],
|
|
@@ -772,6 +798,8 @@
|
|
|
772
798
|
"KDF_ID",
|
|
773
799
|
"KEM_ID",
|
|
774
800
|
"KEM_SUITE_ID",
|
|
801
|
+
"KEY_ID_LABEL",
|
|
802
|
+
"KEY_ID_NONCE_LENGTH",
|
|
775
803
|
"MODE_BASE",
|
|
776
804
|
"NK",
|
|
777
805
|
"NN",
|
|
@@ -864,31 +892,55 @@
|
|
|
864
892
|
"node_modules/uint8arrays/dist/src/to-string.js": {
|
|
865
893
|
"bytesInOutput": 258
|
|
866
894
|
},
|
|
895
|
+
"src/errors.ts": {
|
|
896
|
+
"bytesInOutput": 215
|
|
897
|
+
},
|
|
867
898
|
"src/index.ts": {
|
|
868
|
-
"bytesInOutput":
|
|
899
|
+
"bytesInOutput": 5995
|
|
869
900
|
},
|
|
870
901
|
"src/util.ts": {
|
|
871
|
-
"bytesInOutput":
|
|
902
|
+
"bytesInOutput": 7314
|
|
872
903
|
},
|
|
873
904
|
"src/constants.ts": {
|
|
874
|
-
"bytesInOutput":
|
|
905
|
+
"bytesInOutput": 551
|
|
906
|
+
}
|
|
907
|
+
},
|
|
908
|
+
"bytes": 52563
|
|
909
|
+
},
|
|
910
|
+
"dist/errors.js.map": {
|
|
911
|
+
"imports": [],
|
|
912
|
+
"exports": [],
|
|
913
|
+
"inputs": {},
|
|
914
|
+
"bytes": 1293
|
|
915
|
+
},
|
|
916
|
+
"dist/errors.js": {
|
|
917
|
+
"imports": [],
|
|
918
|
+
"exports": [
|
|
919
|
+
"HpkeError"
|
|
920
|
+
],
|
|
921
|
+
"entryPoint": "src/errors.ts",
|
|
922
|
+
"inputs": {
|
|
923
|
+
"src/errors.ts": {
|
|
924
|
+
"bytesInOutput": 213
|
|
875
925
|
}
|
|
876
926
|
},
|
|
877
|
-
"bytes":
|
|
927
|
+
"bytes": 419
|
|
878
928
|
},
|
|
879
929
|
"dist/index.js.map": {
|
|
880
930
|
"imports": [],
|
|
881
931
|
"exports": [],
|
|
882
932
|
"inputs": {},
|
|
883
|
-
"bytes":
|
|
933
|
+
"bytes": 122106
|
|
884
934
|
},
|
|
885
935
|
"dist/index.js": {
|
|
886
936
|
"imports": [],
|
|
887
937
|
"exports": [
|
|
938
|
+
"HpkeError",
|
|
888
939
|
"create",
|
|
889
940
|
"decrypt",
|
|
890
941
|
"encrypt",
|
|
891
942
|
"encryptKey",
|
|
943
|
+
"keyId",
|
|
892
944
|
"open"
|
|
893
945
|
],
|
|
894
946
|
"entryPoint": "src/index.ts",
|
|
@@ -977,23 +1029,26 @@
|
|
|
977
1029
|
"node_modules/uint8arrays/dist/src/to-string.js": {
|
|
978
1030
|
"bytesInOutput": 258
|
|
979
1031
|
},
|
|
1032
|
+
"src/errors.ts": {
|
|
1033
|
+
"bytesInOutput": 215
|
|
1034
|
+
},
|
|
980
1035
|
"src/constants.ts": {
|
|
981
|
-
"bytesInOutput":
|
|
1036
|
+
"bytesInOutput": 551
|
|
982
1037
|
},
|
|
983
1038
|
"src/util.ts": {
|
|
984
|
-
"bytesInOutput":
|
|
1039
|
+
"bytesInOutput": 8110
|
|
985
1040
|
},
|
|
986
1041
|
"src/index.ts": {
|
|
987
|
-
"bytesInOutput":
|
|
1042
|
+
"bytesInOutput": 6166
|
|
988
1043
|
}
|
|
989
1044
|
},
|
|
990
|
-
"bytes":
|
|
1045
|
+
"bytes": 53408
|
|
991
1046
|
},
|
|
992
1047
|
"dist/util.js.map": {
|
|
993
1048
|
"imports": [],
|
|
994
1049
|
"exports": [],
|
|
995
1050
|
"inputs": {},
|
|
996
|
-
"bytes":
|
|
1051
|
+
"bytes": 121973
|
|
997
1052
|
},
|
|
998
1053
|
"dist/util.js": {
|
|
999
1054
|
"imports": [],
|
|
@@ -1007,6 +1062,7 @@
|
|
|
1007
1062
|
"encryptToString",
|
|
1008
1063
|
"hmac",
|
|
1009
1064
|
"i2osp",
|
|
1065
|
+
"keyId",
|
|
1010
1066
|
"labeledExpand",
|
|
1011
1067
|
"labeledExtract",
|
|
1012
1068
|
"resolveRecipientPublicKey"
|
|
@@ -1097,17 +1153,20 @@
|
|
|
1097
1153
|
"node_modules/uint8arrays/dist/src/to-string.js": {
|
|
1098
1154
|
"bytesInOutput": 258
|
|
1099
1155
|
},
|
|
1156
|
+
"src/errors.ts": {
|
|
1157
|
+
"bytesInOutput": 215
|
|
1158
|
+
},
|
|
1100
1159
|
"src/constants.ts": {
|
|
1101
|
-
"bytesInOutput":
|
|
1160
|
+
"bytesInOutput": 551
|
|
1102
1161
|
},
|
|
1103
1162
|
"src/index.ts": {
|
|
1104
|
-
"bytesInOutput":
|
|
1163
|
+
"bytesInOutput": 5995
|
|
1105
1164
|
},
|
|
1106
1165
|
"src/util.ts": {
|
|
1107
|
-
"bytesInOutput":
|
|
1166
|
+
"bytesInOutput": 8126
|
|
1108
1167
|
}
|
|
1109
1168
|
},
|
|
1110
|
-
"bytes":
|
|
1169
|
+
"bytes": 53353
|
|
1111
1170
|
}
|
|
1112
1171
|
}
|
|
1113
1172
|
}
|
package/dist/util.cjs
CHANGED
|
@@ -30,6 +30,7 @@ __export(util_exports, {
|
|
|
30
30
|
encryptToString: () => encryptToString,
|
|
31
31
|
hmac: () => hmac,
|
|
32
32
|
i2osp: () => i2osp,
|
|
33
|
+
keyId: () => keyId,
|
|
33
34
|
labeledExpand: () => labeledExpand,
|
|
34
35
|
labeledExtract: () => labeledExtract,
|
|
35
36
|
resolveRecipientPublicKey: () => resolveRecipientPublicKey
|
|
@@ -1314,6 +1315,19 @@ function toString2(array, encoding = "utf8") {
|
|
|
1314
1315
|
}
|
|
1315
1316
|
__name(toString2, "toString");
|
|
1316
1317
|
|
|
1318
|
+
// src/errors.ts
|
|
1319
|
+
var HpkeError = class extends Error {
|
|
1320
|
+
static {
|
|
1321
|
+
__name(this, "HpkeError");
|
|
1322
|
+
}
|
|
1323
|
+
code;
|
|
1324
|
+
constructor(code2, message, opts) {
|
|
1325
|
+
super(message, opts);
|
|
1326
|
+
this.name = "HpkeError";
|
|
1327
|
+
this.code = code2;
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1317
1331
|
// src/constants.ts
|
|
1318
1332
|
var KEM_ID = 32;
|
|
1319
1333
|
var KDF_ID = 1;
|
|
@@ -1336,6 +1350,8 @@ var HPKE_SUITE_ID = concat2(
|
|
|
1336
1350
|
i2osp(KDF_ID, 2),
|
|
1337
1351
|
i2osp(AEAD_ID, 2)
|
|
1338
1352
|
);
|
|
1353
|
+
var KEY_ID_LABEL = new TextEncoder().encode("simple-hpke/keyId/v1");
|
|
1354
|
+
var KEY_ID_NONCE_LENGTH = 12;
|
|
1339
1355
|
|
|
1340
1356
|
// src/index.ts
|
|
1341
1357
|
var subtle = globalThis.crypto.subtle;
|
|
@@ -1351,13 +1367,19 @@ var decrypt = Object.assign(decryptBytes, {
|
|
|
1351
1367
|
});
|
|
1352
1368
|
async function decryptBytes(keypair, message, opts) {
|
|
1353
1369
|
if (message.byteLength < WRAPPED_LEN_PREFIX) {
|
|
1354
|
-
throw new
|
|
1370
|
+
throw new HpkeError(
|
|
1371
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1372
|
+
"malformed message: too short"
|
|
1373
|
+
);
|
|
1355
1374
|
}
|
|
1356
1375
|
const wrappedLen = message[0] << 8 | message[1];
|
|
1357
1376
|
const ivStart = WRAPPED_LEN_PREFIX + wrappedLen;
|
|
1358
1377
|
const ctStart = ivStart + NN;
|
|
1359
1378
|
if (message.byteLength < ctStart + AEAD_TAG_LENGTH) {
|
|
1360
|
-
throw new
|
|
1379
|
+
throw new HpkeError(
|
|
1380
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1381
|
+
"malformed message: too short"
|
|
1382
|
+
);
|
|
1361
1383
|
}
|
|
1362
1384
|
const wrapped = message.slice(WRAPPED_LEN_PREFIX, ivStart);
|
|
1363
1385
|
const iv = message.slice(ivStart, ctStart);
|
|
@@ -1368,11 +1390,20 @@ async function decryptBytes(keypair, message, opts) {
|
|
|
1368
1390
|
opts?.info !== void 0 ? { info: opts.info } : void 0
|
|
1369
1391
|
);
|
|
1370
1392
|
const key = await importAesKeyDecryptOnly(keyBytes);
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1393
|
+
let pt;
|
|
1394
|
+
try {
|
|
1395
|
+
pt = await subtle.decrypt(
|
|
1396
|
+
{ name: "AES-GCM", iv },
|
|
1397
|
+
key,
|
|
1398
|
+
ciphertext
|
|
1399
|
+
);
|
|
1400
|
+
} catch (err) {
|
|
1401
|
+
throw new HpkeError(
|
|
1402
|
+
"ERR_DECRYPT_FAILED",
|
|
1403
|
+
"failed to decrypt the message: tampered ciphertext",
|
|
1404
|
+
{ cause: err }
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1376
1407
|
return new Uint8Array(pt);
|
|
1377
1408
|
}
|
|
1378
1409
|
__name(decryptBytes, "decryptBytes");
|
|
@@ -1393,7 +1424,8 @@ async function decryptFromString(keypair, message, opts) {
|
|
|
1393
1424
|
__name(decryptFromString, "decryptFromString");
|
|
1394
1425
|
function validateKeysize(keysize) {
|
|
1395
1426
|
if (keysize !== 128 && keysize !== 256) {
|
|
1396
|
-
throw new
|
|
1427
|
+
throw new HpkeError(
|
|
1428
|
+
"ERR_INVALID_KEYSIZE",
|
|
1397
1429
|
`invalid keysize: ${keysize} (expected 128 or 256)`
|
|
1398
1430
|
);
|
|
1399
1431
|
}
|
|
@@ -1401,7 +1433,8 @@ function validateKeysize(keysize) {
|
|
|
1401
1433
|
__name(validateKeysize, "validateKeysize");
|
|
1402
1434
|
function validateRawKeyBytes(raw) {
|
|
1403
1435
|
if (raw.length !== 16 && raw.length !== 32) {
|
|
1404
|
-
throw new
|
|
1436
|
+
throw new HpkeError(
|
|
1437
|
+
"ERR_INVALID_AES_KEY",
|
|
1405
1438
|
`invalid aesKey length: ${raw.length} bytes (expected 16 or 32)`
|
|
1406
1439
|
);
|
|
1407
1440
|
}
|
|
@@ -1415,19 +1448,20 @@ function normalizeInfo(info) {
|
|
|
1415
1448
|
__name(normalizeInfo, "normalizeInfo");
|
|
1416
1449
|
async function exportAesKeyBytes(key) {
|
|
1417
1450
|
if (!key.extractable) {
|
|
1418
|
-
throw new
|
|
1451
|
+
throw new HpkeError(
|
|
1452
|
+
"ERR_INVALID_AES_KEY",
|
|
1419
1453
|
"aesKey must be extractable: its raw bytes are what get sealed"
|
|
1420
1454
|
);
|
|
1421
1455
|
}
|
|
1422
1456
|
return new Uint8Array(await subtle.exportKey("raw", key));
|
|
1423
1457
|
}
|
|
1424
1458
|
__name(exportAesKeyBytes, "exportAesKeyBytes");
|
|
1425
|
-
async function importAesKey(raw) {
|
|
1459
|
+
async function importAesKey(raw, extractable) {
|
|
1426
1460
|
return subtle.importKey(
|
|
1427
1461
|
"raw",
|
|
1428
1462
|
raw,
|
|
1429
1463
|
{ name: "AES-GCM" },
|
|
1430
|
-
|
|
1464
|
+
extractable,
|
|
1431
1465
|
["encrypt", "decrypt"]
|
|
1432
1466
|
);
|
|
1433
1467
|
}
|
|
@@ -1482,7 +1516,10 @@ async function keySchedule(sharedSecret, info) {
|
|
|
1482
1516
|
__name(keySchedule, "keySchedule");
|
|
1483
1517
|
async function openRawBytes(keypair, wrapped, opts) {
|
|
1484
1518
|
if (wrapped.byteLength < ENC_LENGTH + AEAD_TAG_LENGTH) {
|
|
1485
|
-
throw new
|
|
1519
|
+
throw new HpkeError(
|
|
1520
|
+
"ERR_MALFORMED_ENVELOPE",
|
|
1521
|
+
"malformed envelope: too short"
|
|
1522
|
+
);
|
|
1486
1523
|
}
|
|
1487
1524
|
const info = normalizeInfo(opts?.info);
|
|
1488
1525
|
const enc = wrapped.slice(0, ENC_LENGTH);
|
|
@@ -1494,7 +1531,7 @@ async function openRawBytes(keypair, wrapped, opts) {
|
|
|
1494
1531
|
__name(openRawBytes, "openRawBytes");
|
|
1495
1532
|
async function openBytes(keypair, wrapped, opts) {
|
|
1496
1533
|
const keyBytes = await openRawBytes(keypair, wrapped, opts);
|
|
1497
|
-
return importAesKey(keyBytes);
|
|
1534
|
+
return importAesKey(keyBytes, opts?.extractable ?? false);
|
|
1498
1535
|
}
|
|
1499
1536
|
__name(openBytes, "openBytes");
|
|
1500
1537
|
async function encryptKey(recipient, aesKey, opts) {
|
|
@@ -1518,8 +1555,11 @@ async function encryptKey(recipient, aesKey, opts) {
|
|
|
1518
1555
|
const { key, baseNonce } = await keySchedule(sharedSecret, info);
|
|
1519
1556
|
const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
|
|
1520
1557
|
const wrapped = concat2(enc, ciphertext);
|
|
1521
|
-
const aesGcmKey = await importAesKey(
|
|
1522
|
-
|
|
1558
|
+
const aesGcmKey = await importAesKey(
|
|
1559
|
+
keyBytes,
|
|
1560
|
+
opts?.extractable ?? false
|
|
1561
|
+
);
|
|
1562
|
+
return { wrapped, key: aesGcmKey };
|
|
1523
1563
|
}
|
|
1524
1564
|
__name(encryptKey, "encryptKey");
|
|
1525
1565
|
|
|
@@ -1549,24 +1589,38 @@ async function aeadOpen(key, nonce, ciphertext) {
|
|
|
1549
1589
|
false,
|
|
1550
1590
|
["decrypt"]
|
|
1551
1591
|
);
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1592
|
+
let pt;
|
|
1593
|
+
try {
|
|
1594
|
+
pt = await subtle2.decrypt(
|
|
1595
|
+
{ name: "AES-GCM", iv: nonce },
|
|
1596
|
+
k,
|
|
1597
|
+
ciphertext
|
|
1598
|
+
);
|
|
1599
|
+
} catch (err) {
|
|
1600
|
+
throw new HpkeError(
|
|
1601
|
+
"ERR_DECRYPT_FAILED",
|
|
1602
|
+
"failed to decrypt the wrapped key: wrong keypair, wrong info, or a tampered envelope",
|
|
1603
|
+
{ cause: err }
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1557
1606
|
return new Uint8Array(pt);
|
|
1558
1607
|
}
|
|
1559
1608
|
__name(aeadOpen, "aeadOpen");
|
|
1560
1609
|
async function encryptBytes(recipient, message, aesKey, opts) {
|
|
1561
1610
|
const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
1562
|
-
const {
|
|
1611
|
+
const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
|
|
1563
1612
|
const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
|
|
1564
1613
|
const ct = new Uint8Array(await subtle2.encrypt(
|
|
1565
1614
|
{ name: "AES-GCM", iv },
|
|
1566
1615
|
key,
|
|
1567
1616
|
plaintext
|
|
1568
1617
|
));
|
|
1569
|
-
return concat2(
|
|
1618
|
+
return concat2(
|
|
1619
|
+
i2osp(wrapped.length, WRAPPED_LEN_PREFIX),
|
|
1620
|
+
wrapped,
|
|
1621
|
+
iv,
|
|
1622
|
+
ct
|
|
1623
|
+
);
|
|
1570
1624
|
}
|
|
1571
1625
|
__name(encryptBytes, "encryptBytes");
|
|
1572
1626
|
async function encryptToString(recipient, message, aesKey, opts) {
|
|
@@ -1632,7 +1686,8 @@ async function exportRawPublic(key) {
|
|
|
1632
1686
|
__name(exportRawPublic, "exportRawPublic");
|
|
1633
1687
|
async function importRawPublic(raw) {
|
|
1634
1688
|
if (raw.length !== ENC_LENGTH) {
|
|
1635
|
-
throw new
|
|
1689
|
+
throw new HpkeError(
|
|
1690
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1636
1691
|
`invalid public key length: ${raw.length} bytes (expected ${ENC_LENGTH})`
|
|
1637
1692
|
);
|
|
1638
1693
|
}
|
|
@@ -1651,9 +1706,16 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1651
1706
|
}
|
|
1652
1707
|
if (recipient instanceof CryptoKey) {
|
|
1653
1708
|
if (recipient.type !== "public") {
|
|
1654
|
-
throw new
|
|
1709
|
+
throw new HpkeError(
|
|
1710
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1711
|
+
"recipient CryptoKey must be a public key"
|
|
1712
|
+
);
|
|
1655
1713
|
}
|
|
1656
|
-
assertX25519Algorithm(
|
|
1714
|
+
assertX25519Algorithm(
|
|
1715
|
+
recipient,
|
|
1716
|
+
"recipient CryptoKey",
|
|
1717
|
+
"ERR_INVALID_RECIPIENT_KEY"
|
|
1718
|
+
);
|
|
1657
1719
|
return recipient;
|
|
1658
1720
|
}
|
|
1659
1721
|
if (typeof recipient.publicKey === "string") {
|
|
@@ -1663,17 +1725,28 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1663
1725
|
const pair = recipient;
|
|
1664
1726
|
if (pair.publicKey instanceof CryptoKey) {
|
|
1665
1727
|
if (pair.publicKey.type !== "public") {
|
|
1666
|
-
throw new
|
|
1728
|
+
throw new HpkeError(
|
|
1729
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1730
|
+
"recipient keypair publicKey must be a public key"
|
|
1731
|
+
);
|
|
1667
1732
|
}
|
|
1668
|
-
assertX25519Algorithm(
|
|
1733
|
+
assertX25519Algorithm(
|
|
1734
|
+
pair.publicKey,
|
|
1735
|
+
"recipient keypair publicKey",
|
|
1736
|
+
"ERR_INVALID_RECIPIENT_KEY"
|
|
1737
|
+
);
|
|
1669
1738
|
return pair.publicKey;
|
|
1670
1739
|
}
|
|
1671
|
-
throw new
|
|
1740
|
+
throw new HpkeError(
|
|
1741
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1742
|
+
"unrecognized recipient key form"
|
|
1743
|
+
);
|
|
1672
1744
|
}
|
|
1673
1745
|
__name(resolveRecipientPublicKey, "resolveRecipientPublicKey");
|
|
1674
|
-
function assertX25519Algorithm(key, context) {
|
|
1746
|
+
function assertX25519Algorithm(key, context, code2) {
|
|
1675
1747
|
if (key.algorithm.name !== "X25519") {
|
|
1676
|
-
throw new
|
|
1748
|
+
throw new HpkeError(
|
|
1749
|
+
code2,
|
|
1677
1750
|
`${context} must be an X25519 key (got ${key.algorithm.name})`
|
|
1678
1751
|
);
|
|
1679
1752
|
}
|
|
@@ -1687,7 +1760,8 @@ async function dh(priv, pub) {
|
|
|
1687
1760
|
);
|
|
1688
1761
|
const bytes = new Uint8Array(bits);
|
|
1689
1762
|
if (bytes.every((b) => b === 0)) {
|
|
1690
|
-
throw new
|
|
1763
|
+
throw new HpkeError(
|
|
1764
|
+
"ERR_SMALL_ORDER_KEY",
|
|
1691
1765
|
"X25519 shared secret is all-zero (small-order public key?)"
|
|
1692
1766
|
);
|
|
1693
1767
|
}
|
|
@@ -1726,9 +1800,16 @@ async function encap(pkR) {
|
|
|
1726
1800
|
__name(encap, "encap");
|
|
1727
1801
|
async function decap(enc, keypair) {
|
|
1728
1802
|
if (keypair.privateKey.type !== "private") {
|
|
1729
|
-
throw new
|
|
1803
|
+
throw new HpkeError(
|
|
1804
|
+
"ERR_INVALID_KEYPAIR",
|
|
1805
|
+
"keypair.privateKey must be a private key"
|
|
1806
|
+
);
|
|
1730
1807
|
}
|
|
1731
|
-
assertX25519Algorithm(
|
|
1808
|
+
assertX25519Algorithm(
|
|
1809
|
+
keypair.privateKey,
|
|
1810
|
+
"keypair.privateKey",
|
|
1811
|
+
"ERR_INVALID_KEYPAIR"
|
|
1812
|
+
);
|
|
1732
1813
|
const pkE = await subtle2.importKey(
|
|
1733
1814
|
"raw",
|
|
1734
1815
|
enc,
|
|
@@ -1742,6 +1823,35 @@ async function decap(enc, keypair) {
|
|
|
1742
1823
|
return extractAndExpand(dhBytes, kemContext);
|
|
1743
1824
|
}
|
|
1744
1825
|
__name(decap, "decap");
|
|
1826
|
+
async function keyId(key) {
|
|
1827
|
+
if (key.algorithm.name !== "AES-GCM") {
|
|
1828
|
+
throw new HpkeError(
|
|
1829
|
+
"ERR_INVALID_AES_KEY",
|
|
1830
|
+
`keyId needs an AES-GCM key (got ${key.algorithm.name})`
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
if (!key.usages.includes("encrypt")) {
|
|
1834
|
+
throw new HpkeError(
|
|
1835
|
+
"ERR_INVALID_AES_KEY",
|
|
1836
|
+
"keyId needs a key with the 'encrypt' usage"
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
const labelHash = new Uint8Array(
|
|
1840
|
+
await subtle2.digest("SHA-256", KEY_ID_LABEL)
|
|
1841
|
+
);
|
|
1842
|
+
const nonce = labelHash.slice(0, KEY_ID_NONCE_LENGTH);
|
|
1843
|
+
const ciphertext = new Uint8Array(await subtle2.encrypt(
|
|
1844
|
+
{ name: "AES-GCM", iv: nonce },
|
|
1845
|
+
key,
|
|
1846
|
+
KEY_ID_LABEL
|
|
1847
|
+
));
|
|
1848
|
+
const digest2 = await subtle2.digest(
|
|
1849
|
+
"SHA-256",
|
|
1850
|
+
concat2(KEY_ID_LABEL, ciphertext)
|
|
1851
|
+
);
|
|
1852
|
+
return toString2(new Uint8Array(digest2), "base64url");
|
|
1853
|
+
}
|
|
1854
|
+
__name(keyId, "keyId");
|
|
1745
1855
|
function i2osp(n, len) {
|
|
1746
1856
|
const out = new Uint8Array(len);
|
|
1747
1857
|
let v = n;
|