fix(auth): secureCompare to reuse constantTimeEqual from @oslojs/crypto (#180)

This commit is contained in:
Cotton Hou
2026-04-04 03:00:51 +08:00
committed by GitHub
parent d3cf93f171
commit 4f92c63702

View File

@@ -6,7 +6,9 @@
* Tokens are opaque random values. We store only the SHA-256 hash in the database. * Tokens are opaque random values. We store only the SHA-256 hash in the database.
*/ */
import { sha256 } from "@oslojs/crypto/sha2"; import { hmac } from "@oslojs/crypto/hmac";
import { sha256, SHA256 } from "@oslojs/crypto/sha2";
import { constantTimeEqual } from "@oslojs/crypto/subtle";
import { encodeBase64urlNoPadding, decodeBase64urlIgnorePadding } from "@oslojs/encoding"; import { encodeBase64urlNoPadding, decodeBase64urlIgnorePadding } from "@oslojs/encoding";
const TOKEN_BYTES = 32; // 256 bits of entropy const TOKEN_BYTES = 32; // 256 bits of entropy
@@ -162,16 +164,11 @@ export function computeS256Challenge(codeVerifier: string): string {
* Constant-time comparison to prevent timing attacks * Constant-time comparison to prevent timing attacks
*/ */
export function secureCompare(a: string, b: string): boolean { export function secureCompare(a: string, b: string): boolean {
if (a.length !== b.length) return false; const text = new TextEncoder();
const salt = crypto.getRandomValues(new Uint8Array(TOKEN_BYTES));
const hash = (str: string) => hmac(SHA256, salt, text.encode(str));
const aBytes = new TextEncoder().encode(a); return constantTimeEqual(hash(a), hash(b));
const bBytes = new TextEncoder().encode(b);
let result = 0;
for (let i = 0; i < aBytes.length; i++) {
result |= aBytes[i]! ^ bBytes[i]!;
}
return result === 0;
} }
// ============================================================================ // ============================================================================