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 +60 -20
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/constants.min.cjs.map +1 -1
- package/dist/constants.min.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.min.cjs.map +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/meta.json +4 -4
- package/dist/util.cjs.map +1 -1
- package/dist/util.d.ts +6 -6
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js.map +1 -1
- package/dist/util.min.cjs.map +1 -1
- package/dist/util.min.js.map +1 -1
- package/package.json +1 -1
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
|
-
- [
|
|
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
|
-
##
|
|
58
|
+
## Examples
|
|
49
59
|
|
|
50
|
-
|
|
60
|
+
Create an AES key, or encrypt a message.
|
|
51
61
|
|
|
52
62
|
### Key Wrapping
|
|
53
63
|
|
|
54
|
-
Encrypt an AES key
|
|
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
|
|
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`
|
|
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 +
|
|
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
|
|
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
|
-
//
|
|
184
|
-
const
|
|
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
|
-
//
|
|
187
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
261
|
+
size?:128|256
|
|
222
262
|
info?:Uint8Array|string
|
|
223
263
|
}
|
|
224
264
|
):Promise<Uint8Array>
|