111 lines
4.5 KiB
Rust
111 lines
4.5 KiB
Rust
use ed25519_dalek::{ExpandedSecretKey, Keypair, PublicKey, Signer};
|
|
use rand::rngs::OsRng;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::intrinsics::transmute;
|
|
use std::sync::Arc;
|
|
use x25519_dalek::PublicKey as X25519PublicKey;
|
|
use x25519_dalek::{SharedSecret, StaticSecret};
|
|
|
|
use crate::acns::tor::validation::public_key_to_hostname;
|
|
#[cfg(any(feature = "onionv3"))]
|
|
use crate::acns::tor::{TorAuthenticated, TorProcess};
|
|
#[cfg(any(feature = "onionv3"))]
|
|
use crate::acns::ACNError;
|
|
#[cfg(any(feature = "onionv3"))]
|
|
use ed25519_dalek::SecretKey;
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// Identity - An ed25519 keypair, required for established a Tor v3 onion service and used to
|
|
/// maintain a consistent cryptographic identity for a peer.
|
|
pub struct Identity {
|
|
keypair: Keypair,
|
|
}
|
|
|
|
impl Identity {
|
|
/// Initialize a persistent identity
|
|
pub fn initialize(keypair: Keypair) -> Arc<Identity> {
|
|
Arc::new(Identity { keypair })
|
|
}
|
|
|
|
/// Initialize an ephemeral identity - used for both ephemeral diffie hellman key exchanges
|
|
/// in addition to anonymous primitives connections to various onion services.
|
|
pub fn initialize_ephemeral_identity() -> Identity {
|
|
let mut csprng = OsRng::default();
|
|
let keypair = ed25519_dalek::Keypair::generate(&mut csprng);
|
|
Identity { keypair }
|
|
}
|
|
|
|
/// Sign a message using the encapsulated secret key
|
|
pub fn sign(&self, msg: &[u8]) -> ed25519_dalek::Signature {
|
|
self.keypair.sign(msg)
|
|
}
|
|
|
|
pub fn public_key(&self) -> ed25519_dalek::PublicKey {
|
|
self.keypair.public.clone()
|
|
}
|
|
|
|
pub fn hostname(&self) -> String {
|
|
public_key_to_hostname(&self.keypair.public)
|
|
}
|
|
|
|
/// Perform a diffie hellman exchange between `self` and the `remote_pub_key`
|
|
/// Implementation: this method converts the secret key of `self` into a `StaticSecret`
|
|
/// and the `remote_pub_key` into a `x25519_dalek::PublicKey` such that they can be used
|
|
/// as part of the exchange.
|
|
/// This is used as part of the ephemeral diffie hellman 3-way exchange.
|
|
pub fn edh(&self, remote_pub_key: PublicKey) -> SharedSecret {
|
|
let esk = ExpandedSecretKey::from(&self.keypair.secret).to_bytes();
|
|
let hsk: *const [u8; 32] = unsafe { transmute(esk.as_ptr()) };
|
|
let private_x25519 = StaticSecret::from(unsafe { **&hsk });
|
|
let public_x25519: X25519PublicKey = Identity::to_x25519(remote_pub_key);
|
|
return private_x25519.diffie_hellman(&public_x25519);
|
|
}
|
|
|
|
/// helper function that converts an Edwards PublicKey into a Montgomery PublicKey
|
|
/// i.e. ed25519 to x25519
|
|
fn to_x25519(edwards_pub_key: PublicKey) -> X25519PublicKey {
|
|
let edwards = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(edwards_pub_key.to_bytes().as_slice());
|
|
X25519PublicKey::from(edwards.decompress().unwrap().to_montgomery().to_bytes())
|
|
}
|
|
|
|
/// helper function to authenticated safely to a Tor control port to host an onion service for this identity
|
|
#[cfg(any(feature = "onionv3"))]
|
|
pub fn host_onion_service(&self, authenticated_control_port: &mut TorProcess<TorAuthenticated>, virtual_port: u16, target_port: u16) -> Result<String, ACNError> {
|
|
authenticated_control_port.add_onion_v3(SecretKey::from_bytes(&self.keypair.secret.to_bytes()).unwrap(), virtual_port, target_port)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::primitives::identity::Identity;
|
|
use ed25519_dalek::Signer;
|
|
|
|
#[test]
|
|
fn test_identity() {
|
|
let alice = Identity::initialize_ephemeral_identity();
|
|
let bob = Identity::initialize_ephemeral_identity();
|
|
|
|
assert_eq!(alice.edh(bob.keypair.public).to_bytes(), bob.edh(alice.keypair.public).to_bytes());
|
|
println!("Alice Shared Secret: {} {:?}", alice.hostname(), alice.edh(bob.keypair.public).to_bytes());
|
|
println!("Bob Shared Secret: {} {:?}", bob.hostname(), bob.edh(alice.keypair.public).to_bytes());
|
|
}
|
|
|
|
#[test]
|
|
fn test_serialize() {
|
|
// Create an emphemeral identity
|
|
let alice = Identity::initialize_ephemeral_identity();
|
|
|
|
// Serialize
|
|
let json = serde_json::to_string(&alice).unwrap();
|
|
println!("{}", json);
|
|
|
|
// Deserialize
|
|
let deserialized: Identity = serde_json::from_str(&json).unwrap();
|
|
println!("deserialized = {:?}", deserialized);
|
|
|
|
// Check that we can sign something with the deserialized key and check it with the original
|
|
let sig = deserialized.keypair.sign("message".as_bytes());
|
|
assert_eq!(true, alice.keypair.public.verify_strict("message".as_bytes(), &sig).is_ok());
|
|
}
|
|
}
|