// In crates/libcryptoa/src/lib.rs use std::str::FromStr; use age::{Decryptor, Encryptor, x25519}; use base64::{engine::general_purpose::STANDARD as B64, Engine as _}; use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; use secrecy::ExposeSecret; use thiserror::Error; #[derive(Error, Debug)] pub enum AsymmetricCryptoError { #[error("key parsing failed")] ParseKey, #[error("age crypto error: {0}")] Age(String), #[error("invalid utf-8 in plaintext")] Utf8, #[error("invalid signature length")] SignatureLen, #[error("signature verification failed")] Verify, #[error("base64 decoding failed: {0}")] Base64(#[from] base64::DecodeError), #[error("io error: {0}")] Io(#[from] std::io::Error), } fn parse_recipient(s: &str) -> Result { x25519::Recipient::from_str(s).map_err(|_| AsymmetricCryptoError::ParseKey) } fn parse_identity(s: &str) -> Result { x25519::Identity::from_str(s).map_err(|_| AsymmetricCryptoError::ParseKey) } fn parse_ed25519_signing_key(s: &str) -> Result { let bytes = B64.decode(s)?; let key_bytes: [u8; 32] = bytes.try_into().map_err(|_| AsymmetricCryptoError::ParseKey)?; Ok(SigningKey::from_bytes(&key_bytes)) } fn parse_ed25519_verifying_key(s: &str) -> Result { let bytes = B64.decode(s)?; let key_bytes: [u8; 32] = bytes.try_into().map_err(|_| AsymmetricCryptoError::ParseKey)?; VerifyingKey::from_bytes(&key_bytes).map_err(|_| AsymmetricCryptoError::ParseKey) } pub fn gen_enc_keypair() -> (String, String) { let id = x25519::Identity::generate(); let pk = id.to_public(); (pk.to_string(), id.to_string().expose_secret().to_string()) } pub fn gen_sign_keypair() -> (String, String) { let signing_key = SigningKey::generate(&mut rand::rngs::OsRng); let verifying_key = signing_key.verifying_key(); (B64.encode(verifying_key.to_bytes()), B64.encode(signing_key.to_bytes())) } pub fn encrypt_b64(recipient_str: &str, msg: &str) -> Result { let recipient = parse_recipient(recipient_str)?; let encryptor = Encryptor::with_recipients(vec![Box::new(recipient)]) .ok_or_else(|| AsymmetricCryptoError::Age("Failed to create encryptor".into()))?; let mut encrypted = vec![]; let mut writer = encryptor.wrap_output(&mut encrypted)?; std::io::Write::write_all(&mut writer, msg.as_bytes())?; writer.finish()?; Ok(B64.encode(encrypted)) } pub fn decrypt_b64(identity_str: &str, ct_b64: &str) -> Result { let identity = parse_identity(identity_str)?; let ct = B64.decode(ct_b64)?; let decryptor = Decryptor::new(&ct[..]).map_err(|e| AsymmetricCryptoError::Age(e.to_string()))?; let mut decrypted = vec![]; if let Decryptor::Recipients(d) = decryptor { let mut reader = d.decrypt(std::iter::once(&identity as &dyn age::Identity)) .map_err(|e| AsymmetricCryptoError::Age(e.to_string()))?; std::io::Read::read_to_end(&mut reader, &mut decrypted)?; String::from_utf8(decrypted).map_err(|_| AsymmetricCryptoError::Utf8) } else { Err(AsymmetricCryptoError::Age("Passphrase decryption not supported".into())) } } pub fn sign_b64(signing_secret_str: &str, msg: &str) -> Result { let signing_key = parse_ed25519_signing_key(signing_secret_str)?; let signature = signing_key.sign(msg.as_bytes()); Ok(B64.encode(signature.to_bytes())) } pub fn verify_b64(verify_pub_str: &str, msg: &str, sig_b64: &str) -> Result { let verifying_key = parse_ed25519_verifying_key(verify_pub_str)?; let sig_bytes = B64.decode(sig_b64)?; let signature = Signature::from_slice(&sig_bytes).map_err(|_| AsymmetricCryptoError::SignatureLen)?; Ok(verifying_key.verify(msg.as_bytes(), &signature).is_ok()) }