constant_time_eq/lib.rs
1//! Compares two equal-sized byte strings in constant time.
2//!
3//! The time of the comparison does not depend on:
4//!
5//! * The contents of the inputs;
6//! * The position of the difference(s) between the inputs.
7//!
8//! The time of the comparison can depend on:
9//!
10//! * The memory addresses of the inputs;
11//! * The length of the inputs.
12
13#![cfg_attr(not(feature = "std"), no_std)]
14#![deny(clippy::undocumented_unsafe_blocks)]
15
16#[doc(hidden)]
17pub mod classic;
18
19#[doc(hidden)]
20pub mod generic;
21
22#[cfg(all(
23 any(target_arch = "x86", target_arch = "x86_64"),
24 target_feature = "sse2",
25 not(miri)
26))]
27mod sse2;
28
29#[cfg(all(
30 any(target_arch = "x86", target_arch = "x86_64"),
31 target_feature = "sse2",
32 not(miri)
33))]
34use sse2 as simd;
35
36#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
37mod neon;
38
39#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
40use neon as simd;
41
42#[cfg(not(any(
43 all(
44 any(target_arch = "x86", target_arch = "x86_64"),
45 target_feature = "sse2",
46 not(miri)
47 ),
48 all(target_arch = "aarch64", target_feature = "neon", not(miri))
49)))]
50use generic as simd;
51
52#[cfg(all(target_arch = "aarch64", not(miri)))]
53#[doc(hidden)]
54pub mod dit;
55
56#[cfg(all(target_arch = "aarch64", not(miri)))]
57use dit::with_dit;
58
59/// Runs code with the hardware DIT feature or equivalent enabled when possible.
60#[cfg(any(not(target_arch = "aarch64"), miri))]
61#[inline(always)]
62pub(crate) fn with_dit<T, F>(f: F) -> T
63where
64 F: FnOnce() -> T,
65{
66 f()
67}
68
69/// Compares two equal-sized byte strings in constant time.
70///
71/// # Examples
72///
73/// ```
74/// use constant_time_eq::constant_time_eq;
75///
76/// assert!(constant_time_eq(b"foo", b"foo"));
77/// assert!(!constant_time_eq(b"foo", b"bar"));
78/// assert!(!constant_time_eq(b"bar", b"baz"));
79/// # assert!(constant_time_eq(b"", b""));
80///
81/// // Not equal-sized, so won't take constant time.
82/// assert!(!constant_time_eq(b"foo", b""));
83/// assert!(!constant_time_eq(b"foo", b"quux"));
84/// ```
85#[must_use]
86pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
87 simd::constant_time_eq(a, b)
88}
89
90// Fixed-size array variant.
91
92/// Compares two fixed-size byte strings in constant time.
93///
94/// # Examples
95///
96/// ```
97/// use constant_time_eq::constant_time_eq_n;
98///
99/// assert!(constant_time_eq_n(&[3; 20], &[3; 20]));
100/// assert!(!constant_time_eq_n(&[3; 20], &[7; 20]));
101/// ```
102#[must_use]
103pub fn constant_time_eq_n<const N: usize>(a: &[u8; N], b: &[u8; N]) -> bool {
104 simd::constant_time_eq_n(a, b)
105}
106
107// Fixed-size variants for the most common sizes.
108
109/// Compares two 128-bit byte strings in constant time.
110///
111/// # Examples
112///
113/// ```
114/// use constant_time_eq::constant_time_eq_16;
115///
116/// assert!(constant_time_eq_16(&[3; 16], &[3; 16]));
117/// assert!(!constant_time_eq_16(&[3; 16], &[7; 16]));
118/// ```
119#[inline]
120#[must_use]
121pub fn constant_time_eq_16(a: &[u8; 16], b: &[u8; 16]) -> bool {
122 constant_time_eq_n(a, b)
123}
124
125/// Compares two 256-bit byte strings in constant time.
126///
127/// # Examples
128///
129/// ```
130/// use constant_time_eq::constant_time_eq_32;
131///
132/// assert!(constant_time_eq_32(&[3; 32], &[3; 32]));
133/// assert!(!constant_time_eq_32(&[3; 32], &[7; 32]));
134/// ```
135#[inline]
136#[must_use]
137pub fn constant_time_eq_32(a: &[u8; 32], b: &[u8; 32]) -> bool {
138 constant_time_eq_n(a, b)
139}
140
141/// Compares two 512-bit byte strings in constant time.
142///
143/// # Examples
144///
145/// ```
146/// use constant_time_eq::constant_time_eq_64;
147///
148/// assert!(constant_time_eq_64(&[3; 64], &[3; 64]));
149/// assert!(!constant_time_eq_64(&[3; 64], &[7; 64]));
150/// ```
151#[inline]
152#[must_use]
153pub fn constant_time_eq_64(a: &[u8; 64], b: &[u8; 64]) -> bool {
154 constant_time_eq_n(a, b)
155}