blob: 473b756dc3ed4aa895eeabfbd9c6286c26f2a2f4 [file] [log] [blame]
Cindy Lin6ec3c2b2024-05-16 07:39:23 +00001// 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
17use bssl_crypto::{ed25519, InvalidSignatureError};
18use mls_rs_core::crypto::{CipherSuite, SignaturePublicKey, SignatureSecretKey};
19use mls_rs_crypto_traits::Curve;
20
21use core::array::TryFromSliceError;
22use thiserror::Error;
23
24/// Errors returned from EdDSA.
25#[derive(Debug, Error)]
26pub 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.
63impl From<InvalidSignatureError> for EdDsaError {
64 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)]
71pub struct EdDsa(Curve);
72
73impl EdDsa {
74 /// Creates a new EdDsa.
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.
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.
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`.
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`.
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))]
167mod 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]
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]
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]
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]
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]
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]
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]
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]
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}