use super::{Signature, VerifyingKey};
use crate::error::{Error, ErrorKind};
use sha2::Sha512;
use signature::Signer;
use zeroize::Zeroizing;
const COMBINED_KEY_LENGTH: usize =
ed25519_dalek::EXPANDED_SECRET_KEY_LENGTH + ed25519_dalek::PUBLIC_KEY_LENGTH;
#[derive(Debug)]
pub struct SigningKey {
expanded: ed25519_dalek::hazmat::ExpandedSecretKey,
seed: Option<Zeroizing<[u8; Self::BYTE_SIZE]>>,
}
impl SigningKey {
pub const BYTE_SIZE: usize = 32;
pub fn verifying_key(&self) -> VerifyingKey {
let public_key = ed25519_dalek::VerifyingKey::from(&self.expanded);
VerifyingKey(public_key)
}
pub fn as_bytes(&self) -> Option<&[u8; Self::BYTE_SIZE]> {
self.seed.as_deref()
}
}
impl Signer<Signature> for SigningKey {
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
let signature =
ed25519_dalek::hazmat::raw_sign::<Sha512>(&self.expanded, msg, &self.verifying_key().0);
Ok(signature.to_bytes().into())
}
}
impl TryFrom<&[u8]> for SigningKey {
type Error = Error;
fn try_from(slice: &[u8]) -> Result<Self, Error> {
match slice.len() {
ed25519_dalek::SECRET_KEY_LENGTH => {
let mut seed = Zeroizing::new([0u8; Self::BYTE_SIZE]);
seed.copy_from_slice(&slice[..Self::BYTE_SIZE]);
let secret_key = ed25519_dalek::SecretKey::try_from(seed.as_ref())
.map_err(|_| ErrorKind::InvalidKey)?;
let expanded_key = ed25519_dalek::hazmat::ExpandedSecretKey::from(&secret_key);
Ok(Self {
expanded: expanded_key,
seed: Some(seed),
})
}
ed25519_dalek::EXPANDED_SECRET_KEY_LENGTH => {
let expanded_key = ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(
slice.try_into().map_err(|_| ErrorKind::InvalidKey)?,
);
Ok(Self {
expanded: expanded_key,
seed: None,
})
}
COMBINED_KEY_LENGTH => {
let mut key_bytes: [u8; ed25519_dalek::EXPANDED_SECRET_KEY_LENGTH] = slice
[..ed25519_dalek::EXPANDED_SECRET_KEY_LENGTH]
.try_into()
.map_err(|_| ErrorKind::InvalidKey)?;
key_bytes[..ed25519_dalek::SECRET_KEY_LENGTH].reverse();
let expanded_key = ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(&key_bytes);
Ok(Self {
expanded: expanded_key,
seed: None,
})
}
other_len => Err(ErrorKind::InvalidKey
.context(format!(
"invalid Ed25519 key size: expected 32, 64, or 96, but got {}",
other_len
))
.into()),
}
}
}
impl From<cometbft::private_key::Ed25519> for SigningKey {
fn from(signing_key: cometbft::private_key::Ed25519) -> SigningKey {
signing_key
.as_bytes()
.try_into()
.expect("invalid Ed25519 signing key")
}
}
impl From<&SigningKey> for cometbft_p2p::PublicKey {
fn from(signing_key: &SigningKey) -> cometbft_p2p::PublicKey {
signing_key.verifying_key().into()
}
}