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
// Ideally there would be a feature that we would use in the CI, rather than this ad-hoc list.
24
#![cfg_attr(
25
    not(all(test, feature = "hsv3-service", feature = "relay")),
26
    allow(unused_imports)
27
)]
28

            
29
use crate::{d, pk};
30
use digest::Digest;
31
use thiserror::Error;
32

            
33
use curve25519_dalek::scalar::Scalar;
34
use ed25519_dalek::{hazmat::ExpandedSecretKey, SigningKey, VerifyingKey};
35
use pk::ed25519::ExpandedKeypair;
36

            
37
/// Convert a curve25519 public key (with sign bit) to an ed25519
38
/// public key, for use in ntor key cross-certification.
39
///
40
/// Note that this formula is not standardized; don't use
41
/// it for anything besides cross-certification.
42
pub fn convert_curve25519_to_ed25519_public(
43
    pubkey: &pk::curve25519::PublicKey,
44
    signbit: u8,
45
) -> Option<pk::ed25519::PublicKey> {
46
    use curve25519_dalek::montgomery::MontgomeryPoint;
47

            
48
    let point = MontgomeryPoint(*pubkey.as_bytes());
49
    let edpoint = point.to_edwards(signbit)?;
50

            
51
    // TODO: This is inefficient; we shouldn't have to re-compress
52
    // this point to get the public key we wanted.  But there's no way
53
    // with the current API that I can to construct an ed25519 public
54
    // key from a compressed point.
55
    let compressed_y = edpoint.compress();
56
    pk::ed25519::PublicKey::from_bytes(compressed_y.as_bytes()).ok()
57
}
58

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

            
84
    let h = Sha512::new()
85
        .chain_update(privkey.to_bytes())
86
        .chain_update(&b"Derive high part of ed25519 key from curve25519 key\0"[..])
87
        .finalize();
88

            
89
    let mut bytes = Zeroizing::new([0_u8; 64]);
90
    bytes[0..32].clone_from_slice(&privkey.to_bytes());
91
    bytes[32..64].clone_from_slice(&h[0..32]);
92

            
93
    let secret = ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(&bytes);
94
    let public: pk::ed25519::PublicKey = (&secret).into();
95
    let signbit = public.as_bytes()[31] >> 7;
96

            
97
    #[cfg(debug_assertions)]
98
    {
99
        let curve_pubkey1 = pk::curve25519::PublicKey::from(privkey);
100
        let ed_pubkey1 = convert_curve25519_to_ed25519_public(&curve_pubkey1, signbit)?;
101
        assert_eq!(ed_pubkey1, public);
102
    }
103

            
104
    Some((pk::ed25519::ExpandedKeypair { public, secret }, signbit))
105
}
106

            
107
/// Convert an ed25519 private key to a curve25519 private key.
108
///
109
/// This creates a curve25519 key as described in section-5.1.5 of RFC8032: the bytes of the secret
110
/// part of `keypair` are hashed using SHA-512, and the result is clamped (the first 3 bits of the
111
/// first byte are cleared, the highest bit of the last byte is cleared, the second highest bit of
112
/// the last byte is set).
113
///
114
/// Note: Using the same keypair for multiple purposes (such as key-exchange and signing) is
115
/// considered bad practice. Don't use this function unless you know what you're doing.
116
/// See [On using the same key pair for Ed25519 and an X25519 based
117
/// KEM](https://eprint.iacr.org/2021/509.pdf).
118
///
119
/// This function is needed by the `ArtiNativeKeystore` from `tor-keymgr` to convert ed25519
120
/// private keys to x25519. This is because `ArtiNativeKeystore` stores x25519 private keys as
121
/// ssh-ed25519 OpenSSH keys. Other similar use cases are also valid.
122
///
123
/// It's important to note that converting a private key from ed25519 -> curve25519 -> ed25519 will
124
/// yield an [`ExpandedKeypair`] that is _not_ identical to the
125
/// expanded version of the original [`Keypair`](ed25519_dalek::SigningKey): the lower halves (the keys) of
126
/// the expanded key pairs will be the same, but their upper halves (the nonces) will be different.
127
///
128
/// # Panics
129
///
130
/// If the `debug_assertions` feature is enabled, this function will double-check that the key it
131
/// is about to return is clamped.
132
///
133
/// This panic should be impossible unless we have upgraded x25519-dalek without auditing this
134
/// function.
135
#[cfg(any(test, feature = "cvt-x25519"))]
136
#[deprecated(
137
    since = "0.6.0",
138
    note = "ed25519_to_curve25519 conversion is unused, and no longer supported."
139
)]
140
pub fn convert_ed25519_to_curve25519_private(
141
    keypair: &pk::ed25519::Keypair,
142
) -> pk::curve25519::StaticSecret {
143
    use crate::d::Sha512;
144
    use zeroize::{Zeroize as _, Zeroizing};
145

            
146
    // Generate the key according to section-5.1.5 of rfc8032
147
    let h = Sha512::digest(keypair.to_bytes());
148

            
149
    let mut bytes = Zeroizing::new([0_u8; 32]);
150
    bytes.clone_from_slice(&h[0..32]);
151

            
152
    // Clamp the bytes.  We do not necessarily have to do this, since
153
    // x25519-dalek will handle clamping before it does any computation,  but we
154
    // want to make sure that the StaticSecret we generate is in the usual
155
    // format.
156
    let mut bytes = curve25519_dalek::scalar::clamp_integer(*bytes);
157

            
158
    let secret = pk::curve25519::StaticSecret::from(bytes);
159
    bytes.zeroize();
160

            
161
    secret
162
}
163

            
164
/// An error occurred during a key-blinding operation.
165
#[derive(Error, Debug, PartialEq, Eq)]
166
#[non_exhaustive]
167
pub enum BlindingError {
168
    /// A bad public key was provided for blinding
169
    #[error("Public key was invalid")]
170
    BadPubkey,
171
    /// Dalek failed the scalar multiplication
172
    #[error("Key blinding failed")]
173
    BlindingFailed,
174
}
175

            
176
// Convert this dalek error to a BlindingError
177
impl From<ed25519_dalek::SignatureError> for BlindingError {
178
    fn from(_: ed25519_dalek::SignatureError) -> BlindingError {
179
        BlindingError::BlindingFailed
180
    }
181
}
182

            
183
/// Helper: clamp a blinding factor and use it to compute a blinding factor.
184
///
185
/// Described in part of rend-spec-v3 A.2.
186
///
187
/// This is a common step for public-key and private-key blinding.
188
#[cfg(any(feature = "hsv3-client", feature = "hsv3-service"))]
189
fn clamp_blinding_factor(h: [u8; 32]) -> Scalar {
190
    // Transform it into a scalar so that we can do scalar mult.
191
    Scalar::from_bytes_mod_order(curve25519_dalek::scalar::clamp_integer(h))
192
}
193

            
194
/// Blind the ed25519 public key `pk` using the blinding factor
195
/// `h`, and return the blinded public key.
196
///
197
/// This algorithm is described in `rend-spec-v3.txt`, section A.2.
198
/// In the terminology of that section, the value `pk` corresponds to
199
/// `A`, and
200
/// `h` is the value `h = H(...)`, before clamping.
201
///
202
/// Note that the approach used to clamp `h` to a scalar means
203
/// that different possible values for `h` may yield the same
204
/// output for a given `pk`.  This and other limitations make this
205
/// function unsuitable for use outside the context of
206
/// `rend-spec-v3.txt` without careful analysis.
207
///
208
/// # Errors
209
///
210
/// This function can fail if the input is not actually a valid
211
/// Ed25519 public key.
212
///
213
/// # Availability
214
///
215
/// This function is only available when the `hsv3-client` feature is enabled.
216
#[cfg(feature = "hsv3-client")]
217
pub fn blind_pubkey(pk: &VerifyingKey, h: [u8; 32]) -> Result<VerifyingKey, BlindingError> {
218
    use curve25519_dalek::edwards::CompressedEdwardsY;
219

            
220
    let blinding_factor = clamp_blinding_factor(h);
221

            
222
    // Convert the public key to a point on the curve
223
    let pubkey_point = CompressedEdwardsY(pk.to_bytes())
224
        .decompress()
225
        .ok_or(BlindingError::BadPubkey)?;
226

            
227
    // Do the scalar multiplication and get a point back
228
    let blinded_pubkey_point = (blinding_factor * pubkey_point).compress();
229
    // Turn the point back into bytes and return it
230
    Ok(VerifyingKey::from_bytes(&blinded_pubkey_point.0)?)
231
}
232

            
233
/// Blind the ed25519 secret key `sk` using the blinding factor `h`, and
234
/// return the blinded secret key.
235
///
236
/// This algorithm is described in `rend-spec-v3.txt`, section A.2.
237
/// `h` is the value `h = H(...)`, before clamping.
238
///
239
/// Note that the approach used to clamp `h` to a scalar means that
240
/// different possible values for `h` may yield the same output for a given
241
/// `pk`.  This and other limitations make this function unsuitable for use
242
/// outside the context of `rend-spec-v3.txt` without careful analysis.
243
///
244
/// # Errors
245
///
246
/// This function can fail if the input is not actually a valid Ed25519 secret
247
/// key.
248
///
249
/// # Availability
250
///
251
/// This function is only available when the `hsv3-client` feature is enabled.
252
#[cfg(feature = "hsv3-service")]
253
pub fn blind_keypair(
254
    keypair: &ExpandedKeypair,
255
    h: [u8; 32],
256
) -> Result<ExpandedKeypair, BlindingError> {
257
    use zeroize::Zeroizing;
258

            
259
    /// Fixed string specified in rend-spec-v3.txt, used for blinding the
260
    /// original nonce.  (Technically, any string would do, but this one keeps
261
    /// implementations consistent.)
262
    const RH_BLIND_STRING: &[u8] = b"Derive temporary signing key hash input";
263

            
264
    let blinding_factor = clamp_blinding_factor(h);
265

            
266
    let blinded_secret_scalar = keypair.secret.scalar * blinding_factor;
267

            
268
    let blinded_secret_hash_prefix = {
269
        let mut h = d::Sha512::new();
270
        h.update(RH_BLIND_STRING);
271
        h.update(keypair.secret.hash_prefix);
272
        let mut d = Zeroizing::new([0_u8; 64]);
273
        h.finalize_into(d.as_mut().into());
274
        d[0..32].try_into().expect("slice cast failed")
275
    };
276

            
277
    let secret = ExpandedSecretKey {
278
        scalar: blinded_secret_scalar,
279
        hash_prefix: blinded_secret_hash_prefix,
280
    };
281
    let public = VerifyingKey::from(&secret);
282

            
283
    #[cfg(debug_assertions)]
284
    {
285
        // Make sure that the public key that derives from our
286
        // blinded key is the same as the key that we get when we re-blind the
287
        // public key.
288
        let public2 = blind_pubkey(&keypair.public, h)?;
289
        debug_assert_eq!(public, public2);
290
    }
291

            
292
    Ok(ExpandedKeypair { secret, public })
293
}
294

            
295
#[cfg(test)]
296
mod tests {
297
    // @@ begin test lint list maintained by maint/add_warning @@
298
    #![allow(clippy::bool_assert_comparison)]
299
    #![allow(clippy::clone_on_copy)]
300
    #![allow(clippy::dbg_macro)]
301
    #![allow(clippy::print_stderr)]
302
    #![allow(clippy::print_stdout)]
303
    #![allow(clippy::single_char_pattern)]
304
    #![allow(clippy::unwrap_used)]
305
    #![allow(clippy::unchecked_duration_subtraction)]
306
    #![allow(clippy::useless_vec)]
307
    #![allow(clippy::needless_pass_by_value)]
308
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
309
    use super::*;
310

            
311
    #[test]
312
    fn curve_to_ed_compatible() {
313
        use crate::pk::{curve25519, ed25519};
314
        use ed25519_dalek::Verifier;
315
        use tor_basic_utils::test_rng::testing_rng;
316

            
317
        let rng = testing_rng();
318

            
319
        let curve_sk = curve25519::StaticSecret::random_from_rng(rng);
320
        let curve_pk = curve25519::PublicKey::from(&curve_sk);
321

            
322
        let (ed_kp, signbit) = convert_curve25519_to_ed25519_private(&curve_sk).unwrap();
323
        let ed_sk = &ed_kp.secret;
324
        let ed_pk0 = ed_kp.public;
325
        let ed_pk1: ed25519::PublicKey = (ed_sk).into();
326
        let ed_pk2 = convert_curve25519_to_ed25519_public(&curve_pk, signbit).unwrap();
327

            
328
        let msg = b"tis the gift to be simple";
329
        let sig1 = ed_kp.sign(&msg[..]);
330
        assert!(ed_pk1.verify(&msg[..], &sig1).is_ok());
331
        assert!(ed_pk2.verify(&msg[..], &sig1).is_ok());
332

            
333
        assert_eq!(ed_pk1, ed_pk0);
334
        assert_eq!(ed_pk1, ed_pk2);
335
    }
336

            
337
    #[test]
338
    fn ed_to_curve_compatible() {
339
        use crate::pk::{curve25519, ed25519};
340
        use ed25519_dalek::Verifier;
341
        use tor_basic_utils::test_rng::testing_rng;
342

            
343
        let mut rng = testing_rng();
344
        let ed_kp = ed25519::Keypair::generate(&mut rng);
345
        let ed_ekp1 = ExpandedKeypair::from(&ed_kp);
346

            
347
        #[allow(deprecated)]
348
        let curve_sk = convert_ed25519_to_curve25519_private(&ed_kp);
349
        let curve_pk = curve25519::PublicKey::from(&curve_sk);
350

            
351
        let (ed_ekp2, signbit) = convert_curve25519_to_ed25519_private(&curve_sk).unwrap();
352
        let ed_pk2 = convert_curve25519_to_ed25519_public(&curve_pk, signbit).unwrap();
353

            
354
        assert_eq!(ed_ekp1.public, ed_ekp2.public);
355
        assert_eq!(ed_ekp2.public, ed_pk2);
356
        assert_eq!(ed_ekp1.secret.scalar, ed_ekp2.secret.scalar);
357

            
358
        // Make sure the 2 secret keys are the same. Note: we only look at the
359
        // scalar part of the (expanded) key, not the hash prefix.
360
        assert_eq!(ed_ekp1.secret.scalar, ed_ekp2.secret.scalar);
361

            
362
        let msg = b"tis the gift to be simple";
363

            
364
        for kp in &[&ed_ekp1, &ed_ekp2] {
365
            let sig = kp.sign(&msg[..]);
366
            assert!(ed_ekp1.public.verify(&msg[..], &sig).is_ok());
367
            assert!(ed_ekp2.public.verify(&msg[..], &sig).is_ok());
368
        }
369
    }
370

            
371
    #[test]
372
    #[cfg(all(feature = "hsv3-client", feature = "hsv3-service"))]
373
    fn blinding() {
374
        // Test the ed25519 blinding function.
375
        //
376
        // These test vectors are from our ed25519 implementation and related
377
        // functions. These were automatically generated by the
378
        // ed25519_exts_ref.py script in little-t-tor and they are also used by
379
        // little-t-tor and onionbalance:
380
        use ed25519_dalek::Verifier;
381

            
382
        let seckeys = vec![
383
            b"26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d36",
384
            b"fba7a5366b5cb98c2667a18783f5cf8f4f8d1a2ce939ad22a6e685edde85128d",
385
            b"67e3aa7a14fac8445d15e45e38a523481a69ae35513c9e4143eb1c2196729a0e",
386
            b"d51385942033a76dc17f089a59e6a5a7fe80d9c526ae8ddd8c3a506b99d3d0a6",
387
            b"5c8eac469bb3f1b85bc7cd893f52dc42a9ab66f1b02b5ce6a68e9b175d3bb433",
388
            b"eda433d483059b6d1ff8b7cfbd0fe406bfb23722c8f3c8252629284573b61b86",
389
            b"4377c40431c30883c5fbd9bc92ae48d1ed8a47b81d13806beac5351739b5533d",
390
            b"c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b",
391
            b"c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b",
392
            b"c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b",
393
        ];
394
        let expanded_seckeys = vec![
395
            b"c0a4de23cc64392d85aa1da82b3defddbea946d13bb053bf8489fa9296281f495022f1f7ec0dcf52f07d4c7965c4eaed121d5d88d0a8ff546b06116a20e97755",
396
            b"18a8a69a06790dac778e882f7e868baacfa12521a5c058f5194f3a729184514a2a656fe7799c3e41f43d756da8d9cd47a061316cfe6147e23ea2f90d1ca45f30",
397
            b"58d84f8862d2ecfa30eb491a81c36d05b574310ea69dae18ecb57e992a896656b982187ee96c15bf4caeeab2d0b0ae4cd0b8d17470fc7efa98bb26428f4ef36d",
398
            b"50702d20b3550c6e16033db5ad4fba16436f1ecc7485be6af62b0732ceb5d173c47ccd9d044b6ea99dd99256adcc9c62191be194e7cb1a5b58ddcec85d876a2b",
399
            b"7077464c864c2ed5ed21c9916dc3b3ba6256f8b742fec67658d8d233dadc8d5a7a82c371083cc86892c2c8782dda2a09b6baf016aec51b689183ae59ce932ff2",
400
            b"8883c1387a6c86fc0bd7b9f157b4e4cd83f6885bf55e2706d2235d4527a2f05311a3595953282e436df0349e1bb313a19b3ddbf7a7b91ecce8a2c34abadb38b3",
401
            b"186791ac8d03a3ac8efed6ac360467edd5a3bed2d02b3be713ddd5be53b3287ee37436e5fd7ac43794394507ad440ecfdf59c4c255f19b768a273109e06d7d8e",
402
            b"b003077c1e52a62308eef7950b2d532e1d4a7eea50ad22d8ac11b892851f1c40ffb9c9ff8dcd0c6c233f665a2e176324d92416bfcfcd1f787424c0c667452d86",
403
            b"b003077c1e52a62308eef7950b2d532e1d4a7eea50ad22d8ac11b892851f1c40ffb9c9ff8dcd0c6c233f665a2e176324d92416bfcfcd1f787424c0c667452d86",
404
            b"b003077c1e52a62308eef7950b2d532e1d4a7eea50ad22d8ac11b892851f1c40ffb9c9ff8dcd0c6c233f665a2e176324d92416bfcfcd1f787424c0c667452d86",
405
        ];
406

            
407
        let pubkeys = vec![
408
            b"c2247870536a192d142d056abefca68d6193158e7c1a59c1654c954eccaff894",
409
            b"1519a3b15816a1aafab0b213892026ebf5c0dc232c58b21088d88cb90e9b940d",
410
            b"081faa81992e360ea22c06af1aba096e7a73f1c665bc8b3e4e531c46455fd1dd",
411
            b"73cfa1189a723aad7966137cbffa35140bb40d7e16eae4c40b79b5f0360dd65a",
412
            b"66c1a77104d86461b6f98f73acf3cd229c80624495d2d74d6fda1e940080a96b",
413
            b"d21c294db0e64cb2d8976625786ede1d9754186ae8197a64d72f68c792eecc19",
414
            b"c4d58b4cf85a348ff3d410dd936fa460c4f18da962c01b1963792b9dcc8a6ea6",
415
            b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
416
            b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
417
            b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
418
        ];
419
        let params = vec![
420
            "54a513898b471d1d448a2f3c55c1de2c0ef718c447b04497eeb999ed32027823",
421
            "831e9b5325b5d31b7ae6197e9c7a7baf2ec361e08248bce055908971047a2347",
422
            "ac78a1d46faf3bfbbdc5af5f053dc6dc9023ed78236bec1760dadfd0b2603760",
423
            "f9c84dc0ac31571507993df94da1b3d28684a12ad14e67d0a068aba5c53019fc",
424
            "b1fe79d1dec9bc108df69f6612c72812755751f21ecc5af99663b30be8b9081f",
425
            "81f1512b63ab5fb5c1711a4ec83d379c420574aedffa8c3368e1c3989a3a0084",
426
            "97f45142597c473a4b0e9a12d64561133ad9e1155fe5a9807fe6af8a93557818",
427
            "3f44f6a5a92cde816635dfc12ade70539871078d2ff097278be2a555c9859cd0",
428
            "0000000000000000000000000000000000000000000000000000000000000000",
429
            "1111111111111111111111111111111111111111111111111111111111111111",
430
        ];
431
        let blinded_pubkeys = vec![
432
            "1fc1fa4465bd9d4956fdbdc9d3acb3c7019bb8d5606b951c2e1dfe0b42eaeb41",
433
            "1cbbd4a88ce8f165447f159d9f628ada18674158c4f7c5ead44ce8eb0fa6eb7e",
434
            "c5419ad133ffde7e0ac882055d942f582054132b092de377d587435722deb028",
435
            "3e08d0dc291066272e313014bfac4d39ad84aa93c038478a58011f431648105f",
436
            "59381f06acb6bf1389ba305f70874eed3e0f2ab57cdb7bc69ed59a9b8899ff4d",
437
            "2b946a484344eb1c17c89dd8b04196a84f3b7222c876a07a4cece85f676f87d9",
438
            "c6b585129b135f8769df2eba987e76e089e80ba3a2a6729134d3b28008ac098e",
439
            "0eefdc795b59cabbc194c6174e34ba9451e8355108520554ec285acabebb34ac",
440
            "312404d06a0a9de489904b18d5233e83a50b225977fa8734f2c897a73c067952",
441
            "952a908a4a9e0e5176a2549f8f328955aca6817a9fdc59e3acec5dec50838108",
442
        ];
443
        let blinded_seckeys = vec![
444
            "293c3acff4e902f6f63ddc5d5caa2a57e771db4f24de65d4c28df3232f47fa01171d43f24e3f53e70ec7ac280044ac77d4942dee5d6807118a59bdf3ee647e89",
445
            "38b88f9f9440358da544504ee152fb475528f7c51c285bd1c68b14ade8e29a07b8ceff20dfcf53eb52b891fc078c934efbf0353af7242e7dc51bb32a093afa29",
446
            "4d03ce16a3f3249846aac9de0a0075061495c3b027248eeee47da4ddbaf9e0049217f52e92797462bd890fc274672e05c98f2c82970d640084781334aae0f940",
447
            "51d7db01aaa0d937a9fd7c8c7381445a14d8fa61f43347af5460d7cd8fda9904509ecee77082ce088f7c19d5a00e955eeef8df6fa41686abc1030c2d76807733",
448
            "1f76cab834e222bd2546efa7e073425680ab88df186ff41327d3e40770129b00b57b95a440570659a440a3e4771465022a8e67af86bdf2d0990c54e7bb87ff9a",
449
            "c23588c23ee76093419d07b27c6df5922a03ac58f96c53671456a7d1bdbf560ec492fc87d5ec2a1b185ca5a40541fdef0b1e128fd5c2380c888bfa924711bcab",
450
            "3ed249c6932d076e1a2f6916975914b14e8c739da00992358b8f37d3e790650691b4768f8e556d78f4bdcb9a13b6f6066fe81d3134ae965dc48cd0785b3af2b8",
451
            "288cbfd923cb286d48c084555b5bdd06c05e92fb81acdb45271367f57515380e053d9c00c81e1331c06ab50087be8cfc7dc11691b132614474f1aa9c2503cccd",
452
            "e5cd03eb4cc456e11bc36724b558873df0045729b22d8b748360067a7770ac02053d9c00c81e1331c06ab50087be8cfc7dc11691b132614474f1aa9c2503cccd",
453
            "2cf7ed8b163f5af960d2fc62e1883aa422a6090736b4f18a5456ddcaf78ede0c053d9c00c81e1331c06ab50087be8cfc7dc11691b132614474f1aa9c2503cccd",
454
        ];
455

            
456
        for i in 0..pubkeys.len() {
457
            let sk: [u8; 32] = hex::decode(seckeys[i]).unwrap().try_into().unwrap();
458
            let esk = ExpandedSecretKey::from(&sk);
459
            let kp = SigningKey::from(&sk);
460

            
461
            let esk_bytes_from_c_tor = hex::decode(expanded_seckeys[i]).unwrap();
462
            // Because of the differences in how we calculate the scalar, we
463
            // don't get the same _representation_ of the scalar as we did with
464
            // the C tor implementation.
465
            //
466
            // Therefore we have to do through this silliness to check our result.
467
            let c_scalar =
468
                Scalar::from_bytes_mod_order(esk_bytes_from_c_tor[0..32].try_into().unwrap());
469
            assert_eq!(c_scalar, esk.scalar);
470
            assert_eq!(
471
                hex::encode(esk.hash_prefix),
472
                hex::encode(&esk_bytes_from_c_tor[32..])
473
            );
474

            
475
            let public = (&esk).into();
476
            let kp_in = ExpandedKeypair {
477
                secret: esk,
478
                public,
479
            };
480

            
481
            let pk =
482
                VerifyingKey::from_bytes(&hex::decode(pubkeys[i]).unwrap()[..].try_into().unwrap())
483
                    .unwrap();
484
            assert_eq!(pk, VerifyingKey::from(&kp));
485

            
486
            let param = hex::decode(params[i]).unwrap().try_into().unwrap();
487
            // Blind the secret key, and make sure that the result is expected.
488
            let blinded_kp = blind_keypair(&kp_in, param).unwrap();
489
            assert_eq!(
490
                hex::encode(blinded_kp.to_secret_key_bytes()),
491
                blinded_seckeys[i]
492
            );
493

            
494
            // Make sure that the secret key can be encoded and decoded.
495
            {
496
                let blinded_kp2 =
497
                    ExpandedKeypair::from_secret_key_bytes(blinded_kp.to_secret_key_bytes())
498
                        .unwrap();
499
                assert_eq!(blinded_kp2.public, blinded_kp.public);
500
                assert_eq!(blinded_kp2.secret.scalar, blinded_kp.secret.scalar);
501
                assert_eq!(
502
                    blinded_kp2.secret.hash_prefix,
503
                    blinded_kp.secret.hash_prefix
504
                );
505
            }
506

            
507
            let blinded_pk = blind_pubkey(&pk, param).unwrap();
508

            
509
            // Make sure blinded pk is as expected.
510
            assert_eq!(hex::encode(blinded_pk.to_bytes()), blinded_pubkeys[i]);
511

            
512
            // Make sure that signature made with blinded sk is validated by
513
            // blinded pk.
514
            let sig = blinded_kp.sign(b"hello world");
515
            blinded_pk.verify(b"hello world", &sig).unwrap();
516

            
517
            let blinded_sk_scalar = blinded_kp.secret.scalar;
518
            let pk2 = blinded_sk_scalar * curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
519
            let pk2 = pk2.compress();
520
            assert_eq!(pk2.as_bytes(), blinded_pk.as_bytes());
521
        }
522
    }
523
}