simple-hpke 0.1.3 → 0.2.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/README.md CHANGED
@@ -14,12 +14,22 @@ Hybrid Public Key Encryption
14
14
 
15
15
  1 dependency -- `uint8arrays`.
16
16
 
17
+ On every `create`/`encrypt` call, we generate a fresh ephemeral X25519 keypair,
18
+ do Diffie-Hellman against the recipient public key, and the key schedule derives
19
+ an AES-256-GCM key from that shared secret.
20
+
21
+ That DH-derived key is the wrapping key. The AES key the API hands back is a
22
+ second, independently random key, and the wrapping key encrypts its raw
23
+ bytes. *Key wrapping* here just means "HPKE seal where the plaintext
24
+ happens to be 16 or 32 bytes of key material." The RFC doesn't care what
25
+ the plaintext is.
26
+
17
27
  <details><summary><h2>Contents</h2></summary>
18
28
 
19
29
  <!-- toc -->
20
30
 
21
31
  - [Install](#install)
22
- - [Example](#example)
32
+ - [Examples](#examples)
23
33
  * [Key Wrapping](#key-wrapping)
24
34
  * [Hybrid Encryption](#hybrid-encryption)
25
35
  + [Encrypt / Decrypt](#encrypt--decrypt)
@@ -45,46 +55,47 @@ Hybrid Public Key Encryption
45
55
  npm i -S simple-hpke
46
56
  ```
47
57
 
48
- ## Example
58
+ ## Examples
49
59
 
50
- Wrap an AES key, or encrypt a message.
60
+ Create an AES key, or encrypt a message.
51
61
 
52
62
  ### Key Wrapping
53
63
 
54
- Encrypt an AES key to yourself, then recover it later.
64
+ Encrypt an AES key, then recover it later.
55
65
 
56
66
  ```ts
57
- import { create, seal, open } from 'simple-hpke'
67
+ import { create, encryptKey, open } from 'simple-hpke'
58
68
 
59
69
  // An X25519 keypair. (asymmetric keypair).
60
70
  // The private key can be non-extractable.
61
71
  // HPKE only needs `deriveBits`.
62
72
  const keypair = await crypto.subtle.generateKey(
63
73
  { name: 'X25519' },
64
- false, // extractable
74
+ false, // not extractable
65
75
  ['deriveBits']
66
76
  )
67
77
 
68
78
  // Create a new AES key and encrypt it to your public key.
69
- const { enc, key } = await create(keypair)
79
+ // `key` is the unencrypted new AES key
80
+ // `enc` is the AES key encrypted to the given public key
81
+ const { enc, key } = await create(keypair.publicKey) // or a buffer or string
70
82
 
71
83
  //
72
84
  // Or wrap an existing AES key. The supplied AES key must be extractable.
73
85
  //
74
-
75
86
  const aesKey = await crypto.subtle.generateKey(
76
87
  { name: 'AES-GCM', length: 256 },
77
- true, // extractable
88
+ true, // not extractable
78
89
  ['encrypt', 'decrypt']
79
90
  )
80
91
 
81
92
  // Wrap the existing key.
82
- const { enc: sealedKey } = await seal(keypair, aesKey)
93
+ const { enc: wrappedKey } = await encryptKey(keypair, aesKey)
83
94
 
84
95
  // or pass in just a public key
85
96
 
86
97
  // Later, recover the same key with your private key.
87
- const recoveredKey = await open(keypair, sealedKey)
98
+ const recoveredKey = await open(keypair, wrappedKey)
88
99
 
89
100
  // `recoveredKey` is equal to `aesKey`
90
101
  ```
@@ -96,7 +107,7 @@ const recoveredKey = await open(keypair, sealedKey)
96
107
  ### Hybrid Encryption
97
108
 
98
109
  Encrypt a message with AES-GCM, then encrypt the AES key to a given
99
- public key. The wrapped key is concattenated with the cipher text,
110
+ public key. The encrypted key is concattenated with the cipher text,
100
111
  along with the IV. The recipient uses their private key to open the AES key and
101
112
  decrypt the message.
102
113
 
@@ -151,7 +162,7 @@ new TextDecoder().decode(plaintext) // => 'attack at dawn'
151
162
  So that was a lot of code to encrypt and decrypt a message...
152
163
  This package exposes functions `encrypt` and `decrypt` that do the same thing.
153
164
 
154
- `encrypt` seals an AES key to the recipient, encrypts the message under
165
+ `encrypt` wraps an AES key to the recipient, encrypts the message under
155
166
  that key, and returns a single envelope:
156
167
  `wrappedLen + wrappedKey + iv + ciphertext`
157
168
  (a 2-byte length prefix, the wrapped key, the 12-byte AES-GCM IV, and the
@@ -179,7 +190,7 @@ import { encrypt, decrypt } from 'simple-hpke'
179
190
  // need a public key for the recipient
180
191
  const recipient = await crypto.subtle.generateKey(
181
192
  { name: 'X25519' },
182
- false, // extractable
193
+ false, // not extractable
183
194
  ['deriveBits']
184
195
  )
185
196
 
@@ -249,7 +260,7 @@ async function encrypt (
249
260
  message:Uint8Array|string,
250
261
  aesKey?:CryptoKey|Uint8Array|null,
251
262
  opts?:{
252
- keysize?:128|256
263
+ size?:128|256
253
264
  info?:Uint8Array|string
254
265
  }
255
266
  ):Promise<Uint8Array>
@@ -330,7 +341,7 @@ This exposes ESM and common JS via
330
341
 
331
342
  ### ESM
332
343
  ```js
333
- import { create, seal, open, encrypt, decrypt } from 'simple-hpke'
344
+ import { create, encryptKey, open, encrypt, decrypt } from 'simple-hpke'
334
345
  ```
335
346
 
336
347
  ### Common JS
@@ -1317,16 +1317,6 @@ __name(toString2, "toString");
1317
1317
 
1318
1318
  // src/index.ts
1319
1319
  var subtle = globalThis.crypto.subtle;
1320
- async function create2(recipient, opts) {
1321
- const { wrapped, key } = await encryptKey(recipient, null, opts);
1322
- return { enc: wrapped, key };
1323
- }
1324
- __name(create2, "create");
1325
- async function seal(recipient, aesKey, opts) {
1326
- const { wrapped, key } = await encryptKey(recipient, aesKey, opts);
1327
- return { enc: wrapped, key };
1328
- }
1329
- __name(seal, "seal");
1330
1320
  var open = Object.assign(openBytes, {
1331
1321
  raw: openRawBytes
1332
1322
  });
@@ -1507,7 +1497,7 @@ async function encryptKey(recipient, aesKey, opts) {
1507
1497
  const ciphertext = await aeadSeal(key, baseNonce, keyBytes);
1508
1498
  const wrapped = concat2(enc, ciphertext);
1509
1499
  const aesGcmKey = await importAesKey(keyBytes);
1510
- return { wrapped, key: aesGcmKey };
1500
+ return { enc: wrapped, key: aesGcmKey };
1511
1501
  }
1512
1502
  __name(encryptKey, "encryptKey");
1513
1503
 
@@ -1547,17 +1537,7 @@ async function aeadOpen(key, nonce, ciphertext) {
1547
1537
  __name(aeadOpen, "aeadOpen");
1548
1538
  async function encryptBytes(recipient, message, aesKey, opts) {
1549
1539
  const plaintext = typeof message === "string" ? new TextEncoder().encode(message) : message;
1550
- let enc;
1551
- let key;
1552
- if (!aesKey) {
1553
- const keys = await create2(recipient, opts);
1554
- enc = keys.enc;
1555
- key = keys.key;
1556
- } else {
1557
- const keys = await seal(recipient, aesKey, opts);
1558
- enc = keys.enc;
1559
- key = keys.key;
1560
- }
1540
+ const { enc, key } = await encryptKey(recipient, aesKey, opts);
1561
1541
  const iv = globalThis.crypto.getRandomValues(new Uint8Array(NN));
1562
1542
  const ct = new Uint8Array(await subtle2.encrypt(
1563
1543
  { name: "AES-GCM", iv },