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