xref: /aosp_15_r20/system/authgraph/boringssl/src/ec.rs (revision 4185b0660fbe514985fdcf75410317caad8afad1)
1 // Copyright 2023 Google LLC
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 ////////////////////////////////////////////////////////////////////////////////
16 
17 //! BoringSSL-based implementation for the AuthGraph `Ec` trait.
18 
19 use alloc::borrow::Cow;
20 use alloc::vec::Vec;
21 use authgraph_core::{
22     ag_err, ag_verr,
23     error::Error,
24     key::{
25         check_cose_key_params, EcExchangeKey, EcExchangeKeyPriv, EcExchangeKeyPub, EcSignKey,
26         EcVerifyKey, EcdhSecret, CURVE25519_PRIV_KEY_LEN,
27     },
28     traits, try_to_vec,
29 };
30 use authgraph_wire::ErrorCode;
31 use core::ops::DerefMut;
32 use coset::{cbor::Value, iana, CoseKey, Label};
33 
34 /// Initial byte of SEC1 public key encoding that indicates an uncompressed point.
35 pub const SEC1_UNCOMPRESSED_PREFIX: u8 = 0x04;
36 
37 /// The struct implementing the Authgraph [`EcDh`] trait.
38 pub struct BoringEcDh;
39 
40 impl traits::EcDh for BoringEcDh {
generate_key(&self) -> Result<EcExchangeKey, Error>41     fn generate_key(&self) -> Result<EcExchangeKey, Error> {
42         let (priv_key, pub_key) = create_p256_key_pair(iana::Algorithm::ECDH_ES_HKDF_256)?;
43 
44         Ok(EcExchangeKey {
45             priv_key: EcExchangeKeyPriv(priv_key),
46             pub_key: EcExchangeKeyPub(pub_key),
47         })
48     }
49 
compute_shared_secret( &self, own_key: &EcExchangeKeyPriv, peer_key: &EcExchangeKeyPub, ) -> Result<EcdhSecret, Error>50     fn compute_shared_secret(
51         &self,
52         own_key: &EcExchangeKeyPriv,
53         peer_key: &EcExchangeKeyPub,
54     ) -> Result<EcdhSecret, Error> {
55         let peer_key = p256_ecdh_pkey_from_cose(&peer_key.0)?;
56         let group =
57             ossl!(openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1))?;
58         // This method is an Android modification to the rust-openssl crate.
59         let ec_key = ossl!(openssl::ec::EcKey::private_key_from_der_for_group(&own_key.0, &group))?;
60         let pkey = ossl!(openssl::pkey::PKey::from_ec_key(ec_key))?;
61         let mut deriver = ossl!(openssl::derive::Deriver::new(&pkey))?;
62         ossl!(deriver.set_peer(&peer_key))
63             .map_err(|e| ag_err!(InvalidPeerKeKey, "peer key invalid: {:?}", e))?;
64         let derived = ossl!(deriver.derive_to_vec())?;
65         Ok(EcdhSecret(derived))
66     }
67 }
68 
69 /// The struct implementing the Authgraph [`EcDsa`] trait.
70 pub struct BoringEcDsa;
71 
72 impl traits::EcDsa for BoringEcDsa {
generate_key(&self, curve: iana::EllipticCurve) -> Result<(EcSignKey, EcVerifyKey), Error>73     fn generate_key(&self, curve: iana::EllipticCurve) -> Result<(EcSignKey, EcVerifyKey), Error> {
74         match curve {
75             iana::EllipticCurve::P_256 => create_p256_key_pair(iana::Algorithm::ES256)
76                 .map(|(priv_key, pub_key)| (EcSignKey::P256(priv_key), EcVerifyKey::P256(pub_key))),
77             iana::EllipticCurve::P_384 => create_p384_key_pair(iana::Algorithm::ES384)
78                 .map(|(priv_key, pub_key)| (EcSignKey::P384(priv_key), EcVerifyKey::P384(pub_key))),
79             iana::EllipticCurve::Ed25519 => create_ed25519_key_pair().map(|(priv_key, pub_key)| {
80                 (EcSignKey::Ed25519(priv_key), EcVerifyKey::Ed25519(pub_key))
81             }),
82             _ => Err(ag_err!(Unimplemented, "unexpected curve {curve:?} for ECDSA keygen")),
83         }
84     }
sign(&self, sign_key: &EcSignKey, data: &[u8]) -> Result<Vec<u8>, Error>85     fn sign(&self, sign_key: &EcSignKey, data: &[u8]) -> Result<Vec<u8>, Error> {
86         let pkey;
87         let mut signer = match sign_key {
88             EcSignKey::Ed25519(key) => {
89                 pkey = ossl!(openssl::pkey::PKey::private_key_from_raw_bytes(
90                     key,
91                     openssl::pkey::Id::ED25519
92                 ))?;
93                 // Ed25519 has an internal digest so needs no external digest.
94                 ossl!(openssl::sign::Signer::new_without_digest(&pkey))
95             }
96             EcSignKey::P256(key) => {
97                 let group = ossl!(openssl::ec::EcGroup::from_curve_name(
98                     openssl::nid::Nid::X9_62_PRIME256V1
99                 ))?;
100                 let digest = openssl::hash::MessageDigest::sha256();
101                 let ec_key =
102                     ossl!(openssl::ec::EcKey::private_key_from_der_for_group(key, &group))?;
103                 pkey = ossl!(openssl::pkey::PKey::from_ec_key(ec_key))?;
104                 ossl!(openssl::sign::Signer::new(digest, &pkey))
105             }
106             EcSignKey::P384(key) => {
107                 let group =
108                     ossl!(openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP384R1))?;
109                 // Note that the CDDL for PubKeyECDSA384 specifies SHA-384 as the digest.
110                 let digest = openssl::hash::MessageDigest::sha384();
111                 let ec_key =
112                     ossl!(openssl::ec::EcKey::private_key_from_der_for_group(key, &group))?;
113                 pkey = ossl!(openssl::pkey::PKey::from_ec_key(ec_key))?;
114                 ossl!(openssl::sign::Signer::new(digest, &pkey))
115             }
116         }?;
117         let sig = ossl!(signer.sign_oneshot_to_vec(data))?;
118         match sign_key {
119             EcSignKey::Ed25519(_) => Ok(sig),
120             // BoringSSL emits signatures encoded in an `Ecdsa-Sig-Value` DER structure, as per
121             // RFC 3279 section 2.2.3.  Convert this to R | S form.
122             EcSignKey::P256(_) => ec_der_signature_to_cose(32, &sig),
123             EcSignKey::P384(_) => ec_der_signature_to_cose(48, &sig),
124         }
125     }
126 
verify_signature( &self, verify_key: &EcVerifyKey, data: &[u8], signature: &[u8], ) -> Result<(), Error>127     fn verify_signature(
128         &self,
129         verify_key: &EcVerifyKey,
130         data: &[u8],
131         signature: &[u8],
132     ) -> Result<(), Error> {
133         let signature = match verify_key {
134             EcVerifyKey::Ed25519(_) => Cow::Borrowed(signature),
135             // For NIST curves, convert the R|S format signature to the DER-encoded form that
136             // BoringSSL expects.
137             EcVerifyKey::P256(_) => Cow::Owned(ec_cose_signature_to_der(32, signature)?),
138             EcVerifyKey::P384(_) => Cow::Owned(ec_cose_signature_to_der(48, signature)?),
139         };
140 
141         let pkey;
142         let mut verifier = match verify_key {
143             EcVerifyKey::Ed25519(key) => {
144                 pkey = ed25519_ecdsa_pkey_from_cose(key)?;
145                 ossl!(openssl::sign::Verifier::new_without_digest(&pkey))
146             }
147             EcVerifyKey::P256(key) => {
148                 pkey = p256_ecdsa_pkey_from_cose(key)?;
149                 let digest = openssl::hash::MessageDigest::sha256();
150                 ossl!(openssl::sign::Verifier::new(digest, &pkey))
151             }
152             EcVerifyKey::P384(key) => {
153                 pkey = p384_ecdsa_pkey_from_cose(key)?;
154                 let digest = openssl::hash::MessageDigest::sha384();
155                 ossl!(openssl::sign::Verifier::new(digest, &pkey))
156             }
157         }?;
158         if ossl!(verifier.verify_oneshot(&signature, data))? {
159             Ok(())
160         } else {
161             Err(ag_err!(InvalidSignature, "signature verification failed"))
162         }
163     }
164 }
165 
166 /// Convert a R | S format NIST signature to a DER-encoded form.
ec_cose_signature_to_der(coord_len: usize, signature: &[u8]) -> Result<Vec<u8>, Error>167 fn ec_cose_signature_to_der(coord_len: usize, signature: &[u8]) -> Result<Vec<u8>, Error> {
168     if signature.len() != 2 * coord_len {
169         return Err(ag_err!(
170             InvalidSignature,
171             "expected signature of length {0}+{0}, got {1}",
172             coord_len,
173             signature.len()
174         ));
175     }
176     let r = ossl!(openssl::bn::BigNum::from_slice(&signature[..coord_len]))?;
177     let s = ossl!(openssl::bn::BigNum::from_slice(&signature[coord_len..]))?;
178     let signature = ossl!(openssl::ecdsa::EcdsaSig::from_private_components(r, s))?;
179     ossl!(signature.to_der())
180 }
181 
182 /// Convert a DER-encoded EC NIST signature to R | S form
ec_der_signature_to_cose(coord_len: usize, signature: &[u8]) -> Result<Vec<u8>, Error>183 fn ec_der_signature_to_cose(coord_len: usize, signature: &[u8]) -> Result<Vec<u8>, Error> {
184     let coord_len: i32 = coord_len
185         .try_into()
186         .map_err(|_e| ag_err!(InvalidSignature, "invalid coord len {coord_len}"))?;
187     let signature = ossl!(openssl::ecdsa::EcdsaSig::from_der(signature))?;
188     let mut sig = ossl!(signature.r().to_vec_padded(coord_len))?;
189     sig.extend_from_slice(&ossl!(signature.s().to_vec_padded(coord_len))?);
190     Ok(sig)
191 }
192 
193 /// Create an EC key pair on P-256 curve and return a tuple consisting of the private key bytes
194 /// and the public key as a `CoseKey`.  The `algorithm` parameter indicates the intended use
195 /// for the keypair (ECDH or ECDSA).
create_p256_key_pair(algorithm: iana::Algorithm) -> Result<(Vec<u8>, CoseKey), Error>196 pub fn create_p256_key_pair(algorithm: iana::Algorithm) -> Result<(Vec<u8>, CoseKey), Error> {
197     let group = ossl!(openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1))?;
198     create_nist_key_pair(algorithm, iana::EllipticCurve::P_256, group, 32)
199 }
200 
201 /// Create an EC key pair on P-384 curve and return a tuple consisting of the private key bytes
202 /// and the public key as a `CoseKey`.  The `algorithm` parameter indicates the intended use
203 /// for the keypair (ECDH or ECDSA).
create_p384_key_pair(algorithm: iana::Algorithm) -> Result<(Vec<u8>, CoseKey), Error>204 pub fn create_p384_key_pair(algorithm: iana::Algorithm) -> Result<(Vec<u8>, CoseKey), Error> {
205     let group = ossl!(openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP384R1))?;
206     create_nist_key_pair(algorithm, iana::EllipticCurve::P_384, group, 48)
207 }
208 
create_nist_key_pair( algorithm: iana::Algorithm, curve: iana::EllipticCurve, group: openssl::ec::EcGroup, coord_len: usize, ) -> Result<(Vec<u8>, CoseKey), Error>209 fn create_nist_key_pair(
210     algorithm: iana::Algorithm,
211     curve: iana::EllipticCurve,
212     group: openssl::ec::EcGroup,
213     coord_len: usize,
214 ) -> Result<(Vec<u8>, CoseKey), Error> {
215     let ec_priv_key = ossl!(openssl::ec::EcKey::<openssl::pkey::Private>::generate(&group))?;
216     // Convert the private key to DER-encoded `ECPrivateKey` as per RFC5915 s3, which is
217     // our choice of private key encoding.
218     let priv_key = ossl!(ec_priv_key.private_key_to_der())?;
219 
220     // Get the public key point, and convert to (x, y) coordinates.
221     let pub_key_pt = ec_priv_key.public_key();
222     let mut bn_ctx = ossl!(openssl::bn::BigNumContext::new())?;
223     let pub_key_sec1 = ossl!(pub_key_pt.to_bytes(
224         group.as_ref(),
225         openssl::ec::PointConversionForm::UNCOMPRESSED,
226         bn_ctx.deref_mut()
227     ))?;
228     let (x, y) = crate::ec::coords_from_nist_pub_key(&pub_key_sec1, coord_len)?;
229     let pub_key = coset::CoseKeyBuilder::new_ec2_pub_key(curve, x, y).algorithm(algorithm).build();
230     Ok((priv_key, pub_key))
231 }
232 
create_ed25519_key_pair() -> Result<([u8; CURVE25519_PRIV_KEY_LEN], CoseKey), Error>233 fn create_ed25519_key_pair() -> Result<([u8; CURVE25519_PRIV_KEY_LEN], CoseKey), Error> {
234     let pkey = ossl!(openssl::pkey::PKey::generate_ed25519())?;
235     let priv_key = ossl!(pkey.raw_private_key())?;
236     let priv_key: [u8; CURVE25519_PRIV_KEY_LEN] = priv_key
237         .try_into()
238         .map_err(|e| ag_err!(InternalError, "generated Ed25519 key of unexpected size: {:?}", e))?;
239     let raw_pub_key = ossl!(pkey.raw_public_key())?;
240     let pub_key = coset::CoseKeyBuilder::new_okp_key()
241         .param(iana::OkpKeyParameter::Crv as i64, Value::from(iana::EllipticCurve::Ed25519 as u64))
242         .param(iana::OkpKeyParameter::X as i64, Value::from(raw_pub_key))
243         .algorithm(coset::iana::Algorithm::EdDSA)
244         .build();
245     Ok((priv_key, pub_key))
246 }
247 
248 /// Helper function to return the (x,y) coordinates of a P-256 public key, as a SEC-1 encoded
249 /// uncompressed point. 0x04: uncompressed, followed by x || y coordinates.
coords_from_p256_pub_key(pub_key: &[u8]) -> Result<(Vec<u8>, Vec<u8>), Error>250 pub fn coords_from_p256_pub_key(pub_key: &[u8]) -> Result<(Vec<u8>, Vec<u8>), Error> {
251     coords_from_nist_pub_key(pub_key, 32) // For P-256
252 }
253 
254 /// Helper function to return the (x,y) coordinates of a P-384 public key, as a SEC-1 encoded
255 /// uncompressed point. 0x04: uncompressed, followed by x || y coordinates.
coords_from_p384_pub_key(pub_key: &[u8]) -> Result<(Vec<u8>, Vec<u8>), Error>256 pub fn coords_from_p384_pub_key(pub_key: &[u8]) -> Result<(Vec<u8>, Vec<u8>), Error> {
257     coords_from_nist_pub_key(pub_key, 48) // For P-384
258 }
259 
260 /// Helper function to return the (x,y) coordinates, given the public key as a SEC-1 encoded
261 /// uncompressed point. 0x04: uncompressed, followed by x || y coordinates.
coords_from_nist_pub_key( pub_key: &[u8], coord_len: usize, ) -> Result<(Vec<u8>, Vec<u8>), Error>262 pub fn coords_from_nist_pub_key(
263     pub_key: &[u8],
264     coord_len: usize,
265 ) -> Result<(Vec<u8>, Vec<u8>), Error> {
266     if pub_key.len() != (1 + 2 * coord_len) {
267         return Err(ag_err!(
268             InternalError,
269             "unexpected SEC1 pubkey len of {} for coord_len {coord_len}",
270             pub_key.len(),
271         ));
272     }
273     if pub_key[0] != SEC1_UNCOMPRESSED_PREFIX {
274         return Err(ag_err!(
275             InternalError,
276             "unexpected SEC1 pubkey initial byte {} for coord_len {coord_len}",
277             pub_key[0],
278         ));
279     }
280     Ok((try_to_vec(&pub_key[1..1 + coord_len])?, try_to_vec(&pub_key[1 + coord_len..])?))
281 }
282 
283 /// Convert an ECDH P-256 `COSE_Key` into an [`openssl::pkey::PKey`].
p256_ecdh_pkey_from_cose( cose_key: &coset::CoseKey, ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error>284 fn p256_ecdh_pkey_from_cose(
285     cose_key: &coset::CoseKey,
286 ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error> {
287     nist_pkey_from_cose(
288         cose_key,
289         iana::KeyType::EC2,
290         iana::Algorithm::ECDH_ES_HKDF_256, // ECDH
291         iana::EllipticCurve::P_256,
292         openssl::nid::Nid::X9_62_PRIME256V1,
293         ErrorCode::InvalidPeerKeKey,
294     )
295 }
296 
297 /// Convert an ECDSA P-256 `COSE_Key` into an [`openssl::pkey::PKey`].
p256_ecdsa_pkey_from_cose( cose_key: &coset::CoseKey, ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error>298 fn p256_ecdsa_pkey_from_cose(
299     cose_key: &coset::CoseKey,
300 ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error> {
301     nist_pkey_from_cose(
302         cose_key,
303         iana::KeyType::EC2,
304         iana::Algorithm::ES256, // ECDSA
305         iana::EllipticCurve::P_256,
306         openssl::nid::Nid::X9_62_PRIME256V1,
307         ErrorCode::InvalidCertChain,
308     )
309 }
310 
311 /// Convert an ECDSA P-384 `COSE_Key` into an [`openssl::pkey::PKey`].
p384_ecdsa_pkey_from_cose( cose_key: &coset::CoseKey, ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error>312 fn p384_ecdsa_pkey_from_cose(
313     cose_key: &coset::CoseKey,
314 ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error> {
315     nist_pkey_from_cose(
316         cose_key,
317         iana::KeyType::EC2,
318         iana::Algorithm::ES384,
319         iana::EllipticCurve::P_384,
320         openssl::nid::Nid::SECP384R1,
321         ErrorCode::InvalidCertChain,
322     )
323 }
324 
nist_pkey_from_cose( cose_key: &coset::CoseKey, want_kty: iana::KeyType, want_alg: iana::Algorithm, want_curve: iana::EllipticCurve, nid: openssl::nid::Nid, err_code: ErrorCode, ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error>325 fn nist_pkey_from_cose(
326     cose_key: &coset::CoseKey,
327     want_kty: iana::KeyType,
328     want_alg: iana::Algorithm,
329     want_curve: iana::EllipticCurve,
330     nid: openssl::nid::Nid,
331     err_code: ErrorCode,
332 ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error> {
333     // First check that the COSE_Key looks sensible.
334     check_cose_key_params(cose_key, want_kty, want_alg, want_curve, err_code)?;
335     // Extract (x, y) coordinates (big-endian).
336     let x = cose_key
337         .params
338         .iter()
339         .find_map(|(l, v)| match (l, v) {
340             (Label::Int(l), Value::Bytes(data)) if *l == iana::Ec2KeyParameter::X as i64 => {
341                 Some(data)
342             }
343             _ => None,
344         })
345         .ok_or_else(|| ag_verr!(err_code, "no x coord"))?;
346     let y = cose_key
347         .params
348         .iter()
349         .find_map(|(l, v)| match (l, v) {
350             (Label::Int(l), Value::Bytes(data)) if *l == iana::Ec2KeyParameter::Y as i64 => {
351                 Some(data)
352             }
353             _ => None,
354         })
355         .ok_or_else(|| ag_verr!(err_code, "no y coord"))?;
356 
357     let x = ossl!(openssl::bn::BigNum::from_slice(x))?;
358     let y = ossl!(openssl::bn::BigNum::from_slice(y))?;
359     let group = ossl!(openssl::ec::EcGroup::from_curve_name(nid))?;
360     let ec_key = ossl!(openssl::ec::EcKey::from_public_key_affine_coordinates(&group, &x, &y))?;
361     let pkey = ossl!(openssl::pkey::PKey::from_ec_key(ec_key))?;
362     Ok(pkey)
363 }
364 
365 /// Convert an EdDSA Ed25519 `COSE_Key` into an [`openssl::pkey::PKey`].
ed25519_ecdsa_pkey_from_cose( cose_key: &coset::CoseKey, ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error>366 fn ed25519_ecdsa_pkey_from_cose(
367     cose_key: &coset::CoseKey,
368 ) -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error> {
369     // First check that the COSE_Key looks sensible.
370     check_cose_key_params(
371         cose_key,
372         iana::KeyType::OKP,
373         iana::Algorithm::EdDSA,
374         iana::EllipticCurve::Ed25519,
375         ErrorCode::InvalidCertChain,
376     )?;
377     // Extract x coordinate (little-endian).
378     let x = cose_key
379         .params
380         .iter()
381         .find_map(|(l, v)| match (l, v) {
382             (Label::Int(l), Value::Bytes(data)) if *l == iana::OkpKeyParameter::X as i64 => {
383                 Some(data)
384             }
385             _ => None,
386         })
387         .ok_or_else(|| ag_err!(InvalidCertChain, "no x coord"))?;
388 
389     let pkey =
390         ossl!(openssl::pkey::PKey::public_key_from_raw_bytes(x, openssl::pkey::Id::ED25519))?;
391     Ok(pkey)
392 }
393 
394 #[cfg(test)]
395 mod tests {
396     use super::*;
397     use authgraph_core::traits::EcDsa;
398 
399     #[test]
test_p256_keygen()400     fn test_p256_keygen() {
401         let ecdsa = BoringEcDsa;
402         let (priv_key, pub_key) = ecdsa.generate_key(iana::EllipticCurve::P_256).unwrap();
403         assert!(matches!(priv_key, EcSignKey::P256(_)));
404         let cose_key = if let EcVerifyKey::P256(cose_key) = pub_key {
405             cose_key
406         } else {
407             panic!("Unexpected pub key variant from generate_key()");
408         };
409         assert!(p256_ecdsa_pkey_from_cose(&cose_key).is_ok());
410     }
411 
412     #[test]
test_p384_keygen()413     fn test_p384_keygen() {
414         let ecdsa = BoringEcDsa;
415         let (priv_key, pub_key) = ecdsa.generate_key(iana::EllipticCurve::P_384).unwrap();
416         assert!(matches!(priv_key, EcSignKey::P384(_)));
417         let cose_key = if let EcVerifyKey::P384(cose_key) = pub_key {
418             cose_key
419         } else {
420             panic!("Unexpected pub key variant from generate_key()");
421         };
422         assert!(p384_ecdsa_pkey_from_cose(&cose_key).is_ok());
423     }
424 
425     #[test]
test_ed25519_keygen()426     fn test_ed25519_keygen() {
427         let ecdsa = BoringEcDsa;
428         let (priv_key, pub_key) = ecdsa.generate_key(iana::EllipticCurve::Ed25519).unwrap();
429         assert!(matches!(priv_key, EcSignKey::Ed25519(_)));
430         let cose_key = if let EcVerifyKey::Ed25519(cose_key) = pub_key {
431             cose_key
432         } else {
433             panic!("Unexpected pub key variant from generate_key()");
434         };
435         assert!(ed25519_ecdsa_pkey_from_cose(&cose_key).is_ok());
436     }
437 
438     #[test]
test_signature_convert()439     fn test_signature_convert() {
440         let tests = [
441             (32,
442              concat!(
443                  "3045", // SEQUENCE
444                  "0220", // INTEGER len x20
445                  "2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
446                  "0221", // INTEGER len x21
447                  "00",   // leading zero to avoid appearing negative
448                  "b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
449              ),
450              concat!(
451                  "2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
452                  "b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
453              ),
454             ),
455             (48,
456              concat!(
457                  "3065",  // SEQUENCE
458                  "0230",  // INTEGER len x30
459                  "12b30abef6b5476fe6b612ae557c0425661e26b44b1bfe19daf2ca28e3113083ba8e4ae4cc45a0320abd3394f1c548d7",
460                  "0231",  // INTEGER len x31
461                  "00",    // leading zero
462                  "e7bf25603e2d07076ff30b7a2abec473da8b11c572b35fc631991d5de62ddca7525aaba89325dfd04fecc47bff426f82",
463                  ),
464              concat!(
465                  "12b30abef6b5476fe6b612ae557c0425661e26b44b1bfe19daf2ca28e3113083ba8e4ae4cc45a0320abd3394f1c548d7",
466                  "e7bf25603e2d07076ff30b7a2abec473da8b11c572b35fc631991d5de62ddca7525aaba89325dfd04fecc47bff426f82",
467               ),
468             ),
469             (32,
470              concat!(
471                  "3044", // SEQUENCE
472                  "021f", // INTEGER len x1f
473                  "2ba8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
474                  "0221", // INTEGER len x21
475                  "00",   // leading zero to avoid appearing negative
476                  "b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
477              ),
478              concat!(
479                  "002ba8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
480                  "b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
481              ),
482             ),
483         ];
484         for (coord_len, der_sig, cose_sig) in tests {
485             let sig = hex::decode(der_sig).unwrap();
486             let got = ec_der_signature_to_cose(coord_len, &sig).unwrap();
487             assert_eq!(hex::encode(&got), cose_sig, "DER->COSE mismatch for {der_sig}");
488 
489             let got = ec_cose_signature_to_der(coord_len, &got).unwrap();
490             assert_eq!(hex::encode(&got), der_sig, "COSE->DER mismatch for {cose_sig}");
491         }
492     }
493 
494     #[test]
test_der_signature_convert_fail()495     fn test_der_signature_convert_fail() {
496         let tests = [
497             (
498                 32,
499                 concat!(
500                     "3044", // SEQUENCE **wrong len**
501                     "0220", // INTEGER len x20
502                     "2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
503                     "0221", // INTEGER len x21
504                     "00",   // leading zero to avoid appearing negative
505                     "b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
506                 ),
507             ),
508             (
509                 48,
510                 concat!(
511                     "3065",  // SEQUENCE
512                     "0231",  // INTEGER len x30  **wrong len**
513                     "12b30abef6b5476fe6b612ae557c0425661e26b44b1bfe19daf2ca28e3113083ba8e4ae4cc45a0320abd3394f1c548d7",
514                     "0231",  // INTEGER len x31
515                     "00",    // leading zero
516                     "e7bf25603e2d07076ff30b7a2abec473da8b11c572b35fc631991d5de62ddca7525aaba89325dfd04fecc47bff426f82",
517                 ),
518             ),
519             (
520                 32,
521                 concat!(
522                     "3044", // SEQUENCE
523                     "021f", // INTEGER len x1f **wrong len**
524                     "002ba8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
525                     "0221", // INTEGER len x21
526                     "00",   // leading zero to avoid appearing negative
527                     "b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
528                 ),
529             ),
530             (
531                 32,
532                 concat!(
533                     "3044", // SEQUENCE
534                     "0220", // INTEGER len x20  **unnecessary leading 00**
535                     "002ba8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
536                     "0221", // INTEGER len x21
537                     "00",   // leading zero to avoid appearing negative
538                     "b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
539                 ),
540             ),
541             (
542                 1 + i32::MAX as usize,
543                 concat!(
544                     "3045", // SEQUENCE
545                     "0220", // INTEGER len x20
546                     "2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
547                     "0221", // INTEGER len x21
548                     "00",   // leading zero to avoid appearing negative
549                     "b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
550                 ),
551             ),
552         ];
553         for (coord_len, der_sig) in tests {
554             let sig = hex::decode(der_sig).unwrap();
555             assert!(
556                 ec_der_signature_to_cose(coord_len, &sig).is_err(),
557                 "expected DER decode error for {der_sig}"
558             );
559         }
560     }
561 
562     #[test]
test_cose_signature_convert_fail()563     fn test_cose_signature_convert_fail() {
564         let tests = [
565             (
566                 32,  // data too long
567                 concat!(
568                     "112ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
569                     "11b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
570                 ),
571             ),
572             (
573                 32, // data too short
574                 concat!(
575                     "2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18",
576                     "29f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db",
577                 ),
578             ),
579             (
580                 48, // data too long
581                 concat!(
582                     "b30abef6b5476fe6b612ae557c0425661e26b44b1bfe19daf2ca28e3113083ba8e4ae4cc45a0320abd3394f1c548d7",
583                     "e7bf25603e2d07076ff30b7a2abec473da8b11c572b35fc631991d5de62ddca7525aaba89325dfd04fecc47bff426f82",
584                 ),
585             ),
586         ];
587         for (coord_len, cose_sig) in tests {
588             let sig = hex::decode(cose_sig).unwrap();
589             assert!(
590                 ec_cose_signature_to_der(coord_len, &sig).is_err(),
591                 "expected COSE decode error for {cose_sig}"
592             );
593         }
594     }
595 }
596