{"record":{"id":"47b6ffbf1335bbe8","repo":"denoland/deno","slug":"err-crypto-timing-safe-equal-length","errorCode":"ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH","errorMessage":"Input buffers must have the same byte length","messagePattern":"Input buffers must have the same byte length","errorType":"error_code","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal_binding/_timingSafeEqual.ts","lineNumber":80,"sourceCode":"      );\n    }\n    return new DataView(\n      TypedArrayPrototypeGetBuffer(ab),\n      TypedArrayPrototypeGetByteOffset(ab),\n      TypedArrayPrototypeGetByteLength(ab),\n    );\n  }\n  return new DataView(ab);\n}\n\n/** Compare to array buffers or data views in a way that timing based attacks\n * cannot gain information about the platform. */\nfunction stdTimingSafeEqual(\n  a: ArrayBufferView | ArrayBufferLike | DataView,\n  b: ArrayBufferView | ArrayBufferLike | DataView,\n): boolean {\n  if (byteLengthOf(a) !== byteLengthOf(b)) {\n    throw new ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH();\n  }\n  if (!isDataView(a)) {\n    a = toDataView(a);\n  }\n  if (!isDataView(b)) {\n    b = toDataView(b);\n  }\n  const length = DataViewPrototypeGetByteLength(a);\n  let out = 0;\n  let i = -1;\n  while (++i < length) {\n    out |= DataViewPrototypeGetUint8(a, i) ^ DataViewPrototypeGetUint8(b, i);\n  }\n  return out === 0;\n}\n\nconst timingSafeEqual = (\n  buf1: Buffer | DataView | ArrayBuffer,","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal_binding/_timingSafeEqual.ts#L62-L98","documentation":"Constant-time comparison works by XOR-ing bytes, which inherently reveals length, so both inputs must have the same byte length. Deno's timingSafeEqual (like Node's) throws ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH before comparing when byteLengthOf(a) !== byteLengthOf(b). This is a safety guard, not a size limit.","triggerScenarios":"Comparing a hex-encoded digest (64 chars/bytes) with the raw 32-byte digest; user-supplied token of different length than the stored one; one side still containing a prefix ('Bearer ', 'sha256='); hex vs base64 encodings of the same digest producing different lengths.","commonSituations":"Webhook signature verification (Stripe/GitHub style), session-token comparison, HMAC checks where one side is encoded and the other raw; attacker-controlled input of arbitrary length hitting the compare.","solutions":["Compare fixed-length digests instead of raw inputs: hash both sides with SHA-256 first, then timingSafeEqual(hash(a), hash(b))","Or length-check first and fail closed — length is not secret: if (a.length !== b.length) return false","Make sure both sides are decoded to raw bytes with the same encoding before comparing"],"exampleFix":"// before\nconst ok = crypto.timingSafeEqual(\n  Buffer.from(userMacHex, 'utf8'),   // 64 bytes of hex text\n  computedMac,                        // 32 raw bytes -> throws\n);\n\n// after\nconst ok = Buffer.from(userMacHex, 'hex').length === computedMac.length &&\n  crypto.timingSafeEqual(Buffer.from(userMacHex, 'hex'), computedMac);","handlingStrategy":"validation","validationCode":"function safeTimingSafeEqual(a: Buffer, b: Buffer): boolean {\n  if (a.length !== b.length) return false; // length is not secret\n  return crypto.timingSafeEqual(a, b);\n}\n// or compare digests to eliminate length dependence:\nconst sha256 = (v: Buffer) => crypto.createHash('sha256').update(v).digest();\nconst ok = crypto.timingSafeEqual(sha256(a), sha256(b));","typeGuard":null,"tryCatchPattern":"try {\n  ok = crypto.timingSafeEqual(a, b);\n} catch (e: any) {\n  if (e?.code === 'ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH') ok = false;\n  else throw e;\n}","preventionTips":["Hash both inputs (SHA-256) before comparing when lengths can differ","Strip prefixes ('Bearer ', 'sha256=') before decoding and comparing","Decode hex/base64 to raw bytes on both sides so encodings match"],"tags":["crypto","security","timing-safe","node-compat"],"backgroundTag":"timing-safe-equal-length-mismatch","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}