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
// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT-0
use crate::Error;
use crate::low::{ct, ct_equal};
/// Checks in constant time that `a` and `b` hold the same bytes.
///
/// Their lengths are public: unequal ones fail with [`Error::WrongLength`]
/// before either is read. Unequal contents fail with [`Error::NotEqual`],
/// after reading every byte of both.
pub fn verify(a: &[u8], b: &[u8]) -> Result<(), Error> {
if a.len() != b.len() {
return Err(Error::WrongLength);
}
match ct::into_public(ct_equal(a, b)) {
true => Ok(()),
false => Err(Error::NotEqual),
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
// Under `admin/ctgrind`, memcheck fails this if the comparison branches on
// or indexes by either slice's contents.
#[test]
fn contents_stay_secret() {
for len in [0, 1, 16, 32, 64, 255] {
let a = vec![0x5a; len];
ct::secret_slice(&a);
let b = vec![0x5a; len];
ct::secret_slice(&b);
assert_eq!(verify(&a, &b), Ok(()));
for i in 0..len {
let mut b = vec![0x5a; len];
b[i] = 0xa5;
ct::secret_slice(&b);
assert_eq!(verify(&a, &b), Err(Error::NotEqual));
}
}
}
#[test]
fn unequal_lengths() {
assert_eq!(verify(&[], &[0]), Err(Error::WrongLength));
assert_eq!(verify(&[0; 32], &[0; 31]), Err(Error::WrongLength));
// A prefix of the other is still refused.
assert_eq!(verify(b"tag", b"tag\0"), Err(Error::WrongLength));
}
}