1use core::{
4 convert::{TryFrom, TryInto},
5 fmt::{self, Debug, Formatter},
6};
7
8use curve25519_dalek::{edwards::CompressedEdwardsY, scalar::clamp_integer};
9use ed25519_dalek::{
10 SecretKey, Signature, Signer, SigningKey, VerifyingKey, KEYPAIR_LENGTH, PUBLIC_KEY_LENGTH,
11 SECRET_KEY_LENGTH, SIGNATURE_LENGTH as EDDSA_SIGNATURE_LENGTH,
12};
13use sha2::Digest;
14use subtle::ConstantTimeEq;
15use x25519_dalek::{PublicKey as XPublicKey, StaticSecret as XSecretKey};
16use zeroize::{Zeroize, ZeroizeOnDrop};
17
18use super::{x25519::X25519KeyPair, HasKeyAlg, HasKeyBackend, KeyAlg};
19use crate::{
20 buffer::{ArrayKey, WriteBuffer},
21 error::Error,
22 generic_array::typenum::{U32, U64},
23 jwk::{FromJwk, JwkEncoder, JwkParts, ToJwk},
24 random::KeyMaterial,
25 repr::{KeyGen, KeyMeta, KeyPublicBytes, KeySecretBytes, KeypairBytes, KeypairMeta},
26 sign::{KeySigVerify, KeySign, SignatureType},
27};
28
29pub const JWK_KEY_TYPE: &str = "OKP";
31pub const JWK_CURVE: &str = "Ed25519";
33
34#[derive(Clone)]
36pub struct Ed25519KeyPair {
37 secret: Option<[u8; SECRET_KEY_LENGTH]>,
38 public: [u8; PUBLIC_KEY_LENGTH],
39}
40
41impl Ed25519KeyPair {
42 #[inline]
43 pub(crate) fn from_secret_key(secret: &SecretKey) -> Self {
44 let sk = SigningKey::from(secret);
45 let vk = VerifyingKey::from(&sk);
46 Self {
47 secret: Some(*secret),
48 public: vk.to_bytes(),
49 }
50 }
51
52 pub(crate) fn check_public_bytes(&self, pk: &[u8]) -> Result<(), Error> {
53 if self.public.ct_eq(pk).into() {
54 Ok(())
55 } else {
56 Err(err_msg!(InvalidKeyData, "invalid ed25519 keypair"))
57 }
58 }
59
60 pub fn to_signing_key(&self) -> Option<Ed25519SigningKey> {
62 self.secret
63 .as_ref()
64 .map(|sk| Ed25519SigningKey(SigningKey::from(sk)))
65 }
66
67 pub fn to_x25519_keypair(&self) -> X25519KeyPair {
69 if let Some(secret) = self.secret.as_ref() {
70 let hash = sha2::Sha512::digest(secret);
71 let secret = XSecretKey::from(clamp_integer(hash[..32].try_into().unwrap()));
74 let public = XPublicKey::from(&secret);
75 X25519KeyPair::new(Some(secret), public)
76 } else {
77 let public = XPublicKey::from(
78 CompressedEdwardsY(self.public)
79 .decompress()
80 .unwrap()
81 .to_montgomery()
82 .to_bytes(),
83 );
84 X25519KeyPair::new(None, public)
85 }
86 }
87
88 pub fn sign(&self, message: &[u8]) -> Option<[u8; EDDSA_SIGNATURE_LENGTH]> {
90 self.to_signing_key().map(|sk| sk.sign(message))
91 }
92
93 pub fn verify_signature(&self, message: &[u8], signature: &[u8]) -> bool {
95 if let Ok(sig) = Signature::try_from(signature) {
96 let vk = VerifyingKey::from_bytes(&self.public).unwrap();
97 vk.verify_strict(message, &sig).is_ok()
98 } else {
99 false
100 }
101 }
102}
103
104impl Debug for Ed25519KeyPair {
105 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
106 f.debug_struct("Ed25519KeyPair")
107 .field(
108 "secret",
109 if self.secret.is_some() {
110 &"<secret>"
111 } else {
112 &"None"
113 },
114 )
115 .field("public", &self.public)
116 .finish()
117 }
118}
119
120impl KeyGen for Ed25519KeyPair {
121 fn generate(rng: impl KeyMaterial) -> Result<Self, Error> {
122 let sk = ArrayKey::<U32>::generate(rng);
123 Ok(Self::from_secret_key((&sk).try_into().unwrap()))
124 }
125}
126
127impl HasKeyBackend for Ed25519KeyPair {}
128
129impl HasKeyAlg for Ed25519KeyPair {
130 fn algorithm(&self) -> KeyAlg {
131 KeyAlg::Ed25519
132 }
133}
134
135impl KeyMeta for Ed25519KeyPair {
136 type KeySize = U32;
137}
138
139impl KeySecretBytes for Ed25519KeyPair {
140 fn from_secret_bytes(key: &[u8]) -> Result<Self, Error> {
141 let sk: &[u8; SECRET_KEY_LENGTH] = key.try_into().map_err(|_| err_msg!(InvalidKeyData))?;
142 Ok(Self::from_secret_key(sk))
143 }
144
145 fn with_secret_bytes<O>(&self, f: impl FnOnce(Option<&[u8]>) -> O) -> O {
146 f(self.secret.as_ref().map(|sk| &sk[..]))
147 }
148}
149
150impl KeypairMeta for Ed25519KeyPair {
151 type PublicKeySize = U32;
152 type KeypairSize = U64;
153}
154
155impl KeypairBytes for Ed25519KeyPair {
156 fn from_keypair_bytes(kp: &[u8]) -> Result<Self, Error> {
157 if kp.len() != KEYPAIR_LENGTH {
158 return Err(err_msg!(InvalidKeyData));
159 }
160 let result = Ed25519KeyPair::from_secret_bytes(&kp[..SECRET_KEY_LENGTH])?;
162 result.check_public_bytes(&kp[SECRET_KEY_LENGTH..])?;
163 Ok(result)
164 }
165
166 fn with_keypair_bytes<O>(&self, f: impl FnOnce(Option<&[u8]>) -> O) -> O {
167 if let Some(secret) = self.secret.as_ref() {
168 ArrayKey::<<Self as KeypairMeta>::KeypairSize>::temp(|arr| {
169 arr[..SECRET_KEY_LENGTH].copy_from_slice(secret);
170 arr[SECRET_KEY_LENGTH..].copy_from_slice(&self.public[..]);
171 f(Some(&*arr))
172 })
173 } else {
174 f(None)
175 }
176 }
177}
178
179impl KeyPublicBytes for Ed25519KeyPair {
180 fn from_public_bytes(key: &[u8]) -> Result<Self, Error> {
181 let vk = key
182 .try_into()
183 .ok()
184 .and_then(|k| VerifyingKey::from_bytes(k).ok())
185 .ok_or_else(|| err_msg!(InvalidKeyData))?;
186 Ok(Self {
187 secret: None,
188 public: vk.to_bytes(),
189 })
190 }
191
192 fn with_public_bytes<O>(&self, f: impl FnOnce(&[u8]) -> O) -> O {
193 f(&self.public[..])
194 }
195}
196
197impl KeySign for Ed25519KeyPair {
198 fn write_signature(
199 &self,
200 message: &[u8],
201 sig_type: Option<SignatureType>,
202 out: &mut dyn WriteBuffer,
203 ) -> Result<(), Error> {
204 match sig_type {
205 None | Some(SignatureType::EdDSA) => {
206 if let Some(signer) = self.to_signing_key() {
207 let sig = signer.sign(message);
208 out.buffer_write(&sig[..])?;
209 Ok(())
210 } else {
211 Err(err_msg!(MissingSecretKey))
212 }
213 }
214 #[allow(unreachable_patterns)]
215 _ => Err(err_msg!(Unsupported, "Unsupported signature type")),
216 }
217 }
218}
219
220impl KeySigVerify for Ed25519KeyPair {
221 fn verify_signature(
222 &self,
223 message: &[u8],
224 signature: &[u8],
225 sig_type: Option<SignatureType>,
226 ) -> Result<bool, Error> {
227 match sig_type {
228 None | Some(SignatureType::EdDSA) => Ok(self.verify_signature(message, signature)),
229 #[allow(unreachable_patterns)]
230 _ => Err(err_msg!(Unsupported, "Unsupported signature type")),
231 }
232 }
233}
234
235impl ToJwk for Ed25519KeyPair {
236 fn encode_jwk(&self, enc: &mut dyn JwkEncoder) -> Result<(), Error> {
237 enc.add_str("crv", JWK_CURVE)?;
238 enc.add_str("kty", JWK_KEY_TYPE)?;
239 self.with_public_bytes(|buf| enc.add_as_base64("x", buf))?;
240 if enc.is_secret() {
241 self.with_secret_bytes(|buf| {
242 if let Some(sk) = buf {
243 enc.add_as_base64("d", sk)
244 } else {
245 Ok(())
246 }
247 })?;
248 }
249 Ok(())
250 }
251}
252
253impl FromJwk for Ed25519KeyPair {
254 fn from_jwk_parts(jwk: JwkParts<'_>) -> Result<Self, Error> {
255 if jwk.kty != JWK_KEY_TYPE {
256 return Err(err_msg!(InvalidKeyData, "Unsupported key type"));
257 }
258 if jwk.crv != JWK_CURVE {
259 return Err(err_msg!(InvalidKeyData, "Unsupported key algorithm"));
260 }
261 ArrayKey::<U32>::temp(|pk_arr| {
262 if jwk.x.decode_base64(pk_arr)? != pk_arr.len() {
263 Err(err_msg!(InvalidKeyData))
264 } else if jwk.d.is_some() {
265 ArrayKey::<U32>::temp(|sk_arr| {
266 if jwk.d.decode_base64(sk_arr)? != sk_arr.len() {
267 Err(err_msg!(InvalidKeyData))
268 } else {
269 let kp = Ed25519KeyPair::from_secret_bytes(sk_arr)?;
270 kp.check_public_bytes(pk_arr)?;
271 Ok(kp)
272 }
273 })
274 } else {
275 Ed25519KeyPair::from_public_bytes(pk_arr)
276 }
277 })
278 }
279}
280
281impl Drop for Ed25519KeyPair {
282 fn drop(&mut self) {
283 self.secret.zeroize();
284 self.public.zeroize();
285 }
286}
287
288impl ZeroizeOnDrop for Ed25519KeyPair {}
289
290#[derive(Debug, Clone)]
293pub struct Ed25519SigningKey(SigningKey);
294
295impl Ed25519SigningKey {
296 pub fn sign(&self, message: &[u8]) -> [u8; EDDSA_SIGNATURE_LENGTH] {
298 self.0.sign(message).to_bytes()
299 }
300}
301
302impl ZeroizeOnDrop for Ed25519SigningKey {}
303
304#[cfg(test)]
305mod tests {
306 use base64::Engine;
307
308 use super::*;
309 use crate::repr::{ToPublicBytes, ToSecretBytes};
310
311 #[test]
312 fn expand_keypair() {
313 let seed = b"000000000000000000000000Trustee1";
314 let test_sk = &hex!("3030303030303030303030303030303030303030303030305472757374656531e33aaf381fffa6109ad591fdc38717945f8fabf7abf02086ae401c63e9913097");
315
316 let kp = Ed25519KeyPair::from_secret_bytes(seed).unwrap();
317 assert_eq!(kp.to_keypair_bytes().unwrap(), &test_sk[..]);
318 assert_eq!(kp.to_secret_bytes().unwrap(), &seed[..]);
319
320 let cmp = Ed25519KeyPair::from_keypair_bytes(test_sk).unwrap();
322 assert_eq!(cmp.to_keypair_bytes().unwrap(), &test_sk[..]);
323 }
324
325 #[test]
326 fn ed25519_to_x25519() {
327 let test_keypair = &hex!("1c1179a560d092b90458fe6ab8291215a427fcd6b3927cb240701778ef55201927c96646f2d4632d4fc241f84cbc427fbc3ecaa95becba55088d6c7b81fc5bbf");
328 let x_sk = &hex!("08e7286c232ec71b37918533ea0229bf0c75d3db4731df1c5c03c45bc909475f");
329 let x_pk = &hex!("9b4260484c889158c128796103dc8d8b883977f2ef7efb0facb12b6ca9b2ae3d");
330 let x_pair = Ed25519KeyPair::from_keypair_bytes(test_keypair)
331 .unwrap()
332 .to_x25519_keypair()
333 .to_keypair_bytes()
334 .unwrap();
335 assert_eq!(&x_pair[..32], x_sk);
336 assert_eq!(&x_pair[32..], x_pk);
337 }
338
339 #[test]
340 fn jwk_expected() {
341 let test_pvt_b64 = "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A";
351 let test_pub_b64 = "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo";
352 let test_pvt = base64::engine::general_purpose::URL_SAFE_NO_PAD
353 .decode(test_pvt_b64)
354 .unwrap();
355 let kp = Ed25519KeyPair::from_secret_bytes(&test_pvt).expect("Error creating signing key");
356 let jwk = kp
357 .to_jwk_public(None)
358 .expect("Error converting public key to JWK");
359 let jwk = JwkParts::try_from_str(&jwk).expect("Error parsing JWK output");
360 assert_eq!(jwk.kty, JWK_KEY_TYPE);
361 assert_eq!(jwk.crv, JWK_CURVE);
362 assert_eq!(jwk.x, "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo");
363 let pk_load = Ed25519KeyPair::from_jwk_parts(jwk).unwrap();
364 assert_eq!(kp.to_public_bytes(), pk_load.to_public_bytes());
365
366 let jwk = kp
367 .to_jwk_secret(None)
368 .expect("Error converting private key to JWK");
369 let jwk = JwkParts::from_slice(&jwk).expect("Error parsing JWK output");
370 assert_eq!(jwk.kty, JWK_KEY_TYPE);
371 assert_eq!(jwk.crv, JWK_CURVE);
372 assert_eq!(jwk.x, test_pub_b64);
373 assert_eq!(jwk.d, test_pvt_b64);
374 let sk_load = Ed25519KeyPair::from_jwk_parts(jwk).unwrap();
375 assert_eq!(
376 kp.to_keypair_bytes().unwrap(),
377 sk_load.to_keypair_bytes().unwrap()
378 );
379 }
380
381 #[test]
382 fn sign_verify_expected() {
383 let test_msg = b"This is a dummy message for use with tests";
384 let test_sig = &hex!(
385 "451b5b8e8725321541954997781de51f4142e4a56bab68d24f6a6b92615de5ee
386 fb74134138315859a32c7cf5fe5a488bc545e2e08e5eedfd1fb10188d532d808"
387 );
388 let test_keypair = &hex!(
389 "1c1179a560d092b90458fe6ab8291215a427fcd6b3927cb240701778ef552019
390 27c96646f2d4632d4fc241f84cbc427fbc3ecaa95becba55088d6c7b81fc5bbf"
391 );
392 let kp = Ed25519KeyPair::from_keypair_bytes(test_keypair).unwrap();
393 let sig = &kp.sign(test_msg).unwrap();
394 assert_eq!(sig, test_sig);
395 assert!(kp.verify_signature(test_msg, &sig[..]));
396 assert!(!kp.verify_signature(b"Not the message", &sig[..]));
397 assert!(!kp.verify_signature(test_msg, &[0u8; 64]));
398 }
399
400 #[test]
401 fn round_trip_bytes() {
402 let kp = Ed25519KeyPair::random().unwrap();
403 let cmp = Ed25519KeyPair::from_keypair_bytes(&kp.to_keypair_bytes().unwrap()).unwrap();
404 assert_eq!(
405 kp.to_keypair_bytes().unwrap(),
406 cmp.to_keypair_bytes().unwrap()
407 );
408 }
409}