tapir-rs/src/primitives/identity.rs

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());
}
}