use ed25519_dalek::{Signature, SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH};
use crate::{iana, Error, Key, Label, Signer, Verifier};
#[derive(Clone, Debug)]
pub struct Ed25519Signer {
alg: i64,
kid: Option<Vec<u8>>,
key: SigningKey,
}
impl Ed25519Signer {
pub fn from_secret_key(secret_key: &[u8], kid: Option<Vec<u8>>) -> Result<Self, Error> {
Self::from_secret_key_with_alg(iana::AlgorithmEd25519, secret_key, kid)
}
pub fn from_secret_key_with_alg(
alg: i64,
secret_key: &[u8],
kid: Option<Vec<u8>>,
) -> Result<Self, Error> {
require_supported_alg(alg)?;
let seed: [u8; SECRET_KEY_LENGTH] = secret_key
.try_into()
.map_err(|_| Error::custom("Ed25519 private key must be 32 bytes"))?;
Ok(Self {
alg,
kid,
key: SigningKey::from_bytes(&seed),
})
}
pub fn from_cose_key(key: &Key) -> Result<Self, Error> {
key.require_any_operation(&[iana::KeyOperationSign], "signing")?;
let alg = require_alg(key)?;
key.require_integer_kty(iana::KeyTypeOKP)?;
key.require_integer_parameter(
iana::OKPKeyParameterCrv,
iana::EllipticCurveEd25519,
"curve",
)?;
let signer = Self::from_secret_key_with_alg(
alg,
key.required_bytes(iana::OKPKeyParameterD, "d")?,
key.kid_owned()?,
)?;
if let Some(expected) = key.get_bytes(iana::OKPKeyParameterX)? {
if expected != signer.public_key() {
return Err(Error::custom(
"COSE_Key Ed25519 public key x does not match private key d",
));
}
}
Ok(signer)
}
pub fn public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] {
self.key.verifying_key().to_bytes()
}
pub fn to_cose_key(&self) -> Result<Key, Error> {
let mut key = okp_public_cose_key(
self.alg,
self.key.verifying_key().as_bytes(),
self.kid.as_deref(),
);
key.set_ops([iana::KeyOperationVerify]);
Ok(key)
}
pub fn algorithm(&self) -> i64 {
self.alg
}
}
impl Signer for Ed25519Signer {
fn alg(&self) -> Option<Label> {
Some(self.alg.into())
}
fn kid(&self) -> Option<&[u8]> {
self.kid.as_deref()
}
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
use ed25519_dalek::Signer as _;
Ok(self.key.sign(data).to_bytes().to_vec())
}
}
#[derive(Clone, Debug)]
pub struct Ed25519Verifier {
alg: i64,
kid: Option<Vec<u8>>,
key: VerifyingKey,
}
impl Ed25519Verifier {
pub fn from_public_key(public_key: &[u8], kid: Option<Vec<u8>>) -> Result<Self, Error> {
Self::from_public_key_with_alg(iana::AlgorithmEd25519, public_key, kid)
}
pub fn from_public_key_with_alg(
alg: i64,
public_key: &[u8],
kid: Option<Vec<u8>>,
) -> Result<Self, Error> {
require_supported_alg(alg)?;
let bytes: [u8; PUBLIC_KEY_LENGTH] = public_key
.try_into()
.map_err(|_| Error::custom("Ed25519 public key must be 32 bytes"))?;
let key = VerifyingKey::from_bytes(&bytes)
.map_err(|_| Error::custom("invalid Ed25519 public key"))?;
Ok(Self { alg, kid, key })
}
pub fn from_cose_key(key: &Key) -> Result<Self, Error> {
key.require_any_operation(&[iana::KeyOperationVerify], "signature verification")?;
let alg = require_alg(key)?;
key.require_integer_kty(iana::KeyTypeOKP)?;
key.require_integer_parameter(
iana::OKPKeyParameterCrv,
iana::EllipticCurveEd25519,
"curve",
)?;
Self::from_public_key_with_alg(
alg,
key.required_bytes(iana::OKPKeyParameterX, "x")?,
key.kid_owned()?,
)
}
pub fn public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] {
self.key.to_bytes()
}
pub fn to_cose_key(&self) -> Result<Key, Error> {
let mut key = okp_public_cose_key(self.alg, self.key.as_bytes(), self.kid.as_deref());
key.set_ops([iana::KeyOperationVerify]);
Ok(key)
}
pub fn algorithm(&self) -> i64 {
self.alg
}
}
impl Verifier for Ed25519Verifier {
fn alg(&self) -> Option<Label> {
Some(self.alg.into())
}
fn kid(&self) -> Option<&[u8]> {
self.kid.as_deref()
}
fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), Error> {
let signature = Signature::from_slice(signature)
.map_err(|_| Error::verify("invalid Ed25519 signature"))?;
self.key
.verify_strict(data, &signature)
.map_err(|_| Error::verify("Ed25519 signature mismatch"))
}
}
fn okp_public_cose_key(alg: i64, x: &[u8], kid: Option<&[u8]>) -> Key {
let mut key = Key::new();
key.set_kty(iana::KeyTypeOKP).set_alg(alg);
if let Some(kid) = kid {
key.set_kid(kid.to_vec());
}
key.insert(iana::OKPKeyParameterCrv, iana::EllipticCurveEd25519);
key.insert(iana::OKPKeyParameterX, x.to_vec());
key
}
fn require_alg(key: &Key) -> Result<i64, Error> {
match key.alg()? {
None => Ok(iana::AlgorithmEd25519),
Some(Label::Int(alg)) if matches!(alg, iana::AlgorithmEd25519 | iana::AlgorithmEdDSA) => {
Ok(alg)
}
Some(other) => Err(Error::custom(format!(
"COSE_Key alg mismatch, expected {} or {}, got {other}",
Label::from(iana::AlgorithmEd25519),
Label::from(iana::AlgorithmEdDSA)
))),
}
}
fn require_supported_alg(alg: i64) -> Result<(), Error> {
if matches!(alg, iana::AlgorithmEd25519 | iana::AlgorithmEdDSA) {
Ok(())
} else {
Err(Error::custom(format!(
"unsupported Ed25519 algorithm {}",
Label::from(alg)
)))
}
}