Skip to main content

ssi_cose/
key.rs

1use coset::{
2    iana::{self, EnumI64},
3    CoseKey, KeyType, Label,
4};
5use ssi_claims_core::{ProofValidationError, SignatureError};
6use ssi_crypto::{
7    rand::{CryptoRng, RngCore},
8    PublicKey, SecretKey,
9};
10use std::borrow::Cow;
11
12use crate::{
13    algorithm::{algorithm_name, instantiate_algorithm, preferred_algorithm},
14    CoseSigner, CoseSignerInfo,
15};
16
17/// COSE key resolver.
18pub trait CoseKeyResolver {
19    /// Fetches the COSE key associated to the give identifier.
20    #[allow(async_fn_in_trait)]
21    async fn fetch_public_cose_key(
22        &'_ self,
23        id: Option<&[u8]>,
24    ) -> Result<Cow<'_, CoseKey>, ProofValidationError>;
25}
26
27impl<T: CoseKeyResolver> CoseKeyResolver for &T {
28    async fn fetch_public_cose_key(
29        &'_ self,
30        id: Option<&[u8]>,
31    ) -> Result<Cow<'_, CoseKey>, ProofValidationError> {
32        T::fetch_public_cose_key(*self, id).await
33    }
34}
35
36impl CoseKeyResolver for CoseKey {
37    async fn fetch_public_cose_key(
38        &'_ self,
39        _id: Option<&[u8]>,
40    ) -> Result<Cow<'_, CoseKey>, ProofValidationError> {
41        Ok(Cow::Borrowed(self))
42    }
43}
44
45impl CoseSigner for CoseKey {
46    async fn fetch_info(&self) -> Result<CoseSignerInfo, ssi_claims_core::SignatureError> {
47        Ok(CoseSignerInfo {
48            algorithm: preferred_algorithm(self).map(Cow::into_owned),
49            key_id: self.key_id.clone(),
50        })
51    }
52
53    async fn sign_bytes(&self, signing_bytes: &[u8]) -> Result<Vec<u8>, SignatureError> {
54        let algorithm = preferred_algorithm(self).ok_or(SignatureError::MissingAlgorithm)?;
55        let secret_key = self.decode_secret()?;
56        secret_key
57            .sign(
58                instantiate_algorithm(&algorithm).ok_or_else(|| {
59                    SignatureError::UnsupportedAlgorithm(algorithm_name(&algorithm))
60                })?,
61                signing_bytes,
62            )
63            .map_err(Into::into)
64    }
65}
66
67#[derive(Debug, thiserror::Error)]
68pub enum KeyDecodingError {
69    #[error("unsupported key type")]
70    UnsupportedKeyType(KeyType),
71
72    #[error("missing parameter")]
73    MissingParam(Label),
74
75    #[error("invalid parameter")]
76    InvalidParam(Label),
77
78    #[error("unsupported parameter value")]
79    UnsupportedParam(Label, ciborium::Value),
80
81    #[error("invalid key")]
82    InvalidKey,
83}
84
85impl From<ssi_crypto::key::InvalidPublicKey> for KeyDecodingError {
86    fn from(_value: ssi_crypto::key::InvalidPublicKey) -> Self {
87        Self::InvalidKey
88    }
89}
90
91impl From<ssi_crypto::key::InvalidSecretKey> for KeyDecodingError {
92    fn from(_value: ssi_crypto::key::InvalidSecretKey) -> Self {
93        Self::InvalidKey
94    }
95}
96
97impl From<KeyDecodingError> for ssi_claims_core::SignatureError {
98    fn from(_value: KeyDecodingError) -> Self {
99        Self::InvalidSecretKey
100    }
101}
102
103/// Decode COSE keys.
104pub trait CoseKeyDecode {
105    /// Reads a key parameter, if it exists.
106    fn fetch_param(&self, label: &Label) -> Option<&ciborium::Value>;
107
108    /// Requires the given key parameter.
109    ///
110    /// Returns an error if the key parameter is not present in the key.
111    fn require_param(&self, label: &Label) -> Result<&ciborium::Value, KeyDecodingError> {
112        self.fetch_param(label)
113            .ok_or_else(|| KeyDecodingError::MissingParam(label.clone()))
114    }
115
116    /// Requires and parses the given key parameter.
117    ///
118    /// Returns an error if the key parameter is not present in the key, or
119    /// if the parsing function `f` returns `None`.
120    fn parse_required_param<'a, T>(
121        &'a self,
122        label: &Label,
123        f: impl FnOnce(&'a ciborium::Value) -> Option<T>,
124    ) -> Result<T, KeyDecodingError> {
125        f(self.require_param(label)?).ok_or_else(|| KeyDecodingError::InvalidParam(label.clone()))
126    }
127
128    /// Decodes the COSE key as a public key.
129    fn decode_public(&self) -> Result<ssi_crypto::PublicKey, KeyDecodingError>;
130
131    /// Decodes the COSE key as a secret key.
132    fn decode_secret(&self) -> Result<ssi_crypto::SecretKey, KeyDecodingError>;
133}
134
135impl CoseKeyDecode for CoseKey {
136    /// Fetch a key parameter.
137    fn fetch_param(&self, label: &Label) -> Option<&ciborium::Value> {
138        self.params
139            .iter()
140            .find_map(|(l, value)| if l == label { Some(value) } else { None })
141    }
142
143    fn decode_public(&self) -> Result<ssi_crypto::PublicKey, KeyDecodingError> {
144        match &self.kty {
145            t @ KeyType::Assigned(kty) => {
146                match kty {
147                    // Octet Key Pair.
148                    iana::KeyType::OKP => {
149                        let crv = self.parse_required_param(&OKP_CRV, |v| {
150                            v.as_integer().and_then(|i| i64::try_from(i).ok())
151                        })?;
152
153                        #[allow(unused_variables)]
154                        let x = self.parse_required_param(&OKP_X, ciborium::Value::as_bytes)?;
155
156                        match iana::EllipticCurve::from_i64(crv) {
157                            #[cfg(feature = "ed25519")]
158                            Some(iana::EllipticCurve::Ed25519) => {
159                                ssi_crypto::PublicKey::new_ed25519(x).map_err(Into::into)
160                            }
161                            _ => Err(KeyDecodingError::UnsupportedParam(EC2_CRV, crv.into())),
162                        }
163                    }
164                    // Double Coordinate Curves.
165                    // See: <https://datatracker.ietf.org/doc/html/rfc8152#section-13.1.1>
166                    iana::KeyType::EC2 => {
167                        let crv = self.parse_required_param(&EC2_CRV, |v| {
168                            v.as_integer().and_then(|i| i64::try_from(i).ok())
169                        })?;
170
171                        #[allow(unused_variables)]
172                        let x = self.parse_required_param(&EC2_X, ciborium::Value::as_bytes)?;
173
174                        #[allow(unused_variables)]
175                        let y = self.parse_required_param(
176                            &EC2_Y,
177                            ciborium::Value::as_bytes, // TODO: this can be a `bool`
178                        )?;
179
180                        match iana::EllipticCurve::from_i64(crv) {
181                            #[cfg(feature = "secp256k1")]
182                            Some(iana::EllipticCurve::Secp256k1) => {
183                                ssi_crypto::PublicKey::new_secp256k1(x, y).map_err(Into::into)
184                            }
185                            #[cfg(feature = "secp256r1")]
186                            Some(iana::EllipticCurve::P_256) => {
187                                ssi_crypto::PublicKey::new_p256(x, y).map_err(Into::into)
188                            }
189                            #[cfg(feature = "secp384r1")]
190                            Some(iana::EllipticCurve::P_384) => {
191                                ssi_crypto::PublicKey::new_p384(x, y).map_err(Into::into)
192                            }
193                            _ => Err(KeyDecodingError::UnsupportedParam(EC2_CRV, crv.into())),
194                        }
195                    }
196                    _ => Err(KeyDecodingError::UnsupportedKeyType(t.clone())),
197                }
198            }
199            other => Err(KeyDecodingError::UnsupportedKeyType(other.clone())),
200        }
201    }
202
203    fn decode_secret(&self) -> Result<ssi_crypto::SecretKey, KeyDecodingError> {
204        match &self.kty {
205            t @ KeyType::Assigned(kty) => {
206                match kty {
207                    // Octet Key Pair.
208                    iana::KeyType::OKP => {
209                        let crv = self.parse_required_param(&OKP_CRV, |v| {
210                            v.as_integer().and_then(|i| i64::try_from(i).ok())
211                        })?;
212
213                        #[allow(unused_variables)]
214                        let d = self.parse_required_param(&OKP_D, ciborium::Value::as_bytes)?;
215
216                        match iana::EllipticCurve::from_i64(crv) {
217                            #[cfg(feature = "ed25519")]
218                            Some(iana::EllipticCurve::Ed25519) => {
219                                ssi_crypto::SecretKey::new_ed25519(d).map_err(Into::into)
220                            }
221                            _ => Err(KeyDecodingError::UnsupportedParam(EC2_CRV, crv.into())),
222                        }
223                    }
224                    // Double Coordinate Curves.
225                    // See: <https://datatracker.ietf.org/doc/html/rfc8152#section-13.1.1>
226                    iana::KeyType::EC2 => {
227                        let crv = self.parse_required_param(&EC2_CRV, |v| {
228                            v.as_integer().and_then(|i| i64::try_from(i).ok())
229                        })?;
230
231                        #[allow(unused_variables)]
232                        let d = self.parse_required_param(&EC2_D, ciborium::Value::as_bytes)?;
233
234                        match iana::EllipticCurve::from_i64(crv) {
235                            #[cfg(feature = "secp256k1")]
236                            Some(iana::EllipticCurve::Secp256k1) => {
237                                ssi_crypto::SecretKey::new_secp256k1(d).map_err(Into::into)
238                            }
239                            #[cfg(feature = "secp256r1")]
240                            Some(iana::EllipticCurve::P_256) => {
241                                ssi_crypto::SecretKey::new_p256(d).map_err(Into::into)
242                            }
243                            #[cfg(feature = "secp384r1")]
244                            Some(iana::EllipticCurve::P_384) => {
245                                ssi_crypto::SecretKey::new_p384(d).map_err(Into::into)
246                            }
247                            _ => Err(KeyDecodingError::UnsupportedParam(EC2_CRV, crv.into())),
248                        }
249                    }
250                    _ => Err(KeyDecodingError::UnsupportedKeyType(t.clone())),
251                }
252            }
253            other => Err(KeyDecodingError::UnsupportedKeyType(other.clone())),
254        }
255    }
256}
257
258pub const OKP_CRV: Label = Label::Int(iana::OkpKeyParameter::Crv as i64);
259pub const OKP_X: Label = Label::Int(iana::OkpKeyParameter::X as i64);
260pub const OKP_D: Label = Label::Int(iana::OkpKeyParameter::D as i64);
261
262pub const EC2_CRV: Label = Label::Int(iana::Ec2KeyParameter::Crv as i64);
263pub const EC2_X: Label = Label::Int(iana::Ec2KeyParameter::X as i64);
264pub const EC2_Y: Label = Label::Int(iana::Ec2KeyParameter::Y as i64);
265pub const EC2_D: Label = Label::Int(iana::Ec2KeyParameter::D as i64);
266
267#[derive(Debug, thiserror::Error)]
268pub enum KeyEncodingError {
269    #[error("unsupported key type")]
270    UnsupportedKeyType,
271}
272
273/// COSE key encoding
274pub trait CoseKeyEncode: Sized {
275    fn encode_public(key: &PublicKey) -> Result<CoseKey, KeyEncodingError>;
276
277    fn encode_public_with_id(key: &PublicKey, id: Vec<u8>) -> Result<CoseKey, KeyEncodingError> {
278        let mut cose_key = Self::encode_public(key)?;
279        cose_key.key_id = id;
280        Ok(cose_key)
281    }
282
283    fn encode_secret(key: &SecretKey) -> Result<CoseKey, KeyEncodingError>;
284
285    fn encode_secret_with_id(key: &SecretKey, id: Vec<u8>) -> Result<CoseKey, KeyEncodingError> {
286        let mut cose_key = Self::encode_secret(key)?;
287        cose_key.key_id = id;
288        Ok(cose_key)
289    }
290}
291
292impl CoseKeyEncode for CoseKey {
293    fn encode_public(key: &PublicKey) -> Result<Self, KeyEncodingError> {
294        match key {
295            #[cfg(feature = "ed25519")]
296            PublicKey::Ed25519(key) => Ok(Self {
297                kty: KeyType::Assigned(iana::KeyType::OKP),
298                params: vec![
299                    (OKP_CRV, iana::EllipticCurve::Ed25519.to_i64().into()),
300                    (OKP_X, key.as_bytes().to_vec().into()),
301                ],
302                ..Default::default()
303            }),
304            #[cfg(feature = "secp256k1")]
305            PublicKey::Secp256k1(key) => {
306                use ssi_crypto::k256::elliptic_curve::sec1::ToEncodedPoint;
307                let encoded_point = key.to_encoded_point(false);
308                Ok(Self {
309                    kty: KeyType::Assigned(iana::KeyType::EC2),
310                    params: vec![
311                        (EC2_CRV, iana::EllipticCurve::Secp256k1.to_i64().into()),
312                        (EC2_X, encoded_point.x().unwrap().to_vec().into()),
313                        (EC2_Y, encoded_point.y().unwrap().to_vec().into()),
314                    ],
315                    ..Default::default()
316                })
317            }
318            #[cfg(feature = "secp256r1")]
319            PublicKey::P256(key) => {
320                use ssi_crypto::p256::elliptic_curve::sec1::ToEncodedPoint;
321                let encoded_point = key.to_encoded_point(false);
322                Ok(Self {
323                    kty: KeyType::Assigned(iana::KeyType::EC2),
324                    params: vec![
325                        (EC2_CRV, iana::EllipticCurve::P_256.to_i64().into()),
326                        (EC2_X, encoded_point.x().unwrap().to_vec().into()),
327                        (EC2_Y, encoded_point.y().unwrap().to_vec().into()),
328                    ],
329                    ..Default::default()
330                })
331            }
332            #[cfg(feature = "secp384r1")]
333            PublicKey::P384(key) => {
334                use ssi_crypto::p384::elliptic_curve::sec1::ToEncodedPoint;
335                let encoded_point = key.to_encoded_point(false);
336                Ok(Self {
337                    kty: KeyType::Assigned(iana::KeyType::EC2),
338                    params: vec![
339                        (EC2_CRV, iana::EllipticCurve::P_384.to_i64().into()),
340                        (EC2_X, encoded_point.x().unwrap().to_vec().into()),
341                        (EC2_Y, encoded_point.y().unwrap().to_vec().into()),
342                    ],
343                    ..Default::default()
344                })
345            }
346            _ => Err(KeyEncodingError::UnsupportedKeyType),
347        }
348    }
349
350    fn encode_secret(key: &SecretKey) -> Result<Self, KeyEncodingError> {
351        match key {
352            #[cfg(feature = "ed25519")]
353            SecretKey::Ed25519(key) => {
354                let public_key = key.verifying_key();
355                Ok(Self {
356                    kty: KeyType::Assigned(iana::KeyType::OKP),
357                    params: vec![
358                        (OKP_CRV, iana::EllipticCurve::Ed25519.to_i64().into()),
359                        (OKP_X, public_key.as_bytes().to_vec().into()),
360                        (OKP_D, key.to_bytes().to_vec().into()),
361                    ],
362                    ..Default::default()
363                })
364            }
365            #[cfg(feature = "secp256k1")]
366            SecretKey::Secp256k1(key) => {
367                use ssi_crypto::k256::elliptic_curve::sec1::ToEncodedPoint;
368                let public_key = key.public_key();
369                let encoded_point = public_key.to_encoded_point(false);
370                Ok(Self {
371                    kty: KeyType::Assigned(iana::KeyType::EC2),
372                    params: vec![
373                        (EC2_CRV, iana::EllipticCurve::Secp256k1.to_i64().into()),
374                        (EC2_X, encoded_point.x().unwrap().to_vec().into()),
375                        (EC2_Y, encoded_point.y().unwrap().to_vec().into()),
376                        (EC2_D, key.to_bytes().to_vec().into()),
377                    ],
378                    ..Default::default()
379                })
380            }
381            #[cfg(feature = "secp256r1")]
382            SecretKey::P256(key) => {
383                use ssi_crypto::p256::elliptic_curve::sec1::ToEncodedPoint;
384                let public_key = key.public_key();
385                let encoded_point = public_key.to_encoded_point(false);
386                Ok(Self {
387                    kty: KeyType::Assigned(iana::KeyType::EC2),
388                    params: vec![
389                        (EC2_CRV, iana::EllipticCurve::P_256.to_i64().into()),
390                        (EC2_X, encoded_point.x().unwrap().to_vec().into()),
391                        (EC2_Y, encoded_point.y().unwrap().to_vec().into()),
392                        (EC2_D, key.to_bytes().to_vec().into()),
393                    ],
394                    ..Default::default()
395                })
396            }
397            #[cfg(feature = "secp384r1")]
398            SecretKey::P384(key) => {
399                use ssi_crypto::p384::elliptic_curve::sec1::ToEncodedPoint;
400                let public_key = key.public_key();
401                let encoded_point = public_key.to_encoded_point(false);
402                Ok(Self {
403                    kty: KeyType::Assigned(iana::KeyType::EC2),
404                    params: vec![
405                        (EC2_CRV, iana::EllipticCurve::P_384.to_i64().into()),
406                        (EC2_X, encoded_point.x().unwrap().to_vec().into()),
407                        (EC2_Y, encoded_point.y().unwrap().to_vec().into()),
408                        (EC2_D, key.to_bytes().to_vec().into()),
409                    ],
410                    ..Default::default()
411                })
412            }
413            _ => Err(KeyEncodingError::UnsupportedKeyType),
414        }
415    }
416}
417
418pub trait CoseKeyGenerate {
419    #[cfg(feature = "ed25519")]
420    fn generate_ed25519() -> Self;
421
422    #[cfg(feature = "ed25519")]
423    fn generate_ed25519_from(rng: &mut (impl RngCore + CryptoRng)) -> Self;
424
425    #[cfg(feature = "secp256k1")]
426    fn generate_secp256k1() -> Self;
427
428    #[cfg(feature = "secp256k1")]
429    fn generate_secp256k1_from(rng: &mut (impl RngCore + CryptoRng)) -> Self;
430
431    #[cfg(feature = "secp256r1")]
432    fn generate_p256() -> Self;
433
434    #[cfg(feature = "secp256r1")]
435    fn generate_p256_from(rng: &mut (impl RngCore + CryptoRng)) -> Self;
436
437    #[cfg(feature = "secp384r1")]
438    fn generate_p384() -> Self;
439
440    #[cfg(feature = "secp384r1")]
441    fn generate_p384_from(rng: &mut (impl RngCore + CryptoRng)) -> Self;
442}
443
444impl CoseKeyGenerate for CoseKey {
445    #[cfg(feature = "ed25519")]
446    fn generate_ed25519() -> Self {
447        Self::encode_secret(&ssi_crypto::SecretKey::generate_ed25519()).unwrap()
448    }
449
450    #[cfg(feature = "ed25519")]
451    fn generate_ed25519_from(rng: &mut (impl RngCore + CryptoRng)) -> Self {
452        Self::encode_secret(&ssi_crypto::SecretKey::generate_ed25519_from(rng)).unwrap()
453    }
454
455    #[cfg(feature = "secp256k1")]
456    fn generate_secp256k1() -> Self {
457        Self::encode_secret(&ssi_crypto::SecretKey::generate_secp256k1()).unwrap()
458    }
459
460    #[cfg(feature = "secp256k1")]
461    fn generate_secp256k1_from(rng: &mut (impl RngCore + CryptoRng)) -> Self {
462        Self::encode_secret(&ssi_crypto::SecretKey::generate_secp256k1_from(rng)).unwrap()
463    }
464
465    #[cfg(feature = "secp256r1")]
466    fn generate_p256() -> Self {
467        Self::encode_secret(&ssi_crypto::SecretKey::generate_p256()).unwrap()
468    }
469
470    #[cfg(feature = "secp256r1")]
471    fn generate_p256_from(rng: &mut (impl RngCore + CryptoRng)) -> Self {
472        Self::encode_secret(&ssi_crypto::SecretKey::generate_p256_from(rng)).unwrap()
473    }
474
475    #[cfg(feature = "secp384r1")]
476    fn generate_p384() -> Self {
477        Self::encode_secret(&ssi_crypto::SecretKey::generate_p384()).unwrap()
478    }
479
480    #[cfg(feature = "secp384r1")]
481    fn generate_p384_from(rng: &mut (impl RngCore + CryptoRng)) -> Self {
482        Self::encode_secret(&ssi_crypto::SecretKey::generate_p384_from(rng)).unwrap()
483    }
484}
485
486#[cfg(test)]
487mod tests {
488    use super::{CoseKeyDecode, CoseKeyEncode};
489    use coset::{CborSerializable, CoseKey};
490    use ssi_crypto::{PublicKey, SecretKey};
491
492    /// Public secp256k1 key.
493    ///
494    /// ```cbor-diagnostic
495    /// {
496    ///   1: 1,
497    ///   -1: 6,
498    ///   -2: h'8816d41001dd1a9ddea1232381b2eede803161e88ebb19eaf573d393dec800a7'
499    /// }
500    /// ```
501    #[cfg(feature = "ed25519")]
502    #[test]
503    fn public_ed25519_1() {
504        let input = hex::decode(
505            "a3010120062158208816d41001dd1a9ddea1232381b2eede803161e88ebb19eaf573d393dec800a7",
506        )
507        .unwrap();
508        let cose_key = CoseKey::from_slice(&input).unwrap();
509        let key = cose_key.decode_public().unwrap();
510        assert!(matches!(key, PublicKey::Ed25519(_)));
511        assert_eq!(
512            CoseKey::encode_public_with_id(&key, cose_key.key_id.clone()).unwrap(),
513            cose_key
514        )
515    }
516
517    /// Secret secp256k1 key.
518    ///
519    /// ```cbor-diagnostic
520    /// {
521    ///   1: 1,
522    ///   -1: 6,
523    ///   -2: h'8816d41001dd1a9ddea1232381b2eede803161e88ebb19eaf573d393dec800a7',
524    ///   -4: h'e25df1249ab766fc5a8c9f98d5e311cd4f7d5fd1c6b6a2032adc973056c87dc3'
525    /// }
526    /// ```
527    #[cfg(feature = "ed25519")]
528    #[test]
529    fn secret_ed25519_1() {
530        let input = hex::decode("a4010120062158208816d41001dd1a9ddea1232381b2eede803161e88ebb19eaf573d393dec800a7235820e25df1249ab766fc5a8c9f98d5e311cd4f7d5fd1c6b6a2032adc973056c87dc3").unwrap();
531        let cose_key = CoseKey::from_slice(&input).unwrap();
532        let key = cose_key.decode_secret().unwrap();
533        assert!(matches!(key, SecretKey::Ed25519(_)));
534        assert_eq!(
535            CoseKey::encode_secret_with_id(&key, cose_key.key_id.clone()).unwrap(),
536            cose_key
537        )
538    }
539
540    /// Secret secp256k1 key.
541    ///
542    /// ```cbor-diagnostic
543    /// {
544    ///   1: 2,
545    ///   -1: 8,
546    ///   -2: h'394fd5a1e33b8a67d5fa9ddca42d261219dde202e65bbf07bf2f671e157ac41f',
547    ///   -3: h'199d7db667e74905c8371168b815c267db76243fbfd387fa5f2d8a691099a89a'
548    /// }
549    /// ```
550    #[cfg(feature = "secp256k1")]
551    #[test]
552    fn public_secp256k1_1() {
553        let input = hex::decode("a401022008215820394fd5a1e33b8a67d5fa9ddca42d261219dde202e65bbf07bf2f671e157ac41f225820199d7db667e74905c8371168b815c267db76243fbfd387fa5f2d8a691099a89a").unwrap();
554        let cose_key = CoseKey::from_slice(&input).unwrap();
555        let key = cose_key.decode_public().unwrap();
556        assert!(matches!(key, PublicKey::Secp256k1(_)));
557        assert_eq!(
558            CoseKey::encode_public_with_id(&key, cose_key.key_id.clone()).unwrap(),
559            cose_key
560        )
561    }
562
563    /// Secret secp256k1 key.
564    ///
565    /// ```cbor-diagnostic
566    /// {
567    ///   1: 2,
568    ///   -1: 8,
569    ///   -2: h'394fd5a1e33b8a67d5fa9ddca42d261219dde202e65bbf07bf2f671e157ac41f',
570    ///   -3: h'199d7db667e74905c8371168b815c267db76243fbfd387fa5f2d8a691099a89a',
571    ///   -4: h'3e0fada8be75e5e47ab4c1c91c3f8f9185d1e18a2a16b3400a1eb33c9cdf8b96'
572    /// }
573    /// ```
574    #[cfg(feature = "secp256k1")]
575    #[test]
576    fn secret_secp256k1_1() {
577        let input = hex::decode("a501022008215820394fd5a1e33b8a67d5fa9ddca42d261219dde202e65bbf07bf2f671e157ac41f225820199d7db667e74905c8371168b815c267db76243fbfd387fa5f2d8a691099a89a2358203e0fada8be75e5e47ab4c1c91c3f8f9185d1e18a2a16b3400a1eb33c9cdf8b96").unwrap();
578        let cose_key = CoseKey::from_slice(&input).unwrap();
579        let key = cose_key.decode_secret().unwrap();
580        assert!(matches!(key, SecretKey::Secp256k1(_)));
581        assert_eq!(
582            CoseKey::encode_secret_with_id(&key, cose_key.key_id.clone()).unwrap(),
583            cose_key
584        )
585    }
586
587    /// A public EC (P-256) key with a `kid` of
588    /// "meriadoc.brandybuck@buckland.example".
589    ///
590    /// See: <https://www.rfc-editor.org/rfc/rfc9052.html#appendix-C.7.1>
591    #[cfg(feature = "secp256r1")]
592    #[test]
593    fn public_p256_1() {
594        let input = hex::decode("a5200121582065eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d2258201e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c01020258246d65726961646f632e6272616e64796275636b406275636b6c616e642e6578616d706c65").unwrap();
595        let cose_key = CoseKey::from_slice(&input).unwrap();
596        let key = cose_key.decode_public().unwrap();
597        assert!(matches!(key, PublicKey::P256(_)));
598        assert_eq!(
599            CoseKey::encode_public_with_id(&key, cose_key.key_id.clone()).unwrap(),
600            cose_key
601        )
602    }
603
604    /// A secret EC (P-256) key with a kid of
605    /// "meriadoc.brandybuck@buckland.example".
606    ///
607    /// See: <https://www.rfc-editor.org/rfc/rfc9052.html#appendix-C.7.2>
608    #[cfg(feature = "secp256r1")]
609    #[test]
610    fn secret_p256_1() {
611        let input = hex::decode("a601020258246d65726961646f632e6272616e64796275636b406275636b6c616e642e6578616d706c65200121582065eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d2258201e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c235820aff907c99f9ad3aae6c4cdf21122bce2bd68b5283e6907154ad911840fa208cf").unwrap();
612        let cose_key = CoseKey::from_slice(&input).unwrap();
613        let key = cose_key.decode_secret().unwrap();
614        assert!(matches!(key, SecretKey::P256(_)));
615        assert_eq!(
616            CoseKey::encode_secret_with_id(&key, cose_key.key_id.clone()).unwrap(),
617            cose_key
618        )
619    }
620
621    /// A public EC (P-256) key with a kid of "11".
622    ///
623    /// See: <https://www.rfc-editor.org/rfc/rfc9052.html#appendix-C.7.1>
624    #[cfg(feature = "secp256r1")]
625    #[test]
626    fn public_p256_2() {
627        let input = hex::decode("a52001215820bac5b11cad8f99f9c72b05cf4b9e26d244dc189f745228255a219a86d6a09eff22582020138bf82dc1b6d562be0fa54ab7804a3a64b6d72ccfed6b6fb6ed28bbfc117e010202423131").unwrap();
628        let cose_key = CoseKey::from_slice(&input).unwrap();
629        let key = cose_key.decode_public().unwrap();
630        assert!(matches!(key, PublicKey::P256(_)));
631        assert_eq!(
632            CoseKey::encode_public_with_id(&key, cose_key.key_id.clone()).unwrap(),
633            cose_key
634        )
635    }
636
637    /// A secret EC (P-256) key with a kid of "11".
638    ///
639    /// See: <https://www.rfc-editor.org/rfc/rfc9052.html#appendix-C.7.2>
640    #[cfg(feature = "secp256r1")]
641    #[test]
642    fn secret_p256_2() {
643        let input = hex::decode("a60102024231312001215820bac5b11cad8f99f9c72b05cf4b9e26d244dc189f745228255a219a86d6a09eff22582020138bf82dc1b6d562be0fa54ab7804a3a64b6d72ccfed6b6fb6ed28bbfc117e23582057c92077664146e876760c9520d054aa93c3afb04e306705db6090308507b4d3").unwrap();
644        let cose_key = CoseKey::from_slice(&input).unwrap();
645        let key = cose_key.decode_secret().unwrap();
646        assert!(matches!(key, SecretKey::P256(_)));
647        assert_eq!(
648            CoseKey::encode_secret_with_id(&key, cose_key.key_id.clone()).unwrap(),
649            cose_key
650        )
651    }
652
653    /// A public EC (P-256) key with a kid of "peregrin.took@tuckborough.example".
654    ///
655    /// See: <https://www.rfc-editor.org/rfc/rfc9052.html#appendix-C.7.1>
656    #[cfg(feature = "secp256r1")]
657    #[test]
658    fn public_p256_3() {
659        let input = hex::decode("a5200121582098f50a4ff6c05861c8860d13a638ea56c3f5ad7590bbfbf054e1c7b4d91d6280225820f01400b089867804b8e9fc96c3932161f1934f4223069170d924b7e03bf822bb0102025821706572656772696e2e746f6f6b407475636b626f726f7567682e6578616d706c65").unwrap();
660        let cose_key = CoseKey::from_slice(&input).unwrap();
661        let key = cose_key.decode_public().unwrap();
662        assert!(matches!(key, PublicKey::P256(_)));
663        assert_eq!(
664            CoseKey::encode_public_with_id(&key, cose_key.key_id.clone()).unwrap(),
665            cose_key
666        )
667    }
668
669    /// A secret EC (P-256) key with a kid of
670    /// "peregrin.took@tuckborough.example".
671    ///
672    /// See: <https://www.rfc-editor.org/rfc/rfc9052.html#appendix-C.7.2>
673    #[cfg(feature = "secp256r1")]
674    #[test]
675    fn secret_p256_3() {
676        let input = hex::decode("a601022001025821706572656772696e2e746f6f6b407475636b626f726f7567682e6578616d706c6521582098f50a4ff6c05861c8860d13a638ea56c3f5ad7590bbfbf054e1c7b4d91d6280225820f01400b089867804b8e9fc96c3932161f1934f4223069170d924b7e03bf822bb23582002d1f7e6f26c43d4868d87ceb2353161740aacf1f7163647984b522a848df1c3").unwrap();
677        let cose_key = CoseKey::from_slice(&input).unwrap();
678        let key = cose_key.decode_secret().unwrap();
679        assert!(matches!(key, SecretKey::P256(_)));
680        assert_eq!(
681            CoseKey::encode_secret_with_id(&key, cose_key.key_id.clone()).unwrap(),
682            cose_key
683        )
684    }
685
686    /// A public EC (P-384) key.
687    ///
688    /// ```cbor-diagnostic
689    /// {
690    ///   1: 2,
691    ///   -1: 2,
692    ///   -2: h'fa1d31d39853d37fbfd145675635d52795f5feb3eacf11371ad8c6eb30c6f2493b0ec74d8c5b5a20ebf68ce3e0bd2c07',
693    ///   -3: h'7c2b27b366e4fc73b79d28bac0b18ae2f2b0c4e7849656a71aac8987e60af5af57a9af3faf206afc798fa5fb06db15aa'
694    /// }
695    /// ```
696    #[cfg(feature = "secp384r1")]
697    #[test]
698    fn public_p384_1() {
699        let input = hex::decode("a401022002215830fa1d31d39853d37fbfd145675635d52795f5feb3eacf11371ad8c6eb30c6f2493b0ec74d8c5b5a20ebf68ce3e0bd2c072258307c2b27b366e4fc73b79d28bac0b18ae2f2b0c4e7849656a71aac8987e60af5af57a9af3faf206afc798fa5fb06db15aa").unwrap();
700        let cose_key = CoseKey::from_slice(&input).unwrap();
701        let key = cose_key.decode_public().unwrap();
702        assert!(matches!(key, PublicKey::P384(_)));
703        assert_eq!(
704            CoseKey::encode_public_with_id(&key, cose_key.key_id.clone()).unwrap(),
705            cose_key
706        )
707    }
708
709    /// A secret EC (P-384) key.
710    ///
711    /// ```cbor-diagnostic
712    /// {
713    ///   1: 2,
714    ///   -1: 2,
715    ///   -2: h'fa1d31d39853d37fbfd145675635d52795f5feb3eacf11371ad8c6eb30c6f2493b0ec74d8c5b5a20ebf68ce3e0bd2c07',
716    ///   -3: h'7c2b27b366e4fc73b79d28bac0b18ae2f2b0c4e7849656a71aac8987e60af5af57a9af3faf206afc798fa5fb06db15aa',
717    ///   -4: h'21d8eb2250cdaa19bfb01f03211be11a70ef4739650ed954166531808aa254c1d6d968b36d16184d350600253fa672c0'
718    /// }
719    /// ```
720    #[cfg(feature = "secp384r1")]
721    #[test]
722    fn secret_p384_1() {
723        let input = hex::decode("a501022002215830fa1d31d39853d37fbfd145675635d52795f5feb3eacf11371ad8c6eb30c6f2493b0ec74d8c5b5a20ebf68ce3e0bd2c072258307c2b27b366e4fc73b79d28bac0b18ae2f2b0c4e7849656a71aac8987e60af5af57a9af3faf206afc798fa5fb06db15aa23583021d8eb2250cdaa19bfb01f03211be11a70ef4739650ed954166531808aa254c1d6d968b36d16184d350600253fa672c0").unwrap();
724        let cose_key = CoseKey::from_slice(&input).unwrap();
725        let key = cose_key.decode_secret().unwrap();
726        assert!(matches!(key, SecretKey::P384(_)));
727        assert_eq!(
728            CoseKey::encode_secret_with_id(&key, cose_key.key_id.clone()).unwrap(),
729            cose_key
730        )
731    }
732}