use ed25519_dalek::{SigningKey, pkcs8::DecodePrivateKey};
use zeroize::ZeroizeOnDrop;
use crate::{RawSigner, RawSignerError, SigningAlg};
#[derive(ZeroizeOnDrop)]
pub(crate) struct Ed25519Signer {
signing_key: SigningKey,
}
impl Ed25519Signer {
pub(crate) fn from_private_key(private_key: &[u8]) -> Result<Self, RawSignerError> {
let private_key_pem = std::str::from_utf8(private_key).map_err(|e| {
RawSignerError::InvalidSigningCredentials(format!("invalid private key: {e}"))
})?;
let signing_key = SigningKey::from_pkcs8_pem(private_key_pem).map_err(|e| {
RawSignerError::InvalidSigningCredentials(format!("invalid private key: {e}"))
})?;
Ok(Ed25519Signer { signing_key })
}
}
impl RawSigner for Ed25519Signer {
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, RawSignerError> {
use ed25519_dalek::Signer;
Ok(self
.signing_key
.try_sign(data)
.map_err(|e| RawSignerError::InternalError(format!("signature error: {e}")))?
.to_vec())
}
fn alg(&self) -> SigningAlg {
SigningAlg::Ed25519
}
fn max_signature_size(&self) -> usize {
64
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::panic)]
use super::*;
#[test]
fn rejects_invalid_utf8() {
let Err(err) = Ed25519Signer::from_private_key(&[0xff, 0xfe, 0xfd]) else {
panic!("expected error");
};
assert!(matches!(err, RawSignerError::InvalidSigningCredentials(_)));
}
#[test]
fn rejects_bad_pem() {
let bad = b"-----BEGIN PRIVATE KEY-----\nMA==\n-----END PRIVATE KEY-----\n";
let Err(err) = Ed25519Signer::from_private_key(bad) else {
panic!("expected error");
};
assert!(matches!(err, RawSignerError::InvalidSigningCredentials(_)));
}
}