dcaf/common/cbor_values/
mod.rs

1/*
2 * Copyright (c) 2022 The NAMIB Project Developers.
3 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 * option. This file may not be copied, modified, or distributed
7 * except according to those terms.
8 *
9 * SPDX-License-Identifier: MIT OR Apache-2.0
10 */
11
12//! Contains various helper values for CBOR structures.
13//!
14//! For example, this contains an enum representing a [`ProofOfPossessionKey`].
15//!
16//! # Example
17//! One of the main use cases of the [`ProofOfPossessionKey`]
18//! is for representing a key in the `cnf` claim:
19//! ```
20//! # use dcaf::AccessTokenResponse;
21//! # use dcaf::endpoints::token_req::AccessTokenResponseBuilderError;
22//! # use dcaf::common::cbor_values::{ByteString, ProofOfPossessionKey};
23//! let response: AccessTokenResponse = AccessTokenResponse::builder()
24//!     .access_token(vec![0xDC, 0xAF, 0xDC, 0xAF])
25//!     .cnf(ProofOfPossessionKey::KeyId(vec![0x42])).build()?;
26//! # Ok::<(), AccessTokenResponseBuilderError>(())
27//! ```
28
29use core::fmt::{Debug, Display, Formatter};
30use core::ops::Deref;
31
32use coset::{CoseEncrypt0, CoseKey};
33use strum_macros::IntoStaticStr;
34
35#[cfg(not(feature = "std"))]
36use {alloc::boxed::Box, alloc::format, alloc::vec, alloc::vec::Vec};
37
38#[cfg(test)]
39mod tests;
40
41/// A type intended to be used as a CBOR bytestring, represented as a vector of bytes.
42pub type ByteString = Vec<u8>;
43
44/// A Key ID, represented as a [`ByteString`].
45pub(crate) type KeyId = ByteString;
46
47/// Wrapper around a type `T` which can be created from and turned into an [`i32`].
48pub(crate) struct CborMapValue<T>(pub(crate) T)
49where
50    i32: Into<T>,
51    T: Into<i32> + Copy;
52
53/// A proof-of-possession key as specified by
54/// [RFC 8747, section 3.1](https://datatracker.ietf.org/doc/html/rfc8747#section-3.1).
55///
56/// Can either be a COSE key, an encrypted COSE key, or simply a key ID.
57/// As described in [RFC 9201](https://www.rfc-editor.org/rfc/rfc9201),
58/// PoP keys are used for the `req_cnf` parameter in [`AccessTokenRequest`](crate::AccessTokenRequest),
59/// as well as for the `cnf` and `rs_cnf` parameters in [`AccessTokenResponse`](crate::AccessTokenResponse).
60///
61/// # Example
62/// We showcase creation of an [`AccessTokenRequest`](crate::AccessTokenRequest) in which we set `req_cnf` to a PoP key
63/// with an ID of 0xDCAF which the access token shall be bound to:
64/// ```
65/// # use dcaf::AccessTokenRequest;
66/// # use dcaf::common::cbor_values::{ByteString, ProofOfPossessionKey};
67/// # use dcaf::endpoints::token_req::AccessTokenRequestBuilderError;
68/// let key = ProofOfPossessionKey::KeyId(vec![0xDC, 0xAF]);
69/// let request: AccessTokenRequest = AccessTokenRequest::builder().client_id("test_client").req_cnf(key).build()?;
70/// assert_eq!(request.req_cnf.unwrap().key_id().to_vec(), vec![0xDC, 0xAF]);
71/// # Ok::<(), AccessTokenRequestBuilderError>(())
72/// ```
73#[derive(Debug, PartialEq, Clone, IntoStaticStr)]
74#[allow(clippy::large_enum_variant)] // size difference of ~300 bytes is acceptable
75pub enum ProofOfPossessionKey {
76    /// An unencrypted [`CoseKey`](coset::CoseKey) used to represent an asymmetric public key or
77    /// (if the CWT it's contained in is encrypted) a symmetric key.
78    ///
79    /// For details, see [section 3.2 of RFC 8747](https://datatracker.ietf.org/doc/html/rfc8747#section-3.2).
80    PlainCoseKey(CoseKey),
81
82    /// An encrypted [`CoseKey`](coset::CoseKey) used to represent a symmetric key.
83    ///
84    /// For details, see [section 3.3 of RFC 8747](https://datatracker.ietf.org/doc/html/rfc8747#section-3.3).
85    EncryptedCoseKey(CoseEncrypt0),
86
87    /// Key ID of the actual proof-of-possession key.
88    ///
89    /// Note that as described in [section 6 of RFC 8747](https://datatracker.ietf.org/doc/html/rfc8747#section-6),
90    /// certain caveats apply when choosing to represent a proof-of-possession key by its Key ID.
91    ///
92    /// For details, see [section 3.4 of RFC 8747](https://datatracker.ietf.org/doc/html/rfc8747#section-3.4).
93    KeyId(KeyId),
94}
95
96impl ProofOfPossessionKey {
97    /// Returns the key ID of this PoP key, cloning it if necessary.
98    /// Note that the returned key ID may be empty if no key ID was present in the key.
99    ///
100    /// # Example
101    /// ```
102    /// # use coset::CoseKeyBuilder;
103    /// # use dcaf::common::cbor_values::ProofOfPossessionKey;
104    /// let key = CoseKeyBuilder::new_symmetric_key(vec![0; 5]).key_id(vec![0xDC, 0xAF]).build();
105    /// let pop_key = ProofOfPossessionKey::from(key);
106    /// assert_eq!(pop_key.key_id().to_vec(), vec![0xDC, 0xAF]);
107    /// ```
108    #[must_use]
109    pub fn key_id(&self) -> &KeyId {
110        match self {
111            ProofOfPossessionKey::PlainCoseKey(k) => &k.key_id,
112            ProofOfPossessionKey::KeyId(k) => k,
113            ProofOfPossessionKey::EncryptedCoseKey(k) => {
114                if k.protected.header.key_id.is_empty() {
115                    &k.unprotected.key_id
116                } else {
117                    &k.protected.header.key_id
118                }
119            }
120        }
121    }
122}
123
124impl<T> Deref for CborMapValue<T>
125where
126    T: From<i32> + Into<i32> + Copy,
127{
128    type Target = T;
129
130    fn deref(&self) -> &Self::Target {
131        &self.0
132    }
133}
134
135impl<T> Display for CborMapValue<T>
136where
137    i32: Into<T>,
138    T: Into<i32> + Copy + Display,
139{
140    fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
141        write!(f, "{}", self.0)
142    }
143}
144
145/// Contains various `From`, `TryFrom` and other conversion methods for types of the parent module.
146mod conversion {
147    use ciborium::value::Value;
148    use coset::{AsCborValue, CoseEncrypt0, CoseKey};
149    use erased_serde::Serialize as ErasedSerialize;
150    use serde::de::Error;
151    use serde::{Deserialize, Deserializer, Serialize, Serializer};
152
153    use crate::common::cbor_map::ToCborMap;
154    use crate::error::{TryFromCborMapError, WrongSourceTypeError};
155
156    use super::*;
157
158    impl<T> Serialize for CborMapValue<T>
159    where
160        T: From<i32> + Into<i32> + Copy,
161    {
162        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
163        where
164            S: Serializer,
165        {
166            let cbor_value: i32 = self.0.into();
167            Value::from(cbor_value).serialize(serializer)
168        }
169    }
170
171    impl<'de, T> Deserialize<'de> for CborMapValue<T>
172    where
173        T: From<i32> + Into<i32> + Copy,
174    {
175        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
176        where
177            D: Deserializer<'de>,
178        {
179            if let Ok(Value::Integer(i)) = Value::deserialize(deserializer) {
180                Ok(CborMapValue(
181                    i32::try_from(i)
182                        .map_err(|_| D::Error::custom("CBOR map key too high for i32"))?
183                        .into(),
184                ))
185            } else {
186                Err(D::Error::custom("CBOR map value must be an Integer"))
187            }
188        }
189    }
190
191    impl ToCborMap for ProofOfPossessionKey {
192        fn to_cbor_map(&self) -> Vec<(i128, Option<Box<dyn ErasedSerialize + '_>>)> {
193            // The fact that we have to clone this is a little unfortunate.
194            match self {
195                Self::PlainCoseKey(key) => {
196                    let x: i128 = 1;
197                    vec![(
198                        x,
199                        Some(Box::new(key.clone().to_cbor_value().expect("Invalid key"))),
200                    )]
201                }
202                Self::EncryptedCoseKey(enc) => {
203                    let x: i128 = 2;
204                    vec![(
205                        x,
206                        Some(Box::new(
207                            (*enc).clone().to_cbor_value().expect("Invalid key"),
208                        )),
209                    )]
210                }
211                Self::KeyId(kid) => {
212                    let x: i128 = 3;
213                    vec![(x, Some(Box::new(Value::Bytes(kid.clone()))))]
214                }
215            }
216        }
217
218        fn try_from_cbor_map(map: Vec<(i128, Value)>) -> Result<Self, TryFromCborMapError>
219        where
220            Self: Sized + ToCborMap,
221        {
222            if map.len() != 1 {
223                Err(TryFromCborMapError::from_message(
224                    "given CBOR map must contain exactly one element",
225                ))
226            } else if let Some(entry) = map.into_iter().next() {
227                match entry {
228                    (1, x) => CoseKey::from_cbor_value(x)
229                        .map(ProofOfPossessionKey::PlainCoseKey)
230                        .map_err(|x| {
231                            TryFromCborMapError::from_message(format!(
232                                "couldn't create CoseKey from CBOR value: {x}"
233                            ))
234                        }),
235                    (2, x) => CoseEncrypt0::from_cbor_value(x)
236                        .map(ProofOfPossessionKey::EncryptedCoseKey)
237                        .map_err(|x| {
238                            TryFromCborMapError::from_message(format!(
239                                "couldn't create CoseEncrypt0 from CBOR value: {x}"
240                            ))
241                        }),
242                    (3, Value::Bytes(x)) => Ok(ProofOfPossessionKey::KeyId(x)),
243                    (x, _) => Err(TryFromCborMapError::unknown_field(u8::try_from(x)?)),
244                }
245            } else {
246                unreachable!(
247                    "we have previously verified that map.len() == 1, \
248                so map.into_iter().next() must return a next element"
249                )
250            }
251        }
252    }
253
254    impl From<CoseKey> for ProofOfPossessionKey {
255        fn from(key: CoseKey) -> Self {
256            ProofOfPossessionKey::PlainCoseKey(key)
257        }
258    }
259
260    impl From<ByteString> for ProofOfPossessionKey {
261        fn from(kid: ByteString) -> Self {
262            ProofOfPossessionKey::KeyId(kid)
263        }
264    }
265
266    impl From<CoseEncrypt0> for ProofOfPossessionKey {
267        fn from(enc: CoseEncrypt0) -> Self {
268            ProofOfPossessionKey::EncryptedCoseKey(enc)
269        }
270    }
271
272    impl TryFrom<ProofOfPossessionKey> for CoseKey {
273        type Error = WrongSourceTypeError<ProofOfPossessionKey>;
274
275        fn try_from(
276            value: ProofOfPossessionKey,
277        ) -> Result<Self, WrongSourceTypeError<ProofOfPossessionKey>> {
278            if let ProofOfPossessionKey::PlainCoseKey(key) = value {
279                Ok(key)
280            } else {
281                Err(WrongSourceTypeError::new("PlainCoseKey", value.into()))
282            }
283        }
284    }
285
286    impl TryFrom<ProofOfPossessionKey> for CoseEncrypt0 {
287        type Error = WrongSourceTypeError<ProofOfPossessionKey>;
288
289        fn try_from(
290            value: ProofOfPossessionKey,
291        ) -> Result<Self, WrongSourceTypeError<ProofOfPossessionKey>> {
292            if let ProofOfPossessionKey::EncryptedCoseKey(key) = value {
293                Ok(key)
294            } else {
295                Err(WrongSourceTypeError::new("EncryptedCoseKey", value.into()))
296            }
297        }
298    }
299
300    impl TryFrom<ProofOfPossessionKey> for KeyId {
301        type Error = WrongSourceTypeError<ProofOfPossessionKey>;
302
303        fn try_from(
304            value: ProofOfPossessionKey,
305        ) -> Result<Self, WrongSourceTypeError<ProofOfPossessionKey>> {
306            if let ProofOfPossessionKey::KeyId(kid) = value {
307                Ok(kid)
308            } else {
309                Err(WrongSourceTypeError::new("KeyId", value.into()))
310            }
311        }
312    }
313}