use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey, Signature as DalekSignature};
use rand::rngs::OsRng;
use sha2::{Sha256, Digest};
use alloc::vec::Vec;
use alloc::string::{String, ToString};
use core::convert::TryInto;
use crate::error::{Error, Result};
use crate::format::Module;
pub const PUBLIC_KEY_SIZE: usize = 32;
pub const SECRET_KEY_SIZE: usize = 32;
pub const SIGNATURE_SIZE: usize = 64;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PublicKey {
inner: VerifyingKey,
}
impl PublicKey {
pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_SIZE]) -> Result<Self> {
VerifyingKey::from_bytes(bytes)
.map(|inner| Self { inner })
.map_err(|e| Error::crypto(format!("invalid public key: {}", e)))
}
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
self.inner.to_bytes()
}
pub fn fingerprint(&self) -> [u8; 16] {
let mut hasher = Sha256::new();
hasher.update(self.to_bytes());
let result = hasher.finalize();
let mut fp = [0u8; 16];
fp.copy_from_slice(&result[..16]);
fp
}
pub fn fingerprint_hex(&self) -> String {
hex::encode(self.fingerprint())
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<()> {
self.inner.verify(message, &signature.inner)
.map_err(|_| Error::VerificationFailed {
details: "signature verification failed".into(),
})
}
}
impl AsRef<VerifyingKey> for PublicKey {
fn as_ref(&self) -> &VerifyingKey {
&self.inner
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Signature {
inner: DalekSignature,
}
impl Signature {
pub fn from_bytes(bytes: &[u8; SIGNATURE_SIZE]) -> Result<Self> {
Ok(Self {
inner: DalekSignature::from_bytes(bytes),
})
}
pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] {
self.inner.to_bytes()
}
}
#[derive(Debug)]
pub struct KeyPair {
signing: SigningKey,
}
impl KeyPair {
pub fn generate() -> Self {
let mut csprng = OsRng;
let signing = SigningKey::generate(&mut csprng);
Self { signing }
}
pub fn from_seed(seed: &[u8; SECRET_KEY_SIZE]) -> Self {
let signing = SigningKey::from_bytes(seed);
Self { signing }
}
pub fn to_seed(&self) -> [u8; SECRET_KEY_SIZE] {
self.signing.to_bytes()
}
pub fn public(&self) -> PublicKey {
PublicKey {
inner: self.signing.verifying_key(),
}
}
pub fn sign(&self, message: &[u8]) -> Signature {
Signature {
inner: self.signing.sign(message),
}
}
pub fn sign_module(&self, module: &mut Module) {
let signature = self.sign(&module.code);
module.signature = signature.to_bytes();
module.public_key = self.public().to_bytes();
module.header.flags |= format::FLAG_SIGNED;
}
pub fn verify_module(module: &Module) -> Result<()> {
if module.header.flags & format::FLAG_SIGNED == 0 {
return Err(Error::VerificationFailed {
details: "module is not signed".into(),
});
}
let public_key = PublicKey::from_bytes(&module.public_key)?;
let signature = Signature::from_bytes(&module.signature)?;
public_key.verify(&module.code, &signature)
}
}
impl Clone for KeyPair {
fn clone(&self) -> Self {
Self::from_seed(&self.to_seed())
}
}
use crate::format;
#[cfg(feature = "std")]
pub fn parse_key(source: &str) -> Result<KeyPair> {
use std::fs;
use std::path::Path;
if Path::new(source).exists() {
let contents = fs::read_to_string(source)
.map_err(|e| Error::crypto(format!("failed to read key file: {}", e)))?;
return parse_key_hex(&contents);
}
parse_key_hex(source)
}
fn parse_key_hex(hex_str: &str) -> Result<KeyPair> {
let cleaned: Vec<u8> = hex_str.chars()
.filter(|c| c.is_ascii_hexdigit())
.collect::<String>()
.as_bytes()
.chunks(2)
.map(|chunk| {
u8::from_str_radix(std::str::from_utf8(chunk).unwrap(), 16).unwrap()
})
.collect();
if cleaned.len() != SECRET_KEY_SIZE {
return Err(Error::crypto(format!(
"invalid key length: expected {} bytes, got {}",
SECRET_KEY_SIZE, cleaned.len()
)));
}
let mut seed = [0u8; SECRET_KEY_SIZE];
seed.copy_from_slice(&cleaned);
Ok(KeyPair::from_seed(&seed))
}