daku 0.0.1 → 0.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.
Files changed (4) hide show
  1. package/README.md +193 -118
  2. package/index.js +56 -41
  3. package/package.json +1 -1
  4. package/username.js +8057 -0
package/README.md CHANGED
@@ -1,8 +1,45 @@
1
- # daku
1
+ # DAKU
2
2
 
3
- > Leave no trace. Just authenticate.
3
+ > **Anonymous authentication. Zero personal data.**
4
4
 
5
- Cryptographic authentication library with built-in proof-of-work spam protection. No emails, no passwords, no personal data.
5
+ **DAKU** (pronounced _DAA KU_) means "bandits" in Punjabi. Historically, bandits operated anonymously, often using masks to hide their identity. This library adopts that privacy-first ethos—anonymous cryptographic authentication without personal data.
6
+
7
+ ---
8
+
9
+ ## Why DAKU?
10
+
11
+ **Stop storing passwords. Stop managing email verifications. Stop worrying about data breaches.**
12
+
13
+ DAKU is a simpler approach to user authentication that keeps both you and your users anonymous. No databases of usernames, no password hashes to secure, no personal information to leak.
14
+
15
+ ```javascript
16
+ // Traditional auth: Store emails, hash passwords, manage resets...
17
+ // DAKU: Just verify cryptographic signatures ✨
18
+ const publicKey = await verifyAuth(token);
19
+ ```
20
+
21
+ ### The Problem with Traditional Auth
22
+
23
+ Every traditional authentication system carries risk:
24
+
25
+ - **Passwords**: Users reuse them, forget them, get them stolen
26
+ - **Email/Phone**: Requires collecting personal data (GDPR, privacy laws)
27
+ - **Databases**: Honeypots for hackers; one breach exposes everything
28
+ - **Identity**: Your users leave traces everywhere they sign up
29
+
30
+ ### The DAKU Way
31
+
32
+ Users authenticate with cryptographic keypairs—like Bitcoin wallets, but for your app:
33
+
34
+ - **No signup forms**: Generate a keypair, start using your app
35
+ - **No passwords**: Users never create or remember passwords
36
+ - **No PII collection**: No emails, phones, or personal data
37
+ - **Built-in spam protection**: Proof-of-work prevents abuse
38
+ - **You stay clean**: Nothing sensitive to store, nothing to breach
39
+
40
+ > DAKU uses **secp256k1** signatures (same as Bitcoin/Ethereum) with **proof-of-work** spam protection. Auth tokens expire in 1 minute. Users control their private keys, you just verify signatures.
41
+
42
+ ---
6
43
 
7
44
  ## Installation
8
45
 
@@ -12,188 +49,226 @@ npm install daku
12
49
 
13
50
  ## Quick Start
14
51
 
15
- ### 1. Generate a Keypair
16
-
17
52
  ```javascript
18
- import { generateKeyPair } from 'daku';
53
+ import { generateKeyPair, createAuth, verifyAuth } from "daku";
19
54
 
20
- // Generate once and store securely (localStorage, secure storage, etc.)
55
+ // 1. User generates keypair (client-side)
21
56
  const { privateKey, publicKey } = generateKeyPair();
22
- ```
23
-
24
- ### 2. Client-Side: Create Authentication Request
25
57
 
26
- ```javascript
27
- import { createAuth } from 'daku';
28
-
29
- // Create authentication token (includes timestamp, nonce, signature, and POW)
58
+ // 2. Create auth token (client-side)
30
59
  const token = await createAuth(privateKey);
31
60
 
32
- // Send to your server
33
- fetch('/api/login', {
34
- method: 'POST',
35
- headers: {
36
- 'Content-Type': 'application/json',
37
- 'daku': token
38
- }
39
- });
61
+ // 3. Verify auth (server-side)
62
+ const publicKey = await verifyAuth(token);
63
+ // ✅ Authenticated! publicKey is the unique user ID
40
64
  ```
41
65
 
42
- ### 3. Server-Side: Verify Authentication
66
+ ---
43
67
 
44
- ```javascript
45
- import { verifyAuth } from 'daku';
68
+ ## Core Functions
46
69
 
47
- const publicKey = await verifyAuth(req.headers['daku']);
70
+ ### `generateKeyPair()`
48
71
 
49
- if (publicKey) {
50
- // ✅ Authenticated! Use publicKey as unique user ID
51
- console.log(`User ${publicKey} authenticated`);
52
- } else {
53
- // ❌ Invalid or expired authentication
54
- res.status(401).json({ error: 'Unauthorized' });
55
- }
72
+ **Create a new identity**
73
+
74
+ ```javascript
75
+ const { privateKey, publicKey } = generateKeyPair();
56
76
  ```
57
77
 
58
- ## ExpressJS Middleware
78
+ Generates a secp256k1 keypair. The **privateKey** stays with the user (never share it), the **publicKey** identifies them to your service.
59
79
 
60
- ```javascript
61
- import express from 'express';
62
- import { verifyAuth } from 'daku';
80
+ - Returns: `{ privateKey: string, publicKey: string }`
81
+ - Use case: First-time users, account creation
63
82
 
64
- const app = express();
83
+ ---
65
84
 
66
- // Reusable authentication middleware
67
- const daku = (powDifficulty = 2) => {
68
- return async (req, res, next) => {
69
- const token = req.headers['daku'];
70
-
71
- if (!token) {
72
- return res.status(401).json({ error: 'Unauthorized' });
73
- }
85
+ ### `getPublicKey(privateKey)`
74
86
 
75
- const publicKey = await verifyAuth(token, powDifficulty);
87
+ **Derive the public identity**
76
88
 
77
- if (!publicKey) {
78
- return res.status(401).json({ error: 'Unauthorized' });
79
- }
89
+ ```javascript
90
+ const publicKey = getPublicKey(privateKey);
91
+ ```
80
92
 
81
- // Attach user's public key to request
82
- req.userId = publicKey;
83
- next();
84
- };
85
- };
93
+ Extract the public key from a private key. Useful when users return with their saved privateKey.
86
94
 
87
- // Use on protected routes
88
- app.post('/api/protected', daku(), (req, res) => {
89
- res.json({
90
- message: 'Access granted',
91
- userId: req.userId
92
- });
93
- });
95
+ - Returns: `publicKey` string
96
+ - Deterministic: Same privateKey always produces same publicKey
94
97
 
95
- // Use with custom POW difficulty
96
- app.post('/api/high-security', daku(4), (req, res) => {
97
- res.json({ message: 'High security endpoint' });
98
- });
98
+ ---
99
99
 
100
- app.listen(3000);
101
- ```
100
+ ### `getUsername(publicKey)`
102
101
 
103
- ## Key Features
102
+ **Make public keys human-readable**
103
+
104
+ ```javascript
105
+ const username = await getUsername(publicKey);
106
+ // → "happy-ocean-flows-1234"
107
+ ```
104
108
 
105
- - **🕵️ Anonymous**: No email, phone, or personal data required
106
- - **🛡️ Spam Protection**: Built-in proof-of-work (default: 2 leading zeros)
107
- - **🔐 Secure**: secp256k1 cryptographic signatures (same as Bitcoin/Ethereum)
108
- - **⚡ Lightweight**: Minimal dependencies (@noble/secp256k1, @noble/hashes)
109
- - **🌐 Cross-Platform**: Works in Node.js and browsers
110
- - **⏱️ Time-Limited**: Auth requests expire after 1 minute
109
+ Public keys are long hex strings. `getUsername()` converts them into memorable usernames for your UI.
111
110
 
112
- ## API Reference
111
+ - Returns: Human-readable username string
112
+ - Deterministic: Same publicKey always → same username
113
+ - Format: `adjective-noun-verb-number`
113
114
 
114
- ### Authentication Functions
115
+ > [!IMPORTANT]
116
+ > **Never ask users to create usernames.** DAKU keeps users anonymous. Display the generated username in your UI, but always identify users by their **publicKey** in your database.
115
117
 
116
- #### `createAuth(privateKey, pow = 2)`
118
+ ---
117
119
 
118
- Creates a complete authentication token with timestamp, nonce, signature, and proof-of-work.
120
+ ### `createAuth(privateKey, pow?)`
119
121
 
120
- **Returns:** String (base64-encoded token)
122
+ **Generate authentication token**
121
123
 
122
- **Example:**
123
124
  ```javascript
124
- const token = await createAuth(privateKey);
125
- // "eyJwdWJsaWNrZXkiOiIwMmE..."
125
+ const token = await createAuth(privateKey, 2); // pow = difficulty
126
126
  ```
127
127
 
128
- #### `verifyAuth(token, pow = 2)`
128
+ Creates a signed auth token with timestamp, nonce, signature, and proof-of-work. Send this to your server for verification.
129
+
130
+ - Returns: Base64-encoded auth token
131
+ - Default POW: 2 (adjust for spam protection)
132
+ - Includes: timestamp, nonce, signature, proof-of-work
129
133
 
130
- Verifies authentication token. Checks signature validity, proof-of-work, and timestamp (must be within 1 minute).
134
+ ---
135
+
136
+ ### `verifyAuth(token, pow?)`
131
137
 
132
- **Returns:** `publicKey` string on success, `null` on failure
138
+ **Verify authentication token**
133
139
 
134
- **Example:**
135
140
  ```javascript
136
- const publicKey = await verifyAuth(token);
141
+ const publicKey = await verifyAuth(token, 2);
142
+ if (publicKey) {
143
+ // ✅ Valid! User authenticated
144
+ console.log(`User ${publicKey} logged in`);
145
+ } else {
146
+ // ❌ Invalid or expired
147
+ }
137
148
  ```
138
149
 
150
+ Verifies the signature, proof-of-work, and timestamp (must be < 1 minute old). Returns the user's publicKey on success.
151
+
152
+ - Returns: `publicKey` string or `null`
153
+ - Checks: Signature validity, POW correctness, timestamp freshness
154
+ - Token lifetime: 1 minute
155
+
139
156
  ---
140
157
 
141
- ### General Signing Functions
158
+ ### `sign(message, privateKey, pow?)`
159
+
160
+ **Sign any message**
142
161
 
143
- Use these for signing arbitrary messages (not authentication).
162
+ ```javascript
163
+ const { signature, pow } = await sign("hello world", privateKey, 2);
164
+ ```
165
+
166
+ Create a cryptographic signature for any message with proof-of-work. Lower-level function used by `createAuth()`.
144
167
 
145
- #### `sign(message, privateKey, pow = 2)`
168
+ - Returns: `{ signature: string, pow: number }`
169
+ - Use case: Custom message signing beyond authentication
146
170
 
147
- Signs any message with proof-of-work.
171
+ ---
172
+
173
+ ### `verify(message, signatureData, publicKey, pow?)`
148
174
 
149
- **Returns:** Object with `{ signature, pow }`
175
+ **Verify any signature**
150
176
 
151
- **Example:**
152
177
  ```javascript
153
- const result = await sign('Hello World', privateKey);
154
- // { signature: 'a1b2c3...', pow: 42 }
178
+ const isValid = await verify("hello world", { signature, pow }, publicKey, 2);
155
179
  ```
156
180
 
157
- #### `verify(message, signatureData, publicKey, pow = 2)`
181
+ Verify a message signature and proof-of-work. Lower-level function used by `verifyAuth()`.
158
182
 
159
- Verifies a signed message.
183
+ - Returns: `boolean`
184
+ - Checks: Signature + POW validity
160
185
 
161
- **Returns:** `true` if valid, `false` otherwise
186
+ ---
187
+
188
+ ## Express.js Middleware
162
189
 
163
- **Example:**
164
190
  ```javascript
165
- const isValid = await verify('Hello World', { signature: 'a1b2...', pow: 42 }, publicKey);
191
+ import express from "express";
192
+ import { verifyAuth, getUsername } from "daku";
193
+
194
+ const app = express();
195
+
196
+ // Middleware: Verify DAKU auth
197
+ const daku =
198
+ (powDifficulty = 2) =>
199
+ async (req, res, next) => {
200
+ const token = req.headers["daku"];
201
+ const publicKey = await verifyAuth(token, powDifficulty);
202
+
203
+ if (!publicKey) {
204
+ return res.status(401).json({ error: "Unauthorized" });
205
+ }
206
+
207
+ req.userId = publicKey; // Attach user ID
208
+ next();
209
+ };
210
+
211
+ // Protected route
212
+ app.post("/api/profile", daku(), async (req, res) => {
213
+ const username = await getUsername(req.userId);
214
+ res.json({
215
+ message: `Welcome, ${username}!`,
216
+ userId: req.userId,
217
+ });
218
+ });
219
+
220
+ app.listen(3000);
166
221
  ```
167
222
 
168
223
  ---
169
224
 
170
- ### Utility Functions
225
+ ## Key Benefits
171
226
 
172
- #### `generateKeyPair()`
227
+ | Traditional Auth | DAKU |
228
+ | ----------------------------------- | -------------------------------------- |
229
+ | Manage passwords, hashes, resets | No passwords—just verify signatures |
230
+ | Store emails/phones (PII) | Zero personal data collected |
231
+ | User databases = security liability | Only store public keys (not sensitive) |
232
+ | Slow authentication flows | Instant cryptographic verification |
233
+ | GDPR compliance overhead | No PII = simpler compliance |
234
+ | Spam = manual moderation/CAPTCHAs | Built-in proof-of-work protection |
173
235
 
174
- Generates a new secp256k1 keypair.
236
+ ---
175
237
 
176
- **Returns:** `{ privateKey: string, publicKey: string }`
238
+ ## Features
177
239
 
178
- #### `getPublicKey(privateKey)`
240
+ - **🕵️ Anonymous**: No email, no phone, no personal data
241
+ - **🔐 Secure**: secp256k1 signatures (Bitcoin/Ethereum-grade)
242
+ - **🛡️ Spam-proof**: Configurable proof-of-work difficulty
243
+ - **⚡ Lightweight**: Minimal dependencies, works everywhere
244
+ - **🌐 Universal**: Node.js + Browser compatible
245
+ - **⏱️ Short-lived tokens**: 1-minute expiration (anti-replay)
246
+ - **🌍 Cross-project identity**: Reuse one keypair across apps
179
247
 
180
- Derives the public key from a private key.
248
+ ---
181
249
 
182
- **Returns:** `string` (compressed public key in hex)
250
+ ## Global Identity
183
251
 
184
- #### `sha256(message)`
252
+ Users can reuse **one private key** across multiple services. Same privateKey → same publicKey → same identity everywhere.
185
253
 
186
- SHA-256 hash helper.
254
+ ```javascript
255
+ // User's keypair works on yourapp.com AND anotherapp.com
256
+ const publicKey = getPublicKey(samePrivateKey);
257
+ // → Same publicKey = consistent cross-platform identity
258
+ ```
187
259
 
188
- **Returns:** `Uint8Array`
260
+ > [!WARNING]
261
+ > Reusing keypairs links user identity across services. This enables seamless cross-app experiences but reduces anonymity between services. For per-app isolation, derive or generate separate keys.
189
262
 
190
- ## Use Cases
263
+ ---
191
264
 
192
- - **Authentication**: Use `createAuth()` + `verifyAuth()` for login flows
193
- - **Message Signing**: Use `sign()` + `verify()` for arbitrary data signatures
194
- - **Spam Prevention**: POW difficulty prevents automated abuse
195
- - **Privacy-First Apps**: No PII required, just cryptographic proofs
265
+ ## Security Notes
196
266
 
197
- ## License
267
+ - **Private keys**: Users must store them securely (localStorage, hardware wallets). Lost keys = lost access.
268
+ - **Token lifetime**: Hardcoded to 1 minute; prevent replay attacks.
269
+ - **POW difficulty**: Default = 2 leading zeros. Increase for high-traffic endpoints (trade-off: slower auth).
270
+ - **No server secrets**: DAKU has no shared secrets—everything is public-key cryptography.
271
+
272
+ ---
198
273
 
199
- ISC
274
+ **DAKU: The authentication system that doesn't know anything about your users. By design.**
package/index.js CHANGED
@@ -2,9 +2,11 @@
2
2
  import * as secp from "@noble/secp256k1";
3
3
  import { hmac } from "@noble/hashes/hmac";
4
4
  import { sha256 as nobleSha256 } from "@noble/hashes/sha2";
5
+ import { generateAccountIdentifier } from "./username.js";
5
6
 
6
7
  // Set up HMAC for secp256k1
7
- secp.etc.hmacSha256Sync = (key, ...msgs) => hmac(nobleSha256, key, secp.etc.concatBytes(...msgs));
8
+ secp.etc.hmacSha256Sync = (key, ...msgs) =>
9
+ hmac(nobleSha256, key, secp.etc.concatBytes(...msgs));
8
10
 
9
11
  // Browser compatibility helper for TextEncoder
10
12
  async function getTextEncoder() {
@@ -19,12 +21,14 @@ async function getTextEncoder() {
19
21
 
20
22
  // Helper function to convert bytes to hex
21
23
  function bytesToHex(bytes) {
22
- return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
24
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
25
+ ""
26
+ );
23
27
  }
24
28
 
25
29
  // Helper function to convert hex to bytes
26
30
  function hexToBytes(hex) {
27
- return new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
31
+ return new Uint8Array(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
28
32
  }
29
33
 
30
34
  // Helper function to generate random bytes
@@ -47,7 +51,7 @@ function base64Encode(obj) {
47
51
  if (typeof window !== "undefined") {
48
52
  return btoa(json);
49
53
  } else {
50
- return Buffer.from(json).toString('base64');
54
+ return Buffer.from(json).toString("base64");
51
55
  }
52
56
  }
53
57
 
@@ -56,14 +60,15 @@ function base64Decode(str) {
56
60
  if (typeof window !== "undefined") {
57
61
  return JSON.parse(atob(str));
58
62
  } else {
59
- return JSON.parse(Buffer.from(str, 'base64').toString());
63
+ return JSON.parse(Buffer.from(str, "base64").toString());
60
64
  }
61
65
  }
62
66
 
63
67
  // --- Key Generation ---
64
68
  export function generateKeyPair() {
65
69
  const privateKey = secp.utils.randomPrivateKey();
66
- const publicKey = secp.getPublicKey(privateKey, true); // compressed
70
+ const publicKey = secp.getPublicKey(privateKey, true);
71
+
67
72
  return {
68
73
  privateKey: bytesToHex(privateKey),
69
74
  publicKey: bytesToHex(publicKey),
@@ -77,11 +82,16 @@ export function getPublicKey(privateKeyHex) {
77
82
  return bytesToHex(publicKeyBytes);
78
83
  }
79
84
 
85
+ // --- Account Username from Public Key ---
86
+ export async function getUsername(publicKey) {
87
+ return await generateAccountIdentifier(publicKey);
88
+ }
89
+
80
90
  // --- Hashing (SHA-256) ---
81
91
  export async function sha256(msg) {
82
92
  const encoder = await getTextEncoder();
83
93
  const encoded = encoder.encode(msg);
84
-
94
+
85
95
  if (typeof window === "undefined") {
86
96
  // Node.js - dynamic import
87
97
  const crypto = await import("node:crypto");
@@ -94,88 +104,88 @@ export async function sha256(msg) {
94
104
  }
95
105
 
96
106
  // --- Proof of Work Helper ---
97
- async function solveProofOfWork(message, difficulty = 2) {
107
+ async function solveProofOfWork(message, difficulty = 1) {
98
108
  if (difficulty < 1) {
99
109
  difficulty = 1; // Minimum POW is 1
100
110
  }
101
-
102
- const target = '0'.repeat(difficulty);
111
+
112
+ const target = "0".repeat(difficulty);
103
113
  let nonce = 0;
104
-
114
+
105
115
  while (true) {
106
116
  const combined = message + nonce;
107
117
  const hash = await sha256(combined);
108
118
  const hexHash = bytesToHex(hash);
109
-
119
+
110
120
  if (hexHash.startsWith(target)) {
111
121
  return nonce;
112
122
  }
113
123
  nonce++;
114
-
124
+
115
125
  // Yield to event loop every 1000 attempts to avoid blocking
116
126
  if (nonce % 1000 === 0) {
117
- await new Promise(resolve => setTimeout(resolve, 0));
127
+ await new Promise((resolve) => setTimeout(resolve, 0));
118
128
  }
119
129
  }
120
130
  }
121
131
 
122
132
  // --- Verify Proof of Work ---
123
- async function verifyProofOfWork(message, powNonce, difficulty = 2) {
133
+ async function verifyProofOfWork(message, powNonce, difficulty = 1) {
124
134
  if (difficulty < 1) {
125
135
  difficulty = 1; // Minimum POW is 1
126
136
  }
127
-
137
+
128
138
  if (powNonce === null || powNonce === undefined) {
129
139
  return false;
130
140
  }
131
-
132
- const target = '0'.repeat(difficulty);
141
+
142
+ const target = "0".repeat(difficulty);
133
143
  const combined = message + powNonce;
134
144
  const hash = await sha256(combined);
135
145
  const hexHash = bytesToHex(hash);
136
-
146
+
137
147
  return hexHash.startsWith(target);
138
148
  }
139
149
 
140
150
  // --- Sign Message ---
141
- export async function sign(message, privateKeyHex, pow = 2) {
151
+ export async function sign(message, privateKeyHex, pow = 1) {
142
152
  // Enforce minimum POW of 1
143
153
  if (pow < 1) {
144
154
  pow = 1;
145
155
  }
146
-
156
+
147
157
  const hash = await sha256(message);
148
158
  const privateKeyBytes = hexToBytes(privateKeyHex);
149
159
  const sig = secp.sign(hash, privateKeyBytes);
150
160
  const signature = bytesToHex(sig.toCompactRawBytes());
151
-
161
+
152
162
  // Always generate POW and return object format
153
163
  const powNonce = await solveProofOfWork(message, pow);
154
164
  return { signature, pow: powNonce };
155
165
  }
156
166
 
157
167
  // --- Verify Signature ---
158
- export async function verify(message, signatureData, publicKeyHex, pow = 2) {
168
+ export async function verify(message, signatureData, publicKeyHex, pow = 1) {
159
169
  try {
160
170
  // Enforce minimum POW of 1
161
171
  if (pow < 1) {
162
172
  pow = 1;
163
173
  }
164
-
174
+
165
175
  // Always expect object format { signature, pow }
166
176
  const signatureHex = signatureData.signature;
167
177
  const powNonce = signatureData.pow;
168
-
178
+
169
179
  if (!signatureHex || powNonce === undefined || powNonce === null) {
170
180
  return false;
171
181
  }
172
-
182
+
173
183
  // Verify POW
174
184
  const powValid = await verifyProofOfWork(message, powNonce, pow);
175
185
  if (!powValid) {
176
186
  return false;
177
187
  }
178
-
188
+
179
189
  // Verify signature
180
190
  const hash = await sha256(message);
181
191
  const signatureBytes = hexToBytes(signatureHex);
@@ -192,25 +202,25 @@ export async function createAuth(privateKeyHex, pow = 2) {
192
202
  if (pow < 1) {
193
203
  pow = 1;
194
204
  }
195
-
205
+
196
206
  const publicKeyHex = getPublicKey(privateKeyHex);
197
207
 
198
208
  const timestamp = Date.now();
199
209
  const nonceBytes = await randomBytes(16);
200
210
  const nonce = bytesToHex(nonceBytes);
201
211
  const message = `${timestamp}:${nonce}`;
202
-
212
+
203
213
  const signatureData = await sign(message, privateKeyHex, pow);
204
-
214
+
205
215
  const authPayload = {
206
216
  publickey: publicKeyHex,
207
217
  signature: signatureData.signature,
208
218
  pow: signatureData.pow,
209
219
  message,
210
220
  timestamp,
211
- nonce
221
+ nonce,
212
222
  };
213
-
223
+
214
224
  return base64Encode(authPayload);
215
225
  }
216
226
 
@@ -221,35 +231,40 @@ export async function verifyAuth(token, pow = 2) {
221
231
  if (pow < 1) {
222
232
  pow = 1;
223
233
  }
224
-
234
+
225
235
  // Decode token
226
236
  const authData = base64Decode(token);
227
237
  const publicKeyHex = authData.publickey;
228
238
  const { signature, message, pow: powNonce } = authData;
229
-
230
- if (!publicKeyHex || !signature || !message || powNonce === undefined || powNonce === null) {
239
+
240
+ if (
241
+ !publicKeyHex ||
242
+ !signature ||
243
+ !message ||
244
+ powNonce === undefined ||
245
+ powNonce === null
246
+ ) {
231
247
  return null;
232
248
  }
233
-
249
+
234
250
  // Extract timestamp from message
235
- const timestamp = Number(message.split(':')[0]);
236
-
251
+ const timestamp = Number(message.split(":")[0]);
252
+
237
253
  // Check timestamp is within 1 minute
238
254
  const maxAgeMs = 1 * 60 * 1000; // Hardcoded to 1 minute
239
255
  const now = Date.now();
240
256
  if (isNaN(timestamp) || Math.abs(now - timestamp) > maxAgeMs) {
241
257
  return null;
242
258
  }
243
-
259
+
244
260
  // Verify signature and POW
245
261
  const signatureData = { signature, pow: powNonce };
246
262
  const isValid = await verify(message, signatureData, publicKeyHex, pow);
247
263
  if (!isValid) {
248
264
  return null;
249
265
  }
250
-
266
+
251
267
  return publicKeyHex;
252
-
253
268
  } catch {
254
269
  return null;
255
270
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daku",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Leave no trace. Just authenticate.",
5
5
  "homepage": "https://github.com/besoeasy/daku#readme",
6
6
  "keywords": [