#![allow(dead_code)]
use aes::{Aes128, Aes128Dec, Aes128Enc, Aes256, Aes256Dec, Aes256Enc};
use cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, BlockSizeUser, StreamCipher as _};
use digest::KeyInit;
use polyval::{Polyval, universal_hash::UniversalHash};
use tor_cell::{
chancell::{CELL_DATA_LEN, ChanCmd},
relaycell::msg::SendmeTag,
};
use tor_error::internal;
use zeroize::Zeroizing;
use super::{CryptInit, RelayCellBody};
use crate::{client::circuit::CircuitBinding, util::ct};
const CGO_TAG_LEN: usize = 16;
const CGO_PAYLOAD_LEN: usize = CELL_DATA_LEN - CGO_TAG_LEN;
const CGO_AD_LEN: usize = 16;
const HLEN_UIV: usize = CGO_TAG_LEN + CGO_AD_LEN;
const BLK_LEN: usize = 16;
type BlockLen = typenum::U16;
type Block = [u8; BLK_LEN];
#[cfg_attr(feature = "bench", visibility::make(pub))]
pub(crate) trait BlkCipher:
BlockCipher + KeyInit + BlockSizeUser<BlockSize = BlockLen> + Clone
{
const KEY_LEN: usize;
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
pub(crate) trait BlkCipherEnc: BlkCipher + BlockEncrypt {}
#[cfg_attr(feature = "bench", visibility::make(pub))]
pub(crate) trait BlkCipherDec: BlkCipher + BlockDecrypt {}
impl BlkCipher for Aes128 {
const KEY_LEN: usize = 16;
}
impl BlkCipherEnc for Aes128 {}
impl BlkCipherDec for Aes128 {}
impl BlkCipher for Aes128Enc {
const KEY_LEN: usize = 16;
}
impl BlkCipherEnc for Aes128Enc {}
impl BlkCipher for Aes128Dec {
const KEY_LEN: usize = 16;
}
impl BlkCipherDec for Aes128Dec {}
impl BlkCipher for Aes256 {
const KEY_LEN: usize = 32;
}
impl BlkCipherEnc for Aes256 {}
impl BlkCipherDec for Aes256 {}
impl BlkCipher for Aes256Enc {
const KEY_LEN: usize = 32;
}
impl BlkCipherEnc for Aes256Enc {}
impl BlkCipher for Aes256Dec {
const KEY_LEN: usize = 32;
}
impl BlkCipherDec for Aes256Dec {}
mod et {
use super::*;
pub(super) type EtTweak<'a> = (&'a [u8; CGO_TAG_LEN], u8, &'a [u8; CGO_PAYLOAD_LEN]);
pub(super) const TLEN_ET: usize = CGO_TAG_LEN + 1 + CGO_PAYLOAD_LEN;
#[derive(Clone)]
pub(super) struct EtCipher<BC: BlkCipher> {
kb: BC,
ku: Polyval,
}
impl<BC: BlkCipher> EtCipher<BC> {
fn compute_tweak_hash(&self, tweak: EtTweak<'_>) -> Zeroizing<Block> {
let mut ku = self.ku.clone();
let mut block1 = Zeroizing::new([0_u8; 16]);
block1[0] = tweak.1;
block1[1..16].copy_from_slice(&tweak.2[0..15]);
ku.update(&[(*tweak.0).into(), (*block1).into()]);
ku.update_padded(&tweak.2[15..]);
Zeroizing::new(ku.finalize().into())
}
}
impl<BC: BlkCipherEnc> EtCipher<BC> {
pub(super) fn encrypt(&self, tweak: EtTweak<'_>, block: &mut Block) {
let tag: Zeroizing<[u8; 16]> = self.compute_tweak_hash(tweak);
xor_into(block, &tag);
self.kb.encrypt_block(block.into());
xor_into(block, &tag);
}
}
impl<BC: BlkCipherDec> EtCipher<BC> {
pub(super) fn decrypt(&self, tweak: EtTweak<'_>, block: &mut Block) {
let tag: Zeroizing<[u8; 16]> = self.compute_tweak_hash(tweak);
xor_into(block, &tag);
self.kb.decrypt_block(block.into());
xor_into(block, &tag);
}
}
impl<BC: BlkCipher> CryptInit for EtCipher<BC> {
fn seed_len() -> usize {
BC::key_size() + polyval::KEY_SIZE
}
fn initialize(seed: &[u8]) -> crate::Result<Self> {
if seed.len() != Self::seed_len() {
return Err(internal!("Invalid seed length").into());
}
let (kb, ku) = seed.split_at(BC::key_size());
let ku: &[u8; 16] = ku
.try_into()
.expect("Incorrect key size, even though it was validated!?");
Ok(Self {
kb: BC::new(kb.into()),
ku: Polyval::new(ku.into()),
})
}
}
}
mod prf {
use tor_error::internal;
use super::*;
type PrfTweak = [u8; 16];
const PRF_N0_LEN: usize = CGO_PAYLOAD_LEN;
const PRF_N1_OFFSET: usize = 31 * 16;
const _: () = assert!(PRF_N1_OFFSET >= PRF_N0_LEN);
#[derive(Clone)]
pub(super) struct Prf<BC: BlkCipherEnc> {
k: BC,
b: Polyval,
}
impl<BC: BlkCipherEnc> Prf<BC> {
fn cipher(&self, tweak: &PrfTweak, t: bool) -> ctr::Ctr128BE<BC> {
use {
cipher::{InnerIvInit as _, StreamCipherSeek as _},
ctr::CtrCore,
};
let mut b = self.b.clone(); b.update(&[(*tweak).into()]);
let mut iv = b.finalize();
*iv.last_mut().expect("no last element?") &= 0xC0; let iv: [u8; 16] = iv.into(); let mut cipher: ctr::Ctr128BE<BC> = cipher::StreamCipherCoreWrapper::from_core(
CtrCore::inner_iv_init(self.k.clone(), (&iv).into()),
);
if t {
debug_assert_eq!(cipher.current_pos::<u32>(), 0_u32);
cipher.seek(PRF_N1_OFFSET);
}
cipher
}
pub(super) fn xor_n0_stream(&self, tweak: &PrfTweak, out: &mut [u8; PRF_N0_LEN]) {
let mut stream = self.cipher(tweak, false);
stream.apply_keystream(out);
}
pub(super) fn get_n1_stream(&self, tweak: &PrfTweak, n: usize) -> Zeroizing<Vec<u8>> {
let mut output = Zeroizing::new(vec![0_u8; n]);
self.cipher(tweak, true).apply_keystream(output.as_mut());
output
}
}
impl<BC: BlkCipherEnc> CryptInit for Prf<BC> {
fn seed_len() -> usize {
BC::key_size() + polyval::KEY_SIZE
}
fn initialize(seed: &[u8]) -> crate::Result<Self> {
if seed.len() != Self::seed_len() {
return Err(internal!("Invalid seed length").into());
}
let (k, b) = seed.split_at(BC::key_size());
let b: &[u8; 16] = b
.try_into()
.expect("Incorrect key size, even though it was validated!?");
Ok(Self {
k: BC::new(k.into()),
b: Polyval::new(b.into()),
})
}
}
}
mod uiv {
use super::*;
pub(super) type UivTweak<'a> = (&'a [u8; BLK_LEN], u8);
#[derive(Clone)]
pub(super) struct Uiv<EtBC: BlkCipher, PrfBC: BlkCipherEnc> {
j: et::EtCipher<EtBC>,
s: prf::Prf<PrfBC>,
#[cfg(test)]
pub(super) keys: Zeroizing<Vec<u8>>,
}
fn split(
cell_body: &mut [u8; CELL_DATA_LEN],
) -> (&mut [u8; CGO_TAG_LEN], &mut [u8; CGO_PAYLOAD_LEN]) {
let (left, right) = cell_body.split_at_mut(CGO_TAG_LEN);
(
left.try_into().expect("split_at_mut returned wrong size!"),
right.try_into().expect("split_at_mut returned wrong size!"),
)
}
impl<EtBC: BlkCipherEnc, PrfBC: BlkCipherEnc> Uiv<EtBC, PrfBC> {
pub(super) fn encrypt(&self, tweak: UivTweak<'_>, cell_body: &mut [u8; CELL_DATA_LEN]) {
let (left, right) = split(cell_body);
self.j.encrypt((tweak.0, tweak.1, right), left);
self.s.xor_n0_stream(left, right);
}
}
impl<EtBC: BlkCipherDec, PrfBC: BlkCipherEnc> Uiv<EtBC, PrfBC> {
pub(super) fn decrypt(&self, tweak: UivTweak<'_>, cell_body: &mut [u8; CELL_DATA_LEN]) {
let (left, right) = split(cell_body);
self.s.xor_n0_stream(left, right);
self.j.decrypt((tweak.0, tweak.1, right), left);
}
}
impl<EtBC: BlkCipher, PrfBC: BlkCipherEnc> Uiv<EtBC, PrfBC> {
pub(super) fn update(&mut self, nonce: &mut [u8; BLK_LEN]) {
let n_bytes = Self::seed_len() + BLK_LEN;
let seed = self.s.get_n1_stream(nonce, n_bytes);
#[cfg(test)]
{
self.keys = Zeroizing::new(seed[..Self::seed_len()].to_vec());
}
let (j, s, n) = Self::split_seed(&seed);
self.j = et::EtCipher::initialize(j).expect("Invalid slice len");
self.s = prf::Prf::initialize(s).expect("invalid slice len");
nonce[..].copy_from_slice(n);
}
fn split_seed(seed: &[u8]) -> (&[u8], &[u8], &[u8]) {
let len_j = et::EtCipher::<EtBC>::seed_len();
let len_s = prf::Prf::<PrfBC>::seed_len();
(
&seed[0..len_j],
&seed[len_j..len_j + len_s],
&seed[len_j + len_s..],
)
}
}
impl<EtBC: BlkCipher, PrfBC: BlkCipherEnc> CryptInit for Uiv<EtBC, PrfBC> {
fn seed_len() -> usize {
super::et::EtCipher::<EtBC>::seed_len() + super::prf::Prf::<PrfBC>::seed_len()
}
fn initialize(seed: &[u8]) -> crate::Result<Self> {
if seed.len() != Self::seed_len() {
return Err(internal!("Invalid seed length").into());
}
#[cfg(test)]
let keys = Zeroizing::new(seed.to_vec());
let (j, s, n) = Self::split_seed(seed);
debug_assert!(n.is_empty());
Ok(Self {
j: et::EtCipher::initialize(j)?,
s: prf::Prf::initialize(s)?,
#[cfg(test)]
keys,
})
}
}
}
fn xor_into<const N: usize>(output: &mut [u8; N], input: &[u8; N]) {
for i in 0..N {
output[i] ^= input[i];
}
}
#[inline]
fn first_block(bytes: &[u8]) -> &[u8; BLK_LEN] {
bytes[0..BLK_LEN].try_into().expect("Slice too short!")
}
#[derive(Clone)]
struct CryptState<EtBC: BlkCipher, PrfBC: BlkCipherEnc> {
uiv: uiv::Uiv<EtBC, PrfBC>,
nonce: Zeroizing<[u8; BLK_LEN]>,
tag: Zeroizing<[u8; BLK_LEN]>,
}
impl<EtBC: BlkCipher, PrfBC: BlkCipherEnc> CryptInit for CryptState<EtBC, PrfBC> {
fn seed_len() -> usize {
uiv::Uiv::<EtBC, PrfBC>::seed_len() + BLK_LEN
}
fn initialize(seed: &[u8]) -> crate::Result<Self> {
if seed.len() != Self::seed_len() {
return Err(internal!("Invalid seed length").into());
}
let (j_s, n) = seed.split_at(uiv::Uiv::<EtBC, PrfBC>::seed_len());
Ok(Self {
uiv: uiv::Uiv::initialize(j_s)?,
nonce: Zeroizing::new(n.try_into().expect("invalid splice length")),
tag: Zeroizing::new([0; BLK_LEN]),
})
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(Clone, derive_more::From)]
pub(crate) struct ClientOutbound<EtBC, PrfBC>(CryptState<EtBC, PrfBC>)
where
EtBC: BlkCipherDec,
PrfBC: BlkCipherEnc;
impl<EtBC, PrfBC> super::OutboundClientLayer for ClientOutbound<EtBC, PrfBC>
where
EtBC: BlkCipherDec,
PrfBC: BlkCipherEnc,
{
fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
cell.0[0..BLK_LEN].copy_from_slice(&self.0.nonce[..]);
self.encrypt_outbound(cmd, cell);
self.0.uiv.update(&mut self.0.nonce);
SendmeTag::try_from(&cell.0[0..BLK_LEN]).expect("Block length not a valid sendme tag.")
}
fn encrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) {
let t_new: [u8; BLK_LEN] = *first_block(&*cell.0);
self.0.uiv.decrypt((&self.0.tag, cmd.into()), &mut cell.0);
*self.0.tag = t_new;
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(Clone, derive_more::From)]
pub(crate) struct ClientInbound<EtBC, PrfBC>(CryptState<EtBC, PrfBC>)
where
EtBC: BlkCipherDec,
PrfBC: BlkCipherEnc;
impl<EtBC, PrfBC> super::InboundClientLayer for ClientInbound<EtBC, PrfBC>
where
EtBC: BlkCipherDec,
PrfBC: BlkCipherEnc,
{
fn decrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
let mut t_orig: [u8; BLK_LEN] = *first_block(&*cell.0);
self.0.uiv.decrypt((&self.0.tag, cmd.into()), &mut cell.0);
*self.0.tag = t_orig;
if ct::bytes_eq(&cell.0[..CGO_TAG_LEN], &self.0.nonce[..]) {
self.0.uiv.update(&mut t_orig);
*self.0.nonce = t_orig;
Some((*self.0.tag).into())
} else {
None
}
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(Clone, derive_more::From)]
pub(crate) struct RelayOutbound<EtBC, PrfBC>(CryptState<EtBC, PrfBC>)
where
EtBC: BlkCipherEnc,
PrfBC: BlkCipherEnc;
impl<EtBC, PrfBC> super::OutboundRelayLayer for RelayOutbound<EtBC, PrfBC>
where
EtBC: BlkCipherEnc,
PrfBC: BlkCipherEnc,
{
fn decrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
let tag = SendmeTag::try_from(&cell.0[0..BLK_LEN]).expect("Invalid sendme length");
self.0.uiv.encrypt((&self.0.tag, cmd.into()), &mut cell.0);
*self.0.tag = *first_block(&*cell.0);
if ct::bytes_eq(self.0.tag.as_ref(), &self.0.nonce[..]) {
self.0.uiv.update(&mut self.0.nonce);
Some(tag)
} else {
None
}
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(Clone, derive_more::From)]
pub(crate) struct RelayInbound<EtBC, PrfBC>(CryptState<EtBC, PrfBC>)
where
EtBC: BlkCipherEnc,
PrfBC: BlkCipherEnc;
impl<EtBC, PrfBC> super::InboundRelayLayer for RelayInbound<EtBC, PrfBC>
where
EtBC: BlkCipherEnc,
PrfBC: BlkCipherEnc,
{
fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
cell.0[0..BLK_LEN].copy_from_slice(&self.0.nonce[..]);
self.encrypt_inbound(cmd, cell);
self.0.nonce.copy_from_slice(&cell.0[0..BLK_LEN]);
self.0.uiv.update(&mut self.0.nonce);
(*self.0.tag).into()
}
fn encrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) {
self.0.uiv.encrypt((&self.0.tag, cmd.into()), &mut cell.0);
*self.0.tag = *first_block(&*cell.0);
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(Clone)]
pub(crate) struct CryptStatePair<EtBC, PrfBC>
where
EtBC: BlkCipher,
PrfBC: BlkCipherEnc,
{
outbound: CryptState<EtBC, PrfBC>,
inbound: CryptState<EtBC, PrfBC>,
binding: CircuitBinding,
}
impl<EtBC, PrfBC> CryptInit for CryptStatePair<EtBC, PrfBC>
where
EtBC: BlkCipher,
PrfBC: BlkCipherEnc,
{
fn seed_len() -> usize {
CryptState::<EtBC, PrfBC>::seed_len() * 2 + crate::crypto::binding::CIRC_BINDING_LEN
}
fn initialize(seed: &[u8]) -> crate::Result<Self> {
const {
assert!(EtBC::KEY_LEN == PrfBC::KEY_LEN);
}
if seed.len() != Self::seed_len() {
return Err(internal!("Invalid seed length").into());
}
let slen = CryptState::<EtBC, PrfBC>::seed_len();
let (outb, inb, binding) = (&seed[0..slen], &seed[slen..slen * 2], &seed[slen * 2..]);
Ok(Self {
outbound: CryptState::initialize(outb)?,
inbound: CryptState::initialize(inb)?,
binding: binding.try_into().expect("Invalid slice length"),
})
}
}
impl<EtBC, PrfBC> super::ClientLayer<ClientOutbound<EtBC, PrfBC>, ClientInbound<EtBC, PrfBC>>
for CryptStatePair<EtBC, PrfBC>
where
EtBC: BlkCipherDec,
PrfBC: BlkCipherEnc,
{
fn split_client_layer(
self,
) -> (
ClientOutbound<EtBC, PrfBC>,
ClientInbound<EtBC, PrfBC>,
CircuitBinding,
) {
(self.outbound.into(), self.inbound.into(), self.binding)
}
}
impl<EtBC, PrfBC> super::RelayLayer<RelayOutbound<EtBC, PrfBC>, RelayInbound<EtBC, PrfBC>>
for CryptStatePair<EtBC, PrfBC>
where
EtBC: BlkCipherEnc,
PrfBC: BlkCipherEnc,
{
fn split_relay_layer(
self,
) -> (
RelayOutbound<EtBC, PrfBC>,
RelayInbound<EtBC, PrfBC>,
CircuitBinding,
) {
(self.outbound.into(), self.inbound.into(), self.binding)
}
}
#[cfg(feature = "bench")]
pub mod bench_utils {
pub use super::ClientInbound;
pub use super::ClientOutbound;
pub use super::CryptStatePair;
pub use super::RelayInbound;
pub use super::RelayOutbound;
pub const CGO_THROUGHPUT: u64 = 488;
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use crate::crypto::cell::{
InboundRelayLayer, OutboundClientCrypt, OutboundClientLayer, OutboundRelayLayer,
};
use super::*;
use hex_literal::hex;
use rand::Rng as _;
use tor_basic_utils::test_rng::testing_rng;
#[test]
fn testvec_xor() {
let mut b: [u8; 20] = *b"turning and turning ";
let s = b"in the widening gyre";
xor_into(&mut b, s);
assert_eq!(b[..], hex!("1d1b521a010b4757080a014e1d1b154e0e171545"));
}
#[test]
fn testvec_polyval() {
use polyval::Polyval;
use polyval::universal_hash::UniversalHash;
let h = hex!("25629347589242761d31f826ba4b757b");
let x_1 = hex!("4f4f95668c83dfb6401762bb2d01a262");
let x_2 = hex!("d1a24ddd2721d006bbe45f20d3c9f362");
let mut hash = Polyval::new(&h.into());
hash.update(&[x_1.into(), x_2.into()]);
let result: [u8; 16] = hash.finalize().into();
assert_eq!(result, hex!("f7a3b47b846119fae5b7866cf5e5b77e"));
}
#[allow(non_upper_case_globals)]
const False: bool = false;
#[allow(non_upper_case_globals)]
const True: bool = true;
include!("../../../testdata/cgo_et.rs");
include!("../../../testdata/cgo_prf.rs");
include!("../../../testdata/cgo_uiv.rs");
include!("../../../testdata/cgo_relay.rs");
include!("../../../testdata/cgo_client.rs");
fn unhex<const N: usize>(s: &str) -> [u8; N] {
hex::decode(s).unwrap().try_into().unwrap()
}
#[test]
fn testvec_et() {
for (encrypt, keys, tweak, input, expect_output) in ET_TEST_VECTORS {
let keys: [u8; 32] = unhex(keys);
let tweak: [u8; et::TLEN_ET] = unhex(tweak);
let mut block: [u8; 16] = unhex(input);
let expect_output: [u8; 16] = unhex(expect_output);
let et: et::EtCipher<Aes128> = et::EtCipher::initialize(&keys).unwrap();
let tweak = (
tweak[0..16].try_into().unwrap(),
tweak[16],
&tweak[17..].try_into().unwrap(),
);
if *encrypt {
et.encrypt(tweak, &mut block);
} else {
et.decrypt(tweak, &mut block);
}
assert_eq!(block, expect_output);
}
}
#[test]
fn testvec_prf() {
for (keys, offset, tweak, expect_output) in PRF_TEST_VECTORS {
let keys: [u8; 32] = unhex(keys);
assert!([0, 1].contains(offset));
let tweak: [u8; 16] = unhex(tweak);
let expect_output = hex::decode(expect_output).unwrap();
let prf: prf::Prf<Aes128> = prf::Prf::initialize(&keys).unwrap();
if *offset == 0 {
assert_eq!(expect_output.len(), CGO_PAYLOAD_LEN);
let mut data = [0_u8; CGO_PAYLOAD_LEN];
prf.xor_n0_stream(&tweak, &mut data);
assert_eq!(expect_output[..], data[..]);
} else {
let data = prf.get_n1_stream(&tweak, expect_output.len());
assert_eq!(expect_output[..], data[..]);
}
}
}
#[test]
fn testvec_uiv() {
for (encrypt, keys, tweak, left, right, (expect_left, expect_right)) in UIV_TEST_VECTORS {
let keys: [u8; 64] = unhex(keys);
let tweak: [u8; 17] = unhex(tweak);
let mut cell: [u8; 509] = unhex(&format!("{left}{right}"));
let expected: [u8; 509] = unhex(&format!("{expect_left}{expect_right}"));
let uiv: uiv::Uiv<Aes128, Aes128> = uiv::Uiv::initialize(&keys).unwrap();
let htweak = (tweak[0..16].try_into().unwrap(), tweak[16]);
if *encrypt {
uiv.encrypt(htweak, &mut cell);
} else {
uiv.decrypt(htweak, &mut cell);
}
assert_eq!(cell, expected);
}
}
#[test]
fn testvec_uiv_update() {
let mut rng = testing_rng();
for (keys, nonce, (expect_keys, expect_nonce)) in UIV_UPDATE_TEST_VECTORS {
let keys: [u8; 64] = unhex(keys);
let mut nonce: [u8; 16] = unhex(nonce);
let mut uiv: uiv::Uiv<Aes128, Aes128> = uiv::Uiv::initialize(&keys).unwrap();
let expect_keys: [u8; 64] = unhex(expect_keys);
let expect_nonce: [u8; 16] = unhex(expect_nonce);
uiv.update(&mut nonce);
assert_eq!(&nonce, &expect_nonce);
assert_eq!(&uiv.keys[..], &expect_keys[..]);
let uiv2: uiv::Uiv<Aes128, Aes128> = uiv::Uiv::initialize(&uiv.keys[..]).unwrap();
let tweak: [u8; 16] = rng.random();
let cmd = rng.random();
let mut msg1: [u8; CELL_DATA_LEN] = rng.random();
let mut msg2 = msg1.clone();
uiv.encrypt((&tweak, cmd), &mut msg1);
uiv2.encrypt((&tweak, cmd), &mut msg2);
}
}
#[test]
fn testvec_cgo_relay() {
for (inbound, (k, n, tprime), ad, t, c, output) in CGO_RELAY_TEST_VECTORS {
let k_n: [u8; 80] = unhex(&format!("{k}{n}"));
let tprime: [u8; 16] = unhex(tprime);
let ad: [u8; 1] = unhex(ad);
let msg: [u8; CELL_DATA_LEN] = unhex(&format!("{t}{c}"));
let mut msg = RelayCellBody(Box::new(msg));
let mut state = CryptState::<Aes128, Aes128>::initialize(&k_n).unwrap();
*state.tag = tprime;
let state = if *inbound {
let mut s = RelayInbound::from(state);
s.encrypt_inbound(ad[0].into(), &mut msg);
s.0
} else {
let mut s = RelayOutbound::from(state);
s.decrypt_outbound(ad[0].into(), &mut msg);
s.0
};
let ((ex_k, ex_n, ex_tprime), (ex_t, ex_c)) = output;
let ex_msg: [u8; CELL_DATA_LEN] = unhex(&format!("{ex_t}{ex_c}"));
let ex_k: [u8; 64] = unhex(ex_k);
let ex_n: [u8; 16] = unhex(ex_n);
let ex_tprime: [u8; 16] = unhex(ex_tprime);
assert_eq!(&ex_msg[..], &msg.0[..]);
assert_eq!(&state.uiv.keys[..], &ex_k[..]);
assert_eq!(&state.nonce[..], &ex_n[..]);
assert_eq!(&state.tag[..], &ex_tprime[..]);
}
}
#[test]
fn testvec_cgo_relay_originate() {
for ((k, n, tprime), ad, m, output) in CGO_RELAY_ORIGINATE_TEST_VECTORS {
let k_n: [u8; 80] = unhex(&format!("{k}{n}"));
let tprime: [u8; 16] = unhex(tprime);
let ad: [u8; 1] = unhex(ad);
let msg_body: [u8; CGO_PAYLOAD_LEN] = unhex(m);
let mut msg = [0_u8; CELL_DATA_LEN];
msg[16..].copy_from_slice(&msg_body[..]);
let mut msg = RelayCellBody(Box::new(msg));
let mut state = CryptState::<Aes128, Aes128>::initialize(&k_n).unwrap();
*state.tag = tprime;
let mut state = RelayInbound::from(state);
state.originate(ad[0].into(), &mut msg);
let state = state.0;
let ((ex_k, ex_n, ex_tprime), (ex_t, ex_c)) = output;
let ex_msg: [u8; CELL_DATA_LEN] = unhex(&format!("{ex_t}{ex_c}"));
let ex_k: [u8; 64] = unhex(ex_k);
let ex_n: [u8; 16] = unhex(ex_n);
let ex_tprime: [u8; 16] = unhex(ex_tprime);
assert_eq!(&ex_msg[..], &msg.0[..]);
assert_eq!(&state.uiv.keys[..], &ex_k[..]);
assert_eq!(&state.nonce[..], &ex_n[..]);
assert_eq!(&state.tag[..], &ex_tprime[..]);
}
}
#[test]
fn testvec_cgo_client_originate() {
for (ss, hop, ad, m, output) in CGO_CLIENT_ORIGINATE_TEST_VECTORS {
assert!(*hop > 0); let mut client = OutboundClientCrypt::new();
let mut individual_layers = Vec::new();
for (k, n, tprime) in ss {
let k_n: [u8; 80] = unhex(&format!("{k}{n}"));
let tprime: [u8; 16] = unhex(tprime);
let mut state = CryptState::<Aes128, Aes128>::initialize(&k_n).unwrap();
*state.tag = tprime;
client.add_layer(Box::new(ClientOutbound::from(state.clone())));
individual_layers.push(ClientOutbound::from(state));
}
let ad: [u8; 1] = unhex(ad);
let msg_body: [u8; CGO_PAYLOAD_LEN] = unhex(m);
let mut msg = [0_u8; CELL_DATA_LEN];
msg[16..].copy_from_slice(&msg_body[..]);
let mut msg = RelayCellBody(Box::new(msg));
let mut msg2 = msg.clone();
client
.encrypt(ad[0].into(), &mut msg, (*hop - 1).into())
.unwrap();
{
let hop_idx = usize::from(*hop) - 1;
individual_layers[hop_idx].originate_for(ad[0].into(), &mut msg2);
for idx in (0..hop_idx).rev() {
individual_layers[idx].encrypt_outbound(ad[0].into(), &mut msg2);
}
}
assert_eq!(&msg.0[..], &msg2.0[..]);
let (ex_ss, (ex_t, ex_c)) = output;
let ex_msg: [u8; CELL_DATA_LEN] = unhex(&format!("{ex_t}{ex_c}"));
assert_eq!(&ex_msg[..], &msg.0[..]);
for (layer, (ex_k, ex_n, ex_tprime)) in individual_layers.iter().zip(ex_ss.iter()) {
let state = &layer.0;
let ex_k: [u8; 64] = unhex(ex_k);
let ex_n: [u8; 16] = unhex(ex_n);
let ex_tprime: [u8; 16] = unhex(ex_tprime);
assert_eq!(&state.uiv.keys[..], &ex_k[..]);
assert_eq!(&state.nonce[..], &ex_n[..]);
assert_eq!(&state.tag[..], &ex_tprime);
}
}
}
}