1
//! Key manipulation functions for use with public keys.
2
//!
3
//! Tor does some interesting and not-standard things with its
4
//! curve25519 and ed25519 keys, for several reasons.
5
//!
6
//! In order to prove ownership of a curve25519 private key, Tor
7
//! converts it into an ed25519 key, and then uses that ed25519 key to
8
//! sign its identity key.  We implement this conversion with
9
//! [`convert_curve25519_to_ed25519_public`] and
10
//! [`convert_curve25519_to_ed25519_private`].
11
//!
12
//! In Tor's v3 onion service design, Tor uses a _key blinding_
13
//! algorithm to derive a publicly known Ed25519 key from a different
14
//! Ed25519 key used as the .onion address.  This algorithm allows
15
//! directories to validate the signatures on onion service
16
//! descriptors, without knowing which services they represent.  We
17
//! implement this blinding operation via [`blind_pubkey`].
18
//!
19
//! ## TODO
20
//!
21
//! Recommend more standardized ways to do these things.
22

            
23
use crate::pk;
24
use thiserror::Error;
25

            
26
pub use ed25519_dalek::{ExpandedSecretKey, Keypair, PublicKey, SecretKey, Signature};
27

            
28
/// Convert a curve25519 public key (with sign bit) to an ed25519
29
/// public key, for use in ntor key cross-certification.
30
///
31
/// Note that this formula is not standardized; don't use
32
/// it for anything besides cross-certification.
33
431
pub fn convert_curve25519_to_ed25519_public(
34
431
    pubkey: &pk::curve25519::PublicKey,
35
431
    signbit: u8,
36
431
) -> Option<pk::ed25519::PublicKey> {
37
431
    use curve25519_dalek::montgomery::MontgomeryPoint;
38
431

            
39
431
    let point = MontgomeryPoint(*pubkey.as_bytes());
40
431
    let edpoint = point.to_edwards(signbit)?;
41

            
42
    // TODO: This is inefficient; we shouldn't have to re-compress
43
    // this point to get the public key we wanted.  But there's no way
44
    // with the current API that I can to construct an ed25519 public
45
    // key from a compressed point.
46
431
    let compressed_y = edpoint.compress();
47
431
    pk::ed25519::PublicKey::from_bytes(compressed_y.as_bytes()).ok()
48
431
}
49

            
50
/// Convert a curve25519 private key to an ed25519 public key (and
51
/// give a sign bit) to use with it, for use in ntor key cross-certification.
52
///
53
/// Note that this formula is not standardized; don't use
54
/// it for anything besides cross-certification.
55
///
56
/// *NEVER* use these keys to sign inputs that may be generated by an
57
/// attacker.
58
///
59
/// # Panics
60
///
61
/// If the `debug_assertions` feature is enabled, this function will
62
/// double-check that the key it is about to return is the right
63
/// private key for the public key returned by
64
/// `convert_curve25519_to_ed25519_public`.
65
///
66
/// This panic should be impossible unless there are implementation
67
/// bugs.
68
///
69
/// # Availability
70
///
71
/// This function is only available when the `relay` feature is enabled.
72
#[cfg(any(test, feature = "relay"))]
73
133
pub fn convert_curve25519_to_ed25519_private(
74
133
    privkey: &pk::curve25519::StaticSecret,
75
133
) -> Option<(pk::ed25519::ExpandedSecretKey, u8)> {
76
133
    use crate::d::Sha512;
77
133
    use digest::Digest;
78
133
    use zeroize::Zeroizing;
79
133

            
80
133
    let h = Sha512::new()
81
133
        .chain_update(privkey.to_bytes())
82
133
        .chain_update(&b"Derive high part of ed25519 key from curve25519 key\0"[..])
83
133
        .finalize();
84
133

            
85
133
    let mut bytes = Zeroizing::new([0_u8; 64]);
86
133
    bytes[0..32].clone_from_slice(&privkey.to_bytes());
87
133
    bytes[32..64].clone_from_slice(&h[0..32]);
88

            
89
133
    let result = pk::ed25519::ExpandedSecretKey::from_bytes(&bytes[..]).ok()?;
90
133
    let pubkey: pk::ed25519::PublicKey = (&result).into();
91
133
    let signbit = pubkey.as_bytes()[31] >> 7;
92
133

            
93
133
    #[cfg(debug_assertions)]
94
133
    {
95
133
        let curve_pubkey1 = pk::curve25519::PublicKey::from(privkey);
96
133
        let ed_pubkey1 = convert_curve25519_to_ed25519_public(&curve_pubkey1, signbit)?;
97
133
        assert_eq!(ed_pubkey1, pubkey);
98
    }
99

            
100
133
    Some((result, signbit))
101
133
}
102

            
103
/// An error occurred during a key-blinding operation.
104
#[derive(Error, Debug, PartialEq, Eq)]
105
#[non_exhaustive]
106
pub enum BlindingError {
107
    /// A bad public key was provided for blinding
108
    #[error("Bad pubkey provided")]
109
    BadPubkey,
110
    /// Dalek failed the scalar multiplication
111
    #[error("Key blinding Failed")]
112
    BlindingFailed,
113
}
114

            
115
// Convert this dalek error to a BlindingError
116
impl From<ed25519_dalek::SignatureError> for BlindingError {
117
    fn from(_: ed25519_dalek::SignatureError) -> BlindingError {
118
        BlindingError::BlindingFailed
119
    }
120
}
121

            
122
/// Blind the ed25519 public key `pk` using the blinding parameter
123
/// `param`, and return the blinded public key.
124
///
125
/// This algorithm is described in `rend-spec-v3.txt`, section A.2.
126
/// In the terminology of that section, the value `pk` corresponds to
127
/// `A`, and the value `param` corresponds to `h`.
128
///
129
/// Note that the approach used to clamp `param` to a scalar means
130
/// that different possible values for `param` may yield the same
131
/// output for a given `pk`.  This and other limitations make this
132
/// function unsuitable for use outside the context of
133
/// `rend-spec-v3.txt` without careful analysis.
134
///
135
/// # Errors
136
///
137
/// This function can fail if the input is not actually a valid
138
/// Ed25519 public key.
139
///
140
/// # Availability
141
///
142
/// This function is only available when the `hsv3-client` feature is enabled.
143
#[cfg(feature = "hsv3-client")]
144
10
pub fn blind_pubkey(pk: &PublicKey, mut param: [u8; 32]) -> Result<PublicKey, BlindingError> {
145
10
    use curve25519_dalek::edwards::CompressedEdwardsY;
146
10
    use curve25519_dalek::scalar::Scalar;
147
10

            
148
10
    // Clamp the blinding parameter
149
10
    param[0] &= 248;
150
10
    param[31] &= 63;
151
10
    param[31] |= 64;
152
10

            
153
10
    // Transform it into a scalar so that we can do scalar mult
154
10
    let blinding_factor = Scalar::from_bytes_mod_order(param);
155

            
156
    // Convert the public key to a point on the curve
157
10
    let pubkey_point = CompressedEdwardsY(pk.to_bytes())
158
10
        .decompress()
159
10
        .ok_or(BlindingError::BadPubkey)?;
160

            
161
    // Do the scalar multiplication and get a point back
162
10
    let blinded_pubkey_point = (blinding_factor * pubkey_point).compress();
163
10
    // Turn the point back into bytes and return it
164
10
    Ok(PublicKey::from_bytes(&blinded_pubkey_point.0)?)
165
10
}
166

            
167
#[cfg(test)]
168
mod tests {
169
    #![allow(clippy::unwrap_used)]
170
    use super::*;
171

            
172
    #[test]
173
    fn curve_to_ed_compatible() {
174
        use crate::pk::{curve25519, ed25519};
175
        use crate::util::rand_compat::RngCompatExt;
176
        use rand::thread_rng;
177
        use signature::Verifier;
178

            
179
        let rng = thread_rng().rng_compat();
180

            
181
        let curve_sk = curve25519::StaticSecret::new(rng);
182
        let curve_pk = curve25519::PublicKey::from(&curve_sk);
183

            
184
        let (ed_sk, signbit) = convert_curve25519_to_ed25519_private(&curve_sk).unwrap();
185
        let ed_pk1: ed25519::PublicKey = (&ed_sk).into();
186
        let ed_pk2 = convert_curve25519_to_ed25519_public(&curve_pk, signbit).unwrap();
187

            
188
        let msg = b"tis the gift to be simple";
189
        let sig1 = ed_sk.sign(&msg[..], &ed_pk1);
190
        assert!(ed_pk1.verify(&msg[..], &sig1).is_ok());
191
        assert!(ed_pk2.verify(&msg[..], &sig1).is_ok());
192

            
193
        assert_eq!(ed_pk1, ed_pk2);
194
    }
195

            
196
    #[test]
197
    #[cfg(feature = "hsv3-client")]
198
    fn blinding() {
199
        use std::convert::TryInto;
200

            
201
        // Test the ed25519 blinding function.
202
        //
203
        // These test vectors are from our ed25519 implementation and related
204
        // functions. These were automatically generated by the
205
        // ed25519_exts_ref.py script in little-t-tor and they are also used by
206
        // little-t-tor and onionbalance:
207
        let pubkeys = vec![
208
            b"c2247870536a192d142d056abefca68d6193158e7c1a59c1654c954eccaff894",
209
            b"1519a3b15816a1aafab0b213892026ebf5c0dc232c58b21088d88cb90e9b940d",
210
            b"081faa81992e360ea22c06af1aba096e7a73f1c665bc8b3e4e531c46455fd1dd",
211
            b"73cfa1189a723aad7966137cbffa35140bb40d7e16eae4c40b79b5f0360dd65a",
212
            b"66c1a77104d86461b6f98f73acf3cd229c80624495d2d74d6fda1e940080a96b",
213
            b"d21c294db0e64cb2d8976625786ede1d9754186ae8197a64d72f68c792eecc19",
214
            b"c4d58b4cf85a348ff3d410dd936fa460c4f18da962c01b1963792b9dcc8a6ea6",
215
            b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
216
            b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
217
            b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
218
        ];
219
        let params = vec![
220
            "54a513898b471d1d448a2f3c55c1de2c0ef718c447b04497eeb999ed32027823",
221
            "831e9b5325b5d31b7ae6197e9c7a7baf2ec361e08248bce055908971047a2347",
222
            "ac78a1d46faf3bfbbdc5af5f053dc6dc9023ed78236bec1760dadfd0b2603760",
223
            "f9c84dc0ac31571507993df94da1b3d28684a12ad14e67d0a068aba5c53019fc",
224
            "b1fe79d1dec9bc108df69f6612c72812755751f21ecc5af99663b30be8b9081f",
225
            "81f1512b63ab5fb5c1711a4ec83d379c420574aedffa8c3368e1c3989a3a0084",
226
            "97f45142597c473a4b0e9a12d64561133ad9e1155fe5a9807fe6af8a93557818",
227
            "3f44f6a5a92cde816635dfc12ade70539871078d2ff097278be2a555c9859cd0",
228
            "0000000000000000000000000000000000000000000000000000000000000000",
229
            "1111111111111111111111111111111111111111111111111111111111111111",
230
        ];
231
        let blinded_pubkeys = vec![
232
            "1fc1fa4465bd9d4956fdbdc9d3acb3c7019bb8d5606b951c2e1dfe0b42eaeb41",
233
            "1cbbd4a88ce8f165447f159d9f628ada18674158c4f7c5ead44ce8eb0fa6eb7e",
234
            "c5419ad133ffde7e0ac882055d942f582054132b092de377d587435722deb028",
235
            "3e08d0dc291066272e313014bfac4d39ad84aa93c038478a58011f431648105f",
236
            "59381f06acb6bf1389ba305f70874eed3e0f2ab57cdb7bc69ed59a9b8899ff4d",
237
            "2b946a484344eb1c17c89dd8b04196a84f3b7222c876a07a4cece85f676f87d9",
238
            "c6b585129b135f8769df2eba987e76e089e80ba3a2a6729134d3b28008ac098e",
239
            "0eefdc795b59cabbc194c6174e34ba9451e8355108520554ec285acabebb34ac",
240
            "312404d06a0a9de489904b18d5233e83a50b225977fa8734f2c897a73c067952",
241
            "952a908a4a9e0e5176a2549f8f328955aca6817a9fdc59e3acec5dec50838108",
242
        ];
243

            
244
        for i in 0..pubkeys.len() {
245
            let pk = PublicKey::from_bytes(&hex::decode(pubkeys[i]).unwrap()).unwrap();
246

            
247
            let blinded_pk = blind_pubkey(&pk, hex::decode(params[i]).unwrap().try_into().unwrap());
248

            
249
            assert_eq!(
250
                hex::encode(blinded_pk.unwrap().to_bytes()),
251
                blinded_pubkeys[i]
252
            );
253
        }
254
    }
255
}