simple-hpke 0.1.2 → 0.1.5

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`/`seal` 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,13 +55,13 @@ 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
67
  import { create, seal, open } from 'simple-hpke'
@@ -61,21 +71,23 @@ import { create, seal, open } from 'simple-hpke'
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
79
  const { enc, key } = await create(keypair)
70
80
 
81
+ //
71
82
  // Or wrap an existing AES key. The supplied AES key must be extractable.
83
+ //
72
84
  const aesKey = await crypto.subtle.generateKey(
73
85
  { name: 'AES-GCM', length: 256 },
74
- true, // extractable
86
+ true, // not extractable
75
87
  ['encrypt', 'decrypt']
76
88
  )
77
89
 
78
- // Wrap an existing key.
90
+ // Wrap the existing key.
79
91
  const { enc: sealedKey } = await seal(keypair, aesKey)
80
92
 
81
93
  // or pass in just a public key
@@ -86,8 +98,6 @@ const recoveredKey = await open(keypair, sealedKey)
86
98
  // `recoveredKey` is equal to `aesKey`
87
99
  ```
88
100
 
89
- See [docs/README.md](./docs/README.md) for the full API and rationale.
90
-
91
101
 
92
102
  ------------------------------------------------------
93
103
 
@@ -148,11 +158,11 @@ new TextDecoder().decode(plaintext) // => 'attack at dawn'
148
158
  #### Encrypt / Decrypt
149
159
 
150
160
  So that was a lot of code to encrypt and decrypt a message...
151
- This package exposes functions `encrypt` and `decrypt` which do the same thing.
161
+ This package exposes functions `encrypt` and `decrypt` that do the same thing.
152
162
 
153
163
  `encrypt` seals an AES key to the recipient, encrypts the message under
154
164
  that key, and returns a single envelope:
155
- `wrappedLen + wrapped + iv + ciphertext`
165
+ `wrappedLen + wrappedKey + iv + ciphertext`
156
166
  (a 2-byte length prefix, the wrapped key, the 12-byte AES-GCM IV, and the
157
167
  cipher text). `decrypt` reverses it, returning the plaintext bytes.
158
168
 
@@ -168,27 +178,42 @@ cipher text). `decrypt` reverses it, returning the plaintext bytes.
168
178
 
169
179
 
170
180
  ```ts
181
+ import { toString } from 'uint8arrays'
171
182
  import { encrypt, decrypt } from 'simple-hpke'
172
183
 
184
+ // ----------------------
185
+ // Encrypt
186
+ // ----------------------
187
+
173
188
  // need a public key for the recipient
174
189
  const recipient = await crypto.subtle.generateKey(
175
190
  { name: 'X25519' },
176
- false,
191
+ false, // not extractable
177
192
  ['deriveBits']
178
193
  )
179
194
 
180
- // create a new AES key, encrypt a message, and get back one envelope
181
- const encryptedMessage = await encrypt(recipient, 'hello encryption')
195
+ // create a new AES key, encrypt a message, and get back an "envelope"
196
+ const encryptedMessage = await encrypt(recipient.publicKey, 'hello encryption')
182
197
 
183
- // the recipient recovers the message with their private key
184
- const text = await decrypt.asString(recipient, encryptedMessage)
198
+ // encrypt to a public key as Uint8Array
199
+ const publicBytes = new Uint8Array(
200
+ await subtle.exportKey('raw', recipient.publicKey)
201
+ )
202
+ const envelope = await encrypt(publicBytes, 'hello again')
185
203
 
186
- // use `decrypt` to get a Uint8Array
187
- const bytes = await decrypt(recipient, encryptedMessage)
204
+ // encrypt to a stringified public key
205
+ // create a string with `uint8arrays.toString`
206
+ const publicString = toString(publicBytes, 'base64url')
207
+ const envelopeAgain = await encrypt({
208
+ publicKey: publicString,
209
+ encoding: 'base64url' // <-- this is the default
210
+ }, 'hello again, string version')
188
211
 
189
212
  //
190
- // use an existing AES key
213
+ // encrypt with an existing AES key
214
+ // (a key not generated by this module)
191
215
  //
216
+
192
217
  const existingKey = await crypto.subtle.generateKey(
193
218
  { name: 'AES-GCM', length: 256 },
194
219
  true, // extractable
@@ -200,8 +225,23 @@ const anotherEncryptedMsg = await encrypt(
200
225
  'hello again',
201
226
  existingKey
202
227
  )
228
+
229
+ // ----------------------
230
+ // Decrypt
231
+ // ----------------------
232
+
233
+ // the recipient recovers the message with their private key
234
+ const text = await decrypt.asString(recipient, encryptedMessage)
235
+
236
+ // use `decrypt` to get a Uint8Array
237
+ const bytes = await decrypt(recipient, encryptedMessage)
238
+
239
+ // get a string
240
+ const plaintext = await decrypt.asString(recipient, envelope)
203
241
  ```
204
242
 
243
+ ---
244
+
205
245
  ##### `encrypt`
206
246
 
207
247
  The recipient can be a crypto key, a Uint8Array, or a string public key.
@@ -218,7 +258,7 @@ async function encrypt (
218
258
  message:Uint8Array|string,
219
259
  aesKey?:CryptoKey|Uint8Array|null,
220
260
  opts?:{
221
- keysize?:128|256
261
+ size?:128|256
222
262
  info?:Uint8Array|string
223
263
  }
224
264
  ):Promise<Uint8Array>