@ubay182/sveltekit-hpke-wrapper 1.0.1 โ 1.0.3
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 +198 -162
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,92 +1,144 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @ubay182/sveltekit-hpke-wrapper
|
|
2
2
|
|
|
3
3
|
HPKE (Hybrid Public Key Encryption) wrapper for SvelteKit applications with end-to-end encryption support.
|
|
4
4
|
|
|
5
5
|
## ๐ Features
|
|
6
6
|
|
|
7
|
-
- โ
**Complete HPKE Implementation**
|
|
8
|
-
- โ
**End-to-End Encryption**
|
|
9
|
-
- โ
**SvelteKit Integration**
|
|
10
|
-
- โ
**TypeScript Support**
|
|
11
|
-
- โ
**X25519 Key Exchange**
|
|
12
|
-
- โ
**AES-128-GCM & ChaCha20**
|
|
7
|
+
- โ
**Complete HPKE Implementation** โ RFC 9180 compliant
|
|
8
|
+
- โ
**End-to-End Encryption** โ Client โ Server encryption
|
|
9
|
+
- โ
**SvelteKit Integration** โ Ready-to-use API endpoint creators
|
|
10
|
+
- โ
**TypeScript Support** โ Full type definitions
|
|
11
|
+
- โ
**X25519 Key Exchange** โ Elliptic curve Diffie-Hellman
|
|
12
|
+
- โ
**AES-128-GCM & ChaCha20-Poly1305** โ Authenticated encryption
|
|
13
13
|
|
|
14
14
|
## ๐ฆ Installation
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npm install @
|
|
17
|
+
npm install @ubay182/sveltekit-hpke-wrapper
|
|
18
18
|
# or
|
|
19
|
-
pnpm add @
|
|
20
|
-
# or
|
|
21
|
-
yarn add @hpke/sveltekit-wrapper
|
|
19
|
+
pnpm add @ubay182/sveltekit-hpke-wrapper
|
|
22
20
|
```
|
|
23
21
|
|
|
24
22
|
## ๐ฏ Quick Start
|
|
25
23
|
|
|
26
|
-
### 1.
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
### 1. Client-Side (Svelte Component)
|
|
25
|
+
|
|
26
|
+
```svelte
|
|
27
|
+
<script lang="ts">
|
|
28
|
+
import {
|
|
29
|
+
generateKeyPair,
|
|
30
|
+
hpkeEncrypt,
|
|
31
|
+
hpkeDecrypt,
|
|
32
|
+
uint8ArrayToBase64,
|
|
33
|
+
base64ToUint8Array,
|
|
34
|
+
createHpkeSuite
|
|
35
|
+
} from '@ubay182/sveltekit-hpke-wrapper';
|
|
36
|
+
|
|
37
|
+
let serverPubKey = $state<any>(null);
|
|
38
|
+
let clientPrivKey = $state<any>(null);
|
|
39
|
+
let clientPubKeyB64 = $state('');
|
|
40
|
+
let decryptedText = $state('');
|
|
41
|
+
|
|
42
|
+
// Step 1: Fetch server public key
|
|
43
|
+
async function getServerKey() {
|
|
44
|
+
const res = await fetch('/api/hpke-keys');
|
|
45
|
+
const data = await res.json();
|
|
46
|
+
|
|
47
|
+
const keyBytes = base64ToUint8Array(data.publicKey);
|
|
48
|
+
const suite = createHpkeSuite();
|
|
49
|
+
serverPubKey = await suite.kem.importKey('raw', keyBytes.buffer as ArrayBuffer, true);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Step 2: Generate client key pair
|
|
53
|
+
async function generateClientKeys() {
|
|
54
|
+
const keys = await generateKeyPair();
|
|
55
|
+
clientPrivKey = keys.privateKey;
|
|
56
|
+
clientPubKeyB64 = uint8ArrayToBase64(keys.publicKeyRaw);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Step 3: Encrypt & send
|
|
60
|
+
async function encryptAndSend(payload: any) {
|
|
61
|
+
const message = JSON.stringify(payload);
|
|
62
|
+
const result = await hpkeEncrypt(message, serverPubKey);
|
|
63
|
+
|
|
64
|
+
const ciphertext = uint8ArrayToBase64(new Uint8Array(result.ciphertext));
|
|
65
|
+
const enc = uint8ArrayToBase64(new Uint8Array(result.enc));
|
|
66
|
+
|
|
67
|
+
const res = await fetch('/api/hpke-proxy', {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({ ciphertext, enc, clientPublicKey: clientPubKeyB64 })
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const data = await res.json();
|
|
74
|
+
return data; // { ciphertext, enc }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Step 4: Decrypt server response
|
|
78
|
+
async function decryptResponse(encryptedData: { ciphertext: string; enc: string }) {
|
|
79
|
+
const ct = base64ToUint8Array(encryptedData.ciphertext);
|
|
80
|
+
const enc = base64ToUint8Array(encryptedData.enc);
|
|
81
|
+
|
|
82
|
+
decryptedText = await hpkeDecrypt(
|
|
83
|
+
ct.buffer as ArrayBuffer,
|
|
84
|
+
enc.buffer as ArrayBuffer,
|
|
85
|
+
clientPrivKey
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
89
|
+
```
|
|
39
90
|
|
|
40
|
-
|
|
41
|
-
const publicKeyBase64 = exportKeyToBase64(publicKey);
|
|
91
|
+
### 2. Server-Side (SvelteKit Routes)
|
|
42
92
|
|
|
43
|
-
|
|
44
|
-
const serverPublicKey = await importKeyFromBase64(serverPublicKeyBase64);
|
|
93
|
+
Create a shared HPKE server instance so all routes use the same key pair:
|
|
45
94
|
|
|
46
|
-
|
|
47
|
-
|
|
95
|
+
```typescript
|
|
96
|
+
// src/lib/hpke-server-instance.ts
|
|
97
|
+
import { createHpkeServer, type HpkeServerInstance } from '@ubay182/sveltekit-hpke-wrapper';
|
|
48
98
|
|
|
49
|
-
|
|
50
|
-
const decrypted = await hpkeDecrypt(ciphertext, enc, privateKey);
|
|
99
|
+
export const hpkeServer: HpkeServerInstance = createHpkeServer({ autoGenerateKeys: false });
|
|
51
100
|
```
|
|
52
101
|
|
|
53
|
-
### 2. Server-Side with SvelteKit
|
|
54
|
-
|
|
55
102
|
```typescript
|
|
56
|
-
// src/routes/api/hpke/+server.ts
|
|
57
|
-
import {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
103
|
+
// src/routes/api/hpke-keys/+server.ts
|
|
104
|
+
import { hpkeServer } from '$lib/hpke-server-instance';
|
|
105
|
+
|
|
106
|
+
export async function GET() {
|
|
107
|
+
const publicKey = await hpkeServer.init();
|
|
108
|
+
|
|
109
|
+
return new Response(
|
|
110
|
+
JSON.stringify({
|
|
111
|
+
publicKey,
|
|
112
|
+
algorithm: 'X25519-HKDF-SHA256',
|
|
113
|
+
aead: 'AES-128-GCM'
|
|
114
|
+
}),
|
|
115
|
+
{
|
|
116
|
+
headers: { 'Content-Type': 'application/json' }
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
}
|
|
73
120
|
```
|
|
74
121
|
|
|
75
|
-
### 3. Manual Server Setup
|
|
76
|
-
|
|
77
122
|
```typescript
|
|
78
|
-
|
|
123
|
+
// src/routes/api/hpke-proxy/+server.ts
|
|
124
|
+
import { hpkeServer } from '$lib/hpke-server-instance';
|
|
79
125
|
|
|
80
|
-
|
|
126
|
+
export async function POST({ request }: { request: Request }) {
|
|
127
|
+
const body = await request.json();
|
|
128
|
+
const { ciphertext, enc, clientPublicKey } = body;
|
|
81
129
|
|
|
82
|
-
//
|
|
83
|
-
const
|
|
130
|
+
// Decrypt client message
|
|
131
|
+
const decrypted = await hpkeServer.decrypt(ciphertext, enc, clientPublicKey);
|
|
84
132
|
|
|
85
|
-
//
|
|
86
|
-
const decrypted = await server.decrypt(ciphertext, enc, clientPublicKey);
|
|
133
|
+
// ... process decrypted data, call external APIs, etc. ...
|
|
87
134
|
|
|
88
|
-
// Encrypt response
|
|
89
|
-
const encrypted = await
|
|
135
|
+
// Encrypt response
|
|
136
|
+
const encrypted = await hpkeServer.encrypt(responseData, clientPublicKey);
|
|
137
|
+
|
|
138
|
+
return new Response(JSON.stringify(encrypted), {
|
|
139
|
+
headers: { 'Content-Type': 'application/json' }
|
|
140
|
+
});
|
|
141
|
+
}
|
|
90
142
|
```
|
|
91
143
|
|
|
92
144
|
## ๐ API Reference
|
|
@@ -94,164 +146,152 @@ const encrypted = await server.encrypt(responseData, clientPublicKey);
|
|
|
94
146
|
### Core Functions
|
|
95
147
|
|
|
96
148
|
#### `generateKeyPair()`
|
|
149
|
+
|
|
97
150
|
Generate a new HPKE key pair.
|
|
98
151
|
|
|
99
152
|
```typescript
|
|
100
153
|
async function generateKeyPair(): Promise<{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
154
|
+
publicKey: any; // XCryptoKey
|
|
155
|
+
privateKey: any; // XCryptoKey
|
|
156
|
+
publicKeyRaw: Uint8Array; // Raw bytes for transmission
|
|
157
|
+
}>;
|
|
105
158
|
```
|
|
106
159
|
|
|
107
160
|
#### `hpkeEncrypt(message, recipientPublicKey)`
|
|
108
|
-
|
|
161
|
+
|
|
162
|
+
Encrypt a message. Returns `{ ciphertext: ArrayBuffer, enc: ArrayBuffer }`.
|
|
109
163
|
|
|
110
164
|
```typescript
|
|
111
|
-
|
|
112
|
-
message: string,
|
|
113
|
-
recipientPublicKey: any
|
|
114
|
-
): Promise<{
|
|
115
|
-
ciphertext: ArrayBuffer;
|
|
116
|
-
enc: ArrayBuffer;
|
|
117
|
-
}>
|
|
165
|
+
const { ciphertext, enc } = await hpkeEncrypt('Secret message', serverPublicKey);
|
|
118
166
|
```
|
|
119
167
|
|
|
120
168
|
#### `hpkeDecrypt(ciphertext, enc, recipientPrivateKey)`
|
|
121
|
-
|
|
169
|
+
|
|
170
|
+
Decrypt a message. Returns plaintext string.
|
|
122
171
|
|
|
123
172
|
```typescript
|
|
124
|
-
|
|
125
|
-
ciphertext: ArrayBuffer,
|
|
126
|
-
enc: Uint8Array | ArrayBuffer,
|
|
127
|
-
recipientPrivateKey: any
|
|
128
|
-
): Promise<string>
|
|
173
|
+
const decrypted = await hpkeDecrypt(ciphertextBuffer, encBuffer, privateKey);
|
|
129
174
|
```
|
|
130
175
|
|
|
131
|
-
#### `
|
|
132
|
-
|
|
176
|
+
#### `createHpkeSuite()`
|
|
177
|
+
|
|
178
|
+
Create an HPKE suite with AES-128-GCM.
|
|
133
179
|
|
|
134
180
|
```typescript
|
|
135
|
-
|
|
181
|
+
const suite = createHpkeSuite();
|
|
182
|
+
const keyPair = await suite.kem.generateKeyPair();
|
|
183
|
+
const importedKey = await suite.kem.importKey('raw', keyBytes.buffer, true);
|
|
136
184
|
```
|
|
137
185
|
|
|
138
|
-
#### `
|
|
139
|
-
|
|
186
|
+
#### `createHpkeSuiteChaCha20()`
|
|
187
|
+
|
|
188
|
+
Create an HPKE suite with ChaCha20-Poly1305.
|
|
140
189
|
|
|
141
190
|
```typescript
|
|
142
|
-
|
|
191
|
+
const suite = createHpkeSuiteChaCha20();
|
|
143
192
|
```
|
|
144
193
|
|
|
145
|
-
|
|
194
|
+
#### `exportKeyToBase64(publicKey)`
|
|
146
195
|
|
|
147
|
-
|
|
148
|
-
Create HPKE server instance.
|
|
196
|
+
Export public key to base64 string.
|
|
149
197
|
|
|
150
198
|
```typescript
|
|
151
|
-
|
|
152
|
-
autoGenerateKeys?: boolean; // Default: true
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
interface HpkeServerInstance {
|
|
156
|
-
getPublicKeyBase64(): string;
|
|
157
|
-
decrypt(ciphertext: string, enc: string, clientPublicKey: string): Promise<string>;
|
|
158
|
-
encrypt(message: string, clientPublicKey: string): Promise<{
|
|
159
|
-
ciphertext: string;
|
|
160
|
-
enc: string;
|
|
161
|
-
}>;
|
|
162
|
-
}
|
|
199
|
+
const b64 = exportKeyToBase64(publicKey);
|
|
163
200
|
```
|
|
164
201
|
|
|
165
|
-
|
|
202
|
+
#### `importKeyFromBase64(base64)`
|
|
166
203
|
|
|
167
|
-
|
|
168
|
-
Create complete API endpoints.
|
|
204
|
+
Import public key from base64 string.
|
|
169
205
|
|
|
170
206
|
```typescript
|
|
171
|
-
|
|
172
|
-
autoGenerateKeys?: boolean;
|
|
173
|
-
onRequest?: (decrypted: any, request: Request) => Promise<any>;
|
|
174
|
-
onError?: (error: Error, request: Request) => Promise<Response>;
|
|
175
|
-
}
|
|
207
|
+
const publicKey = await importKeyFromBase64(b64String);
|
|
176
208
|
```
|
|
177
209
|
|
|
178
|
-
|
|
210
|
+
#### `uint8ArrayToBase64(data)` / `base64ToUint8Array(base64)`
|
|
179
211
|
|
|
180
|
-
|
|
212
|
+
Utility functions for encoding/decoding.
|
|
181
213
|
|
|
182
214
|
```typescript
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const suite = createHpkeSuiteChaCha20();
|
|
186
|
-
// Use suite for encryption/decryption
|
|
215
|
+
const b64 = uint8ArrayToBase64(bytes);
|
|
216
|
+
const bytes = base64ToUint8Array(b64);
|
|
187
217
|
```
|
|
188
218
|
|
|
189
|
-
###
|
|
219
|
+
### Server Functions
|
|
220
|
+
|
|
221
|
+
#### `createHpkeServer(config?)`
|
|
222
|
+
|
|
223
|
+
Create an HPKE server instance with key management.
|
|
190
224
|
|
|
191
225
|
```typescript
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const server = createHpkeServer({ autoGenerateKeys: false });
|
|
226
|
+
interface HpkeServerConfig {
|
|
227
|
+
autoGenerateKeys?: boolean; // Default: true
|
|
228
|
+
}
|
|
196
229
|
|
|
197
|
-
|
|
198
|
-
|
|
230
|
+
interface HpkeServerInstance {
|
|
231
|
+
init(): Promise<string>; // Generate keys, return public key as base64
|
|
232
|
+
getPublicKeyBase64(): string;
|
|
233
|
+
decrypt(ciphertext: string, enc: string, clientPublicKey: string): Promise<string>;
|
|
234
|
+
encrypt(message: string, clientPublicKey: string): Promise<{ ciphertext: string; enc: string }>;
|
|
235
|
+
}
|
|
199
236
|
```
|
|
200
237
|
|
|
201
|
-
###
|
|
238
|
+
### SvelteKit Integration
|
|
239
|
+
|
|
240
|
+
#### `createHpkeEndpoint(config?)`
|
|
241
|
+
|
|
242
|
+
Create complete GET/POST handlers for a SvelteKit route.
|
|
202
243
|
|
|
203
244
|
```typescript
|
|
204
245
|
const { GET, POST } = createHpkeEndpoint({
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}),
|
|
213
|
-
{ status: 500 }
|
|
214
|
-
);
|
|
215
|
-
}
|
|
246
|
+
onRequest: async (decryptedData, request) => {
|
|
247
|
+
// Process decrypted request
|
|
248
|
+
return await fetch('https://api.example.com/data', {
|
|
249
|
+
method: 'POST',
|
|
250
|
+
body: JSON.stringify(decryptedData)
|
|
251
|
+
}).then((r) => r.json());
|
|
252
|
+
}
|
|
216
253
|
});
|
|
217
254
|
```
|
|
218
255
|
|
|
219
|
-
## ๐
|
|
220
|
-
|
|
221
|
-
โ ๏ธ **Important**: This is a wrapper library for convenience. For production:
|
|
222
|
-
|
|
223
|
-
1. **Key Storage**: Use HSM, AWS KMS, or Azure Key Vault
|
|
224
|
-
2. **HTTPS**: Always use HTTPS in production
|
|
225
|
-
3. **Authentication**: Implement proper auth mechanisms
|
|
226
|
-
4. **Rate Limiting**: Add rate limiting to prevent abuse
|
|
227
|
-
5. **Key Rotation**: Implement regular key rotation
|
|
228
|
-
6. **Audit**: Have security audits performed
|
|
229
|
-
|
|
230
|
-
## ๐ How It Works
|
|
256
|
+
## ๐ How It Works
|
|
231
257
|
|
|
232
258
|
```
|
|
233
259
|
Client Server
|
|
234
260
|
โ โ
|
|
235
|
-
โโโโ GET /api/hpke
|
|
261
|
+
โโโโ GET /api/hpke-keys โโโโโโ>โ
|
|
236
262
|
โ โ
|
|
237
263
|
โ<โโโโ Public Key (base64) โโโโโ
|
|
238
264
|
โ โ
|
|
239
|
-
โโโโ
|
|
265
|
+
โโโโ generateKeyPair() โโโโโโโโโ (client generates its own keys)
|
|
240
266
|
โ โ
|
|
241
|
-
โโโโ
|
|
267
|
+
โโโโ hpkeEncrypt(payload) โโโโโ
|
|
242
268
|
โ โ
|
|
243
|
-
|
|
244
|
-
โ
|
|
245
|
-
โ โ<โโ Process โโโโ
|
|
269
|
+
โโโโ POST { ciphertext, enc, โ>โ
|
|
270
|
+
โ clientPublicKey } โ
|
|
246
271
|
โ โ
|
|
247
|
-
โ โโโโ
|
|
248
|
-
โ โ
|
|
249
|
-
|
|
272
|
+
โ โโโโ decrypt() โโโ
|
|
273
|
+
โ โ โ
|
|
274
|
+
โ โ<โโ Process โโโโโโ
|
|
275
|
+
โ โ โ
|
|
276
|
+
โ โ<โโ encrypt() โโโโ
|
|
250
277
|
โ โ
|
|
251
|
-
|
|
278
|
+
โ<โโโโ { ciphertext, enc } โโโโโ
|
|
279
|
+
โ โ
|
|
280
|
+
โโโโ hpkeDecrypt(response) โโโโโ
|
|
252
281
|
```
|
|
253
282
|
|
|
254
|
-
##
|
|
283
|
+
## ๐ Security Notes
|
|
284
|
+
|
|
285
|
+
โ ๏ธ **Important**: This is a convenience wrapper library. For production:
|
|
286
|
+
|
|
287
|
+
1. **Key Storage** โ Use HSM, AWS KMS, or Azure Key Vault
|
|
288
|
+
2. **HTTPS** โ Always use HTTPS in production
|
|
289
|
+
3. **Authentication** โ Implement proper auth mechanisms
|
|
290
|
+
4. **Rate Limiting** โ Add rate limiting to prevent abuse
|
|
291
|
+
5. **Key Rotation** โ Implement regular key rotation
|
|
292
|
+
6. **Audit** โ Have security audits performed
|
|
293
|
+
|
|
294
|
+
## ๐งช Development
|
|
255
295
|
|
|
256
296
|
```bash
|
|
257
297
|
# Build the package
|
|
@@ -260,7 +300,7 @@ npm run build
|
|
|
260
300
|
# Type check
|
|
261
301
|
npm run lint
|
|
262
302
|
|
|
263
|
-
# Watch mode
|
|
303
|
+
# Watch mode
|
|
264
304
|
npm run dev
|
|
265
305
|
```
|
|
266
306
|
|
|
@@ -268,12 +308,8 @@ npm run dev
|
|
|
268
308
|
|
|
269
309
|
MIT
|
|
270
310
|
|
|
271
|
-
## ๐ค Contributing
|
|
272
|
-
|
|
273
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
274
|
-
|
|
275
311
|
## ๐ Resources
|
|
276
312
|
|
|
277
|
-
- [RFC 9180
|
|
313
|
+
- [RFC 9180 โ HPKE Specification](https://www.rfc-editor.org/rfc/rfc9180.html)
|
|
278
314
|
- [hpke-js Library](https://github.com/dajiaji/hpke-js)
|
|
279
315
|
- [SvelteKit Documentation](https://kit.svelte.dev/docs)
|
package/package.json
CHANGED