refactor: remove unused constant_time_eq and correct the token doc
All checks were successful
deploy / test-backend (push) Successful in 35m33s
deploy / test-frontend (push) Successful in 10m46s
deploy / build-and-push (push) Successful in 11m31s
deploy / deploy (push) Successful in 13s

constant_time_eq (and the `subtle` dependency) was never wired into any
comparison — the module doc wrongly claimed token comparison used it, but lookup
is an indexed DB equality on the 256-bit SHA-256 hash, so there's no in-process
secret compare to time-attack. Delete the dead fn, its test, and the unused
`subtle` dep; rewrite the doc. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-13 20:22:39 +02:00
parent 9148e23da8
commit b5f7467c47
3 changed files with 4 additions and 16 deletions

1
backend/Cargo.lock generated
View File

@@ -1587,7 +1587,6 @@ dependencies = [
"serde_json", "serde_json",
"sha2", "sha2",
"sqlx", "sqlx",
"subtle",
"sysinfo", "sysinfo",
"tempfile", "tempfile",
"thiserror 1.0.69", "thiserror 1.0.69",

View File

@@ -35,7 +35,6 @@ dotenvy = "0.15"
argon2 = "0.5" argon2 = "0.5"
rand = "0.8" rand = "0.8"
sha2 = "0.10" sha2 = "0.10"
subtle = "2"
base64 = "0.22" base64 = "0.22"
# Image decode + downscale for the analysis worker (keep the page image # Image decode + downscale for the analysis worker (keep the page image
# under the local vision model's token budget). Only the manga page formats. # under the local vision model's token budget). Only the manga page formats.

View File

@@ -3,15 +3,16 @@
//! `generate_token` draws 32 bytes from the OS CSPRNG, encodes them as //! `generate_token` draws 32 bytes from the OS CSPRNG, encodes them as
//! URL-safe base64 (no padding), and returns the raw string alongside its //! URL-safe base64 (no padding), and returns the raw string alongside its
//! SHA-256 hash. Storage holds only the hash; the raw value lives in the //! SHA-256 hash. Storage holds only the hash; the raw value lives in the
//! cookie or `Authorization` header. Comparison goes through //! cookie or `Authorization` header. Token lookup is an indexed equality on
//! `constant_time_eq` to keep timing side channels off the table. //! that 256-bit hash in the database (`WHERE token_hash = $1`), so there's no
//! in-process secret comparison to time-attack: a guess has to match a full
//! SHA-256 digest, and the DB index reveals nothing about how close it came.
use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine as _; use base64::Engine as _;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::RngCore; use rand::RngCore;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use subtle::ConstantTimeEq;
pub const TOKEN_BYTES: usize = 32; pub const TOKEN_BYTES: usize = 32;
pub const HASH_BYTES: usize = 32; pub const HASH_BYTES: usize = 32;
@@ -30,10 +31,6 @@ pub fn hash_token(raw: &str) -> [u8; HASH_BYTES] {
hasher.finalize().into() hasher.finalize().into()
} }
pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
a.ct_eq(b).into()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -58,11 +55,4 @@ mod tests {
assert_eq!(hash_token("abc"), hash_token("abc")); assert_eq!(hash_token("abc"), hash_token("abc"));
assert_ne!(hash_token("abc"), hash_token("abd")); assert_ne!(hash_token("abc"), hash_token("abd"));
} }
#[test]
fn constant_time_eq_compares_correctly() {
assert!(constant_time_eq(b"abc", b"abc"));
assert!(!constant_time_eq(b"abc", b"abd"));
assert!(!constant_time_eq(b"abc", b"abcd"));
}
} }