From 7a791cc5b2a2a623e40dbe87e18ab70c3cae3b92 Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Sat, 7 Nov 2026 14:05:16 -1000 Subject: Check the comparisons that must not leak timing constant_equals was only checked for its answers, on strings that differ in one byte. Summing the plain differences instead of their squares passed, which accepts any signature with the same byte sum, and so did a plain == at the signature or the password check. --- tests/extensions/test-auth.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/extensions/test-auth.lua b/tests/extensions/test-auth.lua index 18017ff..6bd588d 100644 --- a/tests/extensions/test-auth.lua +++ b/tests/extensions/test-auth.lua @@ -231,6 +231,8 @@ h.check("and in linear time", os.clock() - started < 0.5) h.check("equal strings compare equal", constant_equals("abc", "abc")) h.check("differing strings do not", not constant_equals("abc", "abd")) +h.check("nor do strings whose byte differences cancel out", + not constant_equals("ab", "ba") and not constant_equals("ac", "bb")) h.check("differing lengths do not", not constant_equals("a", "ab")) h.check("non strings do not", not constant_equals(nil, "a")) @@ -468,6 +470,29 @@ h.equals("and grants a session that verifies", granted and validate_value("usern local expires = session_expiry(granted) h.check("which expires a week after the login", expires and expires >= before + 604800 and expires <= os.time() + 604800, tostring(expires)) + +-- A plain == at either comparison would leave every check above passing +-- while timing told a visitor how much of a guess was right. +local real_constant_equals = constant_equals +local compared = {} +constant_equals = function(a, b) + compared[#compared + 1] = { a, b } + return real_constant_equals(a, b) +end +local function was_compared(value) + for _, pair in ipairs(compared) do + if pair[1] == value or pair[2] == value then + return true + end + end + return false +end +visit("secret-repo", session) +h.check("a visit compares the signature in constant time", was_compared(session:match("[^|]+$"))) +compared = {} +login("Alice", password, way_back) +h.check("and a login the password's hash", was_compared(stored_hash)) +constant_equals = real_constant_equals h.equals("and logs nothing", h.error_log(), "") out = login("Alice", "wrong", way_back) -- cgit 2.12.1