simple-hpke 0.0.8 → 0.0.11

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/dist/index.d.ts CHANGED
@@ -1,18 +1,4 @@
1
- import { type SupportedEncodings } from 'uint8arrays';
2
- export type Uint8ArrayEncodings = SupportedEncodings;
3
- /**
4
- * A recipient's X25519 public key, in any of four forms:
5
- * - `CryptoKey`: an X25519 public key.
6
- * - `CryptoKeyPair`: its `.publicKey` is used (encryption never needs the
7
- * private half).
8
- * - `Uint8Array`: 32 raw X25519 public-key bytes.
9
- * - `{ publicKey, encoding? }`: the public key as an encoded string;
10
- * `encoding` defaults to `base64url`.
11
- */
12
- export type RecipientKey = CryptoKey | CryptoKeyPair | Uint8Array | {
13
- publicKey: string;
14
- encoding?: Uint8ArrayEncodings;
15
- };
1
+ import { type RecipientKey, encryptToString, encryptBytes } from './util';
16
2
  /**
17
3
  * Wrap an AES key to a recipient's public key.
18
4
  *
@@ -35,6 +21,13 @@ export declare function seal(recipient: RecipientKey, aesKey?: CryptoKey | Uint8
35
21
  wrapped: Uint8Array;
36
22
  key: CryptoKey;
37
23
  }>;
24
+ /**
25
+ * Recover the AES key wrapped by `seal`. Call `open(...)` for a usable
26
+ * AES-GCM `CryptoKey`, or `open.raw(...)` for the raw key bytes.
27
+ */
28
+ export declare const open: typeof openBytes & {
29
+ raw: typeof openRawBytes;
30
+ };
38
31
  /**
39
32
  * Recover an AES key that was wrapped with `seal`, using your private key.
40
33
  *
@@ -59,13 +52,6 @@ declare function openBytes(keypair: CryptoKeyPair, wrapped: Uint8Array, opts?: {
59
52
  declare function openRawBytes(keypair: CryptoKeyPair, wrapped: Uint8Array, opts?: {
60
53
  info: Uint8Array | string;
61
54
  }): Promise<Uint8Array>;
62
- /**
63
- * Recover the AES key wrapped by `seal`. Call `open(...)` for a usable
64
- * AES-GCM `CryptoKey`, or `open.raw(...)` for the raw key bytes.
65
- */
66
- export declare const open: typeof openBytes & {
67
- raw: typeof openRawBytes;
68
- };
69
55
  /**
70
56
  * Seal an AES key to `recipient` and AES-GCM encrypt a message under it.
71
57
  * Call `encrypt(...)` for the raw envelope bytes, or `encrypt.asString(...)`
@@ -131,64 +117,5 @@ declare function decryptFromString(keypair: CryptoKeyPair, message: string, opts
131
117
  info?: Uint8Array | string;
132
118
  buffer: true;
133
119
  }): Promise<Uint8Array>;
134
- /**
135
- * Seal a fresh (or supplied) AES key to `recipient`, then AES-GCM encrypt a
136
- * message under it. The wrapped key, IV, and ciphertext are concatenated
137
- * into a single self-describing envelope.
138
- *
139
- * Wire format: `wrappedLen(2, big-endian) ‖ wrapped ‖ iv(12) ‖ ciphertext`.
140
- * The length prefix lets `decrypt` slice the segments apart for either
141
- * 128- or 256-bit wrapped keys.
142
- *
143
- * @param recipient The recipient's X25519 public key, as a `CryptoKey`,
144
- * `CryptoKeyPair` (its `.publicKey` is used), 32 raw bytes (`Uint8Array`),
145
- * or `{ publicKey:string, encoding? }` (encoding defaults to `base64url`).
146
- * @param message Plaintext to encrypt. A `string` is UTF-8 encoded.
147
- * @param aesKey Optional key, as either an AES-GCM `CryptoKey` or its raw
148
- * bytes (`Uint8Array`, 16 or 32 bytes). Omit to generate a fresh key of
149
- * `opts.keysize` bits. A supplied `CryptoKey` MUST be extractable. Each
150
- * call picks a fresh random 96-bit IV for the message ciphertext, so
151
- * reusing the same `aesKey` across many calls carries the standard
152
- * birthday bound for random nonces (collision risk becomes
153
- * non-negligible around 2^32 messages under one key, NIST SP 800-38D) --
154
- * prefer a fresh key per call (the default) over reusing one at scale.
155
- * @param opts `keysize` (128/256, default 256; ignored when `aesKey` is
156
- * supplied) and `info` (bound into the HPKE key schedule).
157
- * @returns The concatenated envelope bytes.
158
- *
159
- * `encrypt.asString(...)` returns the same envelope as an encoded string.
160
- */
161
- declare function encryptBytes(recipient: RecipientKey, message: Uint8Array | string, aesKey?: CryptoKey | Uint8Array | null, opts?: {
162
- keysize?: 128 | 256;
163
- info?: Uint8Array | string;
164
- }): Promise<Uint8Array>;
165
- /**
166
- * Like `encrypt`, but encodes the envelope bytes to a string — handy for
167
- * transports that carry text (JSON, URLs, headers). Decode with
168
- * `fromString(...)` (or any matching decoder) and pass the bytes to
169
- * `decrypt` / `decrypt.asString`. Exposed as `encrypt.asString`.
170
- *
171
- * @param recipient The recipient's X25519 public key, as a `CryptoKey`,
172
- * `CryptoKeyPair` (its `.publicKey` is used), 32 raw bytes (`Uint8Array`),
173
- * or `{ publicKey:string, encoding? }` (encoding defaults to `base64url`).
174
- * @param message Plaintext to encrypt. A `string` is UTF-8 encoded.
175
- * @param aesKey Optional key, as either an AES-GCM `CryptoKey` or its raw
176
- * bytes (`Uint8Array`, 16 or 32 bytes). Omit to generate a fresh key of
177
- * `opts.keysize` bits. A supplied `CryptoKey` MUST be extractable. Each
178
- * call picks a fresh random 96-bit IV for the message ciphertext, so
179
- * reusing the same `aesKey` across many calls carries the standard
180
- * birthday bound for random nonces (collision risk becomes
181
- * non-negligible around 2^32 messages under one key, NIST SP 800-38D) --
182
- * prefer a fresh key per call (the default) over reusing one at scale.
183
- * @param opts `keysize` (128/256, default 256; ignored when `aesKey` is
184
- * supplied), `info` (bound into the HPKE key schedule), and `encoding`
185
- * (the string encoding of the returned envelope; default `base64url`).
186
- * @returns The encoded envelope string.
187
- */
188
- declare function encryptToString(recipient: RecipientKey, message: Uint8Array | string, aesKey?: CryptoKey | Uint8Array | null, opts?: {
189
- keysize?: 128 | 256;
190
- info?: Uint8Array | string;
191
- encoding?: SupportedEncodings;
192
- }): Promise<string>;
193
120
  export {};
194
121
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,YAAY,EAGjB,eAAe,EACf,YAAY,EAOf,MAAM,QAAQ,CAAA;AAmBf;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,IAAI,CACtB,SAAS,EAAC,YAAY,EACtB,MAAM,CAAC,EAAC,SAAS,GAAC,UAAU,GAAC,IAAI,EACjC,IAAI,CAAC,EAAC;IAEF,OAAO,CAAC,EAAC,GAAG,GAAC,GAAG,CAAA;IAEhB,IAAI,CAAC,EAAC,UAAU,GAAC,MAAM,CAAA;CAC1B,GACH,OAAO,CAAC;IAAE,OAAO,EAAC,UAAU,CAAC;IAAC,GAAG,EAAC,SAAS,CAAA;CAAE,CAAC,CA0B/C;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI;;CAEf,CAAA;AAEF;;;;;;;GAOG;AACH,iBAAe,SAAS,CACpB,OAAO,EAAC,aAAa,EACrB,OAAO,EAAC,UAAU,EAClB,IAAI,CAAC,EAAC;IAAE,IAAI,EAAC,UAAU,GAAC,MAAM,CAAA;CAAE,GAClC,OAAO,CAAC,SAAS,CAAC,CAGnB;AAED;;;;;;;;;GASG;AACH,iBAAe,YAAY,CACvB,OAAO,EAAC,aAAa,EACrB,OAAO,EAAC,UAAU,EAClB,IAAI,CAAC,EAAC;IAAE,IAAI,EAAC,UAAU,GAAC,MAAM,CAAA;CAAE,GAClC,OAAO,CAAC,UAAU,CAAC,CAYpB;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO;;CAElB,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;CAGlB,CAAA;AAEF;;;;;;;;GAQG;AACH,iBAAe,YAAY,CACvB,OAAO,EAAC,aAAa,EACrB,OAAO,EAAC,UAAU,EAClB,IAAI,CAAC,EAAC;IAAE,IAAI,CAAC,EAAC,UAAU,GAAC,MAAM,CAAA;CAAE,GACnC,OAAO,CAAC,UAAU,CAAC,CA6BpB;AAED;;;;;;;;GAQG;AACH,iBAAe,eAAe,CAC1B,OAAO,EAAC,aAAa,EACrB,OAAO,EAAC,UAAU,EAClB,IAAI,CAAC,EAAC;IAAE,IAAI,CAAC,EAAC,UAAU,GAAC,MAAM,CAAA;CAAE,GACnC,OAAO,CAAC,MAAM,CAAC,CAGhB;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAe,iBAAiB,CAC5B,OAAO,EAAC,aAAa,EACrB,OAAO,EAAC,MAAM,EACd,IAAI,CAAC,EAAC;IAAE,IAAI,CAAC,EAAC,UAAU,GAAC,MAAM,CAAC;IAAC,MAAM,CAAC,EAAC,KAAK,CAAA;CAAE,GAClD,OAAO,CAAC,MAAM,CAAC,CAAA;AAEjB,iBAAe,iBAAiB,CAC5B,OAAO,EAAC,aAAa,EACrB,OAAO,EAAC,MAAM,EACd,IAAI,EAAC;IAAE,IAAI,CAAC,EAAC,UAAU,GAAC,MAAM,CAAC;IAAC,MAAM,EAAC,IAAI,CAAA;CAAE,GAC/C,OAAO,CAAC,UAAU,CAAC,CAAA"}
package/dist/index.js CHANGED
@@ -1283,8 +1283,7 @@ function toString2(array, encoding = "utf8") {
1283
1283
  }
1284
1284
  __name(toString2, "toString");
1285
1285
 
1286
- // src/index.ts
1287
- var subtle = globalThis.crypto.subtle;
1286
+ // src/constants.ts
1288
1287
  var KEM_ID = 32;
1289
1288
  var KDF_ID = 1;
1290
1289
  var AEAD_ID = 2;
@@ -1295,6 +1294,7 @@ var ENC_LENGTH = 32;
1295
1294
  var AEAD_TAG_LENGTH = 16;
1296
1295
  var MODE_BASE = 0;
1297
1296
  var HPKE_V1 = new TextEncoder().encode("HPKE-v1");
1297
+ var WRAPPED_LEN_PREFIX = 2;
1298
1298
  var KEM_SUITE_ID = concat2(
1299
1299
  new TextEncoder().encode("KEM"),
1300
1300
  i2osp(KEM_ID, 2)
@@ -1305,174 +1305,58 @@ var HPKE_SUITE_ID = concat2(
1305
1305
  i2osp(KDF_ID, 2),
1306
1306
  i2osp(AEAD_ID, 2)
1307
1307
  );
1308
- async function seal(recipient, aesKey, opts) {
1309
- const info = normalizeInfo(opts?.info);
1310
- let keyBytes;
1311
- if (aesKey instanceof Uint8Array) {
1312
- validateRawKeyBytes(aesKey);
1313
- keyBytes = aesKey;
1314
- } else if (aesKey) {
1315
- keyBytes = await exportAesKeyBytes(aesKey);
1316
- validateRawKeyBytes(keyBytes);
1317
- } else {
1318
- const keysize = opts?.keysize ?? 256;
1319
- validateKeysize(keysize);
1320
- keyBytes = globalThis.crypto.getRandomValues(
1321
- new Uint8Array(keysize / 8)
1322
- );
1323
- }
1324
- const publicKey = await resolveRecipientPublicKey(recipient);
1325
- const { sharedSecret, enc } = await encap(publicKey);
1326
- const { key, baseNonce } = await keySchedule(sharedSecret, info);
1327
- const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
1328
- const wrapped = concat2(enc, ciphertext);
1329
- const aesGcmKey = await importAesKey(keyBytes);
1330
- return { wrapped, key: aesGcmKey };
1331
- }
1332
- __name(seal, "seal");
1333
- async function openBytes(keypair, wrapped, opts) {
1334
- const keyBytes = await openRawBytes(keypair, wrapped, opts);
1335
- return importAesKey(keyBytes);
1336
- }
1337
- __name(openBytes, "openBytes");
1338
- async function openRawBytes(keypair, wrapped, opts) {
1339
- if (wrapped.byteLength < ENC_LENGTH + AEAD_TAG_LENGTH) {
1340
- throw new Error("malformed envelope: too short");
1341
- }
1342
- const info = normalizeInfo(opts?.info);
1343
- const enc = wrapped.slice(0, ENC_LENGTH);
1344
- const ciphertext = wrapped.slice(ENC_LENGTH);
1345
- const sharedSecret = await decap(enc, keypair);
1346
- const { key, baseNonce } = await keySchedule(sharedSecret, info);
1347
- return aeadOpen(key, baseNonce, ciphertext);
1348
- }
1349
- __name(openRawBytes, "openRawBytes");
1350
- var open = Object.assign(openBytes, {
1351
- raw: openRawBytes
1352
- });
1353
- var WRAPPED_LEN_PREFIX = 2;
1354
- var encrypt = Object.assign(encryptBytes, {
1355
- asString: encryptToString
1356
- });
1357
- var decrypt = Object.assign(decryptBytes, {
1358
- asString: decryptToString,
1359
- fromString: decryptFromString
1360
- });
1361
- async function decryptBytes(keypair, message, opts) {
1362
- if (message.byteLength < WRAPPED_LEN_PREFIX) {
1363
- throw new Error("malformed message: too short");
1364
- }
1365
- const wrappedLen = message[0] << 8 | message[1];
1366
- const ivStart = WRAPPED_LEN_PREFIX + wrappedLen;
1367
- const ctStart = ivStart + NN;
1368
- if (message.byteLength < ctStart + AEAD_TAG_LENGTH) {
1369
- throw new Error("malformed message: too short");
1370
- }
1371
- const wrapped = message.slice(WRAPPED_LEN_PREFIX, ivStart);
1372
- const iv = message.slice(ivStart, ctStart);
1373
- const ciphertext = message.slice(ctStart);
1374
- const keyBytes = await openRawBytes(
1375
- keypair,
1376
- wrapped,
1377
- opts?.info !== void 0 ? { info: opts.info } : void 0
1378
- );
1379
- const key = await importAesKeyDecryptOnly(keyBytes);
1380
- const pt = await subtle.decrypt(
1381
- { name: "AES-GCM", iv },
1382
- key,
1383
- ciphertext
1384
- );
1385
- return new Uint8Array(pt);
1386
- }
1387
- __name(decryptBytes, "decryptBytes");
1388
- async function decryptToString(keypair, message, opts) {
1389
- const bytes = await decryptBytes(keypair, message, opts);
1390
- return new TextDecoder().decode(bytes);
1391
- }
1392
- __name(decryptToString, "decryptToString");
1393
- async function decryptFromString(keypair, message, opts) {
1394
- const bytes = fromString2(message, "base64url");
1395
- const plaintext = await decryptBytes(
1396
- keypair,
1397
- bytes,
1398
- opts?.info !== void 0 ? { info: opts.info } : void 0
1399
- );
1400
- return opts?.buffer ? plaintext : new TextDecoder().decode(plaintext);
1401
- }
1402
- __name(decryptFromString, "decryptFromString");
1403
- function validateKeysize(keysize) {
1404
- if (keysize !== 128 && keysize !== 256) {
1405
- throw new Error(
1406
- `invalid keysize: ${keysize} (expected 128 or 256)`
1407
- );
1408
- }
1409
- }
1410
- __name(validateKeysize, "validateKeysize");
1411
- function validateRawKeyBytes(raw) {
1412
- if (raw.length !== 16 && raw.length !== 32) {
1413
- throw new Error(
1414
- `invalid aesKey length: ${raw.length} bytes (expected 16 or 32)`
1415
- );
1416
- }
1417
- }
1418
- __name(validateRawKeyBytes, "validateRawKeyBytes");
1419
- function normalizeInfo(info) {
1420
- if (info === void 0 || info === null) return new Uint8Array(0);
1421
- if (typeof info === "string") return new TextEncoder().encode(info);
1422
- return info;
1423
- }
1424
- __name(normalizeInfo, "normalizeInfo");
1425
- async function exportAesKeyBytes(key) {
1426
- if (!key.extractable) {
1427
- throw new Error(
1428
- "aesKey must be extractable: its raw bytes are what get sealed"
1429
- );
1430
- }
1431
- return new Uint8Array(await subtle.exportKey("raw", key));
1432
- }
1433
- __name(exportAesKeyBytes, "exportAesKeyBytes");
1434
- async function importAesKey(raw) {
1435
- return subtle.importKey(
1308
+
1309
+ // src/util.ts
1310
+ var subtle = globalThis.crypto.subtle;
1311
+ async function aeadSeal(key, nonce, plaintext) {
1312
+ const k = await subtle.importKey(
1436
1313
  "raw",
1437
- raw,
1314
+ key,
1438
1315
  { name: "AES-GCM" },
1439
- true,
1440
- ["encrypt", "decrypt"]
1316
+ false,
1317
+ ["encrypt"]
1318
+ );
1319
+ const ct = await subtle.encrypt(
1320
+ { name: "AES-GCM", iv: nonce },
1321
+ k,
1322
+ plaintext
1441
1323
  );
1324
+ return new Uint8Array(ct);
1442
1325
  }
1443
- __name(importAesKey, "importAesKey");
1444
- async function importAesKeyDecryptOnly(raw) {
1445
- return subtle.importKey(
1326
+ __name(aeadSeal, "aeadSeal");
1327
+ async function aeadOpen(key, nonce, ciphertext) {
1328
+ const k = await subtle.importKey(
1446
1329
  "raw",
1447
- raw,
1330
+ key,
1448
1331
  { name: "AES-GCM" },
1449
1332
  false,
1450
1333
  ["decrypt"]
1451
1334
  );
1335
+ const pt = await subtle.decrypt(
1336
+ { name: "AES-GCM", iv: nonce },
1337
+ k,
1338
+ ciphertext
1339
+ );
1340
+ return new Uint8Array(pt);
1452
1341
  }
1453
- __name(importAesKeyDecryptOnly, "importAesKeyDecryptOnly");
1454
- function i2osp(n, len) {
1455
- const out = new Uint8Array(len);
1456
- let v = n;
1457
- for (let i = len - 1; i >= 0; i--) {
1458
- out[i] = v & 255;
1459
- v = Math.floor(v / 256);
1460
- }
1461
- return out;
1342
+ __name(aeadOpen, "aeadOpen");
1343
+ async function encryptBytes(recipient, message, aesKey, opts) {
1344
+ const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
1345
+ const { wrapped, key } = await seal(recipient, aesKey, opts);
1346
+ const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
1347
+ const ct = new Uint8Array(await subtle.encrypt(
1348
+ { name: "AES-GCM", iv },
1349
+ key,
1350
+ plaintext
1351
+ ));
1352
+ return concat2(i2osp(wrapped.length, WRAPPED_LEN_PREFIX), wrapped, iv, ct);
1462
1353
  }
1463
- __name(i2osp, "i2osp");
1464
- function concat2(...arrays) {
1465
- let total = 0;
1466
- for (const a of arrays) total += a.length;
1467
- const out = new Uint8Array(total);
1468
- let offset = 0;
1469
- for (const a of arrays) {
1470
- out.set(a, offset);
1471
- offset += a.length;
1472
- }
1473
- return out;
1354
+ __name(encryptBytes, "encryptBytes");
1355
+ async function encryptToString(recipient, message, aesKey, opts) {
1356
+ const envelope = await encryptBytes(recipient, message, aesKey, opts);
1357
+ return toString2(envelope, opts?.encoding ?? "base64url");
1474
1358
  }
1475
- __name(concat2, "concat");
1359
+ __name(encryptToString, "encryptToString");
1476
1360
  async function hmac(key, data) {
1477
1361
  const k = await subtle.importKey(
1478
1362
  "raw",
@@ -1641,6 +1525,176 @@ async function decap(enc, keypair) {
1641
1525
  return extractAndExpand(dhBytes, kemContext);
1642
1526
  }
1643
1527
  __name(decap, "decap");
1528
+ function i2osp(n, len) {
1529
+ const out = new Uint8Array(len);
1530
+ let v = n;
1531
+ for (let i = len - 1; i >= 0; i--) {
1532
+ out[i] = v & 255;
1533
+ v = Math.floor(v / 256);
1534
+ }
1535
+ return out;
1536
+ }
1537
+ __name(i2osp, "i2osp");
1538
+ function concat2(...arrays) {
1539
+ let total = 0;
1540
+ for (const a of arrays) total += a.length;
1541
+ const out = new Uint8Array(total);
1542
+ let offset = 0;
1543
+ for (const a of arrays) {
1544
+ out.set(a, offset);
1545
+ offset += a.length;
1546
+ }
1547
+ return out;
1548
+ }
1549
+ __name(concat2, "concat");
1550
+
1551
+ // src/index.ts
1552
+ var subtle2 = globalThis.crypto.subtle;
1553
+ async function seal(recipient, aesKey, opts) {
1554
+ const info = normalizeInfo(opts?.info);
1555
+ let keyBytes;
1556
+ if (aesKey instanceof Uint8Array) {
1557
+ validateRawKeyBytes(aesKey);
1558
+ keyBytes = aesKey;
1559
+ } else if (aesKey) {
1560
+ keyBytes = await exportAesKeyBytes(aesKey);
1561
+ validateRawKeyBytes(keyBytes);
1562
+ } else {
1563
+ const keysize = opts?.keysize ?? 256;
1564
+ validateKeysize(keysize);
1565
+ keyBytes = globalThis.crypto.getRandomValues(
1566
+ new Uint8Array(keysize / 8)
1567
+ );
1568
+ }
1569
+ const publicKey = await resolveRecipientPublicKey(recipient);
1570
+ const { sharedSecret, enc } = await encap(publicKey);
1571
+ const { key, baseNonce } = await keySchedule(sharedSecret, info);
1572
+ const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
1573
+ const wrapped = concat2(enc, ciphertext);
1574
+ const aesGcmKey = await importAesKey(keyBytes);
1575
+ return { wrapped, key: aesGcmKey };
1576
+ }
1577
+ __name(seal, "seal");
1578
+ var open = Object.assign(openBytes, {
1579
+ raw: openRawBytes
1580
+ });
1581
+ async function openBytes(keypair, wrapped, opts) {
1582
+ const keyBytes = await openRawBytes(keypair, wrapped, opts);
1583
+ return importAesKey(keyBytes);
1584
+ }
1585
+ __name(openBytes, "openBytes");
1586
+ async function openRawBytes(keypair, wrapped, opts) {
1587
+ if (wrapped.byteLength < ENC_LENGTH + AEAD_TAG_LENGTH) {
1588
+ throw new Error("malformed envelope: too short");
1589
+ }
1590
+ const info = normalizeInfo(opts?.info);
1591
+ const enc = wrapped.slice(0, ENC_LENGTH);
1592
+ const ciphertext = wrapped.slice(ENC_LENGTH);
1593
+ const sharedSecret = await decap(enc, keypair);
1594
+ const { key, baseNonce } = await keySchedule(sharedSecret, info);
1595
+ return aeadOpen(key, baseNonce, ciphertext);
1596
+ }
1597
+ __name(openRawBytes, "openRawBytes");
1598
+ var encrypt = Object.assign(encryptBytes, {
1599
+ asString: encryptToString
1600
+ });
1601
+ var decrypt = Object.assign(decryptBytes, {
1602
+ asString: decryptToString,
1603
+ fromString: decryptFromString
1604
+ });
1605
+ async function decryptBytes(keypair, message, opts) {
1606
+ if (message.byteLength < WRAPPED_LEN_PREFIX) {
1607
+ throw new Error("malformed message: too short");
1608
+ }
1609
+ const wrappedLen = message[0] << 8 | message[1];
1610
+ const ivStart = WRAPPED_LEN_PREFIX + wrappedLen;
1611
+ const ctStart = ivStart + NN;
1612
+ if (message.byteLength < ctStart + AEAD_TAG_LENGTH) {
1613
+ throw new Error("malformed message: too short");
1614
+ }
1615
+ const wrapped = message.slice(WRAPPED_LEN_PREFIX, ivStart);
1616
+ const iv = message.slice(ivStart, ctStart);
1617
+ const ciphertext = message.slice(ctStart);
1618
+ const keyBytes = await openRawBytes(
1619
+ keypair,
1620
+ wrapped,
1621
+ opts?.info !== void 0 ? { info: opts.info } : void 0
1622
+ );
1623
+ const key = await importAesKeyDecryptOnly(keyBytes);
1624
+ const pt = await subtle2.decrypt(
1625
+ { name: "AES-GCM", iv },
1626
+ key,
1627
+ ciphertext
1628
+ );
1629
+ return new Uint8Array(pt);
1630
+ }
1631
+ __name(decryptBytes, "decryptBytes");
1632
+ async function decryptToString(keypair, message, opts) {
1633
+ const bytes = await decryptBytes(keypair, message, opts);
1634
+ return new TextDecoder().decode(bytes);
1635
+ }
1636
+ __name(decryptToString, "decryptToString");
1637
+ async function decryptFromString(keypair, message, opts) {
1638
+ const bytes = fromString2(message, "base64url");
1639
+ const plaintext = await decryptBytes(
1640
+ keypair,
1641
+ bytes,
1642
+ opts?.info !== void 0 ? { info: opts.info } : void 0
1643
+ );
1644
+ return opts?.buffer ? plaintext : new TextDecoder().decode(plaintext);
1645
+ }
1646
+ __name(decryptFromString, "decryptFromString");
1647
+ function validateKeysize(keysize) {
1648
+ if (keysize !== 128 && keysize !== 256) {
1649
+ throw new Error(
1650
+ `invalid keysize: ${keysize} (expected 128 or 256)`
1651
+ );
1652
+ }
1653
+ }
1654
+ __name(validateKeysize, "validateKeysize");
1655
+ function validateRawKeyBytes(raw) {
1656
+ if (raw.length !== 16 && raw.length !== 32) {
1657
+ throw new Error(
1658
+ `invalid aesKey length: ${raw.length} bytes (expected 16 or 32)`
1659
+ );
1660
+ }
1661
+ }
1662
+ __name(validateRawKeyBytes, "validateRawKeyBytes");
1663
+ function normalizeInfo(info) {
1664
+ if (info === void 0 || info === null) return new Uint8Array(0);
1665
+ if (typeof info === "string") return new TextEncoder().encode(info);
1666
+ return info;
1667
+ }
1668
+ __name(normalizeInfo, "normalizeInfo");
1669
+ async function exportAesKeyBytes(key) {
1670
+ if (!key.extractable) {
1671
+ throw new Error(
1672
+ "aesKey must be extractable: its raw bytes are what get sealed"
1673
+ );
1674
+ }
1675
+ return new Uint8Array(await subtle2.exportKey("raw", key));
1676
+ }
1677
+ __name(exportAesKeyBytes, "exportAesKeyBytes");
1678
+ async function importAesKey(raw) {
1679
+ return subtle2.importKey(
1680
+ "raw",
1681
+ raw,
1682
+ { name: "AES-GCM" },
1683
+ true,
1684
+ ["encrypt", "decrypt"]
1685
+ );
1686
+ }
1687
+ __name(importAesKey, "importAesKey");
1688
+ async function importAesKeyDecryptOnly(raw) {
1689
+ return subtle2.importKey(
1690
+ "raw",
1691
+ raw,
1692
+ { name: "AES-GCM" },
1693
+ false,
1694
+ ["decrypt"]
1695
+ );
1696
+ }
1697
+ __name(importAesKeyDecryptOnly, "importAesKeyDecryptOnly");
1644
1698
  async function keySchedule(sharedSecret, info) {
1645
1699
  const empty2 = new Uint8Array(0);
1646
1700
  const pskIdHash = await labeledExtract(
@@ -1679,55 +1733,6 @@ async function keySchedule(sharedSecret, info) {
1679
1733
  return { key, baseNonce };
1680
1734
  }
1681
1735
  __name(keySchedule, "keySchedule");
1682
- async function aeadSeal(key, nonce, plaintext) {
1683
- const k = await subtle.importKey(
1684
- "raw",
1685
- key,
1686
- { name: "AES-GCM" },
1687
- false,
1688
- ["encrypt"]
1689
- );
1690
- const ct = await subtle.encrypt(
1691
- { name: "AES-GCM", iv: nonce },
1692
- k,
1693
- plaintext
1694
- );
1695
- return new Uint8Array(ct);
1696
- }
1697
- __name(aeadSeal, "aeadSeal");
1698
- async function aeadOpen(key, nonce, ciphertext) {
1699
- const k = await subtle.importKey(
1700
- "raw",
1701
- key,
1702
- { name: "AES-GCM" },
1703
- false,
1704
- ["decrypt"]
1705
- );
1706
- const pt = await subtle.decrypt(
1707
- { name: "AES-GCM", iv: nonce },
1708
- k,
1709
- ciphertext
1710
- );
1711
- return new Uint8Array(pt);
1712
- }
1713
- __name(aeadOpen, "aeadOpen");
1714
- async function encryptBytes(recipient, message, aesKey, opts) {
1715
- const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
1716
- const { wrapped, key } = await seal(recipient, aesKey, opts);
1717
- const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
1718
- const ct = new Uint8Array(await subtle.encrypt(
1719
- { name: "AES-GCM", iv },
1720
- key,
1721
- plaintext
1722
- ));
1723
- return concat2(i2osp(wrapped.length, WRAPPED_LEN_PREFIX), wrapped, iv, ct);
1724
- }
1725
- __name(encryptBytes, "encryptBytes");
1726
- async function encryptToString(recipient, message, aesKey, opts) {
1727
- const envelope = await encryptBytes(recipient, message, aesKey, opts);
1728
- return toString2(envelope, opts?.encoding ?? "base64url");
1729
- }
1730
- __name(encryptToString, "encryptToString");
1731
1736
  export {
1732
1737
  decrypt,
1733
1738
  encrypt,