1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Edwards-curve digital signature algorithm.
16 
17 use bssl_crypto::{ed25519, InvalidSignatureError};
18 use mls_rs_core::crypto::{CipherSuite, SignaturePublicKey, SignatureSecretKey};
19 use mls_rs_crypto_traits::Curve;
20 
21 use core::array::TryFromSliceError;
22 use thiserror::Error;
23 
24 /// Errors returned from EdDSA.
25 #[derive(Debug, Error)]
26 pub enum EdDsaError {
27     /// Error returned when conversion from slice to array fails.
28     #[error(transparent)]
29     TryFromSliceError(#[from] TryFromSliceError),
30     /// Error returned on an invalid signature.
31     #[error("invalid signature")]
32     InvalidSig(InvalidSignatureError),
33     /// Error returned when the private key length is invalid.
34     #[error("EdDSA private key of invalid length {len}, expected length {expected_len}")]
35     InvalidPrivKeyLen {
36         /// Invalid key length.
37         len: usize,
38         /// Expected key length.
39         expected_len: usize,
40     },
41     /// Error returned when the public key length is invalid.
42     #[error("EdDSA public key of invalid length {len}, expected length {expected_len}")]
43     InvalidPubKeyLen {
44         /// Invalid key length.
45         len: usize,
46         /// Expected key length.
47         expected_len: usize,
48     },
49     /// Error returned when the signature length is invalid.
50     #[error("EdDSA signature of invalid length {len}, expected length {expected_len}")]
51     InvalidSigLen {
52         /// Invalid signature length.
53         len: usize,
54         /// Expected signature length.
55         expected_len: usize,
56     },
57     /// Error returned when unsupported cipher suite is requested.
58     #[error("unsupported cipher suite")]
59     UnsupportedCipherSuite,
60 }
61 
62 // Explicitly implemented as InvalidSignatureError's as_dyn_error does not satisfy trait bounds.
63 impl From<InvalidSignatureError> for EdDsaError {
from(e: InvalidSignatureError) -> Self64     fn from(e: InvalidSignatureError) -> Self {
65         EdDsaError::InvalidSig(e)
66     }
67 }
68 
69 /// EdDSA implementation backed by BoringSSL.
70 #[derive(Clone, Debug, Copy, PartialEq, Eq)]
71 pub struct EdDsa(Curve);
72 
73 impl EdDsa {
74     /// Creates a new EdDsa.
new(cipher_suite: CipherSuite) -> Option<Self>75     pub fn new(cipher_suite: CipherSuite) -> Option<Self> {
76         Curve::from_ciphersuite(cipher_suite, /*for_sig=*/ true).map(Self)
77     }
78 
79     /// Generates a key pair.
signature_key_generate( &self, ) -> Result<(SignatureSecretKey, SignaturePublicKey), EdDsaError>80     pub fn signature_key_generate(
81         &self,
82     ) -> Result<(SignatureSecretKey, SignaturePublicKey), EdDsaError> {
83         if self.0 != Curve::Ed25519 {
84             return Err(EdDsaError::UnsupportedCipherSuite);
85         }
86 
87         let private_key = ed25519::PrivateKey::generate();
88         let public_key = private_key.to_public();
89         Ok((private_key.to_seed().to_vec().into(), public_key.as_bytes().to_vec().into()))
90     }
91 
92     /// Derives the public key from the private key.
signature_key_derive_public( &self, secret_key: &SignatureSecretKey, ) -> Result<SignaturePublicKey, EdDsaError>93     pub fn signature_key_derive_public(
94         &self,
95         secret_key: &SignatureSecretKey,
96     ) -> Result<SignaturePublicKey, EdDsaError> {
97         if self.0 != Curve::Ed25519 {
98             return Err(EdDsaError::UnsupportedCipherSuite);
99         }
100         if secret_key.len() != ed25519::SEED_LEN {
101             return Err(EdDsaError::InvalidPrivKeyLen {
102                 len: secret_key.len(),
103                 expected_len: ed25519::SEED_LEN,
104             });
105         }
106 
107         let private_key =
108             ed25519::PrivateKey::from_seed(secret_key[..ed25519::SEED_LEN].try_into()?);
109         Ok(private_key.to_public().as_bytes().to_vec().into())
110     }
111 
112     /// Signs `data` using `secret_key`.
sign( &self, secret_key: &SignatureSecretKey, data: &[u8], ) -> Result<Vec<u8>, EdDsaError>113     pub fn sign(
114         &self,
115         secret_key: &SignatureSecretKey,
116         data: &[u8],
117     ) -> Result<Vec<u8>, EdDsaError> {
118         if self.0 != Curve::Ed25519 {
119             return Err(EdDsaError::UnsupportedCipherSuite);
120         }
121         if secret_key.len() != ed25519::SEED_LEN {
122             return Err(EdDsaError::InvalidPrivKeyLen {
123                 len: secret_key.len(),
124                 expected_len: ed25519::SEED_LEN,
125             });
126         }
127 
128         let private_key =
129             ed25519::PrivateKey::from_seed(secret_key[..ed25519::SEED_LEN].try_into()?);
130         Ok(private_key.sign(data).to_vec())
131     }
132 
133     /// Verifies `signature` is a valid signature of `data` using `public_key`.
verify( &self, public_key: &SignaturePublicKey, signature: &[u8], data: &[u8], ) -> Result<(), EdDsaError>134     pub fn verify(
135         &self,
136         public_key: &SignaturePublicKey,
137         signature: &[u8],
138         data: &[u8],
139     ) -> Result<(), EdDsaError> {
140         if self.0 != Curve::Ed25519 {
141             return Err(EdDsaError::UnsupportedCipherSuite);
142         }
143         if public_key.len() != ed25519::PUBLIC_KEY_LEN {
144             return Err(EdDsaError::InvalidPubKeyLen {
145                 len: public_key.len(),
146                 expected_len: ed25519::PUBLIC_KEY_LEN,
147             });
148         }
149         if signature.len() != ed25519::SIGNATURE_LEN {
150             return Err(EdDsaError::InvalidSigLen {
151                 len: signature.len(),
152                 expected_len: ed25519::SIGNATURE_LEN,
153             });
154         }
155 
156         let public_key = ed25519::PublicKey::from_bytes(
157             public_key.as_bytes()[..ed25519::PUBLIC_KEY_LEN].try_into()?,
158         );
159         match public_key.verify(data, signature[..ed25519::SIGNATURE_LEN].try_into()?) {
160             Ok(_) => Ok(()),
161             Err(e) => Err(EdDsaError::InvalidSig(e)),
162         }
163     }
164 }
165 
166 #[cfg(all(not(mls_build_async), test))]
167 mod test {
168     use super::{EdDsa, EdDsaError};
169     use crate::test_helpers::decode_hex;
170     use assert_matches::assert_matches;
171     use mls_rs_core::crypto::{CipherSuite, SignaturePublicKey, SignatureSecretKey};
172 
173     #[test]
signature_key_generate()174     fn signature_key_generate() {
175         let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
176         assert!(ed25519.signature_key_generate().is_ok());
177     }
178 
179     #[test]
signature_key_derive_public()180     fn signature_key_derive_public() {
181         // Test 1 from https://www.rfc-editor.org/rfc/rfc8032#section-7.1
182         let private_key = SignatureSecretKey::from(
183             decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
184                 .to_vec(),
185         );
186         let expected_public_key = SignaturePublicKey::from(
187             decode_hex::<32>("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a")
188                 .to_vec(),
189         );
190 
191         let ed25519 = EdDsa::new(CipherSuite::CURVE25519_CHACHA).unwrap();
192         assert_eq!(ed25519.signature_key_derive_public(&private_key).unwrap(), expected_public_key);
193     }
194 
195     #[test]
signature_key_derive_public_invalid_key()196     fn signature_key_derive_public_invalid_key() {
197         let private_key_short =
198             SignatureSecretKey::from(decode_hex::<16>("9d61b19deffd5a60ba844af492ec2cc4").to_vec());
199 
200         let ed25519 = EdDsa::new(CipherSuite::CURVE25519_CHACHA).unwrap();
201         assert_matches!(
202             ed25519.signature_key_derive_public(&private_key_short),
203             Err(EdDsaError::InvalidPrivKeyLen { .. })
204         );
205     }
206 
207     #[test]
sign_verify()208     fn sign_verify() {
209         // Test 3 from https://www.rfc-editor.org/rfc/rfc8032#section-7.1
210         let private_key = SignatureSecretKey::from(
211             decode_hex::<32>("c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7")
212                 .to_vec(),
213         );
214         let data: [u8; 2] = decode_hex("af82");
215         let expected_sig = decode_hex::<64>("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a").to_vec();
216 
217         let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
218         let sig = ed25519.sign(&private_key, &data).unwrap();
219         assert_eq!(sig, expected_sig);
220 
221         let public_key = ed25519.signature_key_derive_public(&private_key).unwrap();
222         assert!(ed25519.verify(&public_key, &sig, &data).is_ok());
223     }
224 
225     #[test]
sign_invalid_key()226     fn sign_invalid_key() {
227         let private_key_short =
228             SignatureSecretKey::from(decode_hex::<16>("c5aa8df43f9f837bedb7442f31dcb7b1").to_vec());
229 
230         let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
231         assert_matches!(
232             ed25519.sign(&private_key_short, &decode_hex::<2>("af82")),
233             Err(EdDsaError::InvalidPrivKeyLen { .. })
234         );
235     }
236 
237     #[test]
verify_invalid_key()238     fn verify_invalid_key() {
239         let public_key_short =
240             SignaturePublicKey::from(decode_hex::<16>("fc51cd8e6218a1a38da47ed00230f058").to_vec());
241         let sig = decode_hex::<64>("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a").to_vec();
242         let data: [u8; 2] = decode_hex("af82");
243 
244         let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
245         assert_matches!(
246             ed25519.verify(&public_key_short, &sig, &data),
247             Err(EdDsaError::InvalidPubKeyLen { .. })
248         );
249     }
250 
251     #[test]
verify_invalid_sig()252     fn verify_invalid_sig() {
253         let public_key = SignaturePublicKey::from(
254             decode_hex::<32>("fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025")
255                 .to_vec(),
256         );
257         let sig_short =
258             decode_hex::<32>("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac")
259                 .to_vec();
260         let data: [u8; 2] = decode_hex("af82");
261 
262         let ed25519 = EdDsa::new(CipherSuite::CURVE25519_AES128).unwrap();
263         assert_matches!(
264             ed25519.verify(&public_key, &sig_short, &data),
265             Err(EdDsaError::InvalidSigLen { .. })
266         );
267     }
268 
269     #[test]
unsupported_cipher_suites()270     fn unsupported_cipher_suites() {
271         for suite in vec![
272             CipherSuite::P256_AES128,
273             CipherSuite::P384_AES256,
274             CipherSuite::P521_AES256,
275             CipherSuite::CURVE448_CHACHA,
276             CipherSuite::CURVE448_AES256,
277         ] {
278             assert_matches!(
279                 EdDsa::new(suite).unwrap().signature_key_generate(),
280                 Err(EdDsaError::UnsupportedCipherSuite)
281             );
282         }
283     }
284 }
285