simple-hpke 0.0.11 → 0.1.0

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,57 +1,38 @@
1
1
  import { type RecipientKey, encryptToString, encryptBytes } from './util';
2
2
  /**
3
- * Wrap an AES key to a recipient's public key.
3
+ * Create a new AES key for the given public key.
4
4
  *
5
- * @param recipient The recipient's X25519 public key, as a `CryptoKey`,
6
- * `CryptoKeyPair` (its `.publicKey` is used), 32 raw bytes (`Uint8Array`),
7
- * or `{ publicKey:string, encoding? }` (encoding defaults to `base64url`).
8
- * @param aesKey Optional key to seal, as either an AES-GCM `CryptoKey` or its
9
- * raw bytes (`Uint8Array`, 16 or 32 bytes). Omit to generate a fresh
10
- * extractable key of `opts.keysize` bits. A supplied `CryptoKey` MUST be
11
- * extractable (its raw bytes are sealed).
12
- * @param opts `keysize` (128/256, default 256; ignored when `aesKey` is
13
- * supplied) and `info` (bound into the HPKE key schedule; default empty).
14
- * @returns { wrapped:Uint8Array, key:CryptoKey } The envelope bytes and a
15
- * usable AES-GCM `CryptoKey`.
5
+ * @param recipient Public key for decryptor
6
+ * @param opts `size` and `info`
7
+ * @returns {{ enc, key }} The wrapped envelope bytes and the generated
8
+ * AES-GCM key.
16
9
  */
17
- export declare function seal(recipient: RecipientKey, aesKey?: CryptoKey | Uint8Array | null, opts?: {
18
- keysize?: 128 | 256;
10
+ export declare function create(recipient: RecipientKey, opts?: {
11
+ size?: 128 | 256;
19
12
  info?: Uint8Array | string;
20
13
  }): Promise<{
21
- wrapped: Uint8Array;
14
+ enc: Uint8Array<ArrayBufferLike>;
22
15
  key: CryptoKey;
23
16
  }>;
24
17
  /**
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
- };
31
- /**
32
- * Recover an AES key that was wrapped with `seal`, using your private key.
18
+ * Encrypt the given AES key to the given public key.
33
19
  *
34
- * @param keypair The same X25519 `CryptoKeyPair` used to seal.
35
- * @param wrapped The envelope returned by `seal` (`enc ‖ ciphertext`).
36
- * @param opts `info` — must match the value passed to `seal`.
37
- * @returns The recovered AES-GCM `CryptoKey` (extractable).
20
+ * @returns {{ enc, key }} The wrapped envelope bytes and a usable AES-GCM
21
+ * key with the same raw bytes as `aesKey`.
38
22
  */
39
- declare function openBytes(keypair: CryptoKeyPair, wrapped: Uint8Array, opts?: {
40
- info: Uint8Array | string;
41
- }): Promise<CryptoKey>;
23
+ export declare function seal(recipient: RecipientKey, aesKey: CryptoKey | Uint8Array | null, opts?: {
24
+ info?: Uint8Array | string;
25
+ }): Promise<{
26
+ enc: Uint8Array<ArrayBufferLike>;
27
+ key: CryptoKey;
28
+ }>;
42
29
  /**
43
- * Like `open`, but returns the recovered key as raw bytes instead of
44
- * importing it as an AES-GCM `CryptoKey`. Exposed as `open.raw`.
45
- *
46
- * @param keypair The same X25519 `CryptoKeyPair` used to seal.
47
- * @param wrapped The envelope returned by `seal` (`enc ‖ ciphertext`).
48
- * @param opts `info` — must match the value passed to `seal`.
49
- * @returns The recovered key bytes (16 or 32 bytes, matching whatever
50
- * was sealed).
30
+ * Recover the AES key wrapped by `create` or `seal`. Call `open(...)` for a
31
+ * usable AES-GCM `CryptoKey`, or `open.raw(...)` for the raw key bytes.
51
32
  */
52
- declare function openRawBytes(keypair: CryptoKeyPair, wrapped: Uint8Array, opts?: {
53
- info: Uint8Array | string;
54
- }): Promise<Uint8Array>;
33
+ export declare const open: typeof openBytes & {
34
+ raw: typeof openRawBytes;
35
+ };
55
36
  /**
56
37
  * Seal an AES key to `recipient` and AES-GCM encrypt a message under it.
57
38
  * Call `encrypt(...)` for the raw envelope bytes, or `encrypt.asString(...)`
@@ -117,5 +98,29 @@ declare function decryptFromString(keypair: CryptoKeyPair, message: string, opts
117
98
  info?: Uint8Array | string;
118
99
  buffer: true;
119
100
  }): Promise<Uint8Array>;
101
+ /**
102
+ * Like `open`, but returns the recovered key as raw bytes instead of
103
+ * importing it as an AES-GCM `CryptoKey`. Exposed as `open.raw`.
104
+ *
105
+ * @param keypair The same X25519 `CryptoKeyPair` used to create or seal.
106
+ * @param wrapped The `enc` bytes returned by `create` or `seal`.
107
+ * @param opts `info` — must match the value passed to `create` or `seal`.
108
+ * @returns The recovered key bytes (16 or 32 bytes, matching whatever
109
+ * was wrapped).
110
+ */
111
+ declare function openRawBytes(keypair: CryptoKeyPair, wrapped: Uint8Array, opts?: {
112
+ info: Uint8Array | string;
113
+ }): Promise<Uint8Array>;
114
+ /**
115
+ * Recover an AES key that was wrapped with `create` or `seal`.
116
+ *
117
+ * @param keypair The same X25519 `CryptoKeyPair` used to create or seal.
118
+ * @param wrapped The `enc` bytes returned by `create` or `seal`.
119
+ * @param opts `info` — must match the value passed to `create` or `seal`.
120
+ * @returns The recovered AES-GCM `CryptoKey` (extractable).
121
+ */
122
+ declare function openBytes(keypair: CryptoKeyPair, wrapped: Uint8Array, opts?: {
123
+ info: Uint8Array | string;
124
+ }): Promise<CryptoKey>;
120
125
  export {};
121
126
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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"}
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;AAoBf;;;;;;;GAOG;AACH,wBAAsB,MAAM,CACxB,SAAS,EAAC,YAAY,EACtB,IAAI,CAAC,EAAC;IAEF,IAAI,CAAC,EAAC,GAAG,GAAC,GAAG,CAAA;IAEb,IAAI,CAAC,EAAC,UAAU,GAAC,MAAM,CAAA;CAC1B,GACH,OAAO,CAAC;IAAE,GAAG,EAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IAAC,GAAG,EAAC,SAAS,CAAA;CAAE,CAAC,CAG5D;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CACtB,SAAS,EAAC,YAAY,EACtB,MAAM,EAAC,SAAS,GAAC,UAAU,GAAC,IAAI,EAChC,IAAI,CAAC,EAAC;IAEF,IAAI,CAAC,EAAC,UAAU,GAAC,MAAM,CAAA;CAC1B,GACH,OAAO,CAAC;IAAE,GAAG,EAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IAAC,GAAG,EAAC,SAAS,CAAA;CAAE,CAAC,CAG5D;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI;;CAEf,CAAA;AAEF;;;;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;AA0HrB;;;;;;;;;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;;;;;;;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"}
package/dist/index.js CHANGED
@@ -1342,14 +1342,24 @@ async function aeadOpen(key, nonce, ciphertext) {
1342
1342
  __name(aeadOpen, "aeadOpen");
1343
1343
  async function encryptBytes(recipient, message, aesKey, opts) {
1344
1344
  const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
1345
- const { wrapped, key } = await seal(recipient, aesKey, opts);
1345
+ let enc;
1346
+ let key;
1347
+ if (!aesKey) {
1348
+ const keys = await create2(recipient, opts);
1349
+ enc = keys.enc;
1350
+ key = keys.key;
1351
+ } else {
1352
+ const keys = await seal(recipient, aesKey, opts);
1353
+ enc = keys.enc;
1354
+ key = keys.key;
1355
+ }
1346
1356
  const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
1347
1357
  const ct = new Uint8Array(await subtle.encrypt(
1348
1358
  { name: "AES-GCM", iv },
1349
1359
  key,
1350
1360
  plaintext
1351
1361
  ));
1352
- return concat2(i2osp(wrapped.length, WRAPPED_LEN_PREFIX), wrapped, iv, ct);
1362
+ return concat2(i2osp(enc.length, WRAPPED_LEN_PREFIX), enc, iv, ct);
1353
1363
  }
1354
1364
  __name(encryptBytes, "encryptBytes");
1355
1365
  async function encryptToString(recipient, message, aesKey, opts) {
@@ -1550,51 +1560,19 @@ __name(concat2, "concat");
1550
1560
 
1551
1561
  // src/index.ts
1552
1562
  var subtle2 = globalThis.crypto.subtle;
1563
+ async function create2(recipient, opts) {
1564
+ const { wrapped, key } = await encryptKey(recipient, null, opts);
1565
+ return { enc: wrapped, key };
1566
+ }
1567
+ __name(create2, "create");
1553
1568
  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 };
1569
+ const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
1570
+ return { enc: wrapped, key };
1576
1571
  }
1577
1572
  __name(seal, "seal");
1578
1573
  var open = Object.assign(openBytes, {
1579
1574
  raw: openRawBytes
1580
1575
  });
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
1576
  var encrypt = Object.assign(encryptBytes, {
1599
1577
  asString: encryptToString
1600
1578
  });
@@ -1733,7 +1711,50 @@ async function keySchedule(sharedSecret, info) {
1733
1711
  return { key, baseNonce };
1734
1712
  }
1735
1713
  __name(keySchedule, "keySchedule");
1714
+ async function openRawBytes(keypair, wrapped, opts) {
1715
+ if (wrapped.byteLength < ENC_LENGTH + AEAD_TAG_LENGTH) {
1716
+ throw new Error("malformed envelope: too short");
1717
+ }
1718
+ const info = normalizeInfo(opts?.info);
1719
+ const enc = wrapped.slice(0, ENC_LENGTH);
1720
+ const ciphertext = wrapped.slice(ENC_LENGTH);
1721
+ const sharedSecret = await decap(enc, keypair);
1722
+ const { key, baseNonce } = await keySchedule(sharedSecret, info);
1723
+ return aeadOpen(key, baseNonce, ciphertext);
1724
+ }
1725
+ __name(openRawBytes, "openRawBytes");
1726
+ async function openBytes(keypair, wrapped, opts) {
1727
+ const keyBytes = await openRawBytes(keypair, wrapped, opts);
1728
+ return importAesKey(keyBytes);
1729
+ }
1730
+ __name(openBytes, "openBytes");
1731
+ async function encryptKey(recipient, aesKey, opts) {
1732
+ const info = normalizeInfo(opts?.info);
1733
+ let keyBytes;
1734
+ if (aesKey instanceof Uint8Array) {
1735
+ validateRawKeyBytes(aesKey);
1736
+ keyBytes = aesKey;
1737
+ } else if (aesKey) {
1738
+ keyBytes = await exportAesKeyBytes(aesKey);
1739
+ validateRawKeyBytes(keyBytes);
1740
+ } else {
1741
+ const keysize = opts?.size ?? 256;
1742
+ validateKeysize(keysize);
1743
+ keyBytes = globalThis.crypto.getRandomValues(
1744
+ new Uint8Array(keysize / 8)
1745
+ );
1746
+ }
1747
+ const publicKey = await resolveRecipientPublicKey(recipient);
1748
+ const { sharedSecret, enc } = await encap(publicKey);
1749
+ const { key, baseNonce } = await keySchedule(sharedSecret, info);
1750
+ const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
1751
+ const wrapped = concat2(enc, ciphertext);
1752
+ const aesGcmKey = await importAesKey(keyBytes);
1753
+ return { wrapped, key: aesGcmKey };
1754
+ }
1755
+ __name(encryptKey, "encryptKey");
1736
1756
  export {
1757
+ create2 as create,
1737
1758
  decrypt,
1738
1759
  encrypt,
1739
1760
  open,