Outfox and Lion (#2730)
This commit is contained in:
@@ -76,6 +76,7 @@ members = [
|
||||
"service-providers/network-statistics",
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-outfox",
|
||||
"tools/nym-cli",
|
||||
"tools/ts-rs-cli"
|
||||
]
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "nym-outfox"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rayon = "1.5.1"
|
||||
blake3 = "1.3"
|
||||
zeroize = "1.5"
|
||||
chacha20 = "0.9.0"
|
||||
curve25519-dalek = "3.2"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
# Need this star over here to pull in js into getrandom
|
||||
getrandom = { version = "*", features = ["js"] }
|
||||
thiserror = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.4"
|
||||
fastrand = "1.8"
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::lion::MIN_MESSAGE_LEN;
|
||||
use chacha20::cipher::InvalidLength;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OutFoxError {
|
||||
#[error("Lengths mismatch, expected: {expected}, got: {got}")]
|
||||
LenMismatch { expected: usize, got: usize },
|
||||
#[error("{source}")]
|
||||
ChaCha20InvalidLength {
|
||||
#[from]
|
||||
source: InvalidLength,
|
||||
},
|
||||
#[error("ChaCha20Poly1305 - Opaque error")]
|
||||
ChaCha20Poly1305Error,
|
||||
#[error("Key length must be 32 bytes")]
|
||||
InvalidKeyLength,
|
||||
#[error("Message length must be greater then {MIN_MESSAGE_LEN} bytes")]
|
||||
InvalidMessageLength,
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
//! # The `outfox` format
|
||||
//!
|
||||
//! We define a simple mix packet format geared towards simplicity and performance with the features that are
|
||||
//! specifically required by a stratified mix topology.
|
||||
//!
|
||||
//! `Outfox` assumes that all
|
||||
//! paths are the same length (no need to hide path lengths), mixes are arranged in layers and therefore
|
||||
//! know their position in a message path (no need to hide this position). These assumptions allow us
|
||||
//! to do away with some of the padding traditionally used; further we prioritize efficient computation
|
||||
//! over very low-bandwidth, as it seems the rate of public key operations is a bottleneck for mixes
|
||||
//! rather than the availablility of bandwidth.
|
||||
//!
|
||||
//! ## Overview and Parameters
|
||||
//!
|
||||
//! In a mix network with a stratified topology packets are mixed by nodes at each of the layers. Each layer
|
||||
//! 'strips' the packet from one layer of encryption, recovers the address of the mix at the next layer, and
|
||||
//! passes the decoded packet to them. An identifier per processed message is stored and checked to prevent
|
||||
//! replays of processed messages at each layer. Additional measures, such as adding delays, adding dummy
|
||||
//! traffic or dropping messages can be empued at each mix to frustrate traffic analysis.
|
||||
//!
|
||||
//!
|
||||
//! A layer of mix processing is defined by three parameters, includes in the structure [MixStageParameters]:
|
||||
//! * The `routing_information_length_bytes` (`R`) states the number of bytes representing
|
||||
//! routing information at this layer.
|
||||
//! * The `remaining_header_length_bytes` (`H`) represents the remaining bytes of the packet header.
|
||||
//! * The `payload_length_bytes` (`P`).
|
||||
//!
|
||||
//! In addition we define two system-wide constants, namely `GROUPELEMENTBYTES` (`GE`=32) and
|
||||
//! `TAGBYTES` (`T`=24).
|
||||
//!
|
||||
//! ## Packet format, decoding
|
||||
//!
|
||||
//! A mix at this layer takes in messages of length `GE+T+R+H+P`, and outputs messages of length `H+P`.
|
||||
//!
|
||||
//! An input message is processed as follows:
|
||||
//!
|
||||
//! * The input packet is parsed as a `[Pk, Tag, Header, Payload]` of length `[GE, T, R+H, P]` respectivelly.
|
||||
//! * A master key is derived by performing scalar multiplication with the mix secret 's', ie `K = s * Pk`.
|
||||
//! The master key is stored and checked for duplicates (if it is found processing ends.)
|
||||
//! * The master key is used to perform AEAD decryption of the `Header` with an IV of zeros and the `tag`. If
|
||||
//! decryption fails processing ends. Otherwise the Header is parsed as `[Routing, Next_Header]` of length
|
||||
//! `[R, H]` respectivelly. The routing data `Routing` can be used by the mix to dertermine the next mix.
|
||||
//! * Finally, the master key is used to perform lion decoding of the `Payload` into `Next_Payload`.
|
||||
//! * The output packet for the next mix is `[Next_Header, Next_Payload]`.
|
||||
//!
|
||||
//! As an AEAD we use `chacha20poly1305_ietf` and for public key operations we use `curve25519`.
|
||||
//!
|
||||
//! ## Packet encoding
|
||||
//!
|
||||
//! Encoding is
|
||||
//! performed layer by layer starting with the last hop on the route, and ending with the first. At each stage
|
||||
//! of encoding a new Secret key `Sk` and corresponding `Pk` is chosen. The layer master key for the layer is
|
||||
//! derived using the mix public key. And the master key is used to AEAD encrypt the concatenation of the
|
||||
//! routing data for the layer, and the remaining Header; separately the master key is used to lion encrypt
|
||||
//! the payload. The process is repeated for each layer (from last to first) to construct the full message.
|
||||
|
||||
use chacha20poly1305::AeadInPlace;
|
||||
use chacha20poly1305::ChaCha20Poly1305;
|
||||
use chacha20poly1305::KeyInit;
|
||||
|
||||
use chacha20poly1305::Tag;
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
||||
use curve25519_dalek::montgomery::MontgomeryPoint;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
const GROUPELEMENTBYTES: usize = 32;
|
||||
const TAGBYTES: usize = 16;
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::error::OutFoxError;
|
||||
use crate::lion::*;
|
||||
|
||||
/// A structure that holds mix packet construction parameters. These incluse the length
|
||||
/// of the routing information at each hop, the number of hops, and the payload length.
|
||||
pub struct MixCreationParameters {
|
||||
/// The routing length is inner first, so \[0\] is the innermost routing length, etc (in bytes)
|
||||
pub routing_information_length_by_stage: Vec<usize>,
|
||||
/// The payload length (in bytes)
|
||||
pub payload_length_bytes: usize,
|
||||
}
|
||||
|
||||
impl MixCreationParameters {
|
||||
/// Create a set of parameters for a mix packet format.
|
||||
pub fn new(payload_length_bytes: usize) -> MixCreationParameters {
|
||||
MixCreationParameters {
|
||||
routing_information_length_by_stage: Vec::new(),
|
||||
payload_length_bytes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add another outer layer containing some byte length of routing data.
|
||||
pub fn add_outer_layer(&mut self, routing_information_length_bytes: usize) {
|
||||
self.routing_information_length_by_stage
|
||||
.push(routing_information_length_bytes);
|
||||
}
|
||||
|
||||
/// The length of the buffer needed to build a packet.
|
||||
pub fn total_packet_length(&self) -> usize {
|
||||
let mut len = self.payload_length_bytes;
|
||||
for stage_len in &self.routing_information_length_by_stage {
|
||||
len += stage_len + GROUPELEMENTBYTES + TAGBYTES
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
/// Get the mix packet parameters for a single stage of mixing.
|
||||
pub fn get_stage_params(&self, layer_number: usize) -> (Range<usize>, MixStageParameters) {
|
||||
assert!(layer_number < self.routing_information_length_by_stage.len());
|
||||
|
||||
let mut remaining_header_length_bytes = 0;
|
||||
for (i, stage_len) in self.routing_information_length_by_stage.iter().enumerate() {
|
||||
if i == layer_number {
|
||||
let params = MixStageParameters {
|
||||
routing_information_length_bytes: *stage_len,
|
||||
remaining_header_length_bytes,
|
||||
payload_length_bytes: self.payload_length_bytes,
|
||||
};
|
||||
|
||||
let total_size = self.total_packet_length();
|
||||
let inner_size = params.incoming_packet_length();
|
||||
|
||||
return (total_size - inner_size..total_size, params);
|
||||
} else {
|
||||
remaining_header_length_bytes += stage_len + GROUPELEMENTBYTES + TAGBYTES;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure representing the parameters of a single stage of mixing.
|
||||
pub struct MixStageParameters {
|
||||
/// The routing information length for this stage of mixing
|
||||
pub routing_information_length_bytes: usize,
|
||||
/// The reamining header length for this stage of mixing
|
||||
pub remaining_header_length_bytes: usize,
|
||||
/// The payload length
|
||||
pub payload_length_bytes: usize,
|
||||
}
|
||||
|
||||
impl MixStageParameters {
|
||||
pub fn incoming_packet_length(&self) -> usize {
|
||||
GROUPELEMENTBYTES + TAGBYTES + self.outgoing_packet_length()
|
||||
}
|
||||
|
||||
pub fn outgoing_packet_length(&self) -> usize {
|
||||
self.routing_information_length_bytes
|
||||
+ self.remaining_header_length_bytes
|
||||
+ self.payload_length_bytes
|
||||
}
|
||||
|
||||
pub fn pub_element_range(&self) -> Range<usize> {
|
||||
0..GROUPELEMENTBYTES
|
||||
}
|
||||
|
||||
pub fn tag_range(&self) -> Range<usize> {
|
||||
GROUPELEMENTBYTES..GROUPELEMENTBYTES + TAGBYTES
|
||||
}
|
||||
|
||||
pub fn routing_data_range(&self) -> Range<usize> {
|
||||
GROUPELEMENTBYTES + TAGBYTES
|
||||
..GROUPELEMENTBYTES + TAGBYTES + self.routing_information_length_bytes
|
||||
}
|
||||
|
||||
pub fn header_range(&self) -> Range<usize> {
|
||||
GROUPELEMENTBYTES + TAGBYTES
|
||||
..GROUPELEMENTBYTES
|
||||
+ TAGBYTES
|
||||
+ self.routing_information_length_bytes
|
||||
+ self.remaining_header_length_bytes
|
||||
}
|
||||
|
||||
pub fn payload_range(&self) -> Range<usize> {
|
||||
self.incoming_packet_length() - self.payload_length_bytes..self.incoming_packet_length()
|
||||
}
|
||||
|
||||
pub fn encode_mix_layer(
|
||||
&self,
|
||||
buffer: &mut [u8],
|
||||
user_secret_key: &Scalar,
|
||||
mix_public_key: &MontgomeryPoint,
|
||||
routing_data: &[u8],
|
||||
) -> Result<MontgomeryPoint, OutFoxError> {
|
||||
if buffer.len() != self.incoming_packet_length() {
|
||||
return Err(OutFoxError::LenMismatch {
|
||||
expected: buffer.len(),
|
||||
got: self.incoming_packet_length(),
|
||||
});
|
||||
}
|
||||
|
||||
if routing_data.len() != self.routing_information_length_bytes {
|
||||
return Err(OutFoxError::LenMismatch {
|
||||
expected: routing_data.len(),
|
||||
got: self.routing_information_length_bytes,
|
||||
});
|
||||
}
|
||||
|
||||
let user_public_key = (&ED25519_BASEPOINT_TABLE * user_secret_key).to_montgomery();
|
||||
let shared_key = user_secret_key * mix_public_key;
|
||||
|
||||
// Copy rounting data into buffer
|
||||
buffer[self.routing_data_range()].copy_from_slice(routing_data);
|
||||
|
||||
// Perform the AEAD
|
||||
let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key.0[..])?;
|
||||
// TODO: Should this be all 0s?
|
||||
let nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
let tag = header_aead_key
|
||||
.encrypt_in_place_detached(&nonce.into(), &[], &mut buffer[self.header_range()])
|
||||
.map_err(|_| OutFoxError::ChaCha20Poly1305Error)?;
|
||||
|
||||
// Copy Tag into buffer
|
||||
buffer[self.tag_range()].copy_from_slice(&tag[..]);
|
||||
|
||||
// Copy own public key into buffer
|
||||
buffer[self.pub_element_range()].copy_from_slice(&user_public_key.0[..]);
|
||||
|
||||
// Do a round of LION on the payload
|
||||
lion_transform_encrypt(&mut buffer[self.payload_range()], &shared_key.0)?;
|
||||
|
||||
Ok(shared_key)
|
||||
}
|
||||
|
||||
pub fn decode_mix_layer(
|
||||
&self,
|
||||
buffer: &mut [u8],
|
||||
mix_secret_key: &Scalar,
|
||||
) -> Result<MontgomeryPoint, OutFoxError> {
|
||||
// Check the length of the incoming buffer is correct.
|
||||
if buffer.len() != self.incoming_packet_length() {
|
||||
return Err(OutFoxError::LenMismatch {
|
||||
expected: buffer.len(),
|
||||
got: self.incoming_packet_length(),
|
||||
});
|
||||
}
|
||||
|
||||
// Derive the shared key for this packet
|
||||
let user_public_key = MontgomeryPoint(buffer[self.pub_element_range()].try_into().unwrap());
|
||||
let shared_key = mix_secret_key * user_public_key;
|
||||
|
||||
// Compute the AEAD and check the Tag, if wrong return Err
|
||||
let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key.0[..]).unwrap();
|
||||
let nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
let tag_bytes = buffer[self.tag_range()].to_vec();
|
||||
let tag = Tag::from_slice(&tag_bytes);
|
||||
|
||||
header_aead_key
|
||||
.decrypt_in_place_detached(
|
||||
&nonce.into(),
|
||||
&[],
|
||||
&mut buffer[self.header_range()],
|
||||
tag.as_slice().try_into().unwrap(),
|
||||
)
|
||||
.map_err(|_| OutFoxError::ChaCha20Poly1305Error)?;
|
||||
|
||||
// Do a round of LION on the payload
|
||||
lion_transform_decrypt(&mut buffer[self.payload_range()], &shared_key.0)?;
|
||||
|
||||
Ok(shared_key)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod error;
|
||||
pub mod format;
|
||||
pub mod lion;
|
||||
@@ -0,0 +1,114 @@
|
||||
//! # The lion all-or-nothing transform
|
||||
//!
|
||||
//! The lion transform implements a keyed permutation (block cipher) with a
|
||||
//! variable length block size. It takes a key of 32 bytes, and a message of
|
||||
//! length >= 48 bytes.
|
||||
//!
|
||||
//! The cryptographic primitives used to implement the transform are a
|
||||
//! stream cipher `PRF1(IV, KEY)` (using `crypto_stream_xsalsa20`), a message authentication
|
||||
//! code `PRF2(MSG, KEY)` (using `HMAC-SHA-512-256`) and a key derivation function `KDF(KEY, ID)`
|
||||
//! (using `Blake2b`).
|
||||
//!
|
||||
//! The message to encode is split into two parts `M = [L0, R0]`, where L is 24 bytes, and
|
||||
//! R is the remaining of the message.
|
||||
//!
|
||||
//! Encoding then proceeds in 3 steps:
|
||||
//! * `R1 = PRF1(L0, KDF(key, subkey_0)) XOR R0;`
|
||||
//! * `L1 = PRF2(R1, KDF(key, subkey_1)) XOR L0;`
|
||||
//! * `R2 = PRF1(L1, KDF(key, subkey_2)) XOR R1;`
|
||||
//!
|
||||
//! The output of the transform is the concatenated byte string `M' = [L1, R2]` which has the same
|
||||
//! length as the original message.
|
||||
//!
|
||||
//! ## Manual key schedule.
|
||||
//!
|
||||
//! If you just want to encode / decode using lion as a wide-block block cipher simply use the
|
||||
//! [lion_transform_encrypt] and [lion_transform_decrypt] functions.
|
||||
//!
|
||||
//! If you know what you are doing you can determine your own key schedule for the transform. The
|
||||
//! key schedule for encypt and decrypt are [1, 2, 3] and [3, 2, 1] respectivelly. You may define
|
||||
//! a key schedule that is symmetric (such as [1, 2, 1]) to build a transform T(k,m) that has the
|
||||
//! property m = T(k, T(k, m)).
|
||||
|
||||
use chacha20::cipher::{KeyIvInit, StreamCipher};
|
||||
use chacha20::Key;
|
||||
use chacha20::XChaCha20;
|
||||
use chacha20::XNonce;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::error::OutFoxError;
|
||||
|
||||
pub const MIN_MESSAGE_LEN: usize = 24 * 2;
|
||||
const CONTEXT: &str = "LIONKEYS";
|
||||
const TAG_LEN: usize = 24;
|
||||
|
||||
/// The lion transform encryption function.
|
||||
///
|
||||
/// The `key` must be 32 bytes, and the `message` >= 48. The message is
|
||||
/// mutated to the encrypted message.
|
||||
pub fn lion_transform_encrypt(message: &mut [u8], key: &[u8]) -> Result<(), OutFoxError> {
|
||||
lion_transform(message, key, [1, 2, 3])
|
||||
}
|
||||
|
||||
/// The lion transform decryption function.
|
||||
///
|
||||
/// The `key` must be 32 bytes, and the `message` >= 48. The message
|
||||
/// is mutated to the decrypted message.
|
||||
pub fn lion_transform_decrypt(message: &mut [u8], key: &[u8]) -> Result<(), OutFoxError> {
|
||||
lion_transform(message, key, [3, 2, 1])
|
||||
}
|
||||
|
||||
/// The core of the lion transform function, that takes a message and a key,
|
||||
/// and applies the all-or-nothing transform. The key schedule represents the
|
||||
/// values of the 3 subkeys used by the 3 phases of the transform.
|
||||
///
|
||||
/// The `key` must be 32 bytes, and the `message` >= 48.
|
||||
///
|
||||
/// Unless you know what you are doing use [lion_transform_encrypt] and
|
||||
/// [lion_transform_decrypt] instead.
|
||||
pub fn lion_transform(
|
||||
message: &mut [u8],
|
||||
key: &[u8],
|
||||
key_schedule: [u64; 3],
|
||||
) -> Result<(), OutFoxError> {
|
||||
if key.len() != 32 {
|
||||
return Err(OutFoxError::InvalidKeyLength);
|
||||
}
|
||||
|
||||
if message.len() < MIN_MESSAGE_LEN {
|
||||
return Err(OutFoxError::InvalidMessageLength);
|
||||
}
|
||||
|
||||
// Stage 1: Use stream cipher with Nonce from left size, to xor to the right side
|
||||
let mut derived_key = blake3::derive_key(&format!("{}{}", CONTEXT, key_schedule[0]), key);
|
||||
let lion_stage_1_key = Key::from_slice(&derived_key);
|
||||
let left_short_message = XNonce::from_slice(&message[..TAG_LEN]);
|
||||
|
||||
let mut cipher = XChaCha20::new(lion_stage_1_key, left_short_message);
|
||||
cipher.apply_keystream(&mut message[TAG_LEN..]);
|
||||
|
||||
// Stage 2: Use HMAC of right size, and xor to the left side
|
||||
derived_key = blake3::derive_key(&format!("{}{}", CONTEXT, key_schedule[1]), key);
|
||||
|
||||
let mac = blake3::keyed_hash(&derived_key, &message[TAG_LEN..]);
|
||||
let tag_to_xor = mac.as_bytes();
|
||||
|
||||
// Xor resulting HMAC into the left (short) message
|
||||
for i in 0..TAG_LEN {
|
||||
message[i] ^= tag_to_xor[i];
|
||||
}
|
||||
|
||||
// Stage 3: (same as 1)
|
||||
derived_key = blake3::derive_key(&format!("{}{}", CONTEXT, key_schedule[2]), key);
|
||||
let lion_stage_3_key = Key::from_slice(&derived_key);
|
||||
let left_short_message_final = XNonce::from_slice(&message[..TAG_LEN]);
|
||||
|
||||
let mut cipher = XChaCha20::new(lion_stage_3_key, left_short_message_final);
|
||||
|
||||
cipher.apply_keystream(&mut message[TAG_LEN..]);
|
||||
|
||||
// clean up temp key
|
||||
derived_key.zeroize();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
extern crate nym_outfox;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use nym_outfox::format::*;
|
||||
use nym_outfox::lion::*;
|
||||
|
||||
use std::iter::repeat_with;
|
||||
|
||||
pub fn randombytes(n: usize) -> Vec<u8> {
|
||||
repeat_with(|| fastrand::u8(..)).take(n).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_decode() {
|
||||
let mix_params = MixStageParameters {
|
||||
routing_information_length_bytes: 32,
|
||||
remaining_header_length_bytes: (32 + 16 + 32) * 4,
|
||||
payload_length_bytes: 1024, // 1kb
|
||||
};
|
||||
|
||||
let user_secret_bytes = randombytes(32);
|
||||
let mix_secret_bytes = randombytes(32);
|
||||
|
||||
let user_secret = Scalar::from_bytes_mod_order(user_secret_bytes.try_into().unwrap());
|
||||
let mix_secret = Scalar::from_bytes_mod_order(mix_secret_bytes.try_into().unwrap());
|
||||
let mix_public_key = (&ED25519_BASEPOINT_TABLE * &mix_secret).to_montgomery();
|
||||
|
||||
let routing = [0; 32];
|
||||
|
||||
let buffer = randombytes(mix_params.incoming_packet_length());
|
||||
|
||||
let mut new_buffer = buffer.clone();
|
||||
|
||||
let _ = mix_params
|
||||
.encode_mix_layer(
|
||||
&mut new_buffer[..],
|
||||
&user_secret,
|
||||
&mix_public_key,
|
||||
&routing[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(&new_buffer[mix_params.payload_range()] != &buffer[mix_params.payload_range()]);
|
||||
assert!(&new_buffer[mix_params.routing_data_range()] != &routing[..]);
|
||||
|
||||
let _ = mix_params
|
||||
.decode_mix_layer(&mut new_buffer[..], &mix_secret)
|
||||
.unwrap();
|
||||
|
||||
assert!(&new_buffer[mix_params.payload_range()] == &buffer[mix_params.payload_range()]);
|
||||
assert!(&new_buffer[mix_params.routing_data_range()] == &routing[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lion() {
|
||||
let key = randombytes(32);
|
||||
let message = randombytes(1024);
|
||||
|
||||
let mut message_clone = message.clone();
|
||||
lion_transform(&mut message_clone[..], &key, [1, 2, 3]).unwrap();
|
||||
assert!(&message_clone[..] != &message[..]);
|
||||
|
||||
let mut message_clone_2 = message.clone();
|
||||
lion_transform_encrypt(&mut message_clone_2, &key).unwrap();
|
||||
assert_eq!(message_clone_2, message_clone);
|
||||
|
||||
lion_transform(&mut message_clone[..], &key[..], [3, 2, 1]).unwrap();
|
||||
assert!(&message_clone[..] == &message[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_packet_params() {
|
||||
// Dummy keys -- we will use the same key for each layer
|
||||
let user_secret_bytes = randombytes(32);
|
||||
let mix_secret_bytes = randombytes(32);
|
||||
|
||||
let user_secret = Scalar::from_bytes_mod_order(user_secret_bytes.try_into().unwrap());
|
||||
let mix_secret = Scalar::from_bytes_mod_order(mix_secret_bytes.try_into().unwrap());
|
||||
let mix_public_key = (&ED25519_BASEPOINT_TABLE * &mix_secret).to_montgomery();
|
||||
|
||||
let routing = [0; 32];
|
||||
|
||||
let mut params = MixCreationParameters::new(1025);
|
||||
params.add_outer_layer(32);
|
||||
params.add_outer_layer(32);
|
||||
params.add_outer_layer(32);
|
||||
|
||||
let mut buf = vec![0; params.total_packet_length()];
|
||||
|
||||
let (range0, layer_params0) = params.get_stage_params(0);
|
||||
let _ = layer_params0
|
||||
.encode_mix_layer(
|
||||
&mut buf[range0.clone()],
|
||||
&user_secret,
|
||||
&mix_public_key,
|
||||
&routing[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (range1, layer_params1) = params.get_stage_params(1);
|
||||
let _ = layer_params1
|
||||
.encode_mix_layer(
|
||||
&mut buf[range1.clone()],
|
||||
&user_secret,
|
||||
&mix_public_key,
|
||||
&routing[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (range2, layer_params2) = params.get_stage_params(2);
|
||||
let _ = layer_params2
|
||||
.encode_mix_layer(
|
||||
&mut buf[range2.clone()],
|
||||
&user_secret,
|
||||
&mix_public_key,
|
||||
&routing[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
&buf[params.total_packet_length() - 1025..params.total_packet_length()] != [0; 1025]
|
||||
);
|
||||
|
||||
let _ = layer_params2
|
||||
.decode_mix_layer(&mut buf[range2], &mix_secret)
|
||||
.unwrap();
|
||||
|
||||
let _ = layer_params1
|
||||
.decode_mix_layer(&mut buf[range1], &mix_secret)
|
||||
.unwrap();
|
||||
|
||||
let _ = layer_params0
|
||||
.decode_mix_layer(&mut buf[range0], &mix_secret)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user