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}