simple-hpke 0.1.5 → 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 +137 -23
- package/dist/constants.cjs +115 -53
- 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 +115 -53
- 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 -51
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +53 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +147 -51
- 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 +79 -20
- package/dist/util.cjs +143 -53
- package/dist/util.cjs.map +4 -4
- package/dist/util.d.ts +33 -2
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +143 -53
- 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,32 +892,56 @@
|
|
|
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
|
-
"
|
|
892
|
-
"
|
|
942
|
+
"encryptKey",
|
|
943
|
+
"keyId",
|
|
944
|
+
"open"
|
|
893
945
|
],
|
|
894
946
|
"entryPoint": "src/index.ts",
|
|
895
947
|
"inputs": {
|
|
@@ -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,19 +1350,11 @@ 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;
|
|
1342
|
-
async function create2(recipient, opts) {
|
|
1343
|
-
const { wrapped, key } = await encryptKey(recipient, null, opts);
|
|
1344
|
-
return { enc: wrapped, key };
|
|
1345
|
-
}
|
|
1346
|
-
__name(create2, "create");
|
|
1347
|
-
async function seal(recipient, aesKey, opts) {
|
|
1348
|
-
const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
|
|
1349
|
-
return { enc: wrapped, key };
|
|
1350
|
-
}
|
|
1351
|
-
__name(seal, "seal");
|
|
1352
1358
|
var open = Object.assign(openBytes, {
|
|
1353
1359
|
raw: openRawBytes
|
|
1354
1360
|
});
|
|
@@ -1361,13 +1367,19 @@ var decrypt = Object.assign(decryptBytes, {
|
|
|
1361
1367
|
});
|
|
1362
1368
|
async function decryptBytes(keypair, message, opts) {
|
|
1363
1369
|
if (message.byteLength < WRAPPED_LEN_PREFIX) {
|
|
1364
|
-
throw new
|
|
1370
|
+
throw new HpkeError(
|
|
1371
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1372
|
+
"malformed message: too short"
|
|
1373
|
+
);
|
|
1365
1374
|
}
|
|
1366
1375
|
const wrappedLen = message[0] << 8 | message[1];
|
|
1367
1376
|
const ivStart = WRAPPED_LEN_PREFIX + wrappedLen;
|
|
1368
1377
|
const ctStart = ivStart + NN;
|
|
1369
1378
|
if (message.byteLength < ctStart + AEAD_TAG_LENGTH) {
|
|
1370
|
-
throw new
|
|
1379
|
+
throw new HpkeError(
|
|
1380
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1381
|
+
"malformed message: too short"
|
|
1382
|
+
);
|
|
1371
1383
|
}
|
|
1372
1384
|
const wrapped = message.slice(WRAPPED_LEN_PREFIX, ivStart);
|
|
1373
1385
|
const iv = message.slice(ivStart, ctStart);
|
|
@@ -1378,11 +1390,20 @@ async function decryptBytes(keypair, message, opts) {
|
|
|
1378
1390
|
opts?.info !== void 0 ? { info: opts.info } : void 0
|
|
1379
1391
|
);
|
|
1380
1392
|
const key = await importAesKeyDecryptOnly(keyBytes);
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
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
|
+
}
|
|
1386
1407
|
return new Uint8Array(pt);
|
|
1387
1408
|
}
|
|
1388
1409
|
__name(decryptBytes, "decryptBytes");
|
|
@@ -1403,7 +1424,8 @@ async function decryptFromString(keypair, message, opts) {
|
|
|
1403
1424
|
__name(decryptFromString, "decryptFromString");
|
|
1404
1425
|
function validateKeysize(keysize) {
|
|
1405
1426
|
if (keysize !== 128 && keysize !== 256) {
|
|
1406
|
-
throw new
|
|
1427
|
+
throw new HpkeError(
|
|
1428
|
+
"ERR_INVALID_KEYSIZE",
|
|
1407
1429
|
`invalid keysize: ${keysize} (expected 128 or 256)`
|
|
1408
1430
|
);
|
|
1409
1431
|
}
|
|
@@ -1411,7 +1433,8 @@ function validateKeysize(keysize) {
|
|
|
1411
1433
|
__name(validateKeysize, "validateKeysize");
|
|
1412
1434
|
function validateRawKeyBytes(raw) {
|
|
1413
1435
|
if (raw.length !== 16 && raw.length !== 32) {
|
|
1414
|
-
throw new
|
|
1436
|
+
throw new HpkeError(
|
|
1437
|
+
"ERR_INVALID_AES_KEY",
|
|
1415
1438
|
`invalid aesKey length: ${raw.length} bytes (expected 16 or 32)`
|
|
1416
1439
|
);
|
|
1417
1440
|
}
|
|
@@ -1425,19 +1448,20 @@ function normalizeInfo(info) {
|
|
|
1425
1448
|
__name(normalizeInfo, "normalizeInfo");
|
|
1426
1449
|
async function exportAesKeyBytes(key) {
|
|
1427
1450
|
if (!key.extractable) {
|
|
1428
|
-
throw new
|
|
1451
|
+
throw new HpkeError(
|
|
1452
|
+
"ERR_INVALID_AES_KEY",
|
|
1429
1453
|
"aesKey must be extractable: its raw bytes are what get sealed"
|
|
1430
1454
|
);
|
|
1431
1455
|
}
|
|
1432
1456
|
return new Uint8Array(await subtle.exportKey("raw", key));
|
|
1433
1457
|
}
|
|
1434
1458
|
__name(exportAesKeyBytes, "exportAesKeyBytes");
|
|
1435
|
-
async function importAesKey(raw) {
|
|
1459
|
+
async function importAesKey(raw, extractable) {
|
|
1436
1460
|
return subtle.importKey(
|
|
1437
1461
|
"raw",
|
|
1438
1462
|
raw,
|
|
1439
1463
|
{ name: "AES-GCM" },
|
|
1440
|
-
|
|
1464
|
+
extractable,
|
|
1441
1465
|
["encrypt", "decrypt"]
|
|
1442
1466
|
);
|
|
1443
1467
|
}
|
|
@@ -1492,7 +1516,10 @@ async function keySchedule(sharedSecret, info) {
|
|
|
1492
1516
|
__name(keySchedule, "keySchedule");
|
|
1493
1517
|
async function openRawBytes(keypair, wrapped, opts) {
|
|
1494
1518
|
if (wrapped.byteLength < ENC_LENGTH + AEAD_TAG_LENGTH) {
|
|
1495
|
-
throw new
|
|
1519
|
+
throw new HpkeError(
|
|
1520
|
+
"ERR_MALFORMED_ENVELOPE",
|
|
1521
|
+
"malformed envelope: too short"
|
|
1522
|
+
);
|
|
1496
1523
|
}
|
|
1497
1524
|
const info = normalizeInfo(opts?.info);
|
|
1498
1525
|
const enc = wrapped.slice(0, ENC_LENGTH);
|
|
@@ -1504,7 +1531,7 @@ async function openRawBytes(keypair, wrapped, opts) {
|
|
|
1504
1531
|
__name(openRawBytes, "openRawBytes");
|
|
1505
1532
|
async function openBytes(keypair, wrapped, opts) {
|
|
1506
1533
|
const keyBytes = await openRawBytes(keypair, wrapped, opts);
|
|
1507
|
-
return importAesKey(keyBytes);
|
|
1534
|
+
return importAesKey(keyBytes, opts?.extractable ?? false);
|
|
1508
1535
|
}
|
|
1509
1536
|
__name(openBytes, "openBytes");
|
|
1510
1537
|
async function encryptKey(recipient, aesKey, opts) {
|
|
@@ -1528,7 +1555,10 @@ async function encryptKey(recipient, aesKey, opts) {
|
|
|
1528
1555
|
const { key, baseNonce } = await keySchedule(sharedSecret, info);
|
|
1529
1556
|
const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
|
|
1530
1557
|
const wrapped = concat2(enc, ciphertext);
|
|
1531
|
-
const aesGcmKey = await importAesKey(
|
|
1558
|
+
const aesGcmKey = await importAesKey(
|
|
1559
|
+
keyBytes,
|
|
1560
|
+
opts?.extractable ?? false
|
|
1561
|
+
);
|
|
1532
1562
|
return { wrapped, key: aesGcmKey };
|
|
1533
1563
|
}
|
|
1534
1564
|
__name(encryptKey, "encryptKey");
|
|
@@ -1559,34 +1589,38 @@ async function aeadOpen(key, nonce, ciphertext) {
|
|
|
1559
1589
|
false,
|
|
1560
1590
|
["decrypt"]
|
|
1561
1591
|
);
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
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
|
+
}
|
|
1567
1606
|
return new Uint8Array(pt);
|
|
1568
1607
|
}
|
|
1569
1608
|
__name(aeadOpen, "aeadOpen");
|
|
1570
1609
|
async function encryptBytes(recipient, message, aesKey, opts) {
|
|
1571
1610
|
const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
1572
|
-
|
|
1573
|
-
let key;
|
|
1574
|
-
if (!aesKey) {
|
|
1575
|
-
const keys = await create2(recipient, opts);
|
|
1576
|
-
enc = keys.enc;
|
|
1577
|
-
key = keys.key;
|
|
1578
|
-
} else {
|
|
1579
|
-
const keys = await seal(recipient, aesKey, opts);
|
|
1580
|
-
enc = keys.enc;
|
|
1581
|
-
key = keys.key;
|
|
1582
|
-
}
|
|
1611
|
+
const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
|
|
1583
1612
|
const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
|
|
1584
1613
|
const ct = new Uint8Array(await subtle2.encrypt(
|
|
1585
1614
|
{ name: "AES-GCM", iv },
|
|
1586
1615
|
key,
|
|
1587
1616
|
plaintext
|
|
1588
1617
|
));
|
|
1589
|
-
return concat2(
|
|
1618
|
+
return concat2(
|
|
1619
|
+
i2osp(wrapped.length, WRAPPED_LEN_PREFIX),
|
|
1620
|
+
wrapped,
|
|
1621
|
+
iv,
|
|
1622
|
+
ct
|
|
1623
|
+
);
|
|
1590
1624
|
}
|
|
1591
1625
|
__name(encryptBytes, "encryptBytes");
|
|
1592
1626
|
async function encryptToString(recipient, message, aesKey, opts) {
|
|
@@ -1652,7 +1686,8 @@ async function exportRawPublic(key) {
|
|
|
1652
1686
|
__name(exportRawPublic, "exportRawPublic");
|
|
1653
1687
|
async function importRawPublic(raw) {
|
|
1654
1688
|
if (raw.length !== ENC_LENGTH) {
|
|
1655
|
-
throw new
|
|
1689
|
+
throw new HpkeError(
|
|
1690
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1656
1691
|
`invalid public key length: ${raw.length} bytes (expected ${ENC_LENGTH})`
|
|
1657
1692
|
);
|
|
1658
1693
|
}
|
|
@@ -1671,9 +1706,16 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1671
1706
|
}
|
|
1672
1707
|
if (recipient instanceof CryptoKey) {
|
|
1673
1708
|
if (recipient.type !== "public") {
|
|
1674
|
-
throw new
|
|
1709
|
+
throw new HpkeError(
|
|
1710
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1711
|
+
"recipient CryptoKey must be a public key"
|
|
1712
|
+
);
|
|
1675
1713
|
}
|
|
1676
|
-
assertX25519Algorithm(
|
|
1714
|
+
assertX25519Algorithm(
|
|
1715
|
+
recipient,
|
|
1716
|
+
"recipient CryptoKey",
|
|
1717
|
+
"ERR_INVALID_RECIPIENT_KEY"
|
|
1718
|
+
);
|
|
1677
1719
|
return recipient;
|
|
1678
1720
|
}
|
|
1679
1721
|
if (typeof recipient.publicKey === "string") {
|
|
@@ -1683,17 +1725,28 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1683
1725
|
const pair = recipient;
|
|
1684
1726
|
if (pair.publicKey instanceof CryptoKey) {
|
|
1685
1727
|
if (pair.publicKey.type !== "public") {
|
|
1686
|
-
throw new
|
|
1728
|
+
throw new HpkeError(
|
|
1729
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1730
|
+
"recipient keypair publicKey must be a public key"
|
|
1731
|
+
);
|
|
1687
1732
|
}
|
|
1688
|
-
assertX25519Algorithm(
|
|
1733
|
+
assertX25519Algorithm(
|
|
1734
|
+
pair.publicKey,
|
|
1735
|
+
"recipient keypair publicKey",
|
|
1736
|
+
"ERR_INVALID_RECIPIENT_KEY"
|
|
1737
|
+
);
|
|
1689
1738
|
return pair.publicKey;
|
|
1690
1739
|
}
|
|
1691
|
-
throw new
|
|
1740
|
+
throw new HpkeError(
|
|
1741
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1742
|
+
"unrecognized recipient key form"
|
|
1743
|
+
);
|
|
1692
1744
|
}
|
|
1693
1745
|
__name(resolveRecipientPublicKey, "resolveRecipientPublicKey");
|
|
1694
|
-
function assertX25519Algorithm(key, context) {
|
|
1746
|
+
function assertX25519Algorithm(key, context, code2) {
|
|
1695
1747
|
if (key.algorithm.name !== "X25519") {
|
|
1696
|
-
throw new
|
|
1748
|
+
throw new HpkeError(
|
|
1749
|
+
code2,
|
|
1697
1750
|
`${context} must be an X25519 key (got ${key.algorithm.name})`
|
|
1698
1751
|
);
|
|
1699
1752
|
}
|
|
@@ -1707,7 +1760,8 @@ async function dh(priv, pub) {
|
|
|
1707
1760
|
);
|
|
1708
1761
|
const bytes = new Uint8Array(bits);
|
|
1709
1762
|
if (bytes.every((b) => b === 0)) {
|
|
1710
|
-
throw new
|
|
1763
|
+
throw new HpkeError(
|
|
1764
|
+
"ERR_SMALL_ORDER_KEY",
|
|
1711
1765
|
"X25519 shared secret is all-zero (small-order public key?)"
|
|
1712
1766
|
);
|
|
1713
1767
|
}
|
|
@@ -1746,9 +1800,16 @@ async function encap(pkR) {
|
|
|
1746
1800
|
__name(encap, "encap");
|
|
1747
1801
|
async function decap(enc, keypair) {
|
|
1748
1802
|
if (keypair.privateKey.type !== "private") {
|
|
1749
|
-
throw new
|
|
1803
|
+
throw new HpkeError(
|
|
1804
|
+
"ERR_INVALID_KEYPAIR",
|
|
1805
|
+
"keypair.privateKey must be a private key"
|
|
1806
|
+
);
|
|
1750
1807
|
}
|
|
1751
|
-
assertX25519Algorithm(
|
|
1808
|
+
assertX25519Algorithm(
|
|
1809
|
+
keypair.privateKey,
|
|
1810
|
+
"keypair.privateKey",
|
|
1811
|
+
"ERR_INVALID_KEYPAIR"
|
|
1812
|
+
);
|
|
1752
1813
|
const pkE = await subtle2.importKey(
|
|
1753
1814
|
"raw",
|
|
1754
1815
|
enc,
|
|
@@ -1762,6 +1823,35 @@ async function decap(enc, keypair) {
|
|
|
1762
1823
|
return extractAndExpand(dhBytes, kemContext);
|
|
1763
1824
|
}
|
|
1764
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");
|
|
1765
1855
|
function i2osp(n, len) {
|
|
1766
1856
|
const out = new Uint8Array(len);
|
|
1767
1857
|
let v = n;
|