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