use chacha20poly1305::{
XChaCha20Poly1305,
aead::{Aead, OsRng},
};
use ed25519_dalek::Signer;
use classic_mceliece_rust::{
CRYPTO_CIPHERTEXTBYTES as KEM_CIPHERTEXT_SIZE, CRYPTO_SECRETKEYBYTES as KEM_SECRET_KEY_SIZE,
};
mod modes;
use modes::*;
pub use modes::{
DH_MODE, DH_WITH_HMAC_MODE, HMAC_MODE, KEM_MODE, KEM_WITH_DH_HYBRID_MODE, KemKeyReader,
SESSION_MODE, SESSION_WITH_KEYGEN_MODE, generate_dh_keys, generate_kem_keys,
};
mod decrypt;
pub use decrypt::*;
mod encrypt;
pub use encrypt::*;
mod extract;
use crate::ZEROED_KEY;
pub use extract::*;
const NONCE_SIZE: usize = 24;
const KEY_SIZE: usize = 32;
const SIGNATURE_SIZE: usize = 64;
pub fn generate_fingerprint() -> ([u8; 32], [u8; 32]) {
let fingerprint = ed25519_dalek::SigningKey::generate(&mut OsRng);
(
fingerprint.to_bytes(),
fingerprint.verifying_key().to_bytes(),
)
}
fn usize_to_bytes(val: usize) -> (usize, [u8; 9]) {
let mut out = [0u8; 9];
if val < 128 {
out[0] = ((val << 1) | 1) as u8;
(1, out)
} else {
match val {
128..=255 => {
out[0] = 0 << 2;
out[1] = val as u8;
(2, out)
}
256..=65_535 => {
out[0] = 1 << 2;
out[1..3].copy_from_slice(&(val as u16).to_be_bytes());
(3, out)
}
65_536..=4_294_967_295 => {
out[0] = 2 << 2;
out[1..5].copy_from_slice(&(val as u32).to_be_bytes());
(5, out)
}
_ => {
out[0] = 3 << 2;
out[1..9].copy_from_slice(&(val as u64).to_be_bytes());
(9, out)
}
}
}
}
fn bytes_to_usize(bytes: &[u8]) -> (usize, usize) {
let first_byte = bytes[0];
if (first_byte & 0b00000001) != 0 {
(1, (first_byte >> 1) as usize)
} else {
match (first_byte >> 2) & 0b00000011 {
0 => (2, bytes[1] as usize),
1 => (
3,
u16::from_be_bytes(bytes[1..3].try_into().unwrap()) as usize,
),
2 => (
5,
u32::from_be_bytes(bytes[1..5].try_into().unwrap()) as usize,
),
3 => (
9,
u64::from_be_bytes(bytes[1..9].try_into().unwrap()) as usize,
),
_ => unreachable!(),
}
}
}
fn sign(fingerprint: &[u8; 32], content: &[u8]) -> [u8; 64] {
let fingerprint = ed25519_dalek::SigningKey::from_bytes(fingerprint);
let signature = fingerprint.sign(content);
signature.to_bytes()
}
fn base_encrypt(
fingerprint: &[u8; 32],
nonce: &[u8; 24],
key: &[u8; 32],
content: &mut Vec<u8>,
) -> Result<Vec<u8>, &'static str> {
use chacha20poly1305::KeyInit;
let signature = sign(fingerprint, content);
content.extend(signature);
let content_cipher = XChaCha20Poly1305::new(key.into());
match content_cipher.encrypt(nonce.into(), content.as_ref()) {
Ok(encrypted_content) => Ok(encrypted_content),
Err(_) => Err("failed to encrypt content"),
}
}
fn verify(signature: &[u8; 64], verifier: &[u8; 32], content: &[u8]) -> Result<(), &'static str> {
if verifier == &ZEROED_KEY {
return Err("verifier cannot be all 0s");
}
let signature = ed25519_dalek::Signature::from_bytes(signature);
let verifier = match ed25519_dalek::VerifyingKey::from_bytes(verifier) {
Ok(vk) => vk,
Err(_) => return Err("failed to convert verifying key"),
};
match verifier.verify_strict(content, &signature) {
Ok(_) => Ok(()),
Err(_) => Err("failed to verify signature"),
}
}
fn base_decrypt(
verifier: Option<&[u8; 32]>,
nonce: &[u8; 24],
key: &[u8; 32],
encrypted_content: &[u8],
) -> Result<(Vec<u8>, [u8; KEY_SIZE]), &'static str> {
let content_cipher = {
use chacha20poly1305::KeyInit;
XChaCha20Poly1305::new(key.into())
};
match content_cipher.decrypt(nonce.into(), encrypted_content) {
Ok(mut content) => {
let signature = content.split_off(content.len() - SIGNATURE_SIZE);
match verifier {
Some(verifier) => {
let signature_as_bytes: [u8; SIGNATURE_SIZE] = match signature.try_into() {
Ok(v) => v,
Err(_) => return Err("failed to convert signature to bytes"),
};
verify(&signature_as_bytes, verifier, &content)?;
Ok((content, *key))
}
None => Ok((content, *key)),
}
}
Err(_) => Err("failed to decrypt content"),
}
}