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/errors.cjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/errors.ts
|
|
22
|
+
var errors_exports = {};
|
|
23
|
+
__export(errors_exports, {
|
|
24
|
+
HpkeError: () => HpkeError
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(errors_exports);
|
|
27
|
+
var HpkeError = class extends Error {
|
|
28
|
+
static {
|
|
29
|
+
__name(this, "HpkeError");
|
|
30
|
+
}
|
|
31
|
+
code;
|
|
32
|
+
constructor(code, message, opts) {
|
|
33
|
+
super(message, opts);
|
|
34
|
+
this.name = "HpkeError";
|
|
35
|
+
this.code = code;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=errors.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The set of stable error codes this package raises.\n *\n * Branch on `err.code`, not on `err.message`: the codes are part of the\n * public API, the prose is not.\n */\nexport type HpkeErrorCode =\n 'ERR_INVALID_RECIPIENT_KEY'|\n 'ERR_INVALID_KEYPAIR'|\n 'ERR_SMALL_ORDER_KEY'|\n 'ERR_MALFORMED_ENVELOPE'|\n 'ERR_MALFORMED_MESSAGE'|\n 'ERR_INVALID_KEYSIZE'|\n 'ERR_INVALID_AES_KEY'|\n 'ERR_DECRYPT_FAILED'\n\n/**\n * An `Error` carrying a stable `code`. Every throw site in this package\n * raises one of these, so callers can branch on the failure instead of\n * pattern matching on a message.\n *\n * Where a WebCrypto failure is being wrapped, the original is kept on\n * `cause`.\n */\nexport class HpkeError extends Error {\n readonly code:HpkeErrorCode\n\n constructor (\n code:HpkeErrorCode,\n message:string,\n opts?:{ cause?:unknown }\n ) {\n super(message, opts)\n this.name = 'HpkeError'\n this.code = code\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBO,IAAM,YAAN,cAAwB,MAAM;AAAA,EAxBrC,OAwBqC;AAAA;AAAA;AAAA,EACxB;AAAA,EAET,YACI,MACA,SACA,MACF;AACE,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EAChB;AACJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The set of stable error codes this package raises.
|
|
3
|
+
*
|
|
4
|
+
* Branch on `err.code`, not on `err.message`: the codes are part of the
|
|
5
|
+
* public API, the prose is not.
|
|
6
|
+
*/
|
|
7
|
+
export type HpkeErrorCode = 'ERR_INVALID_RECIPIENT_KEY' | 'ERR_INVALID_KEYPAIR' | 'ERR_SMALL_ORDER_KEY' | 'ERR_MALFORMED_ENVELOPE' | 'ERR_MALFORMED_MESSAGE' | 'ERR_INVALID_KEYSIZE' | 'ERR_INVALID_AES_KEY' | 'ERR_DECRYPT_FAILED';
|
|
8
|
+
/**
|
|
9
|
+
* An `Error` carrying a stable `code`. Every throw site in this package
|
|
10
|
+
* raises one of these, so callers can branch on the failure instead of
|
|
11
|
+
* pattern matching on a message.
|
|
12
|
+
*
|
|
13
|
+
* Where a WebCrypto failure is being wrapped, the original is kept on
|
|
14
|
+
* `cause`.
|
|
15
|
+
*/
|
|
16
|
+
export declare class HpkeError extends Error {
|
|
17
|
+
readonly code: HpkeErrorCode;
|
|
18
|
+
constructor(code: HpkeErrorCode, message: string, opts?: {
|
|
19
|
+
cause?: unknown;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GACrB,2BAA2B,GAC3B,qBAAqB,GACrB,qBAAqB,GACrB,wBAAwB,GACxB,uBAAuB,GACvB,qBAAqB,GACrB,qBAAqB,GACrB,oBAAoB,CAAA;AAExB;;;;;;;GAOG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAChC,QAAQ,CAAC,IAAI,EAAC,aAAa,CAAA;gBAGvB,IAAI,EAAC,aAAa,EAClB,OAAO,EAAC,MAAM,EACd,IAAI,CAAC,EAAC;QAAE,KAAK,CAAC,EAAC,OAAO,CAAA;KAAE;CAM/B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
var HpkeError = class extends Error {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "HpkeError");
|
|
8
|
+
}
|
|
9
|
+
code;
|
|
10
|
+
constructor(code, message, opts) {
|
|
11
|
+
super(message, opts);
|
|
12
|
+
this.name = "HpkeError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
HpkeError
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The set of stable error codes this package raises.\n *\n * Branch on `err.code`, not on `err.message`: the codes are part of the\n * public API, the prose is not.\n */\nexport type HpkeErrorCode =\n 'ERR_INVALID_RECIPIENT_KEY'|\n 'ERR_INVALID_KEYPAIR'|\n 'ERR_SMALL_ORDER_KEY'|\n 'ERR_MALFORMED_ENVELOPE'|\n 'ERR_MALFORMED_MESSAGE'|\n 'ERR_INVALID_KEYSIZE'|\n 'ERR_INVALID_AES_KEY'|\n 'ERR_DECRYPT_FAILED'\n\n/**\n * An `Error` carrying a stable `code`. Every throw site in this package\n * raises one of these, so callers can branch on the failure instead of\n * pattern matching on a message.\n *\n * Where a WebCrypto failure is being wrapped, the original is kept on\n * `cause`.\n */\nexport class HpkeError extends Error {\n readonly code:HpkeErrorCode\n\n constructor (\n code:HpkeErrorCode,\n message:string,\n opts?:{ cause?:unknown }\n ) {\n super(message, opts)\n this.name = 'HpkeError'\n this.code = code\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;AAwBO,IAAM,YAAN,cAAwB,MAAM;AAAA,EAxBrC,OAwBqC;AAAA;AAAA;AAAA,EACxB;AAAA,EAET,YACI,MACA,SACA,MACF;AACE,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EAChB;AACJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var o=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var s=Object.getOwnPropertyNames;var t=Object.prototype.hasOwnProperty;var A=(R,E)=>o(R,"name",{value:E,configurable:!0});var D=(R,E)=>{for(var r in E)o(R,r,{get:E[r],enumerable:!0})},n=(R,E,r,e)=>{if(E&&typeof E=="object"||typeof E=="function")for(let _ of s(E))!t.call(R,_)&&_!==r&&o(R,_,{get:()=>E[_],enumerable:!(e=L(E,_))||e.enumerable});return R};var d=R=>n(o({},"__esModule",{value:!0}),R);var p={};D(p,{HpkeError:()=>I});module.exports=d(p);var I=class extends Error{static{A(this,"HpkeError")}code;constructor(E,r,e){super(r,e),this.name="HpkeError",this.code=E}};
|
|
2
|
+
//# sourceMappingURL=errors.min.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The set of stable error codes this package raises.\n *\n * Branch on `err.code`, not on `err.message`: the codes are part of the\n * public API, the prose is not.\n */\nexport type HpkeErrorCode =\n 'ERR_INVALID_RECIPIENT_KEY'|\n 'ERR_INVALID_KEYPAIR'|\n 'ERR_SMALL_ORDER_KEY'|\n 'ERR_MALFORMED_ENVELOPE'|\n 'ERR_MALFORMED_MESSAGE'|\n 'ERR_INVALID_KEYSIZE'|\n 'ERR_INVALID_AES_KEY'|\n 'ERR_DECRYPT_FAILED'\n\n/**\n * An `Error` carrying a stable `code`. Every throw site in this package\n * raises one of these, so callers can branch on the failure instead of\n * pattern matching on a message.\n *\n * Where a WebCrypto failure is being wrapped, the original is kept on\n * `cause`.\n */\nexport class HpkeError extends Error {\n readonly code:HpkeErrorCode\n\n constructor (\n code:HpkeErrorCode,\n message:string,\n opts?:{ cause?:unknown }\n ) {\n super(message, opts)\n this.name = 'HpkeError'\n this.code = code\n }\n}\n"],
|
|
5
|
+
"mappings": "4dAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GAwBO,IAAMI,EAAN,cAAwB,KAAM,CAxBrC,MAwBqC,CAAAC,EAAA,kBACxB,KAET,YACIC,EACAC,EACAC,EACF,CACE,MAAMD,EAASC,CAAI,EACnB,KAAK,KAAO,YACZ,KAAK,KAAOF,CAChB,CACJ",
|
|
6
|
+
"names": ["errors_exports", "__export", "HpkeError", "__toCommonJS", "HpkeError", "__name", "code", "message", "opts"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The set of stable error codes this package raises.\n *\n * Branch on `err.code`, not on `err.message`: the codes are part of the\n * public API, the prose is not.\n */\nexport type HpkeErrorCode =\n 'ERR_INVALID_RECIPIENT_KEY'|\n 'ERR_INVALID_KEYPAIR'|\n 'ERR_SMALL_ORDER_KEY'|\n 'ERR_MALFORMED_ENVELOPE'|\n 'ERR_MALFORMED_MESSAGE'|\n 'ERR_INVALID_KEYSIZE'|\n 'ERR_INVALID_AES_KEY'|\n 'ERR_DECRYPT_FAILED'\n\n/**\n * An `Error` carrying a stable `code`. Every throw site in this package\n * raises one of these, so callers can branch on the failure instead of\n * pattern matching on a message.\n *\n * Where a WebCrypto failure is being wrapped, the original is kept on\n * `cause`.\n */\nexport class HpkeError extends Error {\n readonly code:HpkeErrorCode\n\n constructor (\n code:HpkeErrorCode,\n message:string,\n opts?:{ cause?:unknown }\n ) {\n super(message, opts)\n this.name = 'HpkeError'\n this.code = code\n }\n}\n"],
|
|
5
|
+
"mappings": "+EAwBO,IAAMA,EAAN,cAAwB,KAAM,CAxBrC,MAwBqC,CAAAC,EAAA,kBACxB,KAET,YACIC,EACAC,EACAC,EACF,CACE,MAAMD,EAASC,CAAI,EACnB,KAAK,KAAO,YACZ,KAAK,KAAOF,CAChB,CACJ",
|
|
6
|
+
"names": ["HpkeError", "__name", "code", "message", "opts"]
|
|
7
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -21,10 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
+
HpkeError: () => HpkeError,
|
|
24
25
|
create: () => create2,
|
|
25
26
|
decrypt: () => decrypt,
|
|
26
27
|
encrypt: () => encrypt,
|
|
27
28
|
encryptKey: () => encryptKey,
|
|
29
|
+
keyId: () => keyId,
|
|
28
30
|
open: () => open
|
|
29
31
|
});
|
|
30
32
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -1307,6 +1309,19 @@ function toString2(array, encoding = "utf8") {
|
|
|
1307
1309
|
}
|
|
1308
1310
|
__name(toString2, "toString");
|
|
1309
1311
|
|
|
1312
|
+
// src/errors.ts
|
|
1313
|
+
var HpkeError = class extends Error {
|
|
1314
|
+
static {
|
|
1315
|
+
__name(this, "HpkeError");
|
|
1316
|
+
}
|
|
1317
|
+
code;
|
|
1318
|
+
constructor(code2, message, opts) {
|
|
1319
|
+
super(message, opts);
|
|
1320
|
+
this.name = "HpkeError";
|
|
1321
|
+
this.code = code2;
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1310
1325
|
// src/constants.ts
|
|
1311
1326
|
var KEM_ID = 32;
|
|
1312
1327
|
var KDF_ID = 1;
|
|
@@ -1329,6 +1344,8 @@ var HPKE_SUITE_ID = concat2(
|
|
|
1329
1344
|
i2osp(KDF_ID, 2),
|
|
1330
1345
|
i2osp(AEAD_ID, 2)
|
|
1331
1346
|
);
|
|
1347
|
+
var KEY_ID_LABEL = new TextEncoder().encode("simple-hpke/keyId/v1");
|
|
1348
|
+
var KEY_ID_NONCE_LENGTH = 12;
|
|
1332
1349
|
|
|
1333
1350
|
// src/util.ts
|
|
1334
1351
|
var subtle = globalThis.crypto.subtle;
|
|
@@ -1356,24 +1373,38 @@ async function aeadOpen(key, nonce, ciphertext) {
|
|
|
1356
1373
|
false,
|
|
1357
1374
|
["decrypt"]
|
|
1358
1375
|
);
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1376
|
+
let pt;
|
|
1377
|
+
try {
|
|
1378
|
+
pt = await subtle.decrypt(
|
|
1379
|
+
{ name: "AES-GCM", iv: nonce },
|
|
1380
|
+
k,
|
|
1381
|
+
ciphertext
|
|
1382
|
+
);
|
|
1383
|
+
} catch (err) {
|
|
1384
|
+
throw new HpkeError(
|
|
1385
|
+
"ERR_DECRYPT_FAILED",
|
|
1386
|
+
"failed to decrypt the wrapped key: wrong keypair, wrong info, or a tampered envelope",
|
|
1387
|
+
{ cause: err }
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1364
1390
|
return new Uint8Array(pt);
|
|
1365
1391
|
}
|
|
1366
1392
|
__name(aeadOpen, "aeadOpen");
|
|
1367
1393
|
async function encryptBytes(recipient, message, aesKey, opts) {
|
|
1368
1394
|
const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
1369
|
-
const {
|
|
1395
|
+
const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
|
|
1370
1396
|
const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
|
|
1371
1397
|
const ct = new Uint8Array(await subtle.encrypt(
|
|
1372
1398
|
{ name: "AES-GCM", iv },
|
|
1373
1399
|
key,
|
|
1374
1400
|
plaintext
|
|
1375
1401
|
));
|
|
1376
|
-
return concat2(
|
|
1402
|
+
return concat2(
|
|
1403
|
+
i2osp(wrapped.length, WRAPPED_LEN_PREFIX),
|
|
1404
|
+
wrapped,
|
|
1405
|
+
iv,
|
|
1406
|
+
ct
|
|
1407
|
+
);
|
|
1377
1408
|
}
|
|
1378
1409
|
__name(encryptBytes, "encryptBytes");
|
|
1379
1410
|
async function encryptToString(recipient, message, aesKey, opts) {
|
|
@@ -1439,7 +1470,8 @@ async function exportRawPublic(key) {
|
|
|
1439
1470
|
__name(exportRawPublic, "exportRawPublic");
|
|
1440
1471
|
async function importRawPublic(raw) {
|
|
1441
1472
|
if (raw.length !== ENC_LENGTH) {
|
|
1442
|
-
throw new
|
|
1473
|
+
throw new HpkeError(
|
|
1474
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1443
1475
|
`invalid public key length: ${raw.length} bytes (expected ${ENC_LENGTH})`
|
|
1444
1476
|
);
|
|
1445
1477
|
}
|
|
@@ -1458,9 +1490,16 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1458
1490
|
}
|
|
1459
1491
|
if (recipient instanceof CryptoKey) {
|
|
1460
1492
|
if (recipient.type !== "public") {
|
|
1461
|
-
throw new
|
|
1493
|
+
throw new HpkeError(
|
|
1494
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1495
|
+
"recipient CryptoKey must be a public key"
|
|
1496
|
+
);
|
|
1462
1497
|
}
|
|
1463
|
-
assertX25519Algorithm(
|
|
1498
|
+
assertX25519Algorithm(
|
|
1499
|
+
recipient,
|
|
1500
|
+
"recipient CryptoKey",
|
|
1501
|
+
"ERR_INVALID_RECIPIENT_KEY"
|
|
1502
|
+
);
|
|
1464
1503
|
return recipient;
|
|
1465
1504
|
}
|
|
1466
1505
|
if (typeof recipient.publicKey === "string") {
|
|
@@ -1470,17 +1509,28 @@ async function resolveRecipientPublicKey(recipient) {
|
|
|
1470
1509
|
const pair = recipient;
|
|
1471
1510
|
if (pair.publicKey instanceof CryptoKey) {
|
|
1472
1511
|
if (pair.publicKey.type !== "public") {
|
|
1473
|
-
throw new
|
|
1512
|
+
throw new HpkeError(
|
|
1513
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1514
|
+
"recipient keypair publicKey must be a public key"
|
|
1515
|
+
);
|
|
1474
1516
|
}
|
|
1475
|
-
assertX25519Algorithm(
|
|
1517
|
+
assertX25519Algorithm(
|
|
1518
|
+
pair.publicKey,
|
|
1519
|
+
"recipient keypair publicKey",
|
|
1520
|
+
"ERR_INVALID_RECIPIENT_KEY"
|
|
1521
|
+
);
|
|
1476
1522
|
return pair.publicKey;
|
|
1477
1523
|
}
|
|
1478
|
-
throw new
|
|
1524
|
+
throw new HpkeError(
|
|
1525
|
+
"ERR_INVALID_RECIPIENT_KEY",
|
|
1526
|
+
"unrecognized recipient key form"
|
|
1527
|
+
);
|
|
1479
1528
|
}
|
|
1480
1529
|
__name(resolveRecipientPublicKey, "resolveRecipientPublicKey");
|
|
1481
|
-
function assertX25519Algorithm(key, context) {
|
|
1530
|
+
function assertX25519Algorithm(key, context, code2) {
|
|
1482
1531
|
if (key.algorithm.name !== "X25519") {
|
|
1483
|
-
throw new
|
|
1532
|
+
throw new HpkeError(
|
|
1533
|
+
code2,
|
|
1484
1534
|
`${context} must be an X25519 key (got ${key.algorithm.name})`
|
|
1485
1535
|
);
|
|
1486
1536
|
}
|
|
@@ -1494,7 +1544,8 @@ async function dh(priv, pub) {
|
|
|
1494
1544
|
);
|
|
1495
1545
|
const bytes = new Uint8Array(bits);
|
|
1496
1546
|
if (bytes.every((b) => b === 0)) {
|
|
1497
|
-
throw new
|
|
1547
|
+
throw new HpkeError(
|
|
1548
|
+
"ERR_SMALL_ORDER_KEY",
|
|
1498
1549
|
"X25519 shared secret is all-zero (small-order public key?)"
|
|
1499
1550
|
);
|
|
1500
1551
|
}
|
|
@@ -1533,9 +1584,16 @@ async function encap(pkR) {
|
|
|
1533
1584
|
__name(encap, "encap");
|
|
1534
1585
|
async function decap(enc, keypair) {
|
|
1535
1586
|
if (keypair.privateKey.type !== "private") {
|
|
1536
|
-
throw new
|
|
1587
|
+
throw new HpkeError(
|
|
1588
|
+
"ERR_INVALID_KEYPAIR",
|
|
1589
|
+
"keypair.privateKey must be a private key"
|
|
1590
|
+
);
|
|
1537
1591
|
}
|
|
1538
|
-
assertX25519Algorithm(
|
|
1592
|
+
assertX25519Algorithm(
|
|
1593
|
+
keypair.privateKey,
|
|
1594
|
+
"keypair.privateKey",
|
|
1595
|
+
"ERR_INVALID_KEYPAIR"
|
|
1596
|
+
);
|
|
1539
1597
|
const pkE = await subtle.importKey(
|
|
1540
1598
|
"raw",
|
|
1541
1599
|
enc,
|
|
@@ -1549,6 +1607,35 @@ async function decap(enc, keypair) {
|
|
|
1549
1607
|
return extractAndExpand(dhBytes, kemContext);
|
|
1550
1608
|
}
|
|
1551
1609
|
__name(decap, "decap");
|
|
1610
|
+
async function keyId(key) {
|
|
1611
|
+
if (key.algorithm.name !== "AES-GCM") {
|
|
1612
|
+
throw new HpkeError(
|
|
1613
|
+
"ERR_INVALID_AES_KEY",
|
|
1614
|
+
`keyId needs an AES-GCM key (got ${key.algorithm.name})`
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
if (!key.usages.includes("encrypt")) {
|
|
1618
|
+
throw new HpkeError(
|
|
1619
|
+
"ERR_INVALID_AES_KEY",
|
|
1620
|
+
"keyId needs a key with the 'encrypt' usage"
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
const labelHash = new Uint8Array(
|
|
1624
|
+
await subtle.digest("SHA-256", KEY_ID_LABEL)
|
|
1625
|
+
);
|
|
1626
|
+
const nonce = labelHash.slice(0, KEY_ID_NONCE_LENGTH);
|
|
1627
|
+
const ciphertext = new Uint8Array(await subtle.encrypt(
|
|
1628
|
+
{ name: "AES-GCM", iv: nonce },
|
|
1629
|
+
key,
|
|
1630
|
+
KEY_ID_LABEL
|
|
1631
|
+
));
|
|
1632
|
+
const digest2 = await subtle.digest(
|
|
1633
|
+
"SHA-256",
|
|
1634
|
+
concat2(KEY_ID_LABEL, ciphertext)
|
|
1635
|
+
);
|
|
1636
|
+
return toString2(new Uint8Array(digest2), "base64url");
|
|
1637
|
+
}
|
|
1638
|
+
__name(keyId, "keyId");
|
|
1552
1639
|
function i2osp(n, len) {
|
|
1553
1640
|
const out = new Uint8Array(len);
|
|
1554
1641
|
let v = n;
|
|
@@ -1575,8 +1662,8 @@ __name(concat2, "concat");
|
|
|
1575
1662
|
// src/index.ts
|
|
1576
1663
|
var subtle2 = globalThis.crypto.subtle;
|
|
1577
1664
|
async function create2(recipient, opts) {
|
|
1578
|
-
const {
|
|
1579
|
-
return {
|
|
1665
|
+
const { wrapped, key } = await encryptKey(recipient, null, opts);
|
|
1666
|
+
return { wrapped, key };
|
|
1580
1667
|
}
|
|
1581
1668
|
__name(create2, "create");
|
|
1582
1669
|
var open = Object.assign(openBytes, {
|
|
@@ -1591,13 +1678,19 @@ var decrypt = Object.assign(decryptBytes, {
|
|
|
1591
1678
|
});
|
|
1592
1679
|
async function decryptBytes(keypair, message, opts) {
|
|
1593
1680
|
if (message.byteLength < WRAPPED_LEN_PREFIX) {
|
|
1594
|
-
throw new
|
|
1681
|
+
throw new HpkeError(
|
|
1682
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1683
|
+
"malformed message: too short"
|
|
1684
|
+
);
|
|
1595
1685
|
}
|
|
1596
1686
|
const wrappedLen = message[0] << 8 | message[1];
|
|
1597
1687
|
const ivStart = WRAPPED_LEN_PREFIX + wrappedLen;
|
|
1598
1688
|
const ctStart = ivStart + NN;
|
|
1599
1689
|
if (message.byteLength < ctStart + AEAD_TAG_LENGTH) {
|
|
1600
|
-
throw new
|
|
1690
|
+
throw new HpkeError(
|
|
1691
|
+
"ERR_MALFORMED_MESSAGE",
|
|
1692
|
+
"malformed message: too short"
|
|
1693
|
+
);
|
|
1601
1694
|
}
|
|
1602
1695
|
const wrapped = message.slice(WRAPPED_LEN_PREFIX, ivStart);
|
|
1603
1696
|
const iv = message.slice(ivStart, ctStart);
|
|
@@ -1608,11 +1701,20 @@ async function decryptBytes(keypair, message, opts) {
|
|
|
1608
1701
|
opts?.info !== void 0 ? { info: opts.info } : void 0
|
|
1609
1702
|
);
|
|
1610
1703
|
const key = await importAesKeyDecryptOnly(keyBytes);
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1704
|
+
let pt;
|
|
1705
|
+
try {
|
|
1706
|
+
pt = await subtle2.decrypt(
|
|
1707
|
+
{ name: "AES-GCM", iv },
|
|
1708
|
+
key,
|
|
1709
|
+
ciphertext
|
|
1710
|
+
);
|
|
1711
|
+
} catch (err) {
|
|
1712
|
+
throw new HpkeError(
|
|
1713
|
+
"ERR_DECRYPT_FAILED",
|
|
1714
|
+
"failed to decrypt the message: tampered ciphertext",
|
|
1715
|
+
{ cause: err }
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1616
1718
|
return new Uint8Array(pt);
|
|
1617
1719
|
}
|
|
1618
1720
|
__name(decryptBytes, "decryptBytes");
|
|
@@ -1633,7 +1735,8 @@ async function decryptFromString(keypair, message, opts) {
|
|
|
1633
1735
|
__name(decryptFromString, "decryptFromString");
|
|
1634
1736
|
function validateKeysize(keysize) {
|
|
1635
1737
|
if (keysize !== 128 && keysize !== 256) {
|
|
1636
|
-
throw new
|
|
1738
|
+
throw new HpkeError(
|
|
1739
|
+
"ERR_INVALID_KEYSIZE",
|
|
1637
1740
|
`invalid keysize: ${keysize} (expected 128 or 256)`
|
|
1638
1741
|
);
|
|
1639
1742
|
}
|
|
@@ -1641,7 +1744,8 @@ function validateKeysize(keysize) {
|
|
|
1641
1744
|
__name(validateKeysize, "validateKeysize");
|
|
1642
1745
|
function validateRawKeyBytes(raw) {
|
|
1643
1746
|
if (raw.length !== 16 && raw.length !== 32) {
|
|
1644
|
-
throw new
|
|
1747
|
+
throw new HpkeError(
|
|
1748
|
+
"ERR_INVALID_AES_KEY",
|
|
1645
1749
|
`invalid aesKey length: ${raw.length} bytes (expected 16 or 32)`
|
|
1646
1750
|
);
|
|
1647
1751
|
}
|
|
@@ -1655,19 +1759,20 @@ function normalizeInfo(info) {
|
|
|
1655
1759
|
__name(normalizeInfo, "normalizeInfo");
|
|
1656
1760
|
async function exportAesKeyBytes(key) {
|
|
1657
1761
|
if (!key.extractable) {
|
|
1658
|
-
throw new
|
|
1762
|
+
throw new HpkeError(
|
|
1763
|
+
"ERR_INVALID_AES_KEY",
|
|
1659
1764
|
"aesKey must be extractable: its raw bytes are what get sealed"
|
|
1660
1765
|
);
|
|
1661
1766
|
}
|
|
1662
1767
|
return new Uint8Array(await subtle2.exportKey("raw", key));
|
|
1663
1768
|
}
|
|
1664
1769
|
__name(exportAesKeyBytes, "exportAesKeyBytes");
|
|
1665
|
-
async function importAesKey(raw) {
|
|
1770
|
+
async function importAesKey(raw, extractable) {
|
|
1666
1771
|
return subtle2.importKey(
|
|
1667
1772
|
"raw",
|
|
1668
1773
|
raw,
|
|
1669
1774
|
{ name: "AES-GCM" },
|
|
1670
|
-
|
|
1775
|
+
extractable,
|
|
1671
1776
|
["encrypt", "decrypt"]
|
|
1672
1777
|
);
|
|
1673
1778
|
}
|
|
@@ -1722,7 +1827,10 @@ async function keySchedule(sharedSecret, info) {
|
|
|
1722
1827
|
__name(keySchedule, "keySchedule");
|
|
1723
1828
|
async function openRawBytes(keypair, wrapped, opts) {
|
|
1724
1829
|
if (wrapped.byteLength < ENC_LENGTH + AEAD_TAG_LENGTH) {
|
|
1725
|
-
throw new
|
|
1830
|
+
throw new HpkeError(
|
|
1831
|
+
"ERR_MALFORMED_ENVELOPE",
|
|
1832
|
+
"malformed envelope: too short"
|
|
1833
|
+
);
|
|
1726
1834
|
}
|
|
1727
1835
|
const info = normalizeInfo(opts?.info);
|
|
1728
1836
|
const enc = wrapped.slice(0, ENC_LENGTH);
|
|
@@ -1734,7 +1842,7 @@ async function openRawBytes(keypair, wrapped, opts) {
|
|
|
1734
1842
|
__name(openRawBytes, "openRawBytes");
|
|
1735
1843
|
async function openBytes(keypair, wrapped, opts) {
|
|
1736
1844
|
const keyBytes = await openRawBytes(keypair, wrapped, opts);
|
|
1737
|
-
return importAesKey(keyBytes);
|
|
1845
|
+
return importAesKey(keyBytes, opts?.extractable ?? false);
|
|
1738
1846
|
}
|
|
1739
1847
|
__name(openBytes, "openBytes");
|
|
1740
1848
|
async function encryptKey(recipient, aesKey, opts) {
|
|
@@ -1758,8 +1866,11 @@ async function encryptKey(recipient, aesKey, opts) {
|
|
|
1758
1866
|
const { key, baseNonce } = await keySchedule(sharedSecret, info);
|
|
1759
1867
|
const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
|
|
1760
1868
|
const wrapped = concat2(enc, ciphertext);
|
|
1761
|
-
const aesGcmKey = await importAesKey(
|
|
1762
|
-
|
|
1869
|
+
const aesGcmKey = await importAesKey(
|
|
1870
|
+
keyBytes,
|
|
1871
|
+
opts?.extractable ?? false
|
|
1872
|
+
);
|
|
1873
|
+
return { wrapped, key: aesGcmKey };
|
|
1763
1874
|
}
|
|
1764
1875
|
__name(encryptKey, "encryptKey");
|
|
1765
1876
|
//# sourceMappingURL=index.cjs.map
|