refactor: remove unused constant_time_eq and correct the token doc
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:
1
backend/Cargo.lock
generated
1
backend/Cargo.lock
generated
@@ -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",
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user