1
//! Public-key cryptography for Tor.
2
//!
3
//! In old places, Tor uses RSA; newer Tor public-key cryptography is
4
//! based on curve25519 and ed25519.
5

            
6
pub mod ed25519;
7
pub mod keymanip;
8
pub mod rsa;
9

            
10
/// Re-exporting Curve25519 implementations.
11
///
12
/// *TODO*: Eventually we should probably recommend using this code via some
13
/// key-agreement trait, but for now we are just re-using the APIs from
14
/// [`x25519_dalek`].
15
pub mod curve25519 {
16
    use derive_deftly::Deftly;
17
    use educe::Educe;
18
    use subtle::ConstantTimeEq;
19

            
20
    use crate::util::ct::derive_deftly_template_PartialEqFromCtEq;
21
    use crate::util::rng::RngCompat;
22

            
23
    /// A keypair containing a [`StaticSecret`] and its corresponding public key.
24
    #[allow(clippy::exhaustive_structs)]
25
    #[derive(Clone, Educe)]
26
    #[educe(Debug)]
27
    pub struct StaticKeypair {
28
        /// The secret part of the key.
29
        #[educe(Debug(ignore))]
30
        pub secret: StaticSecret,
31
        /// The public part of this key.
32
        pub public: PublicKey,
33
    }
34

            
35
    /// A curve25519 secret key that can only be used once,
36
    /// and that can never be inspected.
37
    ///
38
    /// See [`x25519_dalek::EphemeralSecret`] for more information.
39
    pub struct EphemeralSecret(x25519_dalek::EphemeralSecret);
40

            
41
    /// A curve25519 secret key that can be used more than once,
42
    /// and whose value can be inspected.
43
    ///
44
    /// See [`x25519_dalek::StaticSecret`] for more information.
45
    //
46
    // TODO: We may want eventually want to expose ReusableSecret instead of
47
    // StaticSecret, for use in places where we need to use a single secret
48
    // twice in one handshake, but we do not need that secret to be persistent.
49
    //
50
    // The trouble here is that if we use ReusableSecret in these cases, we
51
    // cannot easily construct it for testing purposes.  We could in theory
52
    // kludge something together using a fake Rng, but that might be more
53
    // trouble than we want to go looking for.
54
    #[derive(Clone)]
55
    pub struct StaticSecret(x25519_dalek::StaticSecret);
56

            
57
    impl ConstantTimeEq for StaticSecret {
58
        fn ct_eq(&self, other: &Self) -> subtle::Choice {
59
            let Self { 0: self_secret } = self;
60
            let Self { 0: other_secret } = other;
61

            
62
            self_secret.as_bytes().ct_eq(other_secret.as_bytes())
63
        }
64
    }
65

            
66
    /// A curve15519 public key.
67
    ///
68
    /// See [`x25519_dalek::PublicKey`] for more information.
69
    #[derive(Clone, Copy, Debug, Eq, Hash, Deftly)]
70
    // Sadly not Ord because x25519_dalek::PublicKey isn't
71
    #[allow(clippy::derived_hash_with_manual_eq)]
72
    #[derive_deftly(PartialEqFromCtEq)]
73
    pub struct PublicKey(x25519_dalek::PublicKey);
74

            
75
    impl ConstantTimeEq for PublicKey {
76
4550
        fn ct_eq(&self, other: &Self) -> subtle::Choice {
77
4550
            let Self { 0: self_secret } = self;
78
4550
            let Self { 0: other_secret } = other;
79

            
80
4550
            self_secret.as_bytes().ct_eq(other_secret.as_bytes())
81
4550
        }
82
    }
83

            
84
    /// A shared secret negotiated using curve25519.
85
    ///
86
    /// See [`x25519_dalek::SharedSecret`] for more information
87
    pub struct SharedSecret(x25519_dalek::SharedSecret);
88

            
89
    impl<'a> From<&'a EphemeralSecret> for PublicKey {
90
10374
        fn from(secret: &'a EphemeralSecret) -> Self {
91
10374
            Self((&secret.0).into())
92
10374
        }
93
    }
94

            
95
    impl<'a> From<&'a StaticSecret> for PublicKey {
96
31672
        fn from(secret: &'a StaticSecret) -> Self {
97
31672
            Self((&secret.0).into())
98
31672
        }
99
    }
100

            
101
    impl From<[u8; 32]> for StaticSecret {
102
9010
        fn from(value: [u8; 32]) -> Self {
103
9010
            Self(value.into())
104
9010
        }
105
    }
106
    impl From<[u8; 32]> for PublicKey {
107
906087
        fn from(value: [u8; 32]) -> Self {
108
906087
            Self(value.into())
109
906087
        }
110
    }
111

            
112
    impl EphemeralSecret {
113
        /// Return a new random ephemeral secret key.
114
115
        pub fn random_from_rng<R: rand_core::Rng + rand_core::CryptoRng>(csprng: R) -> Self {
115
115
            Self(x25519_dalek::EphemeralSecret::random_from_rng(
116
115
                RngCompat::new(csprng),
117
115
            ))
118
115
        }
119
        /// Negotiate a shared secret using this secret key and a public key.
120
1638
        pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
121
1638
            SharedSecret(self.0.diffie_hellman(&their_public.0))
122
1638
        }
123
    }
124
    impl StaticSecret {
125
        /// Return a new random static secret key.
126
5369
        pub fn random_from_rng<R: rand_core::Rng + rand_core::CryptoRng>(csprng: R) -> Self {
127
5369
            Self(x25519_dalek::StaticSecret::random_from_rng(RngCompat::new(
128
5369
                csprng,
129
5369
            )))
130
5369
        }
131
        /// Negotiate a shared secret using this secret key and a public key.
132
25753
        pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
133
25753
            SharedSecret(self.0.diffie_hellman(&their_public.0))
134
25753
        }
135
        /// Return the bytes that represent this key.
136
5373
        pub fn to_bytes(&self) -> [u8; 32] {
137
5373
            self.0.to_bytes()
138
5373
        }
139
        /// Return a reference to the bytes that represent this key.
140
        pub fn as_bytes(&self) -> &[u8; 32] {
141
            self.0.as_bytes()
142
        }
143
    }
144
    impl SharedSecret {
145
        /// Return the shared secret as an array of bytes.
146
31941
        pub fn as_bytes(&self) -> &[u8; 32] {
147
31941
            self.0.as_bytes()
148
31941
        }
149
        /// Return true if both keys contributed to this shared secret.
150
        ///
151
        /// See [`x25519_dalek::SharedSecret::was_contributory`] for more information.
152
16744
        pub fn was_contributory(&self) -> bool {
153
16744
            self.0.was_contributory()
154
16744
        }
155
    }
156
    impl PublicKey {
157
        /// Return this public key as a reference to an array of bytes.
158
255168
        pub fn as_bytes(&self) -> &[u8; 32] {
159
255168
            self.0.as_bytes()
160
255168
        }
161
        /// Return this public key as an array of bytes.
162
32305
        pub fn to_bytes(&self) -> [u8; 32] {
163
32305
            self.0.to_bytes()
164
32305
        }
165
    }
166
}
167

            
168
/// A type for a validatable signature.
169
///
170
/// It necessarily includes the signature, the public key, and (a hash
171
/// of?) the document being checked.
172
///
173
/// Having this trait enables us to write code for checking a large number
174
/// of validatable signatures in a way that permits batch signatures for
175
/// Ed25519.
176
///
177
/// To be used with [`validate_all_sigs`].
178
pub trait ValidatableSignature {
179
    /// Check whether this signature is a correct signature for the document.
180
    fn is_valid(&self) -> bool;
181

            
182
    /// Return this value as a validatable Ed25519 signature, if it is one.
183
1937
    fn as_ed25519(&self) -> Option<&ed25519::ValidatableEd25519Signature> {
184
1937
        None
185
1937
    }
186
}
187

            
188
/// Check whether all of the signatures in this Vec are valid.
189
///
190
/// Return `true` if every signature is valid; return `false` if even
191
/// one is invalid.
192
///
193
/// This function should typically give the same result as just
194
/// calling `v.iter().all(ValidatableSignature::is_valid))`, while taking
195
/// advantage of batch verification to whatever extent possible.
196
///
197
/// (See [`ed25519::validate_batch`] for caveats.)
198
34307
pub fn validate_all_sigs(v: &[Box<dyn ValidatableSignature>]) -> bool {
199
    // First we break out the ed25519 signatures (if any) so we can do
200
    // a batch-verification on them.
201
34307
    let mut ed_sigs = Vec::new();
202
34307
    let mut non_ed_sigs = Vec::new();
203
54054
    for sig in v.iter() {
204
54054
        match sig.as_ed25519() {
205
48139
            Some(ed_sig) => ed_sigs.push(ed_sig),
206
5915
            None => non_ed_sigs.push(sig),
207
        }
208
    }
209

            
210
    // Find out if the ed25519 batch is valid.
211
34307
    let ed_batch_is_valid = crate::pk::ed25519::validate_batch(&ed_sigs[..]);
212

            
213
    // if so, verify the rest.
214
34307
    ed_batch_is_valid && non_ed_sigs.iter().all(|b| b.is_valid())
215
34307
}
216

            
217
#[cfg(test)]
218
mod test {
219
    // @@ begin test lint list maintained by maint/add_warning @@
220
    #![allow(clippy::bool_assert_comparison)]
221
    #![allow(clippy::clone_on_copy)]
222
    #![allow(clippy::dbg_macro)]
223
    #![allow(clippy::mixed_attributes_style)]
224
    #![allow(clippy::print_stderr)]
225
    #![allow(clippy::print_stdout)]
226
    #![allow(clippy::single_char_pattern)]
227
    #![allow(clippy::unwrap_used)]
228
    #![allow(clippy::unchecked_time_subtraction)]
229
    #![allow(clippy::useless_vec)]
230
    #![allow(clippy::needless_pass_by_value)]
231
    #![allow(clippy::string_slice)] // See arti#2571
232
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
233
    #[test]
234
    fn validatable_ed_sig() {
235
        use super::ValidatableSignature;
236
        use super::ed25519::{PublicKey, Signature, ValidatableEd25519Signature};
237
        use hex_literal::hex;
238
        let pk = PublicKey::from_bytes(&hex!(
239
            "fc51cd8e6218a1a38da47ed00230f058
240
             0816ed13ba3303ac5deb911548908025"
241
        ))
242
        .unwrap();
243
        let sig: Signature = hex!(
244
            "6291d657deec24024827e69c3abe01a3
245
             0ce548a284743a445e3680d7db5ac3ac
246
             18ff9b538d16f290ae67f760984dc659
247
             4a7c15e9716ed28dc027beceea1ec40a"
248
        )
249
        .into();
250

            
251
        let valid = ValidatableEd25519Signature::new(pk, sig, &hex!("af82"));
252
        let invalid = ValidatableEd25519Signature::new(pk, sig, &hex!("af83"));
253

            
254
        assert!(valid.is_valid());
255
        assert!(!invalid.is_valid());
256
    }
257
}