use crate::error::{Error, Result};
use ed25519_dalek::pkcs8::EncodePrivateKey;
use ed25519_dalek::{SigningKey, VerifyingKey};
use pkcs8::LineEnding;
use rand::rngs::OsRng;
use zeroize::ZeroizeOnDrop;
#[derive(ZeroizeOnDrop)]
pub struct Identity {
signing_key: SigningKey,
}
impl Identity {
pub fn generate() -> Self {
let signing_key = SigningKey::generate(&mut OsRng);
Self { signing_key }
}
pub fn from_hex(private_key_hex: &str) -> Result<Self> {
let bytes = hex::decode(private_key_hex)
.map_err(|e| Error::InvalidFormat(format!("invalid private key hex: {e}")))?;
if bytes.len() != 32 {
return Err(Error::InvalidFormat(format!(
"private key must be 32 bytes, got {}",
bytes.len()
)));
}
let arr: [u8; 32] = bytes.try_into().expect("checked above");
Ok(Self {
signing_key: SigningKey::from_bytes(&arr),
})
}
pub fn private_key_hex(&self) -> String {
hex::encode(self.signing_key.to_bytes())
}
pub fn private_key_pkcs8_pem(&self) -> Result<String> {
self.signing_key
.to_pkcs8_pem(LineEnding::LF)
.map(|pem| pem.to_string())
.map_err(|e| Error::Crypto(format!("pkcs8 pem encode failed: {e}")))
}
pub fn public_key_hex(&self) -> String {
hex::encode(self.signing_key.verifying_key().to_bytes())
}
pub fn fingerprint(&self) -> String {
self.public_key_hex()
}
pub fn sign(&self, message: &str) -> String {
use ed25519_dalek::Signer;
let sig = self.signing_key.sign(message.as_bytes());
hex::encode(sig.to_bytes())
}
pub fn verify(public_key_hex: &str, message: &str, signature_hex: &str) -> Result<bool> {
use ed25519_dalek::Verifier;
let pub_bytes = hex::decode(public_key_hex)
.map_err(|e| Error::InvalidFormat(format!("invalid public key hex: {e}")))?;
let pub_arr: [u8; 32] = pub_bytes
.try_into()
.map_err(|_| Error::InvalidFormat("public key must be 32 bytes".into()))?;
let verifying_key = VerifyingKey::from_bytes(&pub_arr)
.map_err(|e| Error::Crypto(format!("invalid verifying key: {e}")))?;
let sig_bytes = hex::decode(signature_hex)
.map_err(|e| Error::InvalidFormat(format!("invalid signature hex: {e}")))?;
let sig_arr: [u8; 64] = sig_bytes
.try_into()
.map_err(|_| Error::InvalidFormat("signature must be 64 bytes".into()))?;
let signature = ed25519_dalek::Signature::from_bytes(&sig_arr);
Ok(verifying_key.verify(message.as_bytes(), &signature).is_ok())
}
}
impl std::fmt::Debug for Identity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Identity")
.field("fingerprint", &self.fingerprint())
.finish()
}
}