1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Constant-time byte comparison utilities.
//!
//! This module provides comparison functions that execute in constant time
//! relative to the input length, preventing timing side-channel attacks. A
//! naive byte-by-byte comparison short-circuits on the first mismatch, leaking
//! information about *where* two values diverge. An attacker can exploit that
//! timing variance to iteratively guess secret material one byte at a time.
//!
//! # Design Decisions
//!
//! * **XOR + OR accumulator** -- Each byte pair is XOR-ed and the result is
//! folded into a single accumulator with bitwise OR. This avoids any
//! data-dependent branch.
//! * **`std::hint::black_box`** -- Wraps the final accumulator before the
//! equality check so the compiler cannot observe that we only care about
//! zero-vs-nonzero and reintroduce an early exit.
//! * **Length check is *not* constant-time** -- The lengths of the values we
//! compare (e.g. HMAC digests) are public and fixed by the algorithm, so
//! leaking length via an early return is acceptable. This is explicitly
//! documented on the function.
//!
//! # Security Considerations
//!
//! This implementation targets best-effort constant-time behaviour in safe
//! Rust. Hardware-level guarantees (e.g. constant-time XOR on all
//! micro-architectures) are outside our control; however the approach is
//! consistent with widely accepted practice (OpenSSL, ring, subtle).
use black_box;
/// Compares two byte slices in constant time (relative to their length).
///
/// Returns `true` if and only if `a` and `b` have the same length and every
/// byte is identical.
///
/// # Security
///
/// * The comparison iterates over *all* byte positions regardless of where a
/// mismatch occurs, and `std::hint::black_box` prevents the optimiser from
/// short-circuiting the final check.
/// * **Length is not treated as secret.** If the slices differ in length the
/// function returns `false` immediately. This is safe for our use cases
/// (fixed-size HMAC/hash digests) and is documented here so callers are
/// aware of the trade-off.
///
/// # Examples
///
/// ```
/// use entropy_auth::crypto::constant_time::constant_time_eq;
///
/// assert!(constant_time_eq(b"secret", b"secret"));
/// assert!(!constant_time_eq(b"secret", b"differ"));
/// ```