use chacha20poly1305::{
XChaCha20Poly1305, XNonce,
aead::{Aead, AeadCore, KeyInit, OsRng},
};
use uuid::Uuid;
use std::error::Error;
pub enum KeyAlg {
Ed25519Pair,
Blake2SMac256,
}
impl KeyAlg {
#[must_use]
pub fn as_byte(&self) -> u8 {
match self {
Self::Ed25519Pair => 0,
Self::Blake2SMac256 => 1,
}
}
}
#[allow(clippy::type_complexity)]
pub fn generate_ed25519_pair(
cipher: &XChaCha20Poly1305,
rng: &mut OsRng,
) -> Result<(Vec<u8>, [u8; 32], [u8; 32], Uuid), Box<dyn Error>> {
let signing_key = ed25519_dalek::SigningKey::generate(rng);
let verifying_key = signing_key.verifying_key().to_bytes();
let signing_key = signing_key.to_bytes();
let kid = Uuid::now_v7();
let mut kid_key = kid.as_bytes().to_vec();
let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng);
let encrypted_signing_key = match cipher.encrypt(&nonce, &signing_key[..]) {
Ok(v) => v,
Err(err) => return Err(err.to_string().into()),
};
kid_key.extend_from_slice(&verifying_key[..]);
kid_key.extend_from_slice(&nonce[..]);
kid_key.extend_from_slice(&encrypted_signing_key[..]);
Ok((kid_key, signing_key, verifying_key, kid))
}
#[allow(clippy::type_complexity)]
pub fn decrypt_ed25519_pair(
cipher: &XChaCha20Poly1305,
payload: &[u8],
) -> Result<(Uuid, [u8; 32], [u8; 32]), Box<dyn Error>> {
let kid: [u8; 16] = payload[0..16].try_into()?;
let verifying_key: [u8; 32] = payload[16..48].try_into()?;
let nonce = XNonce::from_slice(&payload[48..72]);
let signing_key = match cipher.decrypt(nonce, &payload[72..]) {
Ok(v) => v,
Err(err) => return Err(err.to_string().into()),
};
let signing_key_fixed: [u8; 32] = signing_key[0..32].try_into()?;
let kid = Uuid::from_bytes(kid);
Ok((kid, signing_key_fixed, verifying_key))
}
#[allow(clippy::type_complexity)]
pub fn generate_256_bit_key(
cipher: &XChaCha20Poly1305,
rng: &mut OsRng,
) -> Result<(Vec<u8>, [u8; 32], Uuid), Box<dyn Error>> {
let hmac_key: [u8; 32] = XChaCha20Poly1305::generate_key(rng).into();
let kid = Uuid::now_v7();
let mut kid_key = kid.as_bytes().to_vec();
let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng);
let encrypted_hmac_key = match cipher.encrypt(&nonce, &hmac_key[..]) {
Ok(v) => v,
Err(err) => return Err(err.to_string().into()),
};
kid_key.extend_from_slice(&nonce[..]);
kid_key.extend_from_slice(&encrypted_hmac_key[..]);
Ok((kid_key, hmac_key, kid))
}
pub fn decrypt_256_bit_key(
cipher: &XChaCha20Poly1305,
payload: &[u8],
) -> Result<(Uuid, [u8; 32]), Box<dyn Error>> {
let kid: [u8; 16] = payload[0..16].try_into()?;
let nonce = XNonce::from_slice(&payload[16..40]);
let key = match cipher.decrypt(nonce, &payload[40..]) {
Ok(v) => v,
Err(err) => return Err(err.to_string().into()),
};
let key_fixed: [u8; 32] = key[0..32].try_into()?;
let kid = Uuid::from_bytes(kid);
Ok((kid, key_fixed))
}
#[cfg(test)]
mod test {
use super::*;
const ENCRYPTION_KEY: [u8; 32] = [0u8; 32];
#[test]
fn ed25519() -> Result<(), Box<dyn Error>> {
let mut rng = OsRng;
let cipher = XChaCha20Poly1305::new(&ENCRYPTION_KEY.into());
let (encrypted, signing_key, verifying_key, generated_kid) =
generate_ed25519_pair(&cipher, &mut rng)?;
let (kid, decrypted_signing, verifying) = decrypt_ed25519_pair(&cipher, &encrypted)?;
assert_eq!(kid, generated_kid);
assert_eq!(signing_key, decrypted_signing);
assert_eq!(verifying_key, verifying);
Ok(())
}
#[test]
fn a_256_bit() -> Result<(), Box<dyn Error>> {
let mut rng = OsRng;
let cipher = XChaCha20Poly1305::new(&ENCRYPTION_KEY.into());
let (encrypted, key, generated_kid) = generate_256_bit_key(&cipher, &mut rng)?;
let (kid, decrypted) = decrypt_256_bit_key(&cipher, &encrypted)?;
assert_eq!(kid, generated_kid);
assert_eq!(key, decrypted);
Ok(())
}
}