lockform 2.0.0 → 3.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 CHANGED
@@ -94,6 +94,31 @@ app.post('/webhook', async (req, res) => {
94
94
 
95
95
  ## API Reference
96
96
 
97
+ ### `derivePrivateKey(mnemonic)`
98
+
99
+ Derives a base64-encoded X25519 private key from a 15-word BIP39 recovery phrase. Useful for generating keys to use in edge functions.
100
+
101
+ **Parameters:**
102
+ - `mnemonic` (string): Your 15-word BIP39 recovery phrase
103
+
104
+ **Returns:** `string` - Base64-encoded X25519 private key
105
+
106
+ **Example:**
107
+
108
+ ```typescript
109
+ import { derivePrivateKey } from 'lockform'
110
+
111
+ const mnemonic = 'your fifteen word recovery phrase goes here exactly fifteen words'
112
+ const privateKeyBase64 = derivePrivateKey(mnemonic)
113
+
114
+ console.log(privateKeyBase64)
115
+ // Output: "a1b2c3d4e5f6..." (base64 string)
116
+
117
+ // Store this in your edge function environment variable
118
+ ```
119
+
120
+ **Use case:** Run this once locally to derive your private key, then store the base64 output as an environment variable for edge functions (to avoid PBKDF2 timeout).
121
+
97
122
  ### `decryptWebhookData(options)`
98
123
 
99
124
  Decrypts an encrypted webhook payload from Lockform using X25519 + AES-256-GCM.
@@ -247,6 +272,8 @@ app.listen(3000, () => {
247
272
 
248
273
  ### Deno Edge Function
249
274
 
275
+ **Important:** Edge functions have strict CPU time limits. Use a base64-encoded private key instead of the 15-word recovery phrase to avoid CPU timeout errors. See the performance note below.
276
+
250
277
  ```typescript
251
278
  import { decryptWebhookData, verifyWebhookSignature, type WebhookPayload } from 'lockform'
252
279
 
@@ -340,6 +367,52 @@ The webhook payload structure has changed:
340
367
  - Added: `salt`, `encryption_timestamp`
341
368
  - `algorithm` changed from `RSA-OAEP-4096+AES-256-GCM` to `X25519+AES-256-GCM`
342
369
 
370
+ ## Performance Considerations
371
+
372
+ ### Edge Functions (Deno, Cloudflare Workers, etc.)
373
+
374
+ Edge functions have strict CPU time limits (typically 50-100ms). The PBKDF2 key derivation with 600,000 iterations can take several seconds and will cause timeout errors.
375
+
376
+ **Solution:** Use a base64-encoded private key instead of the recovery phrase.
377
+
378
+ **Option 1: Use the CLI tool (easiest)**
379
+
380
+ ```bash
381
+ npx lockform-derive-key
382
+ # Or if installed: npm run derive-key
383
+ ```
384
+
385
+ This will prompt you for your recovery phrase and output the base64 private key.
386
+
387
+ **Option 2: Use the API programmatically**
388
+
389
+ ```javascript
390
+ import { derivePrivateKey } from 'lockform'
391
+
392
+ const mnemonic = 'your fifteen word recovery phrase here exactly fifteen words'
393
+ const privateKeyBase64 = derivePrivateKey(mnemonic)
394
+ console.log(privateKeyBase64) // Store this in your edge function environment
395
+ ```
396
+
397
+ Then use the base64 key in your edge function:
398
+
399
+ ```typescript
400
+ const recoveryPhrase = Deno.env.get('LOCKFORM_RECOVERY_PHRASE') // Now contains base64 key
401
+
402
+ const result = await decryptWebhookData({
403
+ payload,
404
+ passphrase: recoveryPhrase, // Automatically detects base64 format (no PBKDF2)
405
+ })
406
+ ```
407
+
408
+ The library automatically detects the format:
409
+ - Contains spaces → 15-word mnemonic (slow, runs PBKDF2)
410
+ - No spaces → Base64 private key (fast, skips PBKDF2)
411
+
412
+ ### Node.js / Long-running servers
413
+
414
+ You can use either format. The 15-word recovery phrase works fine in environments without strict CPU time limits.
415
+
343
416
  ## Requirements
344
417
 
345
418
  - Node.js 18.0.0 or higher
package/derive-key.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { derivePrivateKey } from './dist/index.js'
3
+ import { createInterface } from 'readline'
4
+
5
+ const rl = createInterface({
6
+ input: process.stdin,
7
+ output: process.stdout
8
+ })
9
+
10
+ console.log('='.repeat(70))
11
+ console.log('Lockform Private Key Derivation Tool')
12
+ console.log('='.repeat(70))
13
+ console.log()
14
+ console.log('This tool derives a base64-encoded X25519 private key from your')
15
+ console.log('15-word BIP39 recovery phrase for use in edge functions.')
16
+ console.log()
17
+
18
+ rl.question('Enter your 15-word recovery phrase: ', (mnemonic) => {
19
+ try {
20
+ const words = mnemonic.trim().split(/\s+/)
21
+
22
+ if (words.length !== 15) {
23
+ console.error('\n❌ Error: Recovery phrase must be exactly 15 words')
24
+ console.error(` You entered ${words.length} words`)
25
+ process.exit(1)
26
+ }
27
+
28
+ console.log('\n⏳ Deriving private key (this may take a few seconds)...\n')
29
+
30
+ const privateKeyBase64 = derivePrivateKey(mnemonic.trim())
31
+
32
+ console.log('✅ Success! Your base64-encoded private key:\n')
33
+ console.log('─'.repeat(70))
34
+ console.log(privateKeyBase64)
35
+ console.log('─'.repeat(70))
36
+ console.log()
37
+ console.log('Add this to your edge function environment variables:')
38
+ console.log()
39
+ console.log(`LOCKFORM_RECOVERY_PHRASE="${privateKeyBase64}"`)
40
+ console.log()
41
+ console.log('⚠️ Keep this secret! Anyone with this key can decrypt all submissions.')
42
+ console.log()
43
+
44
+ } catch (error) {
45
+ console.error('\n❌ Error:', error.message)
46
+ process.exit(1)
47
+ } finally {
48
+ rl.close()
49
+ }
50
+ })
package/dist/crypto.d.ts CHANGED
@@ -2,6 +2,7 @@ export declare function base64ToArrayBuffer(base64: string): Promise<Uint8Array>
2
2
  export declare function arrayBufferToString(buffer: ArrayBuffer | Uint8Array): string;
3
3
  export declare function importPrivateKeyFromMnemonic(mnemonic: string): Uint8Array;
4
4
  export declare function importPrivateKeyFromBase64(base64: string): Uint8Array;
5
+ export declare function derivePrivateKey(mnemonic: string): string;
5
6
  export declare function decryptSubmission(ciphertext: string, iv: string, salt: string, ephemeralPublicKey: string, privateKey: Uint8Array, metadata?: {
6
7
  formId?: string;
7
8
  encryptionTimestamp?: number;
package/dist/crypto.js CHANGED
@@ -4,6 +4,7 @@ exports.base64ToArrayBuffer = base64ToArrayBuffer;
4
4
  exports.arrayBufferToString = arrayBufferToString;
5
5
  exports.importPrivateKeyFromMnemonic = importPrivateKeyFromMnemonic;
6
6
  exports.importPrivateKeyFromBase64 = importPrivateKeyFromBase64;
7
+ exports.derivePrivateKey = derivePrivateKey;
7
8
  exports.decryptSubmission = decryptSubmission;
8
9
  exports.createHmacSignature = createHmacSignature;
9
10
  const node_crypto_1 = require("node:crypto");
@@ -36,6 +37,10 @@ function importPrivateKeyFromMnemonic(mnemonic) {
36
37
  function importPrivateKeyFromBase64(base64) {
37
38
  return Buffer.from(base64, 'base64');
38
39
  }
40
+ function derivePrivateKey(mnemonic) {
41
+ const privateKeyBytes = importPrivateKeyFromMnemonic(mnemonic);
42
+ return Buffer.from(privateKeyBytes).toString('base64');
43
+ }
39
44
  function deriveSharedSecret(privateKey, publicKey) {
40
45
  return ed25519_1.x25519.getSharedSecret(privateKey, publicKey);
41
46
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { decryptWebhookData } from './decrypt';
2
2
  export { verifyWebhookSignature } from './verify';
3
+ export { derivePrivateKey } from './crypto';
3
4
  export type { WebhookPayload, DecryptedSubmission, DecryptWebhookOptions, VerifySignatureOptions, } from './types';
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifyWebhookSignature = exports.decryptWebhookData = void 0;
3
+ exports.derivePrivateKey = exports.verifyWebhookSignature = exports.decryptWebhookData = void 0;
4
4
  var decrypt_1 = require("./decrypt");
5
5
  Object.defineProperty(exports, "decryptWebhookData", { enumerable: true, get: function () { return decrypt_1.decryptWebhookData; } });
6
6
  var verify_1 = require("./verify");
7
7
  Object.defineProperty(exports, "verifyWebhookSignature", { enumerable: true, get: function () { return verify_1.verifyWebhookSignature; } });
8
+ var crypto_1 = require("./crypto");
9
+ Object.defineProperty(exports, "derivePrivateKey", { enumerable: true, get: function () { return crypto_1.derivePrivateKey; } });
package/package.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "name": "lockform",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
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": [
8
- "dist"
8
+ "dist",
9
+ "derive-key.js"
9
10
  ],
10
11
  "scripts": {
11
12
  "build": "tsc",
12
- "prepublishOnly": "npm run build"
13
+ "prepublishOnly": "npm run build",
14
+ "derive-key": "node derive-key.js"
15
+ },
16
+ "bin": {
17
+ "lockform-derive-key": "./derive-key.js"
13
18
  },
14
19
  "keywords": [
15
20
  "lockform",