use super::{
delay::{DelaySeed, DELAY_SEED_SIZE},
packet::{Actions, KxPublic, Mac, Payload, MAX_HOPS},
};
use arrayref::array_refs;
use arrayvec::ArrayVec;
use blake2::{
digest::{
consts::{U16, U32, U64},
generic_array::{sequence::Concat, GenericArray},
FixedOutput, Mac as DigestMac,
},
Blake2bMac,
};
use c2_chacha::{
stream_cipher::{NewStreamCipher, SyncStreamCipher},
ChaCha20,
};
use curve25519_dalek::{scalar::clamp_integer, MontgomeryPoint, Scalar};
use lioness::LionessDefault;
use rand::{CryptoRng, Rng};
const KX_BLINDING_FACTOR_PERSONAL: &[u8; 16] = b"sphinx-blind-fac";
const SMALL_DERIVED_SECRETS_PERSONAL: &[u8; 16] = b"sphinx-small-d-s";
const PAYLOAD_ENCRYPTION_KEY_PERSONAL: &[u8; 16] = b"sphinx-pl-en-key";
pub const SHARED_SECRET_SIZE: usize = 32;
pub type SharedSecret = [u8; SHARED_SECRET_SIZE];
pub type KxSecret = [u8; 32];
pub fn gen_kx_secret(rng: &mut (impl Rng + CryptoRng)) -> KxSecret {
let mut secret = [0; 32];
rng.fill_bytes(&mut secret);
secret
}
pub fn derive_kx_public(kx_secret: &KxSecret) -> KxPublic {
MontgomeryPoint::mul_base_clamped(*kx_secret).to_bytes()
}
fn derive_kx_blinding_factor(kx_public: &KxPublic, kx_shared_secret: &SharedSecret) -> [u8; 32] {
let kx_public: &GenericArray<_, _> = kx_public.into();
let key = kx_public.concat((*kx_shared_secret).into());
let h = Blake2bMac::<U32>::new_with_salt_and_personal(&key, b"", KX_BLINDING_FACTOR_PERSONAL)
.expect("Key, salt, and personalisation sizes are fixed and small enough");
h.finalize().into_bytes().into()
}
pub fn blind_kx_public(kx_public: &KxPublic, kx_shared_secret: &SharedSecret) -> KxPublic {
MontgomeryPoint(*kx_public)
.mul_clamped(derive_kx_blinding_factor(kx_public, kx_shared_secret))
.to_bytes()
}
pub fn derive_kx_shared_secret(kx_public: &KxPublic, kx_secret: &KxSecret) -> SharedSecret {
MontgomeryPoint(*kx_public).mul_clamped(*kx_secret).to_bytes()
}
pub fn gen_kx_public_and_shared_secrets(
kx_public: &mut KxPublic,
kx_shared_secrets: &mut ArrayVec<SharedSecret, MAX_HOPS>,
rng: &mut (impl Rng + CryptoRng),
their_kx_publics: &[KxPublic],
) {
let kx_secret = gen_kx_secret(rng);
*kx_public = derive_kx_public(&kx_secret);
let mut kx_secret = Scalar::from_bytes_mod_order(clamp_integer(kx_secret));
let mut kx_public = *kx_public;
for (i, their_kx_public) in their_kx_publics.iter().enumerate() {
if i != 0 {
if i != 1 {
kx_public = MontgomeryPoint::mul_base(&kx_secret).to_bytes();
}
let kx_shared_secret = kx_shared_secrets.last().expect(
"On at least second iteration of loop, shared secret pushed every iteration",
);
kx_secret *= Scalar::from_bytes_mod_order(clamp_integer(derive_kx_blinding_factor(
&kx_public,
kx_shared_secret,
)));
}
kx_shared_secrets.push((MontgomeryPoint(*their_kx_public) * kx_secret).to_bytes());
}
}
fn derive_secret(derived: &mut [u8], shared_secret: &SharedSecret, personal: &[u8; 16]) {
for (i, chunk) in derived.chunks_mut(64).enumerate() {
let h = Blake2bMac::<U64>::new_with_salt_and_personal(
shared_secret,
&i.to_le_bytes(),
personal,
)
.expect("Key, salt, and personalisation sizes are fixed and small enough");
h.finalize_into(GenericArray::from_mut_slice(chunk));
}
}
const MAC_KEY_SIZE: usize = 16;
pub type MacKey = [u8; MAC_KEY_SIZE];
const ACTIONS_ENCRYPTION_KEY_SIZE: usize = 32;
pub type ActionsEncryptionKey = [u8; ACTIONS_ENCRYPTION_KEY_SIZE];
const SMALL_DERIVED_SECRETS_SIZE: usize =
MAC_KEY_SIZE + ACTIONS_ENCRYPTION_KEY_SIZE + DELAY_SEED_SIZE;
pub struct SmallDerivedSecrets([u8; SMALL_DERIVED_SECRETS_SIZE]);
impl SmallDerivedSecrets {
pub fn new(shared_secret: &SharedSecret) -> Self {
let mut derived = [0; SMALL_DERIVED_SECRETS_SIZE];
derive_secret(&mut derived, shared_secret, SMALL_DERIVED_SECRETS_PERSONAL);
Self(derived)
}
fn split(&self) -> (&MacKey, &ActionsEncryptionKey, &DelaySeed) {
array_refs![&self.0, MAC_KEY_SIZE, ACTIONS_ENCRYPTION_KEY_SIZE, DELAY_SEED_SIZE]
}
pub fn mac_key(&self) -> &MacKey {
self.split().0
}
pub fn actions_encryption_key(&self) -> &ActionsEncryptionKey {
self.split().1
}
pub fn delay_seed(&self) -> &DelaySeed {
self.split().2
}
}
pub const PAYLOAD_ENCRYPTION_KEY_SIZE: usize = 192;
pub type PayloadEncryptionKey = [u8; PAYLOAD_ENCRYPTION_KEY_SIZE];
pub fn derive_payload_encryption_key(shared_secret: &SharedSecret) -> PayloadEncryptionKey {
let mut derived = [0; PAYLOAD_ENCRYPTION_KEY_SIZE];
derive_secret(&mut derived, shared_secret, PAYLOAD_ENCRYPTION_KEY_PERSONAL);
derived
}
pub fn compute_mac(actions: &[u8], pad: &[u8], key: &MacKey) -> Mac {
let mut h = Blake2bMac::<U16>::new_from_slice(key).expect("Key size is fixed and small enough");
h.update(actions);
h.update(pad);
h.finalize().into_bytes().into()
}
pub fn mac_ok(mac: &Mac, actions: &Actions, key: &MacKey) -> bool {
let mut h = Blake2bMac::<U16>::new_from_slice(key).expect("Key size is fixed and small enough");
h.update(actions);
h.verify(mac.into()).is_ok()
}
pub fn apply_actions_encryption_keystream(data: &mut [u8], key: &ActionsEncryptionKey) {
let mut c = ChaCha20::new(key.into(), &[0; 8].into());
c.apply_keystream(data);
}
pub fn apply_keystream(data: &mut [u8], keystream: &[u8]) {
for (d, k) in data.iter_mut().zip(keystream) {
*d ^= *k;
}
}
pub fn encrypt_payload(payload: &mut Payload, key: &PayloadEncryptionKey) {
let l = LionessDefault::new_raw(key);
l.encrypt(payload).expect("Payload size is fixed and large enough");
}
pub fn decrypt_payload(payload: &mut Payload, key: &PayloadEncryptionKey) {
let l = LionessDefault::new_raw(key);
l.decrypt(payload).expect("Payload size is fixed and large enough");
}