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
/*
* 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](https://www.ietf.org/archive/id/draft-ietf-ace-oauth-authz-46.html).
//!
//! This crate implements the ACE-OAuth
//! (Authentication and Authorization for Constrained Environments using the OAuth 2.0 Framework)
//! framework as defined in [`draft-ietf-ace-oauth-authz-46`](https://www.ietf.org/archive/id/draft-ietf-ace-oauth-authz-46.html).
//! Key features include CBOR-(de-)serializable data models such as [`AccessTokenRequest`],
//! as well as the possibility to create COSE encrypted/signed access tokens
//! (as described in the draft) along with decryption/verification functions.
//! Implementations of the cryptographic functions must be provided by the user by implementing
//! [`CoseEncrypt0Cipher`] or [`CoseSign1Cipher`].
//!
//! Note that actually transmitting the serialized values (e.g. via CoAP) or providing more complex
//! features not mentioned in the ACE-OAuth Internet Draft (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.ietf.org/archive/id/draft-ietf-ace-oauth-authz-46.html#figure-7),
//! say you (the client) want to request an access token from an Authorization Server.
//! For this, you'd need to create an [`AccessTokenRequest`], which has to include at least a
//! `client_id`. We'll also specify an audience, a scope (using [`TextEncodedScope`]---note that
//! [binary-encoded scopes](BinaryEncodedScope) or [AIF-encoded scopes](AifEncodedScope) would also work), as well as a
//! [`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};
//!
//! 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`[^cipher]:
//! ```
//! # use ciborium::value::Value;
//! # use dcaf::error::{AccessTokenError, CoseCipherError};
//! # use dcaf::{CoseCipherCommon, CoseSign1Cipher, ProofOfPossessionKey};
//! use dcaf::{ToCborMap, sign_access_token, verify_access_token};
//! use coset::cwt::ClaimsSetBuilder;
//! use coset::Header;
//! use coset::iana::CwtClaimName;
//!
//! # struct FakeCipher {};
//! # impl CoseCipherCommon for FakeCipher {
//! # type Error = String;
//! #
//! # fn header(&self, unprotected_header: &mut Header, protected_header: &mut Header) -> Result<(), CoseCipherError<Self::Error>> {
//! # Ok(())
//! # }
//! # }
//! # impl CoseSign1Cipher for FakeCipher {
//! # fn generate_signature(&mut self, target: &[u8]) -> Vec<u8> {
//! # target.to_vec()
//! # }
//! # fn verify_signature(&mut self, signature: &[u8], signed_data: &[u8]) -> Result<(), CoseCipherError<Self::Error>> {
//! # if signature.to_vec() != self.generate_signature(signed_data) {
//! # Err(CoseCipherError::VerificationFailure)
//! # } else {
//! # Ok(())
//! # }
//! # }
//! # }
//! # let key = ProofOfPossessionKey::KeyId(vec![0xDC, 0xAF]);
//! # let mut cipher = FakeCipher {};
//! let claims = ClaimsSetBuilder::new()
//! .audience("valve242".to_string())
//! .claim(CwtClaimName::Cnf, key.to_ciborium_value())
//! .claim(CwtClaimName::Scope, Value::Text("read".to_string()))
//! .build();
//! let token = sign_access_token(claims, &mut cipher, None, None, None)?;
//! assert!(verify_access_token(&token, &mut cipher, 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 scope of this crate.
//!
//! # Provided Data Models
//!
//! ## Token Endpoint
//! The most commonly used models will probably be the token endpoint's [`AccessTokenRequest`] and
//! [`AccessTokenResponse`] described in [section 5.8 of the ACE-OAuth draft](https://www.ietf.org/archive/id/draft-ietf-ace-oauth-authz-46.html#section-5.8).
//! In case of an error, an [`ErrorResponse`] should be used.
//!
//! After an initial Unauthorized Resource Request Message, an [`AuthServerRequestCreationHint`] can
//! be used to provide additional information to the client, as described in
//! [section 5.3 of the ACE-OAuth draft](https://www.ietf.org/archive/id/draft-ietf-ace-oauth-authz-46.html#section-5.3).
//!
//! ## Common Data Types
//! Some types used across multiple scenarios include:
//! - [`Scope`] (as described in [section 5.8.1 of the ACE-OAuth draft](https://www.ietf.org/archive/id/draft-ietf-ace-oauth-authz-46.html#section-5.8.1)),
//! either as a [`TextEncodedScope`], a [`BinaryEncodedScope`] or an [`AifEncodedScope`].
//! - [`ProofOfPossessionKey`] as specified in [section 3.1 of RFC 8747](https://datatracker.ietf.org/doc/html/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`] or
//! [`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.
//!
//! Both functions take a [`ClaimsSet`](coset::cwt::ClaimsSet) containing the claims that
//! shall be part of the access token, a cipher implementing the cryptographic operations
//! (explained further below), as well as optional `aad` (additional authenticated data)
//! and un-/protected headers.
//! 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`](crate::error::CoseCipherError::HeaderAlreadySet) error.
//! The function will return a [`Result`] of the opaque [`ByteString`] containing the access token.
//!
//! # Verifying / decrypting Access Tokens
//! In order to verify or decrypt existing access tokens represented as [`ByteString`]s,
//! use [`verify_access_token`] or [`decrypt_access_token`] respectively.
//!
//! Both functions take the access token, a `cipher` for the cryptographic operations and an
//! optional `aad` (additional authenticated data).
//!
//! [`decrypt_access_token`] will return a result containing the decrypted
//! [`ClaimsSet`](coset::cwt::ClaimsSet).
//! [`verify_access_token`] will return an empty result which indicates that the token
//! was successfully verified---an [`Err`](Result::Err) would indicate failure.
//!
//! # Extracting Headers from an Access Token
//! Regardless of whether token was signed, encrypted, or MAC-tagged, you can extract its
//! headers using [`get_token_headers`], which will return an option containing both
//! unprotected and protected headers (or which will be [`None`](Option::None) in case
//! the token is invalid or neither a `COSE_Sign1`, `COSE_Encrypt0`, or `COSE_Mac0` structure).
//!
//! # COSE Cipher
//! As mentioned before, cryptographic functions are outside the scope of this crate.
//! For this reason, the various COSE cipher traits exist; namely,
//! [`CoseEncrypt0Cipher`], [`CoseSign1Cipher`], and [`CoseMac0Cipher`], each implementing
//! a corresponding COSE operation as specified in sections 4, 5, and 6 of
//! [RFC 8152](https://datatracker.ietf.org/doc/html/rfc8152).
//!
//! Note that these ciphers *don't* need to wrap their results in e.g. a `Cose_Encrypt0` structure,
//! this part is already handled using this library (which uses [`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://datatracker.ietf.org/doc/html/rfc8152#section-5.3)).
//!
//! When implementing any of the specific COSE ciphers, you'll also need to implement the
//! [`CoseCipherCommon`] trait, which can be used to set headers specific to your COSE cipher
//! (e.g. the used algorithm).
// 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 ;