1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/*
* Copyright (c) 2022 The NAMIB Project Developers.
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*
* SPDX-License-Identifier: MIT OR Apache-2.0
*/
//! An implementation of the [ACE-OAuth framework (RFC 9200)](https://www.rfc-editor.org/rfc/rfc9200).
//!
//! This crate implements the ACE-OAuth
//! (Authentication and Authorization for Constrained Environments using the OAuth 2.0 Framework)
//! framework as defined in [RFC 9200](https://www.rfc-editor.org/rfc/rfc9200).
//! Key features include CBOR-(de-)serializable data models such as [`AccessTokenRequest`](crate::endpoints::token_req::AccessTokenRequest),
//! as well as the possibility to create COSE encrypted/signed access tokens
//! (as described in the standard) along with decryption/verification functions.
//! Implementations of the cryptographic functions must be provided by the user by implementing
//! [`CoseEncryptCipher`](crate::token::CoseEncryptCipher) or [`CoseSignCipher`](crate::token::CoseSignCipher).
//!
//! Note that actually transmitting the serialized values (e.g., via CoAP) or providing more complex
//! features not mentioned in the ACE-OAuth RFC (e.g., a permission management system for
//! the Authorization Server) is *out of scope* for this crate.
//! This also applies to cryptographic functions, as mentioned in the previous paragraph.
//!
//! The name DCAF was chosen because eventually, it's planned for this crate to support
//! functionality from the [Delegated CoAP Authentication and Authorization Framework (DCAF)](https://dcaf.science/)
//! specified in [`draft-gerdes-ace-dcaf-authorize`](https://datatracker.ietf.org/doc/html/draft-gerdes-ace-dcaf-authorize-04)
//! (which was specified prior to ACE-OAuth and inspired many design choices in it)---
//! specifically, it's planned to support using a CAM (Client Authorization Manager)
//! instead of just a SAM (Server Authorization Manager), as is done in ACE-OAuth.
//! Compatibility with the existing [DCAF implementation in C](https://gitlab.informatik.uni-bremen.de/DCAF/dcaf)
//! (which we'll call `libdcaf` to disambiguate from `dcaf` referring to this crate) is also an
//! additional design goal, though the primary objective is still to support ACE-OAuth.
//!
//! As one of the possible use-cases for this crate is usage on constrained IoT devices,
//! requirements are minimal---as such, while `alloc` is still needed, this crate offers
//! `no_std` support by omitting the default `std` feature.
//!
//! # Usage
//! ```toml
//! [dependencies]
//! dcaf = { version = "^0.3" }
//! ```
//! Or, if you plan to use this crate in a `no_std` environment:
//! ```toml
//! [dependencies]
//! dcaf = { version = "^0.3", default-features = false }
//! ```
//!
//! # Example
//! As mentioned, the main features of this crate are ACE-OAuth data models and
//! token creation/verification functions. We'll quickly introduce both of these here.
//!
//! ## Data models
//! [For example](https://www.rfc-editor.org/rfc/rfc9200#figure-6),
//! let's assume you (the client) want to request an access token from an Authorization Server.
//! For this, you'd need to create an [`AccessTokenRequest`](crate::endpoints::token_req::AccessTokenRequest), which has to include at least a
//! `client_id`. We'll also specify an audience, a scope (using [`TextEncodedScope`](crate::common::scope::TextEncodedScope)---note that
//! [binary-encoded scopes](crate::common::scope::BinaryEncodedScope) or [AIF-encoded scopes](crate::common::scope::AifEncodedScope) would also work), as well as a
//! [`ProofOfPossessionKey`](crate::common::cbor_values::ProofOfPossessionKey) (the key the access token should be bound to) in the `req_cnf` field.
//!
//! Creating, serializing and then de-serializing such a structure would look like this:
//! ```
//! # use std::error::Error;
//! use dcaf::{AccessTokenRequest, ToCborMap, ProofOfPossessionKey, TextEncodedScope};
//!
//! # #[cfg(feature = "std")] {
//! let request = AccessTokenRequest::builder()
//! .client_id("myclient")
//! .audience("valve242")
//! .scope(TextEncodedScope::try_from("read")?)
//! .req_cnf(ProofOfPossessionKey::KeyId(base64::decode("6kg0dXJM13U")?))
//! .build()?;
//! let mut encoded = Vec::new();
//! request.clone().serialize_into(&mut encoded)?;
//! assert_eq!(AccessTokenRequest::deserialize_from(encoded.as_slice())?, request);
//! # }
//! # Ok::<(), Box<dyn Error>>(())
//! ```
//!
//! ## Access Tokens
//! Following up from the previous example, let's assume we now want to create a signed
//! access token containing the existing `key`, as well as claims about the audience and issuer
//! of the token, using an existing cipher of type `FakeCrypto`[^cipher]:
//! ```
//! # use ciborium::value::Value;
//! # use coset::{AsCborValue, CoseKey, CoseKeyBuilder, Header, iana, Label, ProtectedHeader};
//! # use coset::cwt::{ClaimsSetBuilder, Timestamp};
//! # use coset::iana::{Algorithm, CwtClaimName};
//! # use rand::{CryptoRng, RngCore};
//! # use dcaf::{ToCborMap, sign_access_token, verify_access_token, CoseSignCipher};
//! # use dcaf::common::cbor_values::{ByteString, ProofOfPossessionKey};
//! # use dcaf::common::cbor_values::ProofOfPossessionKey::PlainCoseKey;
//! # use dcaf::error::{AccessTokenError, CoseCipherError};
//! use dcaf::token::CoseCipher;
//!
//! # struct FakeCrypto {}
//! #
//! # #[derive(Clone, Copy)]
//! # pub(crate) struct FakeRng;
//! #
//! # fn get_k_from_key(key: &CoseKey) -> Option<Vec<u8>> {
//! # const K_PARAM: i64 = iana::SymmetricKeyParameter::K as i64;
//! # for (label, value) in key.params.iter() {
//! # if let Label::Int(K_PARAM) = label {
//! # if let Value::Bytes(k_val) = value {
//! # return Some(k_val.clone());
//! # }
//! # }
//! # }
//! # None
//! # }
//! #
//! # impl RngCore for FakeRng {
//! # fn next_u32(&mut self) -> u32 {
//! # 0
//! # }
//! #
//! # fn next_u64(&mut self) -> u64 {
//! # 0
//! # }
//! #
//! # fn fill_bytes(&mut self, dest: &mut [u8]) {
//! # dest.fill(0);
//! # }
//! #
//! # fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
//! # dest.fill(0);
//! # Ok(())
//! # }
//! # }
//! #
//! # impl CryptoRng for FakeRng {}
//! #
//! # impl CoseCipher for FakeCrypto {
//! # type Error = String;
//! #
//! # fn set_headers<RNG: RngCore + CryptoRng>(key: &CoseKey, unprotected_header: &mut Header, protected_header: &mut Header, rng: RNG) -> Result<(), CoseCipherError<Self::Error>> {
//! # // We have to later verify these headers really are used.
//! # if let Some(label) = unprotected_header
//! # .rest
//! # .iter()
//! # .find(|x| x.0 == Label::Int(47))
//! # {
//! # return Err(CoseCipherError::existing_header_label(&label.0));
//! # }
//! # if protected_header.alg != None {
//! # return Err(CoseCipherError::existing_header("alg"));
//! # }
//! # unprotected_header.rest.push((Label::Int(47), Value::Null));
//! # protected_header.alg = Some(coset::Algorithm::Assigned(Algorithm::Direct));
//! # Ok(())
//! # }
//! # }
//! #
//! # /// Implements basic operations from the [`CoseSignCipher`](crate::token::CoseSignCipher) trait
//! # /// without actually using any "real" cryptography.
//! # /// This is purely to be used for testing and obviously offers no security at all.
//! # impl CoseSignCipher for FakeCrypto {
//! # fn sign(
//! # key: &CoseKey,
//! # target: &[u8],
//! # unprotected_header: &Header,
//! # protected_header: &Header,
//! # ) -> Vec<u8> {
//! # // We simply append the key behind the data.
//! # let mut signature = target.to_vec();
//! # let k = get_k_from_key(key);
//! # signature.append(&mut k.expect("k must be present in key!"));
//! # signature
//! # }
//! #
//! # fn verify(
//! # key: &CoseKey,
//! # signature: &[u8],
//! # signed_data: &[u8],
//! # unprotected_header: &Header,
//! # protected_header: &ProtectedHeader,
//! # unprotected_signature_header: Option<&Header>,
//! # protected_signature_header: Option<&ProtectedHeader>,
//! # ) -> Result<(), CoseCipherError<Self::Error>> {
//! # if signature
//! # == Self::sign(
//! # key,
//! # signed_data,
//! # unprotected_header,
//! # &protected_header.header,
//! # )
//! # {
//! # Ok(())
//! # } else {
//! # Err(CoseCipherError::VerificationFailure)
//! # }
//! # }
//! # }
//!
//! let rng = FakeRng;
//! let key = CoseKeyBuilder::new_symmetric_key(vec![1,2,3,4,5]).key_id(vec![0xDC, 0xAF]).build();
//! let claims = ClaimsSetBuilder::new()
//! .audience(String::from("coaps://rs.example.com"))
//! .issuer(String::from("coaps://as.example.com"))
//! .claim(CwtClaimName::Cnf, key.clone().to_cbor_value()?)
//! .build();
//! let token = sign_access_token::<FakeCrypto, FakeRng>(&key, claims, None, None, None, rng)?;
//! assert!(verify_access_token::<FakeCrypto>(&key, &token, None).is_ok());
//! # Ok::<(), AccessTokenError<String>>(())
//! ```
//!
//! [^cipher]: Note that we are deliberately omitting details about the implementation of the
//! `cipher` here, since such implementations won't be in the scope of this crate.
//!
//! # Provided Data Models
//!
//! ## Token Endpoint
//! The most commonly used models will probably be the token endpoint's
//! [`AccessTokenRequest`](crate::endpoints::token_req::AccessTokenRequest) and
//! [`AccessTokenResponse`](crate::endpoints::token_req::AccessTokenResponse)
//! described in [section 5.8 of RFC 9200](https://www.rfc-editor.org/rfc/rfc9200#section-5.8).
//! In case of an error, an [`ErrorResponse`](crate::endpoints::token_req::ErrorResponse)
//! should be used.
//!
//! After an initial Unauthorized Resource Request Message, an
//! [`AuthServerRequestCreationHint`](crate::endpoints::creation_hint::AuthServerRequestCreationHint)
//! can be used to provide additional information to the client, as described in
//! [section 5.3 of RFC 9200](https://www.rfc-editor.org/rfc/rfc9200#section-5.3).
//!
//! ## Common Data Types
//! Some types used across multiple scenarios include:
//! - [`Scope`](crate::common::scope::Scope) (as described in
//! [section 5.8.1 of RFC 9200](https://www.rfc-editor.org/rfc/rfc9200#section-5.8.1)),
//! either as a [`TextEncodedScope`](crate::common::scope::TextEncodedScope),
//! a [`BinaryEncodedScope`](crate::common::scope::BinaryEncodedScope) or
//! an [`AifEncodedScope`](crate::common::scope::AifEncodedScope).
//! - [`ProofOfPossessionKey`](crate::common::cbor_values::ProofOfPossessionKey) as specified in
//! [section 3.1 of RFC 8747](https://www.rfc-editor.org/rfc/rfc8747#section-3.1).
//! For example, this will be used in the access token's `cnf` claim.
//! - While not really a data type, various constants representing values used in ACE-OAuth
//! are provided in the [`constants`](crate::common::constants) module.
//!
//! # Creating Access Tokens
//! In order to create access tokens, you can use either [`encrypt_access_token`](crate::token::encrypt_access_token)
//! or [`sign_access_token`](crate::token::sign_access_token),
//! depending on whether you want the access token to be wrapped in a
//! `COSE_Encrypt0` or `COSE_Sign1` structure. Support for a combination of both is planned for the
//! future. In case you want to create a token intended for multiple recipients (each with their
//! own key), you can use [`encrypt_access_token_multiple`](crate::token::encrypt_access_token_multiple)
//! or [`sign_access_token_multiple`](crate::token::sign_access_token_multiple).
//!
//! Both functions take a [`ClaimsSet`](coset::cwt::ClaimsSet) containing the claims that
//! shall be part of the access token, a key used to encrypt or sign the token,
//! optional `aad` (additional authenticated data), un-/protected headers and a cipher (explained
//! further below) identified by type parameter `T`.
//! Note that if the headers you pass in set fields which the cipher wants to set as well,
//! the function will fail with a `HeaderAlreadySet` error.
//! The function will return a [`Result`](::core::result::Result) of the opaque
//! [`ByteString`](crate::common::cbor_values::ByteString) containing the access token.
//!
//! # Verifying and Decrypting Access Tokens
//! In order to verify or decrypt existing access tokens represented as [`ByteString`](crate::common::cbor_values::ByteString)s,
//! use [`verify_access_token`](crate::token::verify_access_token) or
//! [`decrypt_access_token`](crate::token::decrypt_access_token) respectively.
//! In case the token was created for multiple recipients (each with their own key),
//! use [`verify_access_token_multiple`](crate::token::verify_access_token_multiple)
//! or [`decrypt_access_token_multiple`](crate::token::decrypt_access_token_multiple).
//!
//! Both functions take the access token, a `key` used to decrypt or verify, optional `aad`
//! (additional authenticated data) and a cipher implementing cryptographic operations identified
//! by type parameter `T`.
//!
//! [`decrypt_access_token`](crate::token::decrypt_access_token) will return a result containing
//! the decrypted [`ClaimsSet`](coset::cwt::ClaimsSet).
//! [`verify_access_token`](crate::token::verify_access_token) will return an empty result which
//! indicates that the token was successfully verified---an [`Err`](::core::result::Result)
//! would indicate failure.
//!
//! # Extracting Headers from an Access Token
//! Regardless of whether a token was signed, encrypted, or MAC-tagged, you can extract its
//! headers using [`get_token_headers`](crate::token::get_token_headers),
//! which will return an option containing both
//! unprotected and protected headers (or which will be [`None`](core::option::Option::None) in case
//! the token is invalid).
//!
//! # COSE Cipher
//! As mentioned before, cryptographic functions are outside the scope of this crate.
//! For this reason, the various COSE cipher traits exist; namely,
//! [`CoseEncryptCipher`](token::CoseEncryptCipher), [`CoseSignCipher`](token::CoseSignCipher),
//! and [`CoseMacCipher`](token::CoseMacCipher), each implementing
//! a corresponding COSE operation as specified in sections 4, 5, and 6 of
//! [RFC 8152](https://www.rfc-editor.org/rfc/rfc8152).
//! There are also the traits [`MultipleEncryptCipher`](token::MultipleEncryptCipher),
//! [`MultipleSignCipher`](token::MultipleSignCipher), and
//! [`MultipleMacCipher`](token::MultipleMacCipher),
//! which are used for creating tokens intended for multiple recipients.
//!
//! Note that these ciphers *don't* need to wrap their results in, e.g.,
//! a `Cose_Encrypt0` structure, as this part is already handled by this library
//! (which uses [`coset`](coset))---only the cryptographic algorithms themselves need to be implemented
//! (e.g., step 4 of "how to decrypt a message" in [section 5.3 of RFC 8152](https://www.rfc-editor.org/rfc/rfc8152#section-5.3)).
//!
//! When implementing any of the specific COSE ciphers, you'll also need to specify the type
//! of the key (which must be convertible to a `CoseKey`) and implement a method which sets
//! headers for the token, for example, the used algorithm, the key ID, an IV, and so on.
// These ones are a little too eager
extern crate alloc;
extern crate core;
extern crate derive_builder;
pub use ToCborMap;
pub use ;
pub use constants;
pub use ;
pub use AuthServerRequestCreationHint;
pub use ;
pub use ;