@@ -17,6 +17,20 @@ pub fn millis(duration: Duration) -> f64 {
1717 ( duration. as_secs_f64 ( ) * 1_000_000.0 ) . round ( ) / 1000.0
1818}
1919
20+ /// Compare two byte slices in constant time with respect to their contents.
21+ ///
22+ /// The running time depends only on the input lengths, never on the byte
23+ /// values, so it cannot leak (via a timing side channel) how many leading
24+ /// bytes matched. Use this wherever an attacker-supplied value is compared
25+ /// against a secret (passwords, cancel keys); a short-circuiting `==`/`memcmp`
26+ /// there is a covert timing channel that lets an attacker recover the secret
27+ /// byte by byte (cf. PostgreSQL CVE-2026-6478, the MD5 password comparison).
28+ ///
29+ /// Length is not treated as secret: a length mismatch returns `false` early.
30+ pub fn constant_time_eq ( a : & [ u8 ] , b : & [ u8 ] ) -> bool {
31+ aws_lc_rs:: constant_time:: verify_slices_are_equal ( a, b) . is_ok ( )
32+ }
33+
2034pub fn human_duration_optional ( duration : Option < Duration > ) -> String {
2135 if let Some ( duration) = duration {
2236 human_duration ( duration)
@@ -411,4 +425,15 @@ mod test {
411425 }
412426 assert_eq ! ( node_id( ) , Ok ( 1 ) ) ;
413427 }
428+
429+ #[ test]
430+ fn test_constant_time_eq ( ) {
431+ assert ! ( constant_time_eq( b"hunter2" , b"hunter2" ) ) ;
432+ assert ! ( !constant_time_eq( b"hunter2" , b"hunter3" ) ) ;
433+ // Different lengths must not match.
434+ assert ! ( !constant_time_eq( b"hunter2" , b"hunter22" ) ) ;
435+ assert ! ( !constant_time_eq( b"" , b"x" ) ) ;
436+ // Two empty slices are equal.
437+ assert ! ( constant_time_eq( b"" , b"" ) ) ;
438+ }
414439}
0 commit comments