1use ed25519_dalek::{Signature, Signer as _, SigningKey, VerifyingKey};
2use serde::{Deserialize, Serialize};
3
4use relay_crypto::{
5 alg::{AlgorithmError, SigningAlgorithm},
6 hex,
7 kid::kid_from_key,
8 rand_core::{CryptoRng, RngCore},
9 record::Key,
10};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Meta {
14 #[serde(with = "hex::serde")]
15 pub public_key: [u8; 32],
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct Secrets {
20 #[serde(with = "hex::serde")]
21 pub secret: [u8; 32],
22}
23
24pub struct Ed25519;
25
26impl Ed25519 {
27 pub fn generate(
28 &self,
29 mut rng: impl RngCore + CryptoRng,
30 ) -> Result<(Key, Secrets), AlgorithmError> {
31 let mut secret = [0u8; 32];
32 rng.fill_bytes(&mut secret);
33
34 let signing = SigningKey::from_bytes(&secret);
35 let verifying = signing.verifying_key();
36 let public = verifying.to_bytes();
37 let kid = kid_from_key(public.to_vec());
38
39 Ok((
40 Key {
41 alg: Self::alg().to_string(),
42 kid,
43 data: public.to_vec(),
44 },
45 Secrets { secret },
46 ))
47 }
48}
49
50impl SigningAlgorithm for Ed25519 {
51 type Meta = Meta;
52 type Secrets = Secrets;
53
54 fn alg() -> &'static str {
55 "ed25519"
56 }
57
58 fn sign_inner(
59 &self,
60 payload: &[u8],
61 secrets: &Self::Secrets,
62 ) -> Result<(Self::Meta, Vec<u8>), AlgorithmError> {
63 let signing = SigningKey::from_bytes(&secrets.secret);
64 let public = signing.verifying_key().to_bytes();
65 let signature = signing.sign(payload);
66
67 Ok((Meta { public_key: public }, signature.to_bytes().to_vec()))
68 }
69
70 fn verify_inner(
71 &self,
72 meta: &Self::Meta,
73 signature: &[u8],
74 payload: &[u8],
75 ) -> Result<(), AlgorithmError> {
76 let signature = Signature::from_slice(signature)
77 .map_err(|e| AlgorithmError::Error(format!("invalid signature: {e}")))?;
78 let verifying = VerifyingKey::from_bytes(&meta.public_key)
79 .map_err(|e| AlgorithmError::InvalidMeta(format!("invalid public key: {e}")))?;
80
81 verifying
82 .verify_strict(payload, &signature)
83 .map_err(|e| AlgorithmError::Error(format!("signature verification failed: {e}")))
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::x25519_xchacha20poly1305::X25519XChaCha20Poly1305;
91 use relay_core::{chrono::Utc, prelude::Payload};
92 use relay_crypto::{
93 alg::{EncryptionAlgorithm, EncryptionContext, SigningAlgorithm},
94 record::{Key, PubRecord},
95 rng::os_rng_hkdf,
96 };
97
98 #[test]
99 fn sign_verify() {
100 let mut rng = os_rng_hkdf(None, b"test").unwrap();
101
102 let (_key, secrets) = Ed25519.generate(&mut rng).expect("key generation failed");
103
104 let payload = Payload::<()>::new(
105 relay_core::info::EncryptionInfo {
106 alg: "none".into(),
107 data: vec![],
108 },
109 None,
110 b"hello world".to_vec(),
111 );
112
113 let signed = Ed25519
114 .sign(payload, &serde_json::to_vec(&secrets).unwrap())
115 .expect("sign failed");
116
117 assert!(signed.is_signed());
118
119 Ed25519.verify(&signed).expect("verify failed");
120 }
121
122 #[test]
123 fn encrypt_sign_verify_decrypt() {
124 let mut rng = os_rng_hkdf(None, b"test").unwrap();
125
126 let (enc_key, enc_secrets) = X25519XChaCha20Poly1305
127 .generate(&mut rng)
128 .expect("enc generation failed");
129
130 let (_sign_key, sign_secrets) = Ed25519.generate(&mut rng).expect("sign generation failed");
131
132 let record = PubRecord {
133 id: "recipient".to_string(),
134 created_at: Utc::now(),
135 expires_at: None,
136 encryption: enc_key,
137 signing: Key {
138 alg: String::new(),
139 kid: String::new(),
140 data: vec![],
141 },
142 };
143
144 let message = b"Hello, world!";
145 let aad = b"aad";
146
147 let payload: Payload<()> = X25519XChaCha20Poly1305
148 .encrypt(&mut rng, &record, message, aad)
149 .expect("encryption failed");
150
151 let signed = Ed25519
152 .sign(payload, &serde_json::to_vec(&sign_secrets).unwrap())
153 .expect("sign failed");
154
155 Ed25519.verify(&signed).expect("verify failed");
156
157 let context = X25519XChaCha20Poly1305
158 .open(&signed.info, &serde_json::to_vec(&enc_secrets).unwrap())
159 .unwrap();
160
161 let decrypted = context.decrypt(&signed, aad).unwrap();
162
163 assert_eq!(decrypted, message);
164 }
165}