use aes::cipher::{BlockDecrypt, BlockEncrypt};
use base64::{engine::general_purpose::STANDARD as b64, Engine};
use aead::{
generic_array::{typenum, GenericArray},
Aead, AeadCore, KeyInit,
};
use ed25519_dalek::{Signer, Verifier};
pub fn str_to_32_bytes(val: &str) -> [u8; 32] {
let result = blake3::hash(val.as_bytes());
*result.as_bytes()
}
pub fn encode_32_bytes_to_string(val: &[u8; 32]) -> String {
b64.encode(val)
}
pub fn decode_32_bytes_from_string(val: &str) -> Result<[u8; 32], &'static str> {
let decoded_val = match b64.decode(val) {
Ok(val) => val,
Err(_) => return Err("failed to decode val"),
};
let decoded_val_as_bytes: [u8; 32] = match decoded_val.try_into() {
Ok(val) => val,
Err(_) => return Err("failed to convert decoded val into fixed bytes"),
};
Ok(decoded_val_as_bytes)
}
pub fn encrypt_32_bytes(key: &[u8; 32], content: &[u8; 32]) -> Result<[u8; 32], &'static str> {
let cipher = aes::Aes256Enc::new(key.into());
let mut block_one = *GenericArray::<u8, typenum::U16>::from_slice(&content[0..16]);
let mut block_two = *GenericArray::<u8, typenum::U16>::from_slice(&content[16..32]);
cipher.encrypt_block(&mut block_one);
cipher.encrypt_block(&mut block_two);
let mut combined_array: [u8; 32] = [0u8; 32];
combined_array[0..16].copy_from_slice(&block_one);
combined_array[16..32].copy_from_slice(&block_two);
Ok(combined_array)
}
pub fn decrypt_32_bytes(
key: &[u8; 32],
encrypted_content: &[u8; 32],
) -> Result<[u8; 32], &'static str> {
let cipher = aes::Aes256Dec::new(key.into());
let mut block_one = *GenericArray::<u8, typenum::U16>::from_slice(&encrypted_content[0..16]);
let mut block_two = *GenericArray::<u8, typenum::U16>::from_slice(&encrypted_content[16..32]);
cipher.decrypt_block(&mut block_one);
cipher.decrypt_block(&mut block_two);
let mut combined_array: [u8; 32] = [0; 32];
combined_array[0..16].copy_from_slice(&block_one);
combined_array[16..32].copy_from_slice(&block_two);
Ok(combined_array)
}
pub fn aead_encrypt(key: &[u8; 32], content: &[u8]) -> Result<Vec<u8>, &'static str> {
let cipher = chacha20poly1305::XChaCha20Poly1305::new(key.into());
let nonce = chacha20poly1305::XChaCha20Poly1305::generate_nonce(&mut rand_core::OsRng);
let ciphertext = match cipher.encrypt(&nonce, content) {
Ok(ct) => ct,
Err(_) => return Err("failed to encrypt"),
};
let mut out = vec![];
out.extend(nonce);
out.extend(ciphertext);
Ok(out)
}
pub fn aead_decrypt(key: &[u8; 32], encrypted_content: &[u8]) -> Result<Vec<u8>, &'static str> {
let cipher = chacha20poly1305::XChaCha20Poly1305::new(key.into());
let nonce_as_bytes: [u8; 24] = match encrypted_content[0..24].try_into() {
Ok(v) => v,
Err(_) => return Err("failed to convert block one to fixed bytes"),
};
let nonce = chacha20poly1305::XNonce::from(nonce_as_bytes);
match cipher.decrypt(&nonce, &encrypted_content[24..]) {
Ok(out) => Ok(out),
Err(_) => Err("failed to decrypt"),
}
}
pub fn generate_fingerprint() -> ([u8; 32], [u8; 32]) {
let fingerprint = ed25519_dalek::SigningKey::generate(&mut rand_core::OsRng);
(
fingerprint.to_bytes(),
fingerprint.verifying_key().to_bytes(),
)
}
pub fn sign_content(content: &[u8], fingerprint: &[u8; 32]) -> [u8; 64] {
let fingerprint = ed25519_dalek::SigningKey::from_bytes(fingerprint);
let signature = fingerprint.sign(content);
signature.to_bytes()
}
pub fn verify_signature(
signature: &[u8; 64],
verifying_key: &[u8; 32],
content: &[u8],
) -> Result<(), &'static str> {
let signature = ed25519_dalek::Signature::from_bytes(signature);
let verifying_key = match ed25519_dalek::VerifyingKey::from_bytes(verifying_key) {
Ok(vk) => vk,
Err(_) => return Err("failed to generate verifying key from pub signing key"),
};
match verifying_key.verify(content, &signature) {
Ok(_) => Ok(()),
Err(_) => return Err("failed to verify signature for content"),
}
}
pub fn generate_exchange_keys() -> ([u8; 32], [u8; 32]) {
let priv_key = x25519_dalek::StaticSecret::random_from_rng(rand_core::OsRng);
let pub_key = x25519_dalek::PublicKey::from(&priv_key);
(*priv_key.as_bytes(), *pub_key.as_bytes())
}
pub fn encrypt_content(
fingerprint: &[u8; 32],
content: &[u8],
pub_keys: &[u8],
) -> Result<Vec<u8>, &'static str> {
if pub_keys.len() > 2_097_120 {
return Err("cannot encrypt for more than 65,535 keys");
}
let fingerprint = ed25519_dalek::SigningKey::from_bytes(fingerprint);
let mut to_be_encrypted = fingerprint.sign(content).to_vec();
to_be_encrypted.extend(content);
let nonce = chacha20poly1305::XChaCha20Poly1305::generate_nonce(&mut rand_core::OsRng);
let content_key = chacha20poly1305::XChaCha20Poly1305::generate_key(&mut rand_core::OsRng);
let content_cipher = chacha20poly1305::XChaCha20Poly1305::new(&content_key);
let encrypted_content = match content_cipher.encrypt(&nonce, to_be_encrypted.as_ref()) {
Ok(ec) => ec,
Err(_) => return Err("failed to encrypt content"),
};
let mut keys: Vec<u8> = vec![];
let (e_priv_key, e_pub_key) = generate_exchange_keys();
let e_priv_key = x25519_dalek::StaticSecret::from(e_priv_key);
let content_key_as_bytes: [u8; 32] = match content_key.try_into() {
Ok(v) => v,
Err(_) => return Err("failed to convert content key to fixed bytes"),
};
for pub_key in pub_keys.chunks(32) {
let pub_key_as_bytes: [u8; 32] = match pub_key.try_into() {
Ok(v) => v,
Err(_) => return Err("failed to convert pub key to fixed bytes"),
};
let shared_secret = e_priv_key
.diffie_hellman(&x25519_dalek::PublicKey::from(pub_key_as_bytes))
.to_bytes();
match encrypt_32_bytes(&shared_secret, &content_key_as_bytes) {
Ok(encrypted_content_key) => {
keys.extend(encrypted_content_key);
}
Err(err) => return Err(err),
};
}
let mut out = ((keys.len() / 32) as u16).to_be_bytes().to_vec();
out.extend(keys);
out.extend(e_pub_key);
out.extend(nonce);
out.extend(encrypted_content);
Ok(out)
}
pub fn extract_components_for_key_position(
encrypted_content: &[u8],
position: u16,
) -> Result<(Vec<u8>, [u8; 32]), &'static str> {
let key_header_bytes: [u8; 2] = match encrypted_content[0..2].try_into() {
Ok(b) => b,
Err(_) => return Err("failed to convert keys header to bytes"),
};
let key_count = u16::from_be_bytes(key_header_bytes) as usize;
let keys_end = key_count * 32 + 2;
let encrypted_content_key =
match encrypted_content[(2 + position as usize)..(34 + position as usize)].try_into() {
Ok(b) => b,
Err(_) => return Err("failed to convert content key to bytes"),
};
Ok((
encrypted_content[keys_end..].to_vec(),
encrypted_content_key,
))
}
pub fn decrypt_content(
verifying_key: Option<&[u8; 32]>,
priv_key: [u8; 32],
encrypted_key: &[u8; 32],
encrypted_content: &[u8],
) -> Result<Vec<u8>, &'static str> {
let pub_key: [u8; 32] = match encrypted_content[0..32].try_into() {
Ok(key) => key,
Err(_) => return Err("failed to convert pub key to fixed bytes bytes"),
};
let nonce: [u8; 24] = match encrypted_content[32..56].try_into() {
Ok(key) => key,
Err(_) => return Err("failed to convert nonce to fixed bytes bytes"),
};
let priv_key = x25519_dalek::StaticSecret::from(priv_key);
let shared_secret = priv_key.diffie_hellman(&pub_key.into()).to_bytes();
let content_key = match decrypt_32_bytes(&shared_secret, encrypted_key) {
Ok(key) => key,
Err(err) => return Err(err),
};
let content_cipher = chacha20poly1305::XChaCha20Poly1305::new(&content_key.into());
match content_cipher.decrypt(&nonce.into(), &encrypted_content[56..]) {
Ok(content) => {
let signature_as_bytes: [u8; 64] = match content[0..64].try_into() {
Ok(v) => v,
Err(_) => return Err("failed to convert signature to fixed bytes"),
};
let content = content[64..].to_vec();
match verifying_key {
Some(vk) => match verify_signature(&signature_as_bytes, vk, &content) {
Ok(_) => Ok(content),
Err(_) => Err("failed to verify signature"),
},
None => Ok(content),
}
}
Err(_) => return Err("failed to decrypt content"),
}
}