use arrayref::array_ref;
use blake3::Hash;
use bytes::Bytes;
use once_cell::sync::Lazy;
use rand::prelude::*;
use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, CHACHA20_POLY1305};
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
use thiserror::Error;
pub const CLIENT_UP_KEY: &[u8; 32] = b"upload--------------------------";
pub const CLIENT_DN_KEY: &[u8; 32] = b"download------------------------";
pub fn upify_shared_secret(shared_secret: &[u8]) -> Hash {
blake3::keyed_hash(CLIENT_UP_KEY, shared_secret)
}
pub fn dnify_shared_secret(shared_secret: &[u8]) -> Hash {
blake3::keyed_hash(CLIENT_DN_KEY, shared_secret)
}
#[derive(Debug, Clone)]
pub struct NonObfsAead {
key: Arc<LessSafeKey>,
nonce: Arc<AtomicU64>,
}
static SOSISTAB_NOCRYPT: Lazy<bool> = Lazy::new(|| std::env::var("SOSISTAB_NOCRYPT").is_ok());
impl NonObfsAead {
pub fn new(key: &[u8]) -> Self {
let ubk = UnboundKey::new(&CHACHA20_POLY1305, key).unwrap();
Self {
key: Arc::new(LessSafeKey::new(ubk)),
nonce: Arc::new(AtomicU64::new(0)),
}
}
pub fn overhead() -> usize {
12 + CHACHA20_POLY1305.tag_len()
}
pub fn encrypt(&self, msg: &[u8]) -> (u64, Bytes) {
let nonce = self.nonce.fetch_add(1, Ordering::SeqCst);
let mut bnonce = [0; 12];
bnonce[..8].copy_from_slice(&nonce.to_le_bytes());
let mut output = Vec::with_capacity(msg.len() + 32);
output.extend_from_slice(msg);
if !*SOSISTAB_NOCRYPT {
self.key
.seal_in_place_append_tag(
Nonce::assume_unique_for_key(bnonce),
Aad::empty(),
&mut output,
)
.unwrap();
}
output.extend_from_slice(&bnonce);
(nonce, output.into())
}
pub fn decrypt(&self, ctext: &[u8]) -> Result<(u64, Bytes), AeadError> {
if !*SOSISTAB_NOCRYPT && ctext.len() < 8 + CHACHA20_POLY1305.tag_len() {
return Err(AeadError::BadLength);
}
let (cytext, nonce) = ctext.split_at(ctext.len() - 12);
let mut ctext = cytext.to_vec();
if !*SOSISTAB_NOCRYPT {
self.key
.open_in_place(
Nonce::try_assume_unique_for_key(nonce).unwrap(),
Aad::empty(),
&mut ctext,
)
.ok()
.ok_or(AeadError::DecryptionFailure)?;
let truncate_to = ctext.len() - CHACHA20_POLY1305.tag_len();
ctext.truncate(truncate_to);
}
let nonce = u64::from_le_bytes(*array_ref![nonce, 0, 8]);
Ok((nonce, ctext.into()))
}
}
#[derive(Debug, Clone)]
pub struct ObfsAead {
key: Arc<LessSafeKey>,
}
impl ObfsAead {
pub fn new(key: &[u8]) -> Self {
let ubk = UnboundKey::new(&CHACHA20_POLY1305, key).unwrap();
Self {
key: Arc::new(LessSafeKey::new(ubk)),
}
}
pub fn encrypt(&self, msg: &[u8]) -> Bytes {
let mut nonce = [0; 12];
rand::thread_rng().fill_bytes(&mut nonce);
let additional_padding = 128 - (msg.len().min(128));
let padding_len = additional_padding + (16 - msg.len() % 16) + rand::random::<usize>() % 16;
let mut padded_msg = Vec::with_capacity(1 + padding_len + msg.len() + 12);
padded_msg.push(padding_len as u8);
padded_msg.resize(padding_len + 1, 0xff);
padded_msg.extend_from_slice(msg);
self.key
.seal_in_place_append_tag(
Nonce::assume_unique_for_key(nonce),
Aad::empty(),
&mut padded_msg,
)
.unwrap();
padded_msg.extend_from_slice(&nonce);
padded_msg.into()
}
pub fn decrypt(&self, ctext: &[u8]) -> Result<Bytes, AeadError> {
if ctext.len() < CHACHA20_POLY1305.nonce_len() + CHACHA20_POLY1305.tag_len() {
return Err(AeadError::BadLength);
}
let (cytext, nonce) = ctext.split_at(ctext.len() - CHACHA20_POLY1305.nonce_len());
let mut ctext = cytext.to_vec();
self.key
.open_in_place(
Nonce::try_assume_unique_for_key(nonce).unwrap(),
Aad::empty(),
&mut ctext,
)
.ok()
.ok_or(AeadError::DecryptionFailure)?;
let truncate_to = ctext.len() - CHACHA20_POLY1305.tag_len();
ctext.truncate(truncate_to);
let padding_len = ctext[0] as usize;
if padding_len + 1 > ctext.len() {
return Err(AeadError::BadLength);
}
Ok(Bytes::from(ctext).slice((padding_len + 1)..))
}
}
#[derive(Error, Debug)]
pub enum AeadError {
#[error("bad ciphertext length")]
BadLength,
#[error("decryption failure")]
DecryptionFailure,
}
#[derive(Debug, Clone)]
pub struct SymmetricFromAsymmetric(x25519_dalek::PublicKey);
impl SymmetricFromAsymmetric {
pub fn new(pk: x25519_dalek::PublicKey) -> SymmetricFromAsymmetric {
SymmetricFromAsymmetric(pk)
}
pub fn generate_c2s(&self) -> [u8; 32] {
self.generate_temp_key("sosistab-2-c2s")
}
pub fn generate_s2c(&self) -> [u8; 32] {
self.generate_temp_key("sosistab-2-s2c")
}
fn generate_temp_key(&self, ctx: &str) -> [u8; 32] {
let mut key = [0u8; 32];
blake3::derive_key(ctx, self.0.as_bytes(), &mut key);
key
}
}
pub fn triple_ecdh(
my_long_sk: &x25519_dalek::StaticSecret,
my_eph_sk: &x25519_dalek::StaticSecret,
their_long_pk: &x25519_dalek::PublicKey,
their_eph_pk: &x25519_dalek::PublicKey,
) -> blake3::Hash {
let g_e_a = my_eph_sk.diffie_hellman(their_long_pk);
let g_a_e = my_long_sk.diffie_hellman(their_eph_pk);
let g_e_e = my_eph_sk.diffie_hellman(their_eph_pk);
let to_hash = {
let mut to_hash = Vec::new();
if g_e_a.as_bytes() < g_a_e.as_bytes() {
to_hash.extend_from_slice(g_e_a.as_bytes());
to_hash.extend_from_slice(g_a_e.as_bytes());
} else {
to_hash.extend_from_slice(g_a_e.as_bytes());
to_hash.extend_from_slice(g_e_a.as_bytes());
}
to_hash.extend_from_slice(g_e_e.as_bytes());
to_hash
};
blake3::hash(&to_hash)
}