use wolfhsm_sys::{wolfhsm_ed25519_make_key, wolfhsm_ed25519_sign, wolfhsm_ed25519_verify};
use crate::client::Client;
use crate::error::Error;
use crate::key::{with_key, KeyId};
pub struct Ed25519Key {
pub(crate) id: KeyId,
}
impl Ed25519Key {
pub(crate) fn generate(client: &mut Client) -> Result<Self, Error> {
let mut key_id: u16 = KeyId::ERASED.0;
let rc = unsafe { wolfhsm_ed25519_make_key(client.ctx_ptr(), &mut key_id) };
Error::check(rc, "wolfhsm_ed25519_make_key")?;
if key_id == KeyId::ERASED.0 {
return Err(Error::ProtocolError {
msg: "wolfhsm_ed25519_make_key: server returned WH_KEYID_ERASED (0)",
});
}
Ok(Ed25519Key { id: KeyId(key_id) })
}
pub fn public_key(&self, client: &mut Client) -> Result<[u8; 32], Error> {
let mut buf = [0u8; 32];
let rc = unsafe {
wolfhsm_sys::wolfhsm_ed25519_export_public(
client.ctx_ptr(),
self.id.0,
buf.as_mut_ptr(),
)
};
Error::check(rc, "wolfhsm_ed25519_export_public")?;
Ok(buf)
}
pub fn sign(&self, client: &mut Client, msg: &[u8]) -> Result<[u8; 64], Error> {
let msg_len = u32::try_from(msg.len()).map_err(|_| Error::InvalidInput {
msg: "message exceeds u32::MAX bytes",
})?;
let mut buf = [0u8; 64];
let mut sig_len: u32 = 64;
let rc = unsafe {
wolfhsm_ed25519_sign(
client.ctx_ptr(),
self.id.0,
msg.as_ptr(),
msg_len,
buf.as_mut_ptr(),
&mut sig_len,
)
};
Error::check(rc, "wolfhsm_ed25519_sign")?;
if sig_len != 64 {
return Err(Error::ProtocolError {
msg: "wolfhsm_ed25519_sign: unexpected signature length",
});
}
Ok(buf)
}
pub fn verify(
&self,
client: &mut Client,
msg: &[u8],
sig: &[u8; 64],
) -> Result<(), Error> {
let msg_len = u32::try_from(msg.len()).map_err(|_| Error::InvalidInput {
msg: "message exceeds u32::MAX bytes",
})?;
let mut result: core::ffi::c_int = 0;
let rc = unsafe {
wolfhsm_ed25519_verify(
client.ctx_ptr(),
self.id.0,
sig.as_ptr(),
64u32,
msg.as_ptr(),
msg_len,
&mut result,
)
};
Error::check(rc, "wolfhsm_ed25519_verify")?;
if result != 1 {
return Err(Error::SignatureInvalid);
}
Ok(())
}
}
impl Drop for Ed25519Key {
fn drop(&mut self) {
if self.id != KeyId::ERASED {
log::warn!(
"wolfhsm: Ed25519Key (id={}) dropped without eviction — \
HSM cache slot leaked. Use with_ed25519_key().",
self.id.0
);
}
}
}
impl Client {
pub fn with_ed25519_key<F, R>(&mut self, f: F) -> Result<R, Error>
where
F: FnOnce(&Ed25519Key, &mut Client) -> Result<R, Error>,
{
let key = Ed25519Key::generate(self)?;
with_key!(key, self, f)
}
}
pub struct Ed25519Signer<'a> {
key: &'a Ed25519Key,
client: std::cell::RefCell<&'a mut Client>,
}
impl Ed25519Key {
pub fn signer<'a>(&'a self, client: &'a mut Client) -> Ed25519Signer<'a> {
Ed25519Signer {
key: self,
client: std::cell::RefCell::new(client),
}
}
}
impl<'a> signature::Signer<ed25519::Signature> for Ed25519Signer<'a> {
fn try_sign(&self, msg: &[u8]) -> Result<ed25519::Signature, signature::Error> {
let mut client = self.client.borrow_mut();
let sig_bytes = self
.key
.sign(&mut **client, msg)
.map_err(|_| signature::Error::new())?;
Ok(ed25519::Signature::from_bytes(&sig_bytes))
}
}