use super::{
crypto::*,
delay::Delay,
packet::*,
target::{MixnodeIndex, Target},
};
use arrayref::{array_mut_ref, array_refs, mut_array_refs};
use arrayvec::ArrayVec;
use rand::{CryptoRng, Rng};
fn mut_arr_at<T, const N: usize>(slice: &mut [T], offset: usize) -> &mut [T; N] {
(&mut slice[offset..offset + N])
.try_into()
.expect("Slice length is fixed and matches array length")
}
enum PacketKind<'a> {
Request,
Reply(&'a SurbId),
Cover(Option<&'a CoverId>),
}
fn build_header(
header: &mut Header,
kx_shared_secrets: &mut ArrayVec<SharedSecret, MAX_HOPS>,
rng: &mut (impl Rng + CryptoRng),
targets: &[Target],
their_kx_publics: &[KxPublic],
kind: PacketKind,
) -> Delay {
debug_assert_eq!(targets.len() + 1, their_kx_publics.len());
debug_assert!(their_kx_publics.len() <= MAX_HOPS);
let (kx_public, mac_plus_actions) =
mut_array_refs![header, KX_PUBLIC_SIZE, MAC_SIZE + ACTIONS_SIZE];
gen_kx_public_and_shared_secrets(kx_public, kx_shared_secrets, rng, their_kx_publics);
let actions = array_mut_ref![mac_plus_actions, MAC_SIZE, ACTIONS_SIZE];
let mut offset = 0;
let mut total_delay = Delay::zero();
struct Hop {
mac_key: MacKey,
keystream: [u8; ACTIONS_SIZE + MAX_ACTIONS_PAD_SIZE],
start_offset: u16, }
let mut hops: ArrayVec<Hop, { MAX_HOPS - 1 }> = ArrayVec::new();
let mut pad = [0; ACTIONS_SIZE - RAW_ACTION_SIZE];
for (target, kx_shared_secret) in targets.iter().zip(kx_shared_secrets.iter()) {
let start_offset = offset;
offset += RAW_ACTION_SIZE;
let raw_action = match target {
Target::MixnodeIndex(mixnode_index) => mixnode_index.get(),
Target::PeerId(peer_id) => {
*mut_arr_at(actions, offset) = *peer_id;
offset += PEER_ID_SIZE;
RAW_ACTION_FORWARD_TO_PEER_ID
},
};
*mut_arr_at(actions, start_offset) = raw_action.to_le_bytes();
offset += MAC_SIZE;
let sds = SmallDerivedSecrets::new(kx_shared_secret);
total_delay += Delay::exp(sds.delay_seed());
hops.push(Hop {
mac_key: *sds.mac_key(),
keystream: [0; ACTIONS_SIZE + MAX_ACTIONS_PAD_SIZE],
start_offset: start_offset as u16,
});
let keystream = &mut hops.last_mut().expect("Just pushed, so not empty").keystream;
apply_actions_encryption_keystream(keystream, sds.actions_encryption_key());
apply_keystream(&mut pad[..offset], &keystream[ACTIONS_SIZE - start_offset..]);
}
{
let start_offset = offset;
offset += RAW_ACTION_SIZE;
let raw_action = match kind {
PacketKind::Request => RAW_ACTION_DELIVER_REQUEST,
PacketKind::Reply(surb_id) => {
*mut_arr_at(actions, offset) = *surb_id;
offset += SURB_ID_SIZE;
RAW_ACTION_DELIVER_REPLY
},
PacketKind::Cover(None) => RAW_ACTION_DELIVER_COVER,
PacketKind::Cover(Some(cover_id)) => {
*mut_arr_at(actions, offset) = *cover_id;
offset += COVER_ID_SIZE;
RAW_ACTION_DELIVER_COVER_WITH_ID
},
};
*mut_arr_at(actions, start_offset) = raw_action.to_le_bytes();
rng.fill_bytes(&mut actions[offset..]);
let sds =
SmallDerivedSecrets::new(kx_shared_secrets.last().expect("There is at least one hop"));
apply_actions_encryption_keystream(
&mut actions[start_offset..],
sds.actions_encryption_key(),
);
*mut_arr_at(mac_plus_actions, start_offset) =
compute_mac(&actions[start_offset..], &pad[..start_offset], sds.mac_key());
}
for hop in hops.iter().rev() {
let start_offset = hop.start_offset as usize;
apply_keystream(&mut mac_plus_actions[MAC_SIZE + start_offset..], &hop.keystream);
apply_keystream(&mut pad[..start_offset], &hop.keystream[ACTIONS_SIZE - start_offset..]);
*mut_arr_at(mac_plus_actions, start_offset) = compute_mac(
&mac_plus_actions[MAC_SIZE + start_offset..],
&pad[..start_offset],
&hop.mac_key,
);
}
total_delay
}
pub fn mut_payload_data(packet: &mut Packet) -> &mut PayloadData {
array_mut_ref![packet, HEADER_SIZE, PAYLOAD_DATA_SIZE]
}
pub fn complete_request_packet(
packet: &mut Packet,
rng: &mut (impl Rng + CryptoRng),
targets: &[Target],
their_kx_publics: &[KxPublic],
) -> Delay {
debug_assert_eq!(targets.len() + 1, their_kx_publics.len());
debug_assert!(their_kx_publics.len() <= MAX_HOPS);
let (header, payload) = mut_array_refs![packet, HEADER_SIZE, PAYLOAD_SIZE];
let mut kx_shared_secrets = ArrayVec::new();
let total_delay = build_header(
header,
&mut kx_shared_secrets,
rng,
targets,
their_kx_publics,
PacketKind::Request,
);
*array_mut_ref![payload, PAYLOAD_DATA_SIZE, PAYLOAD_TAG_SIZE] = PAYLOAD_TAG;
for kx_shared_secret in kx_shared_secrets.iter().rev() {
encrypt_payload(payload, &derive_payload_encryption_key(kx_shared_secret));
}
total_delay
}
pub const SURB_SIZE: usize = RAW_MIXNODE_INDEX_SIZE + HEADER_SIZE + SHARED_SECRET_SIZE;
pub type Surb = [u8; SURB_SIZE];
pub type SurbPayloadEncryptionKeys = ArrayVec<PayloadEncryptionKey, MAX_HOPS>;
pub fn build_surb(
surb: &mut Surb,
payload_encryption_keys: &mut SurbPayloadEncryptionKeys,
rng: &mut (impl Rng + CryptoRng),
first_mixnode_index: MixnodeIndex,
targets: &[Target],
their_kx_publics: &[KxPublic],
id: &SurbId,
) -> Delay {
debug_assert_eq!(targets.len() + 1, their_kx_publics.len());
debug_assert!(their_kx_publics.len() <= MAX_HOPS);
let (raw_first_mixnode_index, header, shared_secret) =
mut_array_refs![surb, RAW_MIXNODE_INDEX_SIZE, HEADER_SIZE, SHARED_SECRET_SIZE];
*raw_first_mixnode_index = first_mixnode_index.get().to_le_bytes();
let mut kx_shared_secrets = ArrayVec::new();
let total_delay = build_header(
header,
&mut kx_shared_secrets,
rng,
targets,
their_kx_publics,
PacketKind::Reply(id),
);
rng.fill_bytes(shared_secret);
payload_encryption_keys.push(derive_payload_encryption_key(shared_secret));
kx_shared_secrets.pop(); for kx_shared_secret in &kx_shared_secrets {
payload_encryption_keys.push(derive_payload_encryption_key(kx_shared_secret));
}
total_delay
}
pub fn complete_reply_packet(packet: &mut Packet, surb: &Surb) -> Option<MixnodeIndex> {
let (header, payload) = mut_array_refs![packet, HEADER_SIZE, PAYLOAD_SIZE];
let (raw_first_mixnode_index, surb_header, shared_secret) =
array_refs![surb, RAW_MIXNODE_INDEX_SIZE, HEADER_SIZE, SHARED_SECRET_SIZE];
*header = *surb_header;
*array_mut_ref![payload, PAYLOAD_DATA_SIZE, PAYLOAD_TAG_SIZE] = PAYLOAD_TAG;
decrypt_payload(payload, &derive_payload_encryption_key(shared_secret));
let raw_first_mixnode_index = RawMixnodeIndex::from_le_bytes(*raw_first_mixnode_index);
raw_first_mixnode_index.try_into().ok()
}
pub fn build_cover_packet(
packet: &mut Packet,
rng: &mut (impl Rng + CryptoRng),
targets: &[Target],
their_kx_publics: &[KxPublic],
id: Option<&CoverId>,
) -> Delay {
debug_assert_eq!(targets.len() + 1, their_kx_publics.len());
debug_assert!(their_kx_publics.len() <= MAX_HOPS);
let (header, payload) = mut_array_refs![packet, HEADER_SIZE, PAYLOAD_SIZE];
let mut kx_shared_secrets = ArrayVec::new();
let total_delay = build_header(
header,
&mut kx_shared_secrets,
rng,
targets,
their_kx_publics,
PacketKind::Cover(id),
);
rng.fill_bytes(payload);
total_delay
}