1
//! Types and functions for onion service descriptor encryption.
2

            
3
use tor_hscrypto::{pk::HsBlindId, RevisionCounter, Subcredential};
4
use tor_llcrypto::cipher::aes::Aes256Ctr as Cipher;
5
use tor_llcrypto::d::Sha3_256 as Hash;
6
use tor_llcrypto::d::Shake256 as KDF;
7

            
8
use cipher::{KeyIvInit, StreamCipher};
9
use digest::{ExtendableOutput, FixedOutput, Update, XofReader};
10
#[cfg(any(test, feature = "hs-service"))]
11
use rand::{CryptoRng, Rng};
12
use tor_llcrypto::pk::curve25519::PublicKey;
13
use tor_llcrypto::pk::curve25519::StaticSecret;
14
use tor_llcrypto::util::ct::CtByteArray;
15
use zeroize::Zeroizing as Z;
16

            
17
/// Parameters for encrypting or decrypting part of an onion service descriptor.
18
///
19
/// The algorithm is as described in section `[HS-DESC-ENCRYPTION-KEYS]` of
20
/// rend-spec-v3.txt
21
pub(super) struct HsDescEncryption<'a> {
22
    /// First half of the "SECRET_DATA" field.
23
    ///
24
    /// (See rend-spec v3 2.5.1.1 and 2.5.2.1.)
25
    pub(super) blinded_id: &'a HsBlindId,
26
    /// Second half of the "SECRET_DATA" field.
27
    ///
28
    /// This is absent when handling the superencryption layer (2.5.1.1).
29
    /// For the encryption layer, it is `descriptor_cookie` (2.5.2.1)
30
    /// which is present when descriptor-encryption authentication via
31
    /// `KP_hsc_desc_enc` is in use.
32
    pub(super) desc_enc_nonce: Option<&'a HsDescEncNonce>,
33
    /// The "subcredential" of the onion service.
34
    pub(super) subcredential: &'a Subcredential,
35
    /// The current revision of the onion service descriptor being decrypted.
36
    pub(super) revision: RevisionCounter,
37
    /// A "personalization string".
38
    ///
39
    /// This is set to one of two constants depending on the layer being
40
    /// decrypted.
41
    pub(super) string_const: &'a [u8],
42
}
43

            
44
/// The length of a client ID.
45
pub(crate) const HS_DESC_CLIENT_ID_LEN: usize = 8;
46

            
47
/// The length of the `AuthClient` IV.
48
pub(crate) const HS_DESC_IV_LEN: usize = 16;
49

            
50
/// The length of an `N_hs_desc_enc` nonce (also known as a "descriptor cookie").
51
pub(crate) const HS_DESC_ENC_NONCE_LEN: usize = 16;
52

            
53
/// A value used in deriving the encryption key for the inner (encryption) layer
54
/// of onion service encryption.
55
///
56
/// This is  `N_hs_desc_enc` in the spec, where sometimes we also call it a
57
/// "descriptor cookie".
58
#[derive(derive_more::AsRef, derive_more::From)]
59
pub(super) struct HsDescEncNonce([u8; HS_DESC_ENC_NONCE_LEN]);
60

            
61
/// Length of our cryptographic salt.
62
const SALT_LEN: usize = 16;
63
/// Length of our ersatz MAC.
64
const MAC_LEN: usize = 32;
65

            
66
impl<'a> HsDescEncryption<'a> {
67
    /// Length of our MAC key.
68
    const MAC_KEY_LEN: usize = 32;
69
    /// Length of the cipher key that we use.
70
    const CIPHER_KEY_LEN: usize = 32;
71
    /// Length of our cipher's IV.
72
    const IV_LEN: usize = 16;
73

            
74
    /// Encrypt a given bytestring using these encryption parameters.
75
    #[cfg(any(test, feature = "hs-service"))]
76
310
    pub(super) fn encrypt<R: Rng + CryptoRng>(&self, rng: &mut R, data: &[u8]) -> Vec<u8> {
77
310
        let output_len = data.len() + SALT_LEN + MAC_LEN;
78
310
        let mut output = Vec::with_capacity(output_len);
79
310
        let salt: [u8; SALT_LEN] = rng.gen();
80
310

            
81
310
        let (mut cipher, mut mac) = self.init(&salt);
82
310

            
83
310
        output.extend_from_slice(&salt[..]);
84
310
        output.extend_from_slice(data);
85
310
        cipher.apply_keystream(&mut output[SALT_LEN..]);
86
310
        mac.update(&output[SALT_LEN..]);
87
310
        let mut mac_val = Default::default();
88
310
        mac.finalize_into(&mut mac_val);
89
310
        output.extend_from_slice(&mac_val);
90
310
        debug_assert_eq!(output.len(), output_len);
91

            
92
310
        output
93
310
    }
94
    /// Decrypt a given bytestring that was first encrypted using these
95
    /// encryption parameters.
96
626
    pub(super) fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, DecryptionError> {
97
626
        if data.len() < SALT_LEN + MAC_LEN {
98
6
            return Err(DecryptionError::default());
99
620
        }
100
620
        let msg_len = data.len() - SALT_LEN - MAC_LEN;
101
620

            
102
620
        let salt = data[0..SALT_LEN]
103
620
            .try_into()
104
620
            .expect("Failed try_into for 16-byte array.");
105
620
        let ciphertext = &data[SALT_LEN..(SALT_LEN + msg_len)];
106
620

            
107
620
        let expected_mac = CtByteArray::from(
108
620
            <[u8; MAC_LEN]>::try_from(&data[SALT_LEN + msg_len..SALT_LEN + msg_len + MAC_LEN])
109
620
                .expect("Failed try_into for 32-byte array."),
110
620
        );
111
620
        let (mut cipher, mut mac) = self.init(&salt);
112
620

            
113
620
        // check mac.
114
620
        mac.update(ciphertext);
115
620
        let mut received_mac = CtByteArray::from([0_u8; MAC_LEN]);
116
620
        mac.finalize_into(received_mac.as_mut().into());
117
620
        if received_mac != expected_mac {
118
12
            return Err(DecryptionError::default());
119
608
        }
120
608

            
121
608
        let mut decrypted = ciphertext.to_vec();
122
608
        cipher.apply_keystream(&mut decrypted[..]);
123
608

            
124
608
        Ok(decrypted)
125
626
    }
126

            
127
    /// Return the cryptographic objects that are used for en/decrypting and
128
    /// authenticating a HsDesc layer, given these parameters and a provided
129
    /// salt.
130
    ///
131
    /// Calculates `SECRET_KEY` and `SECRET_IV` (as `Cipher`) and `MAC_KEY` (as `Hash`)
132
    /// from rend-spec-v3 2.5.3 (`[HS-DESC-ENCRYPTION-KEYS]`).
133
    ///
134
    /// `Hash` is the required intermediate value in the calculation of `D_MAC`:
135
    /// It is in the state just after the `SALT` has been added;
136
    /// the ciphertext should be added, and then it should be finalized.
137
6546
    fn init(&self, salt: &[u8; 16]) -> (Cipher, Hash) {
138
6546
        let mut key_stream = self.get_kdf(salt).finalize_xof();
139
6546

            
140
6546
        let mut key = Z::new([0_u8; Self::CIPHER_KEY_LEN]);
141
6546
        let mut iv = Z::new([0_u8; Self::IV_LEN]);
142
6546
        let mut mac_key = Z::new([0_u8; Self::MAC_KEY_LEN]);
143
6546
        key_stream.read(&mut key[..]);
144
6546
        key_stream.read(&mut iv[..]);
145
6546
        key_stream.read(&mut mac_key[..]);
146
6546

            
147
6546
        let cipher = Cipher::new(key.as_ref().into(), iv.as_ref().into());
148
6546

            
149
6546
        let mut mac = Hash::default();
150
6546
        mac.update(&(Self::MAC_KEY_LEN as u64).to_be_bytes());
151
6546
        mac.update(&mac_key[..]);
152
6546
        mac.update(&(salt.len() as u64).to_be_bytes());
153
6546
        mac.update(&salt[..]);
154
6546

            
155
6546
        (cipher, mac)
156
6546
    }
157

            
158
    /// Return a KDF that can yield the keys to be used for encryption with
159
    /// these key parameters.
160
    ///
161
    /// Calculates `keys` from rend-spec-v3 2.5.3 (`[HS-DESC-ENCRYPTION-KEYS]`)
162
    /// as required for the two instantiations of `HS-DESC-ENCRYPTION-KEYS` in
163
    /// 2.5.1.1 ("First layer encryption logic") and 2.5.2.1 ("Second layer
164
    /// encryption logic").
165
6546
    fn get_kdf(&self, salt: &[u8; 16]) -> KDF {
166
6546
        let mut kdf = KDF::default();
167
6546

            
168
6546
        // secret_input = SECRET_DATA | N_hs_subcred | INT_8(revision_counter)
169
6546
        //
170
6546
        // (SECRET_DATA is always KP_blind_id (2.5.1.1), or KP_blind_id | N_hs_desc_nonce) (2.5.2.1).
171
6546
        kdf.update(self.blinded_id.as_ref());
172
6546
        if let Some(cookie) = self.desc_enc_nonce {
173
90
            kdf.update(cookie.as_ref());
174
6456
        }
175
6546
        kdf.update(self.subcredential.as_ref());
176
6546
        kdf.update(&u64::from(self.revision).to_be_bytes());
177
6546

            
178
6546
        // keys = KDF(secret_input | salt | STRING_CONSTANT, S_KEY_LEN + S_IV_LEN + MAC_KEY_LEN)
179
6546
        kdf.update(salt);
180
6546
        kdf.update(self.string_const);
181
6546

            
182
6546
        kdf
183
6546
    }
184
}
185

            
186
/// An error that occurs when decrypting an onion service descriptor.
187
///
188
/// This error is deliberately uninformative, to avoid side channels.
189
#[non_exhaustive]
190
#[derive(Clone, Debug, Default, thiserror::Error)]
191
#[error("Unable to decrypt onion service descriptor.")]
192
pub struct DecryptionError {}
193

            
194
/// Create the CLIENT-ID and COOKIE-KEY required for hidden service restricted discovery.
195
///
196
/// This is used by HS clients to decrypt the descriptor cookie from the onion service descriptor,
197
/// and by HS services to build the client-auth sections of descriptors.
198
///
199
/// Section 2.5.1.2. of rend-spec-v3 says:
200
/// ```text
201
///     SECRET_SEED = x25519(hs_y, client_X)
202
///                 = x25519(client_y, hs_X)
203
///     KEYS = KDF(N_hs_subcred | SECRET_SEED, 40)
204
///     CLIENT-ID = first 8 bytes of KEYS
205
///     COOKIE-KEY = last 32 bytes of KEYS
206
///
207
/// Where:
208
///     hs_{X,y} = K{P,S}_hss_desc_enc
209
///     client_{X,Y} = K{P,S}_hsc_desc_enc
210
/// ```
211
94
pub(crate) fn build_descriptor_cookie_key(
212
94
    our_secret_key: &StaticSecret,
213
94
    their_public_key: &PublicKey,
214
94
    subcredential: &Subcredential,
215
94
) -> (CtByteArray<8>, [u8; 32]) {
216
94
    let secret_seed = our_secret_key.diffie_hellman(their_public_key);
217
94
    let mut kdf = KDF::default();
218
94
    kdf.update(subcredential.as_ref());
219
94
    kdf.update(secret_seed.as_bytes());
220
94
    let mut keys = kdf.finalize_xof();
221
94
    let mut client_id = CtByteArray::from([0_u8; 8]);
222
94
    let mut cookie_key = [0_u8; 32];
223
94
    keys.read(client_id.as_mut());
224
94
    keys.read(&mut cookie_key);
225
94

            
226
94
    (client_id, cookie_key)
227
94
}
228

            
229
#[cfg(test)]
230
mod test {
231
    // @@ begin test lint list maintained by maint/add_warning @@
232
    #![allow(clippy::bool_assert_comparison)]
233
    #![allow(clippy::clone_on_copy)]
234
    #![allow(clippy::dbg_macro)]
235
    #![allow(clippy::mixed_attributes_style)]
236
    #![allow(clippy::print_stderr)]
237
    #![allow(clippy::print_stdout)]
238
    #![allow(clippy::single_char_pattern)]
239
    #![allow(clippy::unwrap_used)]
240
    #![allow(clippy::unchecked_duration_subtraction)]
241
    #![allow(clippy::useless_vec)]
242
    #![allow(clippy::needless_pass_by_value)]
243
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
244

            
245
    use super::*;
246
    use tor_basic_utils::test_rng::testing_rng;
247

            
248
    #[test]
249
    fn roundtrip_basics() {
250
        let blinded_id = [7; 32].into();
251
        let subcredential = [11; 32].into();
252
        let revision = 13.into();
253
        let string_const = "greetings puny humans";
254
        let params = HsDescEncryption {
255
            blinded_id: &blinded_id,
256
            desc_enc_nonce: None,
257
            subcredential: &subcredential,
258
            revision,
259
            string_const: string_const.as_bytes(),
260
        };
261

            
262
        let mut rng = testing_rng();
263

            
264
        let bigmsg: Vec<u8> = (1..123).cycle().take(1021).collect();
265
        for message in [&b""[..], &b"hello world"[..], &bigmsg[..]] {
266
            let mut encrypted = params.encrypt(&mut rng, message);
267
            assert_eq!(encrypted.len(), message.len() + 48);
268
            let decrypted = params.decrypt(&encrypted[..]).unwrap();
269
            assert_eq!(message, &decrypted);
270

            
271
            // Make sure we can't decrypt a partial input.
272
            let decryption_err = params.decrypt(&encrypted[..encrypted.len() - 1]);
273
            assert!(decryption_err.is_err());
274
            // Frob a point in the encrypted form and ensure we won't decrypt.
275
            encrypted[7] ^= 3;
276
            let decryption_err = params.decrypt(&encrypted[..]);
277
            assert!(decryption_err.is_err());
278
        }
279
    }
280

            
281
    #[test]
282
    fn too_short() {
283
        let blinded_id = [7; 32].into();
284
        let subcredential = [11; 32].into();
285
        let revision = 13.into();
286
        let string_const = "greetings puny humans";
287
        let params = HsDescEncryption {
288
            blinded_id: &blinded_id,
289
            desc_enc_nonce: None,
290
            subcredential: &subcredential,
291
            revision,
292
            string_const: string_const.as_bytes(),
293
        };
294

            
295
        assert!(params.decrypt(b"").is_err());
296
        assert!(params.decrypt(&[0_u8; 47]).is_err());
297
    }
298
}