use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use sha2::{Digest, Sha256};
use crate::types::{AlgoChatError, Result, SIGNATURE_SIZE};
pub const ED25519_SIGNATURE_SIZE: usize = SIGNATURE_SIZE;
pub fn sign_encryption_key(
encryption_public_key: &[u8],
signing_key: &SigningKey,
) -> Result<[u8; 64]> {
if encryption_public_key.len() != 32 {
return Err(AlgoChatError::InvalidPublicKey(format!(
"Encryption public key must be 32 bytes, got {}",
encryption_public_key.len()
)));
}
let signature = signing_key.sign(encryption_public_key);
Ok(signature.to_bytes())
}
pub fn verify_encryption_key(
encryption_public_key: &[u8],
verifying_key: &VerifyingKey,
signature: &[u8],
) -> Result<bool> {
if encryption_public_key.len() != 32 {
return Err(AlgoChatError::InvalidPublicKey(format!(
"Encryption public key must be 32 bytes, got {}",
encryption_public_key.len()
)));
}
if signature.len() != SIGNATURE_SIZE {
return Err(AlgoChatError::InvalidSignature(format!(
"Signature must be {} bytes, got {}",
SIGNATURE_SIZE,
signature.len()
)));
}
let signature_bytes: [u8; 64] = signature
.try_into()
.map_err(|_| AlgoChatError::InvalidSignature("Invalid signature bytes".into()))?;
let signature = Signature::from_bytes(&signature_bytes);
Ok(verifying_key
.verify(encryption_public_key, &signature)
.is_ok())
}
pub fn verify_encryption_key_bytes(
encryption_public_key: &[u8],
ed25519_public_key: &[u8],
signature: &[u8],
) -> Result<bool> {
if ed25519_public_key.len() != 32 {
return Err(AlgoChatError::InvalidPublicKey(format!(
"Ed25519 public key must be 32 bytes, got {}",
ed25519_public_key.len()
)));
}
let key_bytes: [u8; 32] = ed25519_public_key
.try_into()
.map_err(|_| AlgoChatError::InvalidPublicKey("Invalid public key bytes".into()))?;
let verifying_key = VerifyingKey::from_bytes(&key_bytes).map_err(|e| {
AlgoChatError::InvalidPublicKey(format!("Invalid Ed25519 public key: {}", e))
})?;
verify_encryption_key(encryption_public_key, &verifying_key, signature)
}
pub fn fingerprint(public_key: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(public_key);
let hash = hasher.finalize();
hash.iter()
.take(8)
.map(|b| format!("{:02X}", b))
.collect::<Vec<_>>()
.chunks(2)
.map(|chunk| chunk.join(""))
.collect::<Vec<_>>()
.join(" ")
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::OsRng;
#[test]
fn test_sign_and_verify() {
let signing_key = SigningKey::generate(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let encryption_key = [42u8; 32];
let signature = sign_encryption_key(&encryption_key, &signing_key).unwrap();
assert_eq!(signature.len(), 64);
let valid = verify_encryption_key(&encryption_key, &verifying_key, &signature).unwrap();
assert!(valid);
}
#[test]
fn test_verify_wrong_key() {
let signing_key = SigningKey::generate(&mut OsRng);
let wrong_key = SigningKey::generate(&mut OsRng).verifying_key();
let encryption_key = [42u8; 32];
let signature = sign_encryption_key(&encryption_key, &signing_key).unwrap();
let valid = verify_encryption_key(&encryption_key, &wrong_key, &signature).unwrap();
assert!(!valid);
}
#[test]
fn test_verify_wrong_message() {
let signing_key = SigningKey::generate(&mut OsRng);
let verifying_key = signing_key.verifying_key();
let encryption_key = [42u8; 32];
let wrong_key = [99u8; 32];
let signature = sign_encryption_key(&encryption_key, &signing_key).unwrap();
let valid = verify_encryption_key(&wrong_key, &verifying_key, &signature).unwrap();
assert!(!valid);
}
#[test]
fn test_fingerprint() {
let key = [0u8; 32];
let fp = fingerprint(&key);
assert_eq!(fp.len(), 19); assert!(fp.chars().all(|c| c.is_ascii_hexdigit() || c == ' '));
}
#[test]
fn test_invalid_key_length() {
let signing_key = SigningKey::generate(&mut OsRng);
let result = sign_encryption_key(&[0u8; 16], &signing_key);
assert!(matches!(result, Err(AlgoChatError::InvalidPublicKey(_))));
}
#[test]
fn test_invalid_signature_length() {
let verifying_key = SigningKey::generate(&mut OsRng).verifying_key();
let result = verify_encryption_key(&[0u8; 32], &verifying_key, &[0u8; 32]);
assert!(matches!(result, Err(AlgoChatError::InvalidSignature(_))));
}
}