lockform 1.0.0 → 2.0.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 +132 -17
- package/dist/crypto.d.ts +8 -5
- package/dist/crypto.js +49 -36
- package/dist/decrypt.js +12 -3
- package/dist/types.d.ts +4 -2
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Lockform
|
|
2
2
|
|
|
3
|
-
Official SDK for processing Lockform webhook submissions with end-to-end encryption.
|
|
3
|
+
Official SDK for processing Lockform webhook submissions with end-to-end encryption using X25519 + AES-256-GCM.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -13,29 +13,52 @@ npm install lockform
|
|
|
13
13
|
- **Decrypt webhook data**: Easily decrypt encrypted form submissions received via webhooks
|
|
14
14
|
- **Signature verification**: Verify webhook authenticity using HMAC-SHA256 signatures
|
|
15
15
|
- **Field mapping**: Automatically map field IDs to human-readable CSV names
|
|
16
|
+
- **X25519 encryption**: Modern, fast elliptic curve cryptography
|
|
17
|
+
- **BIP39 support**: Works with 15-word recovery phrases or base64 private keys
|
|
16
18
|
- **TypeScript support**: Full type definitions included
|
|
17
19
|
|
|
18
20
|
## Quick Start
|
|
19
21
|
|
|
20
22
|
### Decrypting Webhook Data
|
|
21
23
|
|
|
24
|
+
You can decrypt webhooks using either your 15-word recovery phrase or a base64-encoded private key:
|
|
25
|
+
|
|
26
|
+
**Using recovery phrase:**
|
|
27
|
+
|
|
22
28
|
```typescript
|
|
23
29
|
import { decryptWebhookData } from 'lockform'
|
|
24
30
|
|
|
25
|
-
const
|
|
26
|
-
YOUR_PRIVATE_KEY_HERE
|
|
27
|
-
-----END PRIVATE KEY-----`
|
|
31
|
+
const mnemonic = 'your fifteen word recovery phrase goes here and must be exactly fifteen words'
|
|
28
32
|
|
|
29
33
|
app.post('/webhook', async (req, res) => {
|
|
30
34
|
const payload = req.body
|
|
31
35
|
|
|
32
36
|
const result = await decryptWebhookData({
|
|
33
37
|
payload,
|
|
34
|
-
|
|
38
|
+
passphrase: mnemonic,
|
|
35
39
|
})
|
|
36
40
|
|
|
37
41
|
console.log('Mapped data:', result.mappedData)
|
|
42
|
+
res.json({ success: true })
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Using base64 private key:**
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { decryptWebhookData } from 'lockform'
|
|
50
|
+
|
|
51
|
+
const privateKeyBase64 = 'your-base64-encoded-x25519-private-key'
|
|
52
|
+
|
|
53
|
+
app.post('/webhook', async (req, res) => {
|
|
54
|
+
const payload = req.body
|
|
55
|
+
|
|
56
|
+
const result = await decryptWebhookData({
|
|
57
|
+
payload,
|
|
58
|
+
passphrase: privateKeyBase64,
|
|
59
|
+
})
|
|
38
60
|
|
|
61
|
+
console.log('Mapped data:', result.mappedData)
|
|
39
62
|
res.json({ success: true })
|
|
40
63
|
})
|
|
41
64
|
```
|
|
@@ -61,11 +84,10 @@ app.post('/webhook', async (req, res) => {
|
|
|
61
84
|
|
|
62
85
|
const result = await decryptWebhookData({
|
|
63
86
|
payload: req.body,
|
|
64
|
-
|
|
87
|
+
passphrase: process.env.LOCKFORM_RECOVERY_PHRASE,
|
|
65
88
|
})
|
|
66
89
|
|
|
67
90
|
console.log('Decrypted data:', result.mappedData)
|
|
68
|
-
|
|
69
91
|
res.json({ success: true })
|
|
70
92
|
})
|
|
71
93
|
```
|
|
@@ -74,11 +96,11 @@ app.post('/webhook', async (req, res) => {
|
|
|
74
96
|
|
|
75
97
|
### `decryptWebhookData(options)`
|
|
76
98
|
|
|
77
|
-
Decrypts an encrypted webhook payload from Lockform.
|
|
99
|
+
Decrypts an encrypted webhook payload from Lockform using X25519 + AES-256-GCM.
|
|
78
100
|
|
|
79
101
|
**Parameters:**
|
|
80
102
|
- `options.payload` (WebhookPayload): The webhook payload received from Lockform
|
|
81
|
-
- `options.
|
|
103
|
+
- `options.passphrase` (string): Your 15-word BIP39 recovery phrase (or optionally, base64-encoded X25519 private key)
|
|
82
104
|
|
|
83
105
|
**Returns:** `Promise<DecryptedSubmission>`
|
|
84
106
|
|
|
@@ -101,10 +123,11 @@ Decrypts an encrypted webhook payload from Lockform.
|
|
|
101
123
|
```typescript
|
|
102
124
|
const result = await decryptWebhookData({
|
|
103
125
|
payload: webhookPayload,
|
|
104
|
-
|
|
126
|
+
passphrase: myRecoveryPhrase,
|
|
105
127
|
})
|
|
106
128
|
|
|
107
|
-
console.log(result.mappedData)
|
|
129
|
+
console.log(result.mappedData) // { name: "John Doe", email: "john@example.com" }
|
|
130
|
+
console.log(result.rawData) // { "field-id-1": "John Doe", "field-id-2": "john@example.com" }
|
|
108
131
|
```
|
|
109
132
|
|
|
110
133
|
### `verifyWebhookSignature(options)`
|
|
@@ -143,10 +166,12 @@ interface WebhookPayload {
|
|
|
143
166
|
form_id: string
|
|
144
167
|
ciphertext: string
|
|
145
168
|
iv: string
|
|
146
|
-
|
|
169
|
+
salt: string
|
|
170
|
+
ephemeral_public_key: string
|
|
147
171
|
auth_tag: string
|
|
148
172
|
algorithm: string
|
|
149
173
|
nonce: string
|
|
174
|
+
encryption_timestamp: number
|
|
150
175
|
timestamp: string
|
|
151
176
|
field_mapping: Record<string, string>
|
|
152
177
|
}
|
|
@@ -170,6 +195,8 @@ interface DecryptedSubmission {
|
|
|
170
195
|
|
|
171
196
|
## Complete Example
|
|
172
197
|
|
|
198
|
+
### Node.js / Express
|
|
199
|
+
|
|
173
200
|
```typescript
|
|
174
201
|
import express from 'express'
|
|
175
202
|
import { decryptWebhookData, verifyWebhookSignature } from 'lockform'
|
|
@@ -177,7 +204,7 @@ import { decryptWebhookData, verifyWebhookSignature } from 'lockform'
|
|
|
177
204
|
const app = express()
|
|
178
205
|
app.use(express.json())
|
|
179
206
|
|
|
180
|
-
const
|
|
207
|
+
const RECOVERY_PHRASE = process.env.LOCKFORM_RECOVERY_PHRASE
|
|
181
208
|
const WEBHOOK_SECRET = process.env.LOCKFORM_WEBHOOK_SECRET
|
|
182
209
|
|
|
183
210
|
app.post('/lockform-webhook', async (req, res) => {
|
|
@@ -199,7 +226,7 @@ app.post('/lockform-webhook', async (req, res) => {
|
|
|
199
226
|
|
|
200
227
|
const result = await decryptWebhookData({
|
|
201
228
|
payload,
|
|
202
|
-
|
|
229
|
+
passphrase: RECOVERY_PHRASE,
|
|
203
230
|
})
|
|
204
231
|
|
|
205
232
|
console.log('Form ID:', result.metadata.form_id)
|
|
@@ -218,12 +245,100 @@ app.listen(3000, () => {
|
|
|
218
245
|
})
|
|
219
246
|
```
|
|
220
247
|
|
|
248
|
+
### Deno Edge Function
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import { decryptWebhookData, verifyWebhookSignature, type WebhookPayload } from 'lockform'
|
|
252
|
+
|
|
253
|
+
Deno.serve(async (req) => {
|
|
254
|
+
if (req.method !== 'POST') {
|
|
255
|
+
return new Response(
|
|
256
|
+
JSON.stringify({ error: 'Method not allowed' }),
|
|
257
|
+
{ status: 405, headers: { 'Content-Type': 'application/json' } }
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const recoveryPhrase = Deno.env.get('LOCKFORM_RECOVERY_PHRASE')
|
|
262
|
+
const webhookSecret = Deno.env.get('WEBHOOK_SECRET')
|
|
263
|
+
|
|
264
|
+
if (!recoveryPhrase) {
|
|
265
|
+
return new Response(
|
|
266
|
+
JSON.stringify({ error: 'Recovery phrase not configured' }),
|
|
267
|
+
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const signature = req.headers.get('x-signature-sha256')
|
|
272
|
+
const rawBody = await req.text()
|
|
273
|
+
const payload: WebhookPayload = JSON.parse(rawBody)
|
|
274
|
+
|
|
275
|
+
if (webhookSecret && signature) {
|
|
276
|
+
const isValid = await verifyWebhookSignature({
|
|
277
|
+
payload: rawBody,
|
|
278
|
+
signature,
|
|
279
|
+
secret: webhookSecret,
|
|
280
|
+
})
|
|
281
|
+
if (!isValid) {
|
|
282
|
+
return new Response(
|
|
283
|
+
JSON.stringify({ error: 'Invalid signature' }),
|
|
284
|
+
{ status: 401, headers: { 'Content-Type': 'application/json' } }
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const result = await decryptWebhookData({
|
|
290
|
+
payload,
|
|
291
|
+
passphrase: recoveryPhrase,
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
console.log('New submission:', result.mappedData)
|
|
295
|
+
|
|
296
|
+
return new Response(
|
|
297
|
+
JSON.stringify({ success: true }),
|
|
298
|
+
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
|
299
|
+
)
|
|
300
|
+
})
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Cryptographic Details
|
|
304
|
+
|
|
305
|
+
Lockform uses modern, audited cryptography for maximum security:
|
|
306
|
+
|
|
307
|
+
- **Key Exchange**: X25519 (Curve25519 Diffie-Hellman)
|
|
308
|
+
- **Symmetric Encryption**: AES-256-GCM (Galois/Counter Mode)
|
|
309
|
+
- **Key Derivation**: HKDF-SHA256 with separate salt
|
|
310
|
+
- **Mnemonic-to-Key**: PBKDF2-SHA512 (600,000 iterations)
|
|
311
|
+
- **Mnemonic**: BIP39 (15 words, 160 bits entropy)
|
|
312
|
+
|
|
313
|
+
**Why X25519 instead of RSA?**
|
|
314
|
+
- Smaller keys (32 bytes vs 4096 bits)
|
|
315
|
+
- Faster operations
|
|
316
|
+
- Better security per bit
|
|
317
|
+
- Modern, constant-time implementation
|
|
318
|
+
- Forward secrecy with ephemeral keys
|
|
319
|
+
|
|
221
320
|
## Security Best Practices
|
|
222
321
|
|
|
223
322
|
1. **Always verify signatures**: Use `verifyWebhookSignature` to ensure webhooks are genuinely from Lockform
|
|
224
|
-
2. **
|
|
225
|
-
3. **
|
|
226
|
-
4. **
|
|
323
|
+
2. **Protect your recovery phrase**: Store your 15-word recovery phrase in environment variables, never commit it to version control
|
|
324
|
+
3. **Never share your recovery phrase**: Anyone with your 15-word recovery phrase can decrypt all submissions
|
|
325
|
+
4. **Use HTTPS**: Always use HTTPS endpoints for webhooks in production
|
|
326
|
+
5. **Validate data**: Always validate the decrypted data before processing it
|
|
327
|
+
6. **Implement idempotency**: Use the `submission_id` to prevent duplicate processing
|
|
328
|
+
7. **Rate limiting**: Implement rate limiting on your webhook endpoint
|
|
329
|
+
|
|
330
|
+
## Migration from v1.x (RSA) to v2.x (X25519)
|
|
331
|
+
|
|
332
|
+
If you're migrating from the RSA-based v1.x version:
|
|
333
|
+
|
|
334
|
+
1. **Update your package**: `npm install lockform@latest`
|
|
335
|
+
2. **Update your credentials**: Use your 15-word recovery phrase instead of PEM-formatted RSA keys
|
|
336
|
+
3. **Update your code**: Pass your recovery phrase as the `passphrase` parameter (renamed from `privateKey`)
|
|
337
|
+
|
|
338
|
+
The webhook payload structure has changed:
|
|
339
|
+
- `wrapped_key` → `ephemeral_public_key`
|
|
340
|
+
- Added: `salt`, `encryption_timestamp`
|
|
341
|
+
- `algorithm` changed from `RSA-OAEP-4096+AES-256-GCM` to `X25519+AES-256-GCM`
|
|
227
342
|
|
|
228
343
|
## Requirements
|
|
229
344
|
|
package/dist/crypto.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function decryptSubmission(ciphertext: string, iv: string,
|
|
1
|
+
export declare function base64ToArrayBuffer(base64: string): Promise<Uint8Array>;
|
|
2
|
+
export declare function arrayBufferToString(buffer: ArrayBuffer | Uint8Array): string;
|
|
3
|
+
export declare function importPrivateKeyFromMnemonic(mnemonic: string): Uint8Array;
|
|
4
|
+
export declare function importPrivateKeyFromBase64(base64: string): Uint8Array;
|
|
5
|
+
export declare function decryptSubmission(ciphertext: string, iv: string, salt: string, ephemeralPublicKey: string, privateKey: Uint8Array, metadata?: {
|
|
6
|
+
formId?: string;
|
|
7
|
+
encryptionTimestamp?: number;
|
|
8
|
+
}): Promise<string>;
|
|
6
9
|
export declare function createHmacSignature(payload: string, secret: string): Promise<string>;
|
package/dist/crypto.js
CHANGED
|
@@ -2,55 +2,68 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.base64ToArrayBuffer = base64ToArrayBuffer;
|
|
4
4
|
exports.arrayBufferToString = arrayBufferToString;
|
|
5
|
-
exports.
|
|
5
|
+
exports.importPrivateKeyFromMnemonic = importPrivateKeyFromMnemonic;
|
|
6
|
+
exports.importPrivateKeyFromBase64 = importPrivateKeyFromBase64;
|
|
6
7
|
exports.decryptSubmission = decryptSubmission;
|
|
7
8
|
exports.createHmacSignature = createHmacSignature;
|
|
8
9
|
const node_crypto_1 = require("node:crypto");
|
|
10
|
+
const ed25519_1 = require("@noble/curves/ed25519");
|
|
11
|
+
const hkdf_1 = require("@noble/hashes/hkdf");
|
|
12
|
+
const sha2_1 = require("@noble/hashes/sha2");
|
|
13
|
+
const pbkdf2_1 = require("@noble/hashes/pbkdf2");
|
|
14
|
+
const sha2_2 = require("@noble/hashes/sha2");
|
|
15
|
+
const bip39_1 = require("bip39");
|
|
9
16
|
const crypto = node_crypto_1.webcrypto;
|
|
10
17
|
async function base64ToArrayBuffer(base64) {
|
|
11
|
-
const
|
|
12
|
-
const bytes = new Uint8Array(
|
|
13
|
-
for (let i = 0; i <
|
|
14
|
-
bytes[i] =
|
|
18
|
+
const binary = Buffer.from(base64, 'base64').toString('binary');
|
|
19
|
+
const bytes = new Uint8Array(binary.length);
|
|
20
|
+
for (let i = 0; i < binary.length; i++) {
|
|
21
|
+
bytes[i] = binary.charCodeAt(i);
|
|
15
22
|
}
|
|
16
|
-
return bytes
|
|
23
|
+
return bytes;
|
|
17
24
|
}
|
|
18
25
|
function arrayBufferToString(buffer) {
|
|
19
26
|
return new TextDecoder().decode(buffer);
|
|
20
27
|
}
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const binaryDer = await base64ToArrayBuffer(pemContents);
|
|
29
|
-
return await crypto.subtle.importKey('pkcs8', binaryDer, {
|
|
30
|
-
name: 'RSA-OAEP',
|
|
31
|
-
hash: 'SHA-256',
|
|
32
|
-
}, false, ['unwrapKey']);
|
|
28
|
+
function importPrivateKeyFromMnemonic(mnemonic) {
|
|
29
|
+
const seed = (0, bip39_1.mnemonicToSeedSync)(mnemonic);
|
|
30
|
+
const privateKey = (0, pbkdf2_1.pbkdf2)(sha2_2.sha512, seed, 'lockform-x25519-v1', {
|
|
31
|
+
c: 600_000,
|
|
32
|
+
dkLen: 32,
|
|
33
|
+
});
|
|
34
|
+
return privateKey;
|
|
33
35
|
}
|
|
34
|
-
|
|
36
|
+
function importPrivateKeyFromBase64(base64) {
|
|
37
|
+
return Buffer.from(base64, 'base64');
|
|
38
|
+
}
|
|
39
|
+
function deriveSharedSecret(privateKey, publicKey) {
|
|
40
|
+
return ed25519_1.x25519.getSharedSecret(privateKey, publicKey);
|
|
41
|
+
}
|
|
42
|
+
function deriveAESKey(sharedSecret, salt) {
|
|
43
|
+
const info = new TextEncoder().encode('lockform-encryption-v1');
|
|
44
|
+
return (0, hkdf_1.hkdf)(sha2_1.sha256, sharedSecret, salt, info, 32);
|
|
45
|
+
}
|
|
46
|
+
async function decryptWithAES(ciphertext, key, iv, additionalData) {
|
|
47
|
+
const cryptoKey = await crypto.subtle.importKey('raw', key, { name: 'AES-GCM' }, false, ['decrypt']);
|
|
48
|
+
const decrypted = await crypto.subtle.decrypt({
|
|
49
|
+
name: 'AES-GCM',
|
|
50
|
+
iv: iv,
|
|
51
|
+
...(additionalData && { additionalData }),
|
|
52
|
+
}, cryptoKey, ciphertext);
|
|
53
|
+
const decoder = new TextDecoder();
|
|
54
|
+
return decoder.decode(decrypted);
|
|
55
|
+
}
|
|
56
|
+
async function decryptSubmission(ciphertext, iv, salt, ephemeralPublicKey, privateKey, metadata) {
|
|
35
57
|
const ciphertextBuffer = await base64ToArrayBuffer(ciphertext);
|
|
36
58
|
const ivBuffer = await base64ToArrayBuffer(iv);
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}, {
|
|
46
|
-
name: 'AES-GCM',
|
|
47
|
-
length: 256,
|
|
48
|
-
}, false, ['decrypt']);
|
|
49
|
-
const decryptedBuffer = await crypto.subtle.decrypt({
|
|
50
|
-
name: 'AES-GCM',
|
|
51
|
-
iv: ivBuffer,
|
|
52
|
-
}, unwrappedKey, combinedCiphertext);
|
|
53
|
-
return arrayBufferToString(decryptedBuffer);
|
|
59
|
+
const saltBuffer = await base64ToArrayBuffer(salt);
|
|
60
|
+
const ephemeralPublicKeyBuffer = await base64ToArrayBuffer(ephemeralPublicKey);
|
|
61
|
+
const sharedSecret = deriveSharedSecret(privateKey, ephemeralPublicKeyBuffer);
|
|
62
|
+
const aesKey = deriveAESKey(sharedSecret, saltBuffer);
|
|
63
|
+
const aad = metadata
|
|
64
|
+
? new TextEncoder().encode(`${metadata.formId ?? ''}:${metadata.encryptionTimestamp ?? 0}`)
|
|
65
|
+
: undefined;
|
|
66
|
+
return await decryptWithAES(ciphertextBuffer, aesKey, ivBuffer, aad);
|
|
54
67
|
}
|
|
55
68
|
async function createHmacSignature(payload, secret) {
|
|
56
69
|
const encoder = new TextEncoder();
|
package/dist/decrypt.js
CHANGED
|
@@ -3,9 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.decryptWebhookData = decryptWebhookData;
|
|
4
4
|
const crypto_1 = require("./crypto");
|
|
5
5
|
async function decryptWebhookData(options) {
|
|
6
|
-
const { payload,
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const { payload, passphrase } = options;
|
|
7
|
+
let privateKeyBytes;
|
|
8
|
+
if (passphrase.includes(' ')) {
|
|
9
|
+
privateKeyBytes = (0, crypto_1.importPrivateKeyFromMnemonic)(passphrase);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
privateKeyBytes = (0, crypto_1.importPrivateKeyFromBase64)(passphrase);
|
|
13
|
+
}
|
|
14
|
+
const decryptedData = await (0, crypto_1.decryptSubmission)(payload.ciphertext, payload.iv, payload.salt, payload.ephemeral_public_key, privateKeyBytes, {
|
|
15
|
+
formId: payload.form_id,
|
|
16
|
+
encryptionTimestamp: payload.encryption_timestamp,
|
|
17
|
+
});
|
|
9
18
|
const rawData = JSON.parse(decryptedData);
|
|
10
19
|
const mappedData = {};
|
|
11
20
|
for (const [fieldId, value] of Object.entries(rawData)) {
|
package/dist/types.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ export interface WebhookPayload {
|
|
|
4
4
|
form_id: string;
|
|
5
5
|
ciphertext: string;
|
|
6
6
|
iv: string;
|
|
7
|
-
|
|
7
|
+
salt: string;
|
|
8
|
+
ephemeral_public_key: string;
|
|
8
9
|
auth_tag: string;
|
|
9
10
|
algorithm: string;
|
|
10
11
|
nonce: string;
|
|
12
|
+
encryption_timestamp: number;
|
|
11
13
|
timestamp: string;
|
|
12
14
|
field_mapping: Record<string, string>;
|
|
13
15
|
}
|
|
@@ -24,7 +26,7 @@ export interface DecryptedSubmission {
|
|
|
24
26
|
}
|
|
25
27
|
export interface DecryptWebhookOptions {
|
|
26
28
|
payload: WebhookPayload;
|
|
27
|
-
|
|
29
|
+
passphrase: string;
|
|
28
30
|
}
|
|
29
31
|
export interface VerifySignatureOptions {
|
|
30
32
|
payload: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lockform",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Official SDK for processing Lockform webhook submissions with end-to-end encryption",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Official SDK for processing Lockform webhook submissions with end-to-end encryption using X25519 + AES-256-GCM",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -17,11 +17,18 @@
|
|
|
17
17
|
"webhook",
|
|
18
18
|
"e2e",
|
|
19
19
|
"forms",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
20
|
+
"x25519",
|
|
21
|
+
"curve25519",
|
|
22
|
+
"aes-gcm",
|
|
23
|
+
"bip39"
|
|
22
24
|
],
|
|
23
25
|
"author": "",
|
|
24
26
|
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@noble/curves": "^1.6.0",
|
|
29
|
+
"@noble/hashes": "^1.5.0",
|
|
30
|
+
"bip39": "^3.1.0"
|
|
31
|
+
},
|
|
25
32
|
"devDependencies": {
|
|
26
33
|
"@types/node": "^20.0.0",
|
|
27
34
|
"typescript": "^5.0.0"
|