rfc6979/
ct_cmp.rs

1//! Constant-time comparison helpers for [`ByteArray`].
2
3use crate::{ArrayLength, ByteArray};
4use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
5
6/// Constant-time equals.
7pub(crate) fn ct_eq<N: ArrayLength<u8>>(a: &ByteArray<N>, b: &ByteArray<N>) -> Choice {
8    let mut ret = Choice::from(1);
9
10    for (a, b) in a.iter().zip(b.iter()) {
11        ret.conditional_assign(&Choice::from(0), !a.ct_eq(b));
12    }
13
14    ret
15}
16
17/// Constant-time less than.
18///
19/// Inputs are interpreted as big endian integers.
20pub(crate) fn ct_lt<N: ArrayLength<u8>>(a: &ByteArray<N>, b: &ByteArray<N>) -> Choice {
21    let mut borrow = 0;
22
23    // Perform subtraction with borrow a byte-at-a-time, interpreting a
24    // no-borrow condition as the less-than case
25    for (&a, &b) in a.iter().zip(b.iter()).rev() {
26        let c = (b as u16).wrapping_add(borrow >> (u8::BITS - 1));
27        borrow = (a as u16).wrapping_sub(c) >> u8::BITS as u8;
28    }
29
30    !borrow.ct_eq(&0)
31}
32
33#[cfg(test)]
34mod tests {
35    const A: [u8; 4] = [0, 0, 0, 0];
36    const B: [u8; 4] = [0, 0, 0, 1];
37    const C: [u8; 4] = [0xFF, 0, 0, 0];
38    const D: [u8; 4] = [0xFF, 0, 0, 1];
39    const E: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFE];
40    const F: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF];
41
42    #[test]
43    fn ct_eq() {
44        use super::ct_eq;
45
46        assert_eq!(ct_eq(&A.into(), &A.into()).unwrap_u8(), 1);
47        assert_eq!(ct_eq(&B.into(), &B.into()).unwrap_u8(), 1);
48        assert_eq!(ct_eq(&C.into(), &C.into()).unwrap_u8(), 1);
49        assert_eq!(ct_eq(&D.into(), &D.into()).unwrap_u8(), 1);
50        assert_eq!(ct_eq(&E.into(), &E.into()).unwrap_u8(), 1);
51        assert_eq!(ct_eq(&F.into(), &F.into()).unwrap_u8(), 1);
52
53        assert_eq!(ct_eq(&A.into(), &B.into()).unwrap_u8(), 0);
54        assert_eq!(ct_eq(&C.into(), &D.into()).unwrap_u8(), 0);
55        assert_eq!(ct_eq(&E.into(), &F.into()).unwrap_u8(), 0);
56    }
57
58    #[test]
59    fn ct_lt() {
60        use super::ct_lt;
61
62        assert_eq!(ct_lt(&A.into(), &A.into()).unwrap_u8(), 0);
63        assert_eq!(ct_lt(&B.into(), &B.into()).unwrap_u8(), 0);
64        assert_eq!(ct_lt(&C.into(), &C.into()).unwrap_u8(), 0);
65        assert_eq!(ct_lt(&D.into(), &D.into()).unwrap_u8(), 0);
66        assert_eq!(ct_lt(&E.into(), &E.into()).unwrap_u8(), 0);
67        assert_eq!(ct_lt(&F.into(), &F.into()).unwrap_u8(), 0);
68
69        assert_eq!(ct_lt(&A.into(), &B.into()).unwrap_u8(), 1);
70        assert_eq!(ct_lt(&A.into(), &C.into()).unwrap_u8(), 1);
71        assert_eq!(ct_lt(&B.into(), &A.into()).unwrap_u8(), 0);
72        assert_eq!(ct_lt(&C.into(), &A.into()).unwrap_u8(), 0);
73
74        assert_eq!(ct_lt(&B.into(), &C.into()).unwrap_u8(), 1);
75        assert_eq!(ct_lt(&B.into(), &D.into()).unwrap_u8(), 1);
76        assert_eq!(ct_lt(&C.into(), &B.into()).unwrap_u8(), 0);
77        assert_eq!(ct_lt(&D.into(), &B.into()).unwrap_u8(), 0);
78
79        assert_eq!(ct_lt(&C.into(), &D.into()).unwrap_u8(), 1);
80        assert_eq!(ct_lt(&C.into(), &E.into()).unwrap_u8(), 1);
81        assert_eq!(ct_lt(&D.into(), &C.into()).unwrap_u8(), 0);
82        assert_eq!(ct_lt(&E.into(), &C.into()).unwrap_u8(), 0);
83
84        assert_eq!(ct_lt(&E.into(), &F.into()).unwrap_u8(), 1);
85        assert_eq!(ct_lt(&F.into(), &E.into()).unwrap_u8(), 0);
86    }
87}