extern crate rand;
use subtle::ConstantTimeEq;
use self::rand::Rng;
use ecdh_wrapper::{PublicKey, PrivateKey, exp};
use super::utils::xor_assign;
use super::constants::{NODE_ID_SIZE, HEADER_SIZE, MAX_HOPS, ROUTING_INFO_SIZE, PER_HOP_ROUTING_INFO_SIZE,
V0_AD, PAYLOAD_TAG_SIZE, SURB_SIZE};
use super::internal_crypto::{SPRP_KEY_SIZE, SPRP_IV_SIZE, GROUP_ELEMENT_SIZE, PacketKeys, kdf,
StreamCipher, MAC_SIZE, hmac, sprp_encrypt, sprp_decrypt};
use super::commands::{RoutingCommand, commands_to_vec, NextHop};
use super::error::{SphinxHeaderCreateError, SphinxPacketCreateError, SphinxSurbCreateError,
SphinxPacketFromSurbError, SphinxDecryptSurbError};
const SPRP_KEY_MATERIAL_SIZE: usize = SPRP_KEY_SIZE + SPRP_IV_SIZE;
#[derive(Clone)]
pub struct PathHop {
pub id: [u8; NODE_ID_SIZE],
pub public_key: PublicKey,
pub commands: Option<Vec<RoutingCommand>>,
}
pub struct SprpKey {
pub key: [u8; SPRP_KEY_SIZE],
pub iv: [u8; SPRP_IV_SIZE],
}
impl SprpKey {
pub fn reset(&mut self) {
self.key = [0u8; SPRP_KEY_SIZE];
self.iv = [0u8; SPRP_IV_SIZE];
}
}
pub fn create_header<R: Rng>(rng: &mut R, path: Vec<PathHop>) -> Result<([u8; HEADER_SIZE], Vec<SprpKey>), SphinxHeaderCreateError> {
let num_hops = path.len();
if num_hops > MAX_HOPS {
return Err(SphinxHeaderCreateError::PathTooLongError);
}
let keypair = PrivateKey::generate(rng)?;
let mut group_elements: Vec<PublicKey> = vec![];
let mut keys: Vec<PacketKeys> = vec![];
let mut shared_secret: [u8; GROUP_ELEMENT_SIZE] = keypair.exp(&path[0].public_key);
keys.push(kdf(&shared_secret));
let mut group_element = keypair.public_key();
group_elements.push(group_element);
let mut i = 1;
while i < num_hops {
shared_secret = keypair.exp(&path[i].public_key);
let mut j = 0;
while j < i {
shared_secret = exp(&shared_secret, &keys[j].blinding_factor);
j += 1;
}
keys.push(kdf(&shared_secret));
group_element.blind(&keys[i-1].blinding_factor);
group_elements.push(group_element);
i += 1;
}
let mut ri_keystream: Vec<Vec<u8>> = vec![];
let mut ri_padding: Vec<Vec<u8>> = vec![];
let mut i = 0;
while i < num_hops {
let mut steam_cipher = StreamCipher::new(&keys[i].header_encryption, &keys[i].header_encryption_iv);
let stream = steam_cipher.generate(ROUTING_INFO_SIZE + PER_HOP_ROUTING_INFO_SIZE);
let ks_len = stream.len() - ((i+1) * PER_HOP_ROUTING_INFO_SIZE);
ri_keystream.push(stream[..ks_len].to_vec());
ri_padding.push(stream[ks_len..].to_vec());
if i > 0 {
let prev_pad_len = ri_padding[i-1].len();
let current = ri_padding[i-1].clone();
xor_assign(&mut ri_padding[i][..prev_pad_len], ¤t);
}
i += 1;
}
let mut routing_info = vec![];
let mut mac = [0u8; MAC_SIZE];
let skipped_hops = MAX_HOPS - num_hops;
if skipped_hops > 0 {
routing_info = vec![0u8; skipped_hops * PER_HOP_ROUTING_INFO_SIZE];
}
let mut i: i8 = num_hops as i8 - 1;
while i >= 0 {
let _is_terminal = i == num_hops as i8 - 1;
let _cmd_vec = path[i as usize].commands.as_ref();
let _cmd_bytes_result = commands_to_vec(_cmd_vec.unwrap(), _is_terminal);
if _cmd_bytes_result.is_err() {
return Err(SphinxHeaderCreateError::SerializeCommandsError);
}
let mut _ri_fragment = _cmd_bytes_result.unwrap();
if !_is_terminal {
let _next_id = path[i as usize + 1].id.clone();
let _next_mac = mac.clone();
let _next_cmd = RoutingCommand::NextHop(
NextHop {
id: _next_id,
mac: _next_mac,
},
);
_ri_fragment.extend(_next_cmd.to_vec());
}
let _pad_len = PER_HOP_ROUTING_INFO_SIZE - _ri_fragment.len();
if _pad_len > 0 {
_ri_fragment.extend(vec![0u8; _pad_len]);
}
let mut _tmp = _ri_fragment.to_owned();
_tmp.extend(routing_info);
routing_info = _tmp;
xor_assign(&mut routing_info, ri_keystream[i as usize].as_slice());
let mut _data = vec![];
_data.extend(V0_AD.iter());
_data.extend(group_elements[i as usize].to_vec());
_data.extend(routing_info.to_owned());
if i > 0 {
_data.extend(&ri_padding[i as usize - 1]);
}
mac = hmac(&keys[i as usize].header_mac, &_data);
i -= 1;
}
let mut _header = vec![];
_header.extend(V0_AD.iter());
_header.extend(group_elements[0].to_vec());
_header.extend(routing_info);
_header.extend(mac.iter());
let mut header = [0u8; HEADER_SIZE];
header.copy_from_slice(&_header);
let mut sprp_keys = vec![];
let mut i = 0;
while i < num_hops {
let k = SprpKey{
key: keys[i].payload_encryption,
iv: keys[i].header_encryption_iv,
};
sprp_keys.push(k);
i += 1
}
return Ok((header, sprp_keys));
}
pub fn new_packet<R: Rng>(rng: &mut R, path: Vec<PathHop>, payload: Vec<u8>) -> Result<Vec<u8>, SphinxPacketCreateError>{
let _path_len = path.len();
let _header_result = create_header(rng, path);
if _header_result.is_err() {
return Err(SphinxPacketCreateError::CreateHeaderError);
}
let _tmp = _header_result.unwrap();
let header = _tmp.0;
let sprp_keys = _tmp.1;
let mut i = _path_len as i8 - 1;
let mut _payload = payload.to_vec();
let mut _payload = vec![0u8; PAYLOAD_TAG_SIZE];
_payload.extend(payload);
while i >= 0 {
_payload = sprp_encrypt(&sprp_keys[i as usize].key, &sprp_keys[i as usize].iv, _payload.clone());
i -= 1;
}
let mut packet: Vec<u8> = Vec::new();
packet.extend(header.iter());
packet.extend(&_payload);
return Ok(packet);
}
pub fn new_surb<R: Rng>(rng: &mut R, path: Vec<PathHop>) -> Result<([u8; SURB_SIZE], Vec<u8>), SphinxSurbCreateError> {
let _path_len = path.len();
let mut key_payload = [0u8; SPRP_KEY_MATERIAL_SIZE];
rng.fill_bytes(&mut key_payload);
let mut _id = [0u8; NODE_ID_SIZE];
_id.copy_from_slice(&path[0].id[..]);
let _header_result = create_header(rng, path);
if _header_result.is_err() {
return Err(SphinxSurbCreateError::CreateHeaderError);
}
let _tmp = _header_result.unwrap();
let header = _tmp.0;
let mut sprp_keys = _tmp.1;
let mut k: Vec<u8> = Vec::new();
let mut i = (_path_len - 1) as i8;
while i >= 0 {
k.extend(sprp_keys[i as usize].key.iter());
k.extend(sprp_keys[i as usize].iv.iter());
sprp_keys[i as usize].reset();
i -= 1;
}
k.extend(key_payload.iter());
let mut _surb: Vec<u8> = vec![];
_surb.extend(header.iter());
_surb.extend(_id.iter());
_surb.extend(key_payload[..].iter());
let mut surb = [0u8; SURB_SIZE];
surb.copy_from_slice(_surb.as_slice());
return Ok((surb, k));
}
pub fn new_packet_from_surb(surb: [u8; SURB_SIZE], payload: Vec<u8>) -> Result<(Vec<u8>, [u8; NODE_ID_SIZE]), SphinxPacketFromSurbError>{
let (header, id, key, iv) = array_refs![&surb, HEADER_SIZE, NODE_ID_SIZE, SPRP_KEY_SIZE, SPRP_IV_SIZE];
let mut packet = Vec::new();
packet.extend(header.iter());
let mut crypt_payload = Vec::new();
crypt_payload.extend(&[0u8; PAYLOAD_TAG_SIZE]);
crypt_payload.extend(payload.iter());
let ciphertext = sprp_encrypt(key, iv, crypt_payload[..].to_vec());
packet.extend(&ciphertext);
return Ok((packet, *id));
}
pub fn decrypt_surb_payload(payload: Vec<u8>, keys: Vec<u8>) -> Result<Vec<u8>, SphinxDecryptSurbError> {
assert!(keys.len() % SPRP_KEY_MATERIAL_SIZE == 0);
let num_hops = keys.len() / SPRP_KEY_MATERIAL_SIZE;
if keys.len() % SPRP_KEY_MATERIAL_SIZE != 0 || num_hops < 1 {
return Err(SphinxDecryptSurbError::InvalidSurbKeys);
}
if payload.len() < PAYLOAD_TAG_SIZE {
return Err(SphinxDecryptSurbError::TruncatedPayloadError);
}
let mut sprp_key = [0u8; SPRP_KEY_SIZE];
let mut sprp_iv = [0u8; SPRP_IV_SIZE];
let mut k = &keys[0..];
let mut b = payload.to_vec();
let mut i = 0;
while i < num_hops {
sprp_key.copy_from_slice(&k[..SPRP_KEY_SIZE]);
sprp_iv.copy_from_slice(&k[SPRP_KEY_SIZE..SPRP_KEY_SIZE+SPRP_IV_SIZE]);
k = &k[SPRP_KEY_SIZE+SPRP_IV_SIZE..];
if i == num_hops - 1 {
b = match sprp_decrypt(&sprp_key, &sprp_iv, b.to_vec()) {
Ok(x) => x,
Err(_) => {
return Err(SphinxDecryptSurbError::DecryptError);
},
}
} else {
b = sprp_encrypt(&sprp_key, &sprp_iv, b.to_vec());
}
i += 1;
}
let tag = [0u8; PAYLOAD_TAG_SIZE];
if b[..PAYLOAD_TAG_SIZE].ct_eq(&tag).unwrap_u8() == 0 {
return Err(SphinxDecryptSurbError::InvalidTag)
}
return Ok(b[PAYLOAD_TAG_SIZE..].to_vec());
}