Skip to main content

opaque_vx/key_exchange/group/
ed25519.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) VexaHub and contributors.
3// Copyright (c) Meta Platforms, Inc. and affiliates.
4
5//! Key Exchange group implementation for Ed25519
6
7use core::iter;
8
9use curve25519_dalek::edwards::CompressedEdwardsY;
10use curve25519_dalek::traits::IsIdentity;
11use curve25519_dalek::{EdwardsPoint, Scalar};
12use digest::Digest;
13pub use ed25519_dalek;
14use ed25519_dalek::hazmat::ExpandedSecretKey;
15use ed25519_dalek::{SecretKey, Sha512};
16use generic_array::GenericArray;
17use generic_array::typenum::{U32, U64};
18use rand::{CryptoRng, Rng};
19use zeroize::{Zeroize, ZeroizeOnDrop};
20
21use super::Group;
22use crate::ciphersuite::CipherSuite;
23use crate::errors::{InternalError, ProtocolError};
24use crate::key_exchange::sigma_i::hash_eddsa::implementation::HashEddsaImpl;
25use crate::key_exchange::sigma_i::pure_eddsa::implementation::PureEddsaImpl;
26pub use crate::key_exchange::sigma_i::shared::PreHash;
27use crate::key_exchange::sigma_i::{CachedMessage, Message, MessageBuilder};
28use crate::serialization::{ConcatExt, SliceExt, UpdateExt};
29
30/// Implementation for Ed25519.
31pub struct Ed25519;
32
33impl Group for Ed25519 {
34    type Pk = VerifyingKey;
35    type PkLen = U32;
36    type Sk = SigningKey;
37    type SkLen = U32;
38
39    fn serialize_pk(pk: &Self::Pk) -> GenericArray<u8, Self::PkLen> {
40        pk.compressed.0.into()
41    }
42
43    fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError> {
44        let bytes = bytes.take_array::<U32>("public key")?;
45
46        VerifyingKey::from_bytes(bytes.into())
47    }
48
49    fn random_sk<R: Rng + CryptoRng>(rng: &mut R) -> Self::Sk {
50        let mut sk = <[u8; 32]>::default();
51        rng.fill_bytes(&mut sk);
52
53        SigningKey::from_bytes(sk)
54    }
55
56    fn derive_scalar(seed: GenericArray<u8, Self::SkLen>) -> Result<Self::Sk, InternalError> {
57        Ok(SigningKey::from_bytes(seed.into()))
58    }
59
60    fn public_key(sk: &Self::Sk) -> Self::Pk {
61        sk.verifying_key
62    }
63
64    fn serialize_sk(sk: &Self::Sk) -> GenericArray<u8, Self::SkLen> {
65        sk.sk.into()
66    }
67
68    fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError> {
69        Ok(SigningKey::from_bytes(
70            bytes.take_array::<U32>("secret key")?.into(),
71        ))
72    }
73}
74
75impl PureEddsaImpl for Ed25519 {
76    type Signature = Signature;
77    type SignatureLen = U64;
78
79    fn sign<CS: CipherSuite, KE: Group>(
80        sk: &Self::Sk,
81        message: &Message<CS, KE>,
82    ) -> (Self::Signature, CachedMessage<CS, KE>) {
83        (sign(sk, false, message.sign_message()), message.to_cached())
84    }
85
86    /// Validates that the signature was created by signing the given message
87    /// with the corresponding private key.
88    fn verify<CS: CipherSuite, KE: Group>(
89        pk: &Self::Pk,
90        message_builder: MessageBuilder<'_, CS>,
91        state: CachedMessage<CS, KE>,
92        signature: &Self::Signature,
93    ) -> Result<(), ProtocolError> {
94        verify(
95            pk,
96            false,
97            message_builder.build::<KE>(state).verify_message(),
98            signature,
99        )
100    }
101
102    fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
103        Signature::deserialize_take(bytes)
104    }
105
106    fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
107        signature.serialize()
108    }
109}
110
111impl HashEddsaImpl for Ed25519 {
112    type Signature = Signature;
113    type SignatureLen = U64;
114    type VerifyState<CS: CipherSuite, KE: Group> = PreHash<Sha512>;
115
116    fn sign<CS: CipherSuite, KE: Group>(
117        sk: &Self::Sk,
118        message: &Message<CS, KE>,
119    ) -> (Self::Signature, Self::VerifyState<CS, KE>) {
120        let hash = message.hash::<Sha512>();
121
122        (
123            sign(sk, true, iter::once(hash.sign.finalize().as_slice())),
124            PreHash(hash.verify.finalize()),
125        )
126    }
127
128    /// Validates that the signature was created by signing the given message
129    /// with the corresponding private key.
130    fn verify<CS: CipherSuite, KE: Group>(
131        pk: &Self::Pk,
132        state: Self::VerifyState<CS, KE>,
133        signature: &Self::Signature,
134    ) -> Result<(), ProtocolError> {
135        verify(pk, true, iter::once(state.0.as_slice()), signature)
136    }
137
138    fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
139        Signature::deserialize_take(bytes)
140    }
141
142    fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
143        signature.serialize()
144    }
145}
146
147// This contains a manual implementation of EdDSA because `ed25519-dalek`
148// doesn't support message streaming. See
149// TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/556.
150fn sign<'a>(
151    sk: &SigningKey,
152    pre_hash: bool,
153    message: impl Clone + Iterator<Item = &'a [u8]>,
154) -> Signature {
155    let mut h = Sha512::new();
156
157    if pre_hash {
158        h.update(b"SigEd25519 no Ed25519 collisions");
159        h.update([1]); // Ed25519ph
160        h.update([0]);
161    }
162
163    h.update(sk.hash_prefix);
164    h.update_iter(message.clone());
165
166    let r = Scalar::from_hash(h);
167    #[allow(non_snake_case)]
168    let R = EdwardsPoint::mul_base(&r).compress();
169
170    h = Sha512::new();
171
172    if pre_hash {
173        h.update(b"SigEd25519 no Ed25519 collisions");
174        h.update([1]); // Ed25519ph
175        h.update([0]);
176    }
177
178    h.update(R.as_bytes());
179    h.update(sk.verifying_key.compressed.0);
180    h.update_iter(message);
181
182    let k = Scalar::from_hash(h);
183    let s: Scalar = (k * sk.scalar) + r;
184
185    Signature { R, s }
186}
187
188fn verify<'a>(
189    pk: &VerifyingKey,
190    pre_hash: bool,
191    message: impl Iterator<Item = &'a [u8]>,
192    signature: &Signature,
193) -> Result<(), ProtocolError> {
194    let mut h = Sha512::new();
195
196    if pre_hash {
197        h.update(b"SigEd25519 no Ed25519 collisions");
198        h.update([1]); // Ed25519ph
199        h.update([0]);
200    }
201
202    h.update(signature.R.as_bytes());
203    h.update(pk.compressed.as_bytes());
204    h.update_iter(message);
205    let k = Scalar::from_hash(h);
206
207    #[allow(non_snake_case)]
208    let minus_A: EdwardsPoint = -pk.point;
209    #[allow(non_snake_case)]
210    let expected_R =
211        EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s).compress();
212
213    if expected_R == signature.R {
214        Ok(())
215    } else {
216        Err(ProtocolError::InvalidLoginError)
217    }
218}
219
220/// Ed25519 verifying key.
221// `ed25519_dalek::VerifyingKey` doesn't implement `Zeroize`.
222// TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/747.
223// Required for manual implementation of EdDSA.
224// TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/556.
225#[derive(Clone, Copy, Debug, Eq, PartialEq, Zeroize)]
226pub struct VerifyingKey {
227    point: EdwardsPoint,
228    compressed: CompressedEdwardsY,
229}
230
231impl VerifyingKey {
232    fn from_bytes(bytes: [u8; 32]) -> Result<Self, ProtocolError> {
233        let compressed = CompressedEdwardsY(bytes);
234
235        if let Some(point) = compressed.decompress().filter(|point| !point.is_identity()) {
236            Ok(Self { point, compressed })
237        } else {
238            Err(ProtocolError::SerializationError)
239        }
240    }
241}
242
243#[cfg(feature = "serde")]
244impl<'de> serde::Deserialize<'de> for VerifyingKey {
245    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
246    where
247        D: serde::Deserializer<'de>,
248    {
249        use core::fmt::{self, Formatter};
250
251        use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};
252
253        struct VerifyingKeyVisitor;
254
255        impl<'de> Visitor<'de> for VerifyingKeyVisitor {
256            type Value = VerifyingKey;
257
258            fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
259                Formatter::write_str(formatter, "tuple struct VerifyingKey")
260            }
261
262            fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
263            where
264                D: Deserializer<'de>,
265            {
266                let compressed = CompressedEdwardsY::deserialize(deserializer)?;
267                VerifyingKey::from_bytes(compressed.0).map_err(Error::custom)
268            }
269
270            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
271            where
272                A: SeqAccess<'de>,
273            {
274                let compressed: CompressedEdwardsY = seq.next_element()?.ok_or_else(|| {
275                    Error::invalid_length(0, &"tuple struct VerifyingKey with 1 element")
276                })?;
277                VerifyingKey::from_bytes(compressed.0).map_err(Error::custom)
278            }
279        }
280
281        deserializer.deserialize_newtype_struct("VerifyingKey", VerifyingKeyVisitor)
282    }
283}
284
285#[cfg(feature = "serde")]
286impl serde::Serialize for VerifyingKey {
287    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
288    where
289        S: serde::Serializer,
290    {
291        serializer.serialize_newtype_struct("VerifyingKey", &self.compressed)
292    }
293}
294
295/// Ed25519 signing key.
296// We store the `ExpandedSecret` in memory to avoid computing it on demand and then discarding it
297// again.
298#[derive(Clone, Debug, Eq, PartialEq, ZeroizeOnDrop)]
299pub struct SigningKey {
300    // `ed25519_dalek::SigningKey` doesn't implement `Zeroize`. See
301    // https://github.com/dalek-cryptography/curve25519-dalek/pull/747
302    // Required for manual implementation of EdDSA.
303    // TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/556.
304    sk: SecretKey,
305    verifying_key: VerifyingKey,
306    // `ed25519_dalek::ExpandedSecret` doesn't implement traits we need. See
307    // TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/748 and
308    // https://github.com/dalek-cryptography/curve25519-dalek/pull/747.
309    scalar: Scalar,
310    hash_prefix: [u8; 32],
311}
312
313impl SigningKey {
314    fn from_bytes(sk: [u8; 32]) -> Self {
315        let ExpandedSecretKey {
316            scalar,
317            hash_prefix,
318        } = ExpandedSecretKey::from(&sk);
319        let point = EdwardsPoint::mul_base(&scalar);
320        let verifying_key = VerifyingKey {
321            point,
322            compressed: point.compress(),
323        };
324
325        SigningKey {
326            sk,
327            verifying_key,
328            scalar,
329            hash_prefix,
330        }
331    }
332}
333
334#[cfg(feature = "serde")]
335impl<'de> serde::Deserialize<'de> for SigningKey {
336    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
337    where
338        D: serde::Deserializer<'de>,
339    {
340        use core::fmt::{self, Formatter};
341
342        use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};
343
344        struct SigningKeyVisitor;
345
346        impl<'de> Visitor<'de> for SigningKeyVisitor {
347            type Value = SigningKey;
348
349            fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
350                Formatter::write_str(formatter, "tuple struct SigningKey")
351            }
352
353            fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
354            where
355                D: Deserializer<'de>,
356            {
357                let sk = Scalar::deserialize(deserializer)?;
358                Ok(SigningKey::from_bytes(sk.to_bytes()))
359            }
360
361            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
362            where
363                A: SeqAccess<'de>,
364            {
365                let sk: Scalar = seq.next_element()?.ok_or_else(|| {
366                    Error::invalid_length(0, &"tuple struct SigningKey with 1 element")
367                })?;
368                Ok(SigningKey::from_bytes(sk.to_bytes()))
369            }
370        }
371
372        deserializer.deserialize_newtype_struct("SigningKey", SigningKeyVisitor)
373    }
374}
375
376#[cfg(feature = "serde")]
377impl serde::Serialize for SigningKey {
378    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
379    where
380        S: serde::Serializer,
381    {
382        serializer.serialize_newtype_struct("SigningKey", &self.sk)
383    }
384}
385
386/// Ed25519 Signature.
387// `ed25519_dalek::Signature` doesn't implement validation with Serde de/serialization.
388#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
389#[derive(Clone, Copy, Debug, Eq, PartialEq)]
390#[allow(non_snake_case)]
391pub struct Signature {
392    R: CompressedEdwardsY,
393    s: Scalar,
394}
395
396impl Signature {
397    /// Expects the `R` and `s` components of an Ed25519 signature with no added
398    /// framing.
399    pub fn from_slice(mut bytes: &[u8]) -> Result<Self, ProtocolError> {
400        Self::deserialize_take(&mut bytes)
401    }
402
403    fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
404        #[allow(non_snake_case)]
405        let R = CompressedEdwardsY(bytes.take_array::<U32>("signature R")?.into());
406
407        let s = Scalar::from_canonical_bytes(bytes.take_array::<U32>("signature s")?.into())
408            .into_option()
409            .ok_or(ProtocolError::SerializationError)?;
410
411        Ok(Self { R, s })
412    }
413
414    fn serialize(&self) -> GenericArray<u8, U64> {
415        GenericArray::<u8, U32>::from(self.R.0)
416            .cat(GenericArray::<u8, U32>::from(self.s.to_bytes()))
417    }
418}
419
420impl Zeroize for Signature {
421    fn zeroize(&mut self) {
422        self.R.0 = [0; 32];
423        self.s = Scalar::default();
424    }
425}
426
427#[cfg(test)]
428mod test {
429    use std::iter;
430
431    use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey};
432    use rand::rand_core::UnwrapErr;
433    use rand::rngs::SysRng;
434
435    use super::*;
436
437    #[test]
438    fn pure_eddsa() {
439        let mut message = [0; 1024];
440        UnwrapErr(SysRng).fill_bytes(&mut message);
441
442        let mut sk = SecretKey::default();
443        UnwrapErr(SysRng).fill_bytes(&mut sk);
444        let signing_key = SigningKey::from_bytes(&sk);
445
446        let signature = signing_key.sign(&message);
447
448        let custom_sk = Ed25519::deserialize_take_sk(&mut sk.as_slice()).unwrap();
449        let custom_signature = sign(&custom_sk, false, iter::once(message.as_slice()));
450
451        assert_eq!(
452            signature.to_bytes(),
453            custom_signature.serialize().as_slice()
454        );
455
456        let verifying_key = VerifyingKey::from(&signing_key);
457        verifying_key.verify(&message, &signature).unwrap();
458
459        let custom_pk = Ed25519::public_key(&custom_sk);
460        verify(
461            &custom_pk,
462            false,
463            iter::once(message.as_slice()),
464            &custom_signature,
465        )
466        .unwrap();
467    }
468
469    #[test]
470    fn hash_eddsa() {
471        let mut message = [0; 1024];
472        UnwrapErr(SysRng).fill_bytes(&mut message);
473        let message = Sha512::new_with_prefix(message);
474        let pre_hash = message.clone().finalize();
475
476        let mut sk = SecretKey::default();
477        UnwrapErr(SysRng).fill_bytes(&mut sk);
478        let signing_key = SigningKey::from_bytes(&sk);
479
480        let signature = signing_key.sign_prehashed(message.clone(), None).unwrap();
481
482        let custom_sk = Ed25519::deserialize_take_sk(&mut sk.as_slice()).unwrap();
483        let custom_signature = sign(&custom_sk, true, iter::once(pre_hash.as_slice()));
484
485        assert_eq!(
486            signature.to_bytes(),
487            custom_signature.serialize().as_slice()
488        );
489
490        let verifying_key = VerifyingKey::from(&signing_key);
491        verifying_key
492            .verify_prehashed(message, None, &signature)
493            .unwrap();
494
495        let custom_pk = Ed25519::public_key(&custom_sk);
496        verify(
497            &custom_pk,
498            true,
499            iter::once(pre_hash.as_slice()),
500            &custom_signature,
501        )
502        .unwrap();
503    }
504}