mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 01:31:08 +00:00
Pad both buffers to equal length before constant-time comparison. Key fix: call timingSafeEqual unconditionally and store the result before the && length check, ensuring the constant-time comparison always runs regardless of buffer lengths. This avoids JavaScript's && short-circuit evaluation which would skip timingSafeEqual on length mismatches, preserving the timing side-channel. Changes: - Pad hash and signature buffers to maxLen before comparison - Store timingSafeEqual result before combining with length check - Add explanatory comment about the short-circuit avoidance
25 lines
969 B
TypeScript
25 lines
969 B
TypeScript
import crypto from "node:crypto";
|
|
|
|
export function validateLineSignature(
|
|
body: string,
|
|
signature: string,
|
|
channelSecret: string,
|
|
): boolean {
|
|
const hash = crypto.createHmac("SHA256", channelSecret).update(body).digest("base64");
|
|
const hashBuffer = Buffer.from(hash);
|
|
const signatureBuffer = Buffer.from(signature);
|
|
|
|
// Pad to equal length before constant-time comparison to prevent
|
|
// leaking length information via early-return timing.
|
|
const maxLen = Math.max(hashBuffer.length, signatureBuffer.length);
|
|
const paddedHash = Buffer.alloc(maxLen);
|
|
const paddedSig = Buffer.alloc(maxLen);
|
|
hashBuffer.copy(paddedHash);
|
|
signatureBuffer.copy(paddedSig);
|
|
|
|
// Call timingSafeEqual unconditionally to ensure constant-time execution
|
|
// regardless of length mismatch (avoids && short-circuit timing leak).
|
|
const timingResult = crypto.timingSafeEqual(paddedHash, paddedSig);
|
|
return hashBuffer.length === signatureBuffer.length && timingResult;
|
|
}
|