sphinx_packet/header/
keys.rsuse std::convert::TryInto;
use std::fmt;
use crate::constants::{
BLINDING_FACTOR_SIZE, HKDF_INPUT_SEED, INTEGRITY_MAC_KEY_SIZE, PAYLOAD_KEY_SIZE,
ROUTING_KEYS_LENGTH,
};
use crate::crypto;
use crate::crypto::STREAM_CIPHER_KEY_SIZE;
use crate::route::Node;
use hkdf::Hkdf;
use sha2::Sha256;
use x25519_dalek::{PublicKey, StaticSecret};
pub type StreamCipherKey = [u8; STREAM_CIPHER_KEY_SIZE];
pub type HeaderIntegrityMacKey = [u8; INTEGRITY_MAC_KEY_SIZE];
pub type PayloadKey = [u8; PAYLOAD_KEY_SIZE];
#[derive(Clone)]
pub struct RoutingKeys {
pub stream_cipher_key: StreamCipherKey,
pub header_integrity_hmac_key: HeaderIntegrityMacKey,
pub payload_key: PayloadKey,
pub blinding_factor: StaticSecret,
}
impl RoutingKeys {
pub fn derive(shared_key: PublicKey) -> Self {
let hkdf = Hkdf::<Sha256>::new(None, shared_key.as_bytes());
let mut i = 0;
let mut output = [0u8; ROUTING_KEYS_LENGTH];
hkdf.expand(HKDF_INPUT_SEED, &mut output).unwrap();
let mut stream_cipher_key: [u8; crypto::STREAM_CIPHER_KEY_SIZE] = Default::default();
stream_cipher_key.copy_from_slice(&output[i..i + crypto::STREAM_CIPHER_KEY_SIZE]);
i += crypto::STREAM_CIPHER_KEY_SIZE;
let mut header_integrity_hmac_key: [u8; INTEGRITY_MAC_KEY_SIZE] = Default::default();
header_integrity_hmac_key.copy_from_slice(&output[i..i + INTEGRITY_MAC_KEY_SIZE]);
i += INTEGRITY_MAC_KEY_SIZE;
let mut payload_key: [u8; PAYLOAD_KEY_SIZE] = [0u8; PAYLOAD_KEY_SIZE];
payload_key.copy_from_slice(&output[i..i + PAYLOAD_KEY_SIZE]);
i += PAYLOAD_KEY_SIZE;
let blinding_factor_bytes: [u8; BLINDING_FACTOR_SIZE] =
output[i..i + BLINDING_FACTOR_SIZE].try_into().unwrap();
let blinding_factor = StaticSecret::from(blinding_factor_bytes);
Self {
stream_cipher_key,
header_integrity_hmac_key,
payload_key,
blinding_factor,
}
}
}
impl fmt::Debug for RoutingKeys {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{:?} {:?} {:?}",
self.stream_cipher_key,
self.header_integrity_hmac_key,
self.payload_key.to_vec()
)
}
}
impl PartialEq for RoutingKeys {
fn eq(&self, other: &RoutingKeys) -> bool {
self.stream_cipher_key == other.stream_cipher_key
&& self.header_integrity_hmac_key == other.header_integrity_hmac_key
&& self.payload_key.to_vec() == other.payload_key.to_vec()
}
}
pub struct KeyMaterial {
pub initial_shared_secret: PublicKey,
pub routing_keys: Vec<RoutingKeys>,
}
impl KeyMaterial {
pub fn derive(route: &[Node], initial_secret: &StaticSecret) -> Self {
let initial_shared_secret = PublicKey::from(initial_secret);
let mut routing_keys = Vec::with_capacity(route.len());
let mut blinding_factors = vec![initial_secret.clone()];
for (i, node) in route.iter().enumerate() {
let shared_key = blinding_factors
.iter()
.fold(node.pub_key, |acc, blinding_factor| {
PublicKey::from(blinding_factor.diffie_hellman(&acc).to_bytes())
});
let node_routing_keys = RoutingKeys::derive(shared_key);
if i != route.len() + 1 {
blinding_factors.push(node_routing_keys.blinding_factor.clone());
}
routing_keys.push(node_routing_keys);
}
Self {
initial_shared_secret,
routing_keys,
}
}
}
#[cfg(test)]
mod deriving_key_material {
use super::*;
use crate::route::Node;
#[cfg(test)]
mod with_an_empty_route {
use super::*;
#[test]
fn it_returns_no_routing_keys() {
let empty_route: Vec<Node> = vec![];
let initial_secret = StaticSecret::random();
let key_material = KeyMaterial::derive(&empty_route, &initial_secret);
assert_eq!(0, key_material.routing_keys.len());
assert_eq!(
PublicKey::from(&initial_secret).as_bytes(),
key_material.initial_shared_secret.as_bytes()
)
}
}
#[cfg(test)]
mod for_a_route_with_3_forward_hops {
use super::*;
use crate::test_utils::random_node;
fn setup() -> (Vec<Node>, StaticSecret, KeyMaterial) {
let route: Vec<Node> = vec![random_node(), random_node(), random_node()];
let initial_secret = StaticSecret::random();
let key_material = KeyMaterial::derive(&route, &initial_secret);
(route, initial_secret, key_material)
}
#[test]
fn it_returns_number_of_shared_keys_equal_to_length_of_the_route() {
let (_, _, key_material) = setup();
assert_eq!(3, key_material.routing_keys.len());
}
#[test]
fn it_returns_correctly_inited_shared_secret() {
let (_, initial_secret, key_material) = setup();
assert_eq!(
PublicKey::from(&initial_secret).as_bytes(),
key_material.initial_shared_secret.as_bytes()
);
}
#[test]
fn it_generates_correct_routing_keys() {
let (route, initial_secret, key_material) = setup();
let mut expected_accumulator = vec![initial_secret];
for (i, node) in route.iter().enumerate() {
let expected_shared_key =
expected_accumulator
.iter()
.fold(node.pub_key, |acc, blinding_factor| {
PublicKey::from(blinding_factor.diffie_hellman(&acc).to_bytes())
});
let expected_routing_keys = RoutingKeys::derive(expected_shared_key);
expected_accumulator.push(expected_routing_keys.blinding_factor);
let expected_routing_keys = RoutingKeys::derive(expected_shared_key);
assert_eq!(expected_routing_keys, key_material.routing_keys[i])
}
}
}
}
#[cfg(test)]
mod key_derivation_function {
use super::*;
#[test]
fn it_expands_the_seed_key_to_expected_length() {
let initial_secret = StaticSecret::random();
let shared_key = PublicKey::from(&initial_secret);
let routing_keys = RoutingKeys::derive(shared_key);
assert_eq!(
crypto::STREAM_CIPHER_KEY_SIZE,
routing_keys.stream_cipher_key.len()
);
}
#[test]
fn it_returns_the_same_output_for_two_equal_inputs() {
let initial_secret = StaticSecret::random();
let shared_key = PublicKey::from(&initial_secret);
let routing_keys1 = RoutingKeys::derive(shared_key);
let routing_keys2 = RoutingKeys::derive(shared_key);
assert_eq!(routing_keys1, routing_keys2);
}
}