Skip to content

Commit 71a6039

Browse files
committed
refactor: move timingSafeEqual out of worker-utils to encryption follow-up PR
Restore local timingSafeEqual implementations in cloudflare-db-proxy, cloudflare-webhook-agent-ingest, and kiloclaw to keep this PR focused on non-crypto utilities. The shared extraction will land in PR #697.
1 parent 9b4db06 commit 71a6039

5 files changed

Lines changed: 39 additions & 67 deletions

File tree

cloudflare-db-proxy/src/utils/auth.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { Context } from 'hono';
22
import type { ContentfulStatusCode } from 'hono/utils/http-status';
3-
import { timingSafeEqual } from '@kilocode/worker-utils';
43
import type { Env, ErrorCode } from '../types';
54

65
/**
@@ -49,6 +48,21 @@ export function verifyToken(providedToken: string, storedToken: string): boolean
4948
return timingSafeEqual(providedToken, storedToken);
5049
}
5150

51+
/**
52+
* Timing-safe string comparison using crypto.subtle.timingSafeEqual
53+
*/
54+
function timingSafeEqual(a: string, b: string): boolean {
55+
const encoder = new TextEncoder();
56+
const aBytes = encoder.encode(a);
57+
const bBytes = encoder.encode(b);
58+
59+
if (aBytes.length !== bBytes.length) {
60+
crypto.subtle.timingSafeEqual(aBytes, aBytes);
61+
return false;
62+
}
63+
return crypto.subtle.timingSafeEqual(aBytes, bBytes);
64+
}
65+
5266
/**
5367
* Create an error response with standard format
5468
*/

cloudflare-webhook-agent-ingest/src/util/webhook-auth.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { timingSafeEqual } from '@kilocode/worker-utils';
2-
31
export type WebhookAuthInput = {
42
header: string;
53
secret: string;
@@ -33,6 +31,17 @@ export async function compareWebhookSecret(hash: string, secret: string): Promis
3331
return timingSafeEqual(hash, candidateHash);
3432
}
3533

34+
export function timingSafeEqual(a: string, b: string): boolean {
35+
if (a.length !== b.length) {
36+
return false;
37+
}
38+
let result = 0;
39+
for (let index = 0; index < a.length; index += 1) {
40+
result |= a.charCodeAt(index) ^ b.charCodeAt(index);
41+
}
42+
return result === 0;
43+
}
44+
3645
export function sanitizeWebhookAuth(auth: StoredWebhookAuth | null): {
3746
webhookAuthHeader: string | undefined;
3847
webhookAuthConfigured: boolean;

kiloclaw/src/auth/middleware.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { Context, Next } from 'hono';
22
import { getCookie } from 'hono/cookie';
3-
import { timingSafeEqual } from '@kilocode/worker-utils';
43
import type { AppEnv } from '../types';
54
import { KILOCLAW_AUTH_COOKIE } from '../config';
65
import { validateKiloToken } from './jwt';
@@ -102,3 +101,16 @@ export async function internalApiMiddleware(c: Context<AppEnv>, next: Next) {
102101

103102
return next();
104103
}
104+
105+
function timingSafeEqual(a: string, b: string): boolean {
106+
const encoder = new TextEncoder();
107+
const aBytes = encoder.encode(a);
108+
const bBytes = encoder.encode(b);
109+
110+
if (aBytes.length !== bBytes.length) {
111+
// Compare a against itself so the timing is constant regardless of length mismatch
112+
crypto.subtle.timingSafeEqual(aBytes, aBytes);
113+
return false;
114+
}
115+
return crypto.subtle.timingSafeEqual(aBytes, bBytes);
116+
}

packages/worker-utils/src/index.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,24 @@
1-
// 1. DO retry
21
export { withDORetry, DEFAULT_DO_RETRY_CONFIG } from './do-retry.js';
32
export type { DORetryConfig } from './do-retry.js';
43

5-
// 2. Backend auth middleware
64
export { backendAuthMiddleware } from './backend-auth-middleware.js';
75

8-
// 3. Timeout
96
export { withTimeout } from './timeout.js';
107

11-
// 4. R2 client
128
export { createR2Client } from './r2-client.js';
139
export type { R2Client, R2ClientConfig } from './r2-client.js';
1410

15-
// 5. Response helpers
1611
export { resSuccess, resError } from './res.js';
1712
export type { SuccessResponse, ErrorResponse, ApiResponse } from './res.js';
1813

19-
// 6. Zod JSON validator
2014
export { zodJsonValidator } from './zod-json-validator.js';
2115

22-
// 7. Timing-safe equal
23-
export { timingSafeEqual } from './timing-safe-equal.js';
24-
25-
// 8. Format error
2616
export { formatError } from './format-error.js';
2717

28-
// 9. Extract bearer token
2918
export { extractBearerToken } from './extract-bearer-token.js';
3019

31-
// 10. Error handler
3220
export { createErrorHandler } from './error-handler.js';
3321

34-
// 11. Not-found handler
3522
export { createNotFoundHandler } from './not-found-handler.js';
3623

37-
// 12-13. Shared types
3824
export type { Owner, MCPServerConfig } from './types.js';

packages/worker-utils/src/timing-safe-equal.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)