1
//! A simple order-preserving encryption function.
2
//!
3
//! This function is used to generate revision counters for onion service
4
//! descriptors.  It is not suitable for other purposes.
5
//!
6
//! The scheme here is the one described in the specifications
7
//! as "Encrypted Time In Period".
8
//!
9
//! It is loosely based on the scheme first described in
10
//! G. Bebek. "Anti-tamper database research: Inference control
11
//! techniques."" Technical Report EECS 433 Final Report, Case
12
//! Western Reserve University, November 2002.
13

            
14
// NOTE:
15
//
16
// We use the same algorithm here as in C tor, not because it is a great
17
// algorithm, but because there has been a community of onion service operators
18
// who try to achieve load balancing by running multiple onion services with the
19
// same keys, and letting them "race" to publish at the HsDirs.  This only
20
// works if all the onion service instances produce the same revision counters.
21

            
22
use cipher::{KeyIvInit as _, StreamCipher as _};
23
use digest::Digest as _;
24

            
25
use tor_llcrypto::{cipher::aes::Aes256Ctr, d::Sha3_256};
26
use zeroize::Zeroizing;
27

            
28
/// Key for a simple order-preserving encryption on the offset from the start of an SRV protocol
29
/// run.
30
///
31
/// The algorithm here is chosen to be the same as used in the C tor
32
/// implementation.
33
#[derive(Clone, Debug)]
34
pub struct AesOpeKey {
35
    /// The key for our counter-mode cipher.
36
    key: Zeroizing<[u8; 32]>,
37
}
38

            
39
/// A prefix used when deriving an AES key for this purpose.
40
const DERIVATION_PREFIX: &[u8] = b"rev-counter-generation";
41

            
42
impl AesOpeKey {
43
    /// Construct a new [`AesOpeKey`] from a given secret.
44
    ///
45
    /// The secret should be unpredictable by an adversary.
46
504
    pub fn from_secret(secret: &[u8]) -> Self {
47
504
        let mut h = Sha3_256::new();
48
504
        h.update(DERIVATION_PREFIX);
49
504
        h.update(secret);
50
504
        let key = Zeroizing::new(h.finalize().into());
51
504
        AesOpeKey { key }
52
504
    }
53

            
54
    /// Encrypt `offset` to a 64-bit number.
55
    ///
56
    /// (We do not implement a decryption operation.)
57
    ///
58
    /// # Limitations
59
    ///
60
    /// Like all order-preserving encryption, this scheme leaks information by
61
    /// its nature.  It also leaks more information than necessary: (the
62
    /// adversary can get a rough estimate for our input by dividing the output
63
    /// by 0x8001). The only security property that this algorithm tries to
64
    /// provide is that it prevents an adversary from inferring our clock skew.
65
    ///
66
    /// This algorithm is also not very efficient in its implementation.
67
    /// We expect that the result will become unacceptable if the time period is
68
    /// ever larger than a few days.
69
504
    pub fn encrypt(&self, offset: SrvPeriodOffset) -> u64 {
70
        // We add "1" here per the spec, since the encryption of 0 is 0.
71
504
        self.encrypt_inner(offset.0.saturating_add(1))
72
504
    }
73

            
74
    /// Implementation for the order-preserving encryption algorithm:
75
    ///
76
    /// For security, requires that `n` is at least 1.
77
582
    fn encrypt_inner(&self, n: u32) -> u64 {
78
582
        let iv = [0; 16].into();
79
582
        let mut ctr = Aes256Ctr::new((&*self.key).into(), &iv);
80

            
81
        /// Number of u16s to create at once.
82
        const BUF_LEN: usize = 8 * 1024;
83
        /// Number of bytes in a u16
84
        const STEP: usize = 2;
85

            
86
        // We start our accumulator at `n` because we want every increase in the
87
        // input to increase our output by at least 1, but it is otherwise
88
        // possible for one of our randomly generated u16s to be 0x0000.
89
582
        let mut result = u64::from(n);
90
582
        let mut n = n as usize;
91
582
        let mut buf = [0_u8; BUF_LEN * STEP];
92
2720
        while n >= BUF_LEN {
93
2138
            buf.fill(0);
94
2138
            ctr.apply_keystream(&mut buf[..]);
95
2138
            result += add_slice_as_le_u16(&buf[..]);
96
2138
            n -= BUF_LEN;
97
2138
        }
98
582
        if n > 0 {
99
582
            buf.fill(0);
100
582
            ctr.apply_keystream(&mut buf[..n * STEP]);
101
582
            result += add_slice_as_le_u16(&buf[..n * STEP]);
102
582
        }
103
582
        result
104
582
    }
105
}
106

            
107
/// An opaque offset within an SRV period.
108
///
109
/// Used by onion services to compute a HsDesc revision counter.
110
#[derive(Copy, Clone, Debug, PartialEq, derive_more::From)]
111
pub struct SrvPeriodOffset(
112
    // An offset, in seconds.
113
    pub(crate) u32,
114
);
115

            
116
/// Treating `slice` as a sequence of little-endian 2-byte words,
117
/// add them into a u64.
118
///
119
/// # Panics
120
///
121
/// Panics if slice is not even in size.
122
2726
fn add_slice_as_le_u16(slice: &[u8]) -> u64 {
123
2726
    assert_eq!(slice.len() % 2, 0);
124
2726
    slice
125
2726
        .chunks_exact(2)
126
132620043
        .map(|bytepair| {
127
132619400
            let a: [u8; 2] = bytepair.try_into().expect("chunk was not of size 2!");
128
132619400
            u64::from(u16::from_le_bytes(a))
129
132619400
        })
130
2726
        .sum()
131
2726
}
132

            
133
#[cfg(test)]
134
mod test {
135
    use super::*;
136
    use hex_literal::hex;
137

            
138
    #[test]
139
    fn add_slice() {
140
        assert_eq!(6, add_slice_as_le_u16(&[1, 0, 2, 0, 3, 0]));
141
        assert_eq!(0x600, add_slice_as_le_u16(&[0, 1, 0, 2, 0, 3]));
142
        assert_eq!(
143
            419477,
144
            add_slice_as_le_u16(b"This is a string of moderate length!")
145
        );
146
    }
147

            
148
    #[test]
149
    fn test_vec() {
150
        let key = hex!("19e05891d55232c08c2cad91d612fdb9cbd6691949a0742434a76c80bc6992fe");
151
        let key = AesOpeKey { key: key.into() };
152

            
153
        // Test vectors taken from C tor.
154
        for (inp, expected) in [
155
            (82283, 2695743564_u64),
156
            (72661, 2381548866_u64),
157
            (72941, 2390408421_u64),
158
            (123122, 4036781069_u64),
159
            (12154, 402067100_u64),
160
            (121574, 3986197593_u64),
161
            (11391, 376696838_u64),
162
            (65845, 2161801517_u64),
163
            (86301, 2828270975_u64),
164
            (61284, 2013616892_u64),
165
            (70505, 2313368870_u64),
166
            (30438, 1001394664_u64),
167
            (60150, 1977329668_u64),
168
            (114800, 3764946628_u64),
169
            (109403, 3585352477_u64),
170
            (21893, 721388468_u64),
171
            (123569, 4051780471_u64),
172
            (95617, 3134921876_u64),
173
            (48561, 1597596985_u64),
174
            (53334, 1753691710_u64),
175
            (92746, 3040874493_u64),
176
            (7110, 234966492_u64),
177
            (9612, 318326551_u64),
178
            (106958, 3506124249_u64),
179
            (46889, 1542219146_u64),
180
            (87790, 2877361609_u64),
181
            (68878, 2260369112_u64),
182
            (47917, 1576681737_u64),
183
            (121128, 3971553290_u64),
184
            (108602, 3559176081_u64),
185
            (28217, 929692460_u64),
186
            (69498, 2280554161_u64),
187
            (63870, 2098322675_u64),
188
            (57542, 1891698992_u64),
189
            (122148, 4004515805_u64),
190
            (46254, 1521227949_u64),
191
            (42850, 1408996941_u64),
192
            (92661, 3037901517_u64),
193
            (57720, 1897369989_u64),
194
        ] {
195
            assert_eq!(key.encrypt_inner(inp), expected);
196
        }
197
    }
198
}