An implementation of the ACE-OAuth framework in Rust.
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
. 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)
specified
in draft-gerdes-ace-dcaf-authorize
(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
(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.
toml
[dependencies]
dcaf = { version = "^0.2.0" }
Or, if you plan to use this crate in a no_std
environment:
toml
[dependencies]
dcaf = { version = "^0.2.0", default-features = false }
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.
For example, 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 or AIF-encoded scopes 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: ```rust use dcaf::{AccessTokenRequest, ToCborMap, ProofOfPossessionKey, TextEncodedScope};
let request = AccessTokenRequest::builder() .clientid("myclient") .audience("valve242") .scope(TextEncodedScope::tryfrom("read")?) .reqcnf(ProofOfPossessionKey::KeyId(base64::decode("6kg0dXJM13U")?)) .build()?; let mut encoded = Vec::new(); request.clone().serializeinto(&mut encoded)?; asserteq!(AccessTokenRequest::deserializefrom(encoded.as_slice())?, request); ```
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]:
```rust
use dcaf::{ToCborMap, signaccesstoken, verifyaccesstoken};
use coset::cwt::ClaimsSetBuilder;
use coset::Header;
use coset::iana::CwtClaimName;
let claims = ClaimsSetBuilder::new() .audience("valve242".tostring()) .claim(CwtClaimName::Cnf, key.tociboriumvalue()) .claim(CwtClaimName::Scope, Value::Text("read".tostring())) .build(); let token = signaccesstoken(claims, &mut cipher, None, None, None)?; assert!(verifyaccesstoken(&token, &mut cipher, None).is_ok()); ```
cipher
here, since such implementations won't be in scope of this crate.
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. 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.
Some types used across multiple scenarios include:
Scope
(as described
in section 5.8.1 of the ACE-OAuth draft)
, either as a TextEncodedScope
or as a BinaryEncodedScope
.ProofOfPossessionKey
as specified
in section 3.1 of RFC 8747. For example, this will be
used in the access token's cnf
claim.constants
module.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
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
error. The function will return a Result
of the opaque ByteString
containing the access token.
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
.
verify_access_token
will return an empty result which indicates that the token was successfully verified---an Err
would indicate failure.
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
in case the token
is invalid or neither a COSE_Sign1
, COSE_Encrypt0
, or COSE_Mac0
structure).
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.
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).
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).
You can find a list of changes in CHANGELOG.md.
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
This project is currently maintained by the following developers:
| Name | Email Address | GitHub Username | |:----------------:|:--------------------:|:--------------------------------------------:| | Falko Galperin | falko1@uni-bremen.de | @falko17 | | Hugo Hakim Damer | hdamer@uni-bremen.de | @pulsastrix |