use ed25519_dalek::{Signature, Signer as _, SigningKey, VerifyingKey};
use serde::{Deserialize, Serialize};
use relay_crypto::{
alg::{AlgorithmError, SigningAlgorithm},
hex,
kid::kid_from_key,
rand_core::{CryptoRng, RngCore},
record::Key,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Meta {
#[serde(with = "hex::serde")]
pub public_key: [u8; 32],
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Secrets {
#[serde(with = "hex::serde")]
pub secret: [u8; 32],
}
pub struct Ed25519;
impl Ed25519 {
pub fn generate(
&self,
mut rng: impl RngCore + CryptoRng,
) -> Result<(Key, Secrets), AlgorithmError> {
let mut secret = [0u8; 32];
rng.fill_bytes(&mut secret);
let signing = SigningKey::from_bytes(&secret);
let verifying = signing.verifying_key();
let public = verifying.to_bytes();
let kid = kid_from_key(public.to_vec());
Ok((
Key {
alg: Self::alg().to_string(),
kid,
data: public.to_vec(),
},
Secrets { secret },
))
}
}
impl SigningAlgorithm for Ed25519 {
type Meta = Meta;
type Secrets = Secrets;
fn alg() -> &'static str {
"ed25519"
}
fn sign_inner(
&self,
payload: &[u8],
secrets: &Self::Secrets,
) -> Result<(Self::Meta, Vec<u8>), AlgorithmError> {
let signing = SigningKey::from_bytes(&secrets.secret);
let public = signing.verifying_key().to_bytes();
let signature = signing.sign(payload);
Ok((Meta { public_key: public }, signature.to_bytes().to_vec()))
}
fn verify_inner(
&self,
meta: &Self::Meta,
signature: &[u8],
payload: &[u8],
) -> Result<(), AlgorithmError> {
let signature = Signature::from_slice(signature)
.map_err(|e| AlgorithmError::Error(format!("invalid signature: {e}")))?;
let verifying = VerifyingKey::from_bytes(&meta.public_key)
.map_err(|e| AlgorithmError::InvalidMeta(format!("invalid public key: {e}")))?;
verifying
.verify_strict(payload, &signature)
.map_err(|e| AlgorithmError::Error(format!("signature verification failed: {e}")))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::x25519_xchacha20poly1305::X25519XChaCha20Poly1305;
use relay_core::{chrono::Utc, prelude::Payload};
use relay_crypto::{
alg::{EncryptionAlgorithm, EncryptionContext, SigningAlgorithm},
record::{Key, PubRecord},
rng::os_rng_hkdf,
};
#[test]
fn sign_verify() {
let mut rng = os_rng_hkdf(None, b"test").unwrap();
let (_key, secrets) = Ed25519.generate(&mut rng).expect("key generation failed");
let payload = Payload::<()>::new(
relay_core::info::EncryptionInfo {
alg: "none".into(),
data: vec![],
},
None,
b"hello world".to_vec(),
);
let signed = Ed25519
.sign(payload, &serde_json::to_vec(&secrets).unwrap())
.expect("sign failed");
assert!(signed.is_signed());
Ed25519.verify(&signed).expect("verify failed");
}
#[test]
fn encrypt_sign_verify_decrypt() {
let mut rng = os_rng_hkdf(None, b"test").unwrap();
let (enc_key, enc_secrets) = X25519XChaCha20Poly1305
.generate(&mut rng)
.expect("enc generation failed");
let (_sign_key, sign_secrets) = Ed25519.generate(&mut rng).expect("sign generation failed");
let record = PubRecord {
id: "recipient".to_string(),
created_at: Utc::now(),
expires_at: None,
encryption: enc_key,
signing: Key {
alg: String::new(),
kid: String::new(),
data: vec![],
},
};
let message = b"Hello, world!";
let aad = b"aad";
let payload: Payload<()> = X25519XChaCha20Poly1305
.encrypt(&mut rng, &record, message, aad)
.expect("encryption failed");
let signed = Ed25519
.sign(payload, &serde_json::to_vec(&sign_secrets).unwrap())
.expect("sign failed");
Ed25519.verify(&signed).expect("verify failed");
let context = X25519XChaCha20Poly1305
.open(&signed.info, &serde_json::to_vec(&enc_secrets).unwrap())
.unwrap();
let decrypted = context.decrypt(&signed, aad).unwrap();
assert_eq!(decrypted, message);
}
}