1#![cfg_attr(
25 not(all(test, feature = "hsv3-service", feature = "relay")),
26 allow(unused_imports)
27)]
28
29use crate::{d, pk};
30use digest::Digest;
31use thiserror::Error;
32
33use curve25519_dalek::scalar::Scalar;
34use ed25519_dalek::hazmat::ExpandedSecretKey;
35use pk::ed25519::{ExpandedKeypair, PublicKey};
36
37pub 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 let compressed_y = edpoint.compress();
56 pk::ed25519::PublicKey::from_bytes(compressed_y.as_bytes()).ok()
57}
58
59#[cfg(any(test, feature = "cvt-x25519"))]
78pub fn convert_curve25519_to_ed25519_private(
79 privkey: &pk::curve25519::StaticSecret,
80) -> Option<(pk::ed25519::ExpandedKeypair, u8)> {
81 use crate::{d::Sha512, pk::ed25519::PublicKey};
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 = 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#[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)]
140pub 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 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 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#[derive(Error, Debug, PartialEq, Eq)]
166#[non_exhaustive]
167pub enum BlindingError {
168 #[error("Public key was invalid")]
170 BadPubkey,
171 #[error("Key blinding failed")]
173 BlindingFailed,
174}
175
176impl From<ed25519_dalek::SignatureError> for BlindingError {
178 fn from(_: ed25519_dalek::SignatureError) -> BlindingError {
179 BlindingError::BlindingFailed
180 }
181}
182
183#[cfg(any(feature = "hsv3-client", feature = "hsv3-service"))]
189fn clamp_blinding_factor(h: [u8; 32]) -> Scalar {
190 Scalar::from_bytes_mod_order(curve25519_dalek::scalar::clamp_integer(h))
192}
193
194#[cfg(any(feature = "hsv3-client", feature = "hsv3-service"))]
213#[cfg_attr(docsrs, doc(cfg(feature = "hsv3-client")))]
214#[cfg_attr(feature = "hsv3-client", visibility::make(pub))]
215fn blind_pubkey(pk: &PublicKey, h: [u8; 32]) -> Result<PublicKey, BlindingError> {
216 use curve25519_dalek::edwards::CompressedEdwardsY;
217
218 let blinding_factor = clamp_blinding_factor(h);
219
220 let pubkey_point = CompressedEdwardsY(pk.to_bytes())
222 .decompress()
223 .ok_or(BlindingError::BadPubkey)?;
224
225 let blinded_pubkey_point = (blinding_factor * pubkey_point).compress();
227 Ok(PublicKey::from_bytes(&blinded_pubkey_point.0)?)
229}
230
231#[cfg(feature = "hsv3-service")]
247pub fn blind_keypair(
248 keypair: &ExpandedKeypair,
249 h: [u8; 32],
250) -> Result<ExpandedKeypair, BlindingError> {
251 use zeroize::Zeroizing;
252
253 const RH_BLIND_STRING: &[u8] = b"Derive temporary signing key hash input";
257
258 let blinding_factor = clamp_blinding_factor(h);
259
260 let blinded_secret_scalar = keypair.secret.scalar * blinding_factor;
261
262 let blinded_secret_hash_prefix = {
263 let mut h = d::Sha512::new();
264 h.update(RH_BLIND_STRING);
265 h.update(keypair.secret.hash_prefix);
266 let mut d = Zeroizing::new([0_u8; 64]);
267 h.finalize_into(d.as_mut().into());
268 d[0..32].try_into().expect("slice cast failed")
269 };
270
271 let secret = ExpandedSecretKey {
272 scalar: blinded_secret_scalar,
273 hash_prefix: blinded_secret_hash_prefix,
274 };
275 let public = PublicKey(ed25519_dalek::VerifyingKey::from(&secret));
276
277 {
278 let public2 = blind_pubkey(keypair.public(), h)?;
282 assert_eq!(public, public2);
283 }
284
285 Ok(ExpandedKeypair { secret, public })
286}
287
288#[cfg(test)]
289mod tests {
290 #![allow(clippy::bool_assert_comparison)]
292 #![allow(clippy::clone_on_copy)]
293 #![allow(clippy::dbg_macro)]
294 #![allow(clippy::mixed_attributes_style)]
295 #![allow(clippy::print_stderr)]
296 #![allow(clippy::print_stdout)]
297 #![allow(clippy::single_char_pattern)]
298 #![allow(clippy::unwrap_used)]
299 #![allow(clippy::unchecked_time_subtraction)]
300 #![allow(clippy::useless_vec)]
301 #![allow(clippy::needless_pass_by_value)]
302 use super::*;
304 use crate::pk::ed25519::Keypair;
305
306 #[test]
307 fn curve_to_ed_compatible() {
308 use crate::pk::{curve25519, ed25519};
309 use tor_basic_utils::test_rng::testing_rng;
310
311 let rng = testing_rng();
312
313 let curve_sk = curve25519::StaticSecret::random_from_rng(rng);
314 let curve_pk = curve25519::PublicKey::from(&curve_sk);
315
316 let (ed_kp, signbit) = convert_curve25519_to_ed25519_private(&curve_sk).unwrap();
317 let ed_sk = &ed_kp.secret;
318 let ed_pk0 = ed_kp.public;
319 let ed_pk1: ed25519::PublicKey = ed25519::PublicKey((ed_sk).into());
320 let ed_pk2 = convert_curve25519_to_ed25519_public(&curve_pk, signbit).unwrap();
321
322 let msg = b"tis the gift to be simple";
323 let sig1 = ed_kp.sign(&msg[..]);
324 assert!(ed_pk1.verify(&msg[..], &sig1).is_ok());
325 assert!(ed_pk2.verify(&msg[..], &sig1).is_ok());
326
327 assert_eq!(ed_pk1, ed_pk0);
328 assert_eq!(ed_pk1, ed_pk2);
329 }
330
331 #[test]
332 fn ed_to_curve_compatible() {
333 use crate::pk::{curve25519, ed25519};
334 use tor_basic_utils::test_rng::testing_rng;
335
336 let mut rng = testing_rng();
337 let ed_kp = ed25519::Keypair::generate(&mut rng);
338 let ed_ekp1 = ExpandedKeypair::from(&ed_kp);
339
340 #[allow(deprecated)]
341 let curve_sk = convert_ed25519_to_curve25519_private(&ed_kp);
342 let curve_pk = curve25519::PublicKey::from(&curve_sk);
343
344 let (ed_ekp2, signbit) = convert_curve25519_to_ed25519_private(&curve_sk).unwrap();
345 let ed_pk2 = convert_curve25519_to_ed25519_public(&curve_pk, signbit).unwrap();
346
347 assert_eq!(ed_ekp1.public, ed_ekp2.public);
348 assert_eq!(ed_ekp2.public, ed_pk2);
349 assert_eq!(ed_ekp1.secret.scalar, ed_ekp2.secret.scalar);
350
351 assert_eq!(ed_ekp1.secret.scalar, ed_ekp2.secret.scalar);
354
355 let msg = b"tis the gift to be simple";
356
357 for kp in &[&ed_ekp1, &ed_ekp2] {
358 let sig = kp.sign(&msg[..]);
359 assert!(ed_ekp1.public.verify(&msg[..], &sig).is_ok());
360 assert!(ed_ekp2.public.verify(&msg[..], &sig).is_ok());
361 }
362 }
363
364 #[test]
365 #[cfg(all(feature = "hsv3-client", feature = "hsv3-service"))]
366 fn blinding() {
367 let seckeys = vec![
375 b"26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d36",
376 b"fba7a5366b5cb98c2667a18783f5cf8f4f8d1a2ce939ad22a6e685edde85128d",
377 b"67e3aa7a14fac8445d15e45e38a523481a69ae35513c9e4143eb1c2196729a0e",
378 b"d51385942033a76dc17f089a59e6a5a7fe80d9c526ae8ddd8c3a506b99d3d0a6",
379 b"5c8eac469bb3f1b85bc7cd893f52dc42a9ab66f1b02b5ce6a68e9b175d3bb433",
380 b"eda433d483059b6d1ff8b7cfbd0fe406bfb23722c8f3c8252629284573b61b86",
381 b"4377c40431c30883c5fbd9bc92ae48d1ed8a47b81d13806beac5351739b5533d",
382 b"c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b",
383 b"c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b",
384 b"c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b",
385 ];
386 let expanded_seckeys = vec![
387 b"c0a4de23cc64392d85aa1da82b3defddbea946d13bb053bf8489fa9296281f495022f1f7ec0dcf52f07d4c7965c4eaed121d5d88d0a8ff546b06116a20e97755",
388 b"18a8a69a06790dac778e882f7e868baacfa12521a5c058f5194f3a729184514a2a656fe7799c3e41f43d756da8d9cd47a061316cfe6147e23ea2f90d1ca45f30",
389 b"58d84f8862d2ecfa30eb491a81c36d05b574310ea69dae18ecb57e992a896656b982187ee96c15bf4caeeab2d0b0ae4cd0b8d17470fc7efa98bb26428f4ef36d",
390 b"50702d20b3550c6e16033db5ad4fba16436f1ecc7485be6af62b0732ceb5d173c47ccd9d044b6ea99dd99256adcc9c62191be194e7cb1a5b58ddcec85d876a2b",
391 b"7077464c864c2ed5ed21c9916dc3b3ba6256f8b742fec67658d8d233dadc8d5a7a82c371083cc86892c2c8782dda2a09b6baf016aec51b689183ae59ce932ff2",
392 b"8883c1387a6c86fc0bd7b9f157b4e4cd83f6885bf55e2706d2235d4527a2f05311a3595953282e436df0349e1bb313a19b3ddbf7a7b91ecce8a2c34abadb38b3",
393 b"186791ac8d03a3ac8efed6ac360467edd5a3bed2d02b3be713ddd5be53b3287ee37436e5fd7ac43794394507ad440ecfdf59c4c255f19b768a273109e06d7d8e",
394 b"b003077c1e52a62308eef7950b2d532e1d4a7eea50ad22d8ac11b892851f1c40ffb9c9ff8dcd0c6c233f665a2e176324d92416bfcfcd1f787424c0c667452d86",
395 b"b003077c1e52a62308eef7950b2d532e1d4a7eea50ad22d8ac11b892851f1c40ffb9c9ff8dcd0c6c233f665a2e176324d92416bfcfcd1f787424c0c667452d86",
396 b"b003077c1e52a62308eef7950b2d532e1d4a7eea50ad22d8ac11b892851f1c40ffb9c9ff8dcd0c6c233f665a2e176324d92416bfcfcd1f787424c0c667452d86",
397 ];
398
399 let pubkeys = vec![
400 b"c2247870536a192d142d056abefca68d6193158e7c1a59c1654c954eccaff894",
401 b"1519a3b15816a1aafab0b213892026ebf5c0dc232c58b21088d88cb90e9b940d",
402 b"081faa81992e360ea22c06af1aba096e7a73f1c665bc8b3e4e531c46455fd1dd",
403 b"73cfa1189a723aad7966137cbffa35140bb40d7e16eae4c40b79b5f0360dd65a",
404 b"66c1a77104d86461b6f98f73acf3cd229c80624495d2d74d6fda1e940080a96b",
405 b"d21c294db0e64cb2d8976625786ede1d9754186ae8197a64d72f68c792eecc19",
406 b"c4d58b4cf85a348ff3d410dd936fa460c4f18da962c01b1963792b9dcc8a6ea6",
407 b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
408 b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
409 b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
410 ];
411 let params = vec![
412 "54a513898b471d1d448a2f3c55c1de2c0ef718c447b04497eeb999ed32027823",
413 "831e9b5325b5d31b7ae6197e9c7a7baf2ec361e08248bce055908971047a2347",
414 "ac78a1d46faf3bfbbdc5af5f053dc6dc9023ed78236bec1760dadfd0b2603760",
415 "f9c84dc0ac31571507993df94da1b3d28684a12ad14e67d0a068aba5c53019fc",
416 "b1fe79d1dec9bc108df69f6612c72812755751f21ecc5af99663b30be8b9081f",
417 "81f1512b63ab5fb5c1711a4ec83d379c420574aedffa8c3368e1c3989a3a0084",
418 "97f45142597c473a4b0e9a12d64561133ad9e1155fe5a9807fe6af8a93557818",
419 "3f44f6a5a92cde816635dfc12ade70539871078d2ff097278be2a555c9859cd0",
420 "0000000000000000000000000000000000000000000000000000000000000000",
421 "1111111111111111111111111111111111111111111111111111111111111111",
422 ];
423 let blinded_pubkeys = vec![
424 "1fc1fa4465bd9d4956fdbdc9d3acb3c7019bb8d5606b951c2e1dfe0b42eaeb41",
425 "1cbbd4a88ce8f165447f159d9f628ada18674158c4f7c5ead44ce8eb0fa6eb7e",
426 "c5419ad133ffde7e0ac882055d942f582054132b092de377d587435722deb028",
427 "3e08d0dc291066272e313014bfac4d39ad84aa93c038478a58011f431648105f",
428 "59381f06acb6bf1389ba305f70874eed3e0f2ab57cdb7bc69ed59a9b8899ff4d",
429 "2b946a484344eb1c17c89dd8b04196a84f3b7222c876a07a4cece85f676f87d9",
430 "c6b585129b135f8769df2eba987e76e089e80ba3a2a6729134d3b28008ac098e",
431 "0eefdc795b59cabbc194c6174e34ba9451e8355108520554ec285acabebb34ac",
432 "312404d06a0a9de489904b18d5233e83a50b225977fa8734f2c897a73c067952",
433 "952a908a4a9e0e5176a2549f8f328955aca6817a9fdc59e3acec5dec50838108",
434 ];
435 let blinded_seckeys = vec![
436 "293c3acff4e902f6f63ddc5d5caa2a57e771db4f24de65d4c28df3232f47fa01171d43f24e3f53e70ec7ac280044ac77d4942dee5d6807118a59bdf3ee647e89",
437 "38b88f9f9440358da544504ee152fb475528f7c51c285bd1c68b14ade8e29a07b8ceff20dfcf53eb52b891fc078c934efbf0353af7242e7dc51bb32a093afa29",
438 "4d03ce16a3f3249846aac9de0a0075061495c3b027248eeee47da4ddbaf9e0049217f52e92797462bd890fc274672e05c98f2c82970d640084781334aae0f940",
439 "51d7db01aaa0d937a9fd7c8c7381445a14d8fa61f43347af5460d7cd8fda9904509ecee77082ce088f7c19d5a00e955eeef8df6fa41686abc1030c2d76807733",
440 "1f76cab834e222bd2546efa7e073425680ab88df186ff41327d3e40770129b00b57b95a440570659a440a3e4771465022a8e67af86bdf2d0990c54e7bb87ff9a",
441 "c23588c23ee76093419d07b27c6df5922a03ac58f96c53671456a7d1bdbf560ec492fc87d5ec2a1b185ca5a40541fdef0b1e128fd5c2380c888bfa924711bcab",
442 "3ed249c6932d076e1a2f6916975914b14e8c739da00992358b8f37d3e790650691b4768f8e556d78f4bdcb9a13b6f6066fe81d3134ae965dc48cd0785b3af2b8",
443 "288cbfd923cb286d48c084555b5bdd06c05e92fb81acdb45271367f57515380e053d9c00c81e1331c06ab50087be8cfc7dc11691b132614474f1aa9c2503cccd",
444 "e5cd03eb4cc456e11bc36724b558873df0045729b22d8b748360067a7770ac02053d9c00c81e1331c06ab50087be8cfc7dc11691b132614474f1aa9c2503cccd",
445 "2cf7ed8b163f5af960d2fc62e1883aa422a6090736b4f18a5456ddcaf78ede0c053d9c00c81e1331c06ab50087be8cfc7dc11691b132614474f1aa9c2503cccd",
446 ];
447
448 for i in 0..pubkeys.len() {
449 let sk: [u8; 32] = hex::decode(seckeys[i]).unwrap().try_into().unwrap();
450 let esk: ExpandedSecretKey = ExpandedSecretKey::from(&sk);
451 let kp = Keypair((&sk).into());
452
453 let esk_bytes_from_c_tor = hex::decode(expanded_seckeys[i]).unwrap();
454 let c_scalar =
460 Scalar::from_bytes_mod_order(esk_bytes_from_c_tor[0..32].try_into().unwrap());
461 assert_eq!(c_scalar, esk.scalar);
462 assert_eq!(
463 hex::encode(esk.hash_prefix),
464 hex::encode(&esk_bytes_from_c_tor[32..])
465 );
466
467 let public = PublicKey((&esk).into());
468 let kp_in = ExpandedKeypair {
469 secret: esk,
470 public,
471 };
472
473 let pk =
474 PublicKey::from_bytes(&hex::decode(pubkeys[i]).unwrap()[..].try_into().unwrap())
475 .unwrap();
476 assert_eq!(pk, PublicKey((&kp.0).into()));
477
478 let param = hex::decode(params[i]).unwrap().try_into().unwrap();
479 let blinded_kp = blind_keypair(&kp_in, param).unwrap();
481 assert_eq!(
482 hex::encode(blinded_kp.to_secret_key_bytes()),
483 blinded_seckeys[i]
484 );
485
486 {
488 let blinded_kp2 =
489 ExpandedKeypair::from_secret_key_bytes(blinded_kp.to_secret_key_bytes())
490 .unwrap();
491 assert_eq!(blinded_kp2.public, blinded_kp.public);
492 assert_eq!(blinded_kp2.secret.scalar, blinded_kp.secret.scalar);
493 assert_eq!(
494 blinded_kp2.secret.hash_prefix,
495 blinded_kp.secret.hash_prefix
496 );
497 }
498
499 let blinded_pk = blind_pubkey(&pk, param).unwrap();
500
501 assert_eq!(hex::encode(blinded_pk.to_bytes()), blinded_pubkeys[i]);
503
504 let sig = blinded_kp.sign(b"hello world");
507 blinded_pk.verify(b"hello world", &sig).unwrap();
508
509 let blinded_sk_scalar = blinded_kp.secret.scalar;
510 let pk2 = blinded_sk_scalar * curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
511 let pk2 = pk2.compress();
512 assert_eq!(pk2.as_bytes(), blinded_pk.as_bytes());
513 }
514 }
515}