Expand description
An implementation of the ACE-OAuth framework (RFC 9200).
This crate implements the ACE-OAuth
(Authentication and Authorization for Constrained Environments using the OAuth 2.0 Framework)
framework as defined in RFC 9200.
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 standard) along with decryption/verification functions.
Implementations of the cryptographic functions must be provided by the user by implementing
CoseEncryptCipher
or 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)
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.
Usage
[dependencies]
dcaf = { version = "^0.3" }
Or, if you plan to use this crate in a no_std
environment:
[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,
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
, 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:
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);
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
1:
use dcaf::token::CoseCipher;
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());
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 RFC 9200.
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 RFC 9200.
Common Data Types
Some types used across multiple scenarios include:
Scope
(as described in section 5.8.1 of RFC 9200), either as aTextEncodedScope
, aBinaryEncodedScope
or anAifEncodedScope
.ProofOfPossessionKey
as specified in section 3.1 of RFC 8747. For example, this will be used in the access token’scnf
claim.- While not really a data type, various constants representing values used in ACE-OAuth
are provided in the
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. In case you want to create a token intended for multiple recipients (each with their
own key), you can use encrypt_access_token_multiple
or sign_access_token_multiple
.
Both functions take a 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
of the opaque
ByteString
containing the access token.
Verifying and 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.
In case the token was created for multiple recipients (each with their own key),
use verify_access_token_multiple
or 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
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.
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
,
which will return an option containing both
unprotected and protected headers (or which will be 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
, CoseSignCipher
,
and CoseMacCipher
, each implementing
a corresponding COSE operation as specified in sections 4, 5, and 6 of
RFC 8152.
There are also the traits MultipleEncryptCipher
,
MultipleSignCipher
, and
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
)—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 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.
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. ↩
Modules
- Common types used throughout the crate.
- Contains various constants defined in the standards related to ACE-OAuth.
- Contains CBOR-serializable data types for the endpoints of ACE-OAuth.
- Contains error types used across this crate.
Structs
- Request for an access token, sent from the client, as defined in section 5.8.1 of RFC 9200.
- Response to an
AccessTokenRequest
containing the Access Token among additional information, as defined in section 5.8.2 of RFC 9200. - A scope encoded using the Authorization Information Format (AIF) for ACE.
- This is sent by an RS as a response to an Unauthorized Resource Request Message to help the sender of the Unauthorized Resource Request Message acquire a valid access token.
- A scope encoded using a custom binary encoding. See Scope for more information.
- Details about an error which occurred for an access token request.
- A scope encoded using the Authorization Information Format (AIF) for ACE as in
AifEncodedScope
, but only consisting of a singleAifEncodedScopeElement
instead of an array of them. - A scope encoded as a space-delimited list of strings, as defined in RFC 6749, section 1.3.
Enums
- Profiles for ACE-OAuth as specified in section 5.8.4.3 of RFC 9200.
- Error code specifying what went wrong for a token request, as specified in section 5.2 of RFC 6749 and section 5.8.3 of RFC 9200.
- Type of the resource owner’s authorization used by the client to obtain an access token. For more information, see section 1.3 of RFC 6749.
- A proof-of-possession key as specified by RFC 8747, section 3.1.
- Scope of an access token as specified in RFC 9200, section 5.8.1.
- The type of the token issued as described in section 7.1 of RFC 6749.
Traits
- Provides basic operations for encrypting and decrypting COSE structures.
- Provides basic operations for generating and verifying MAC tags for COSE structures.
- Provides basic operations for signing and verifying COSE structures.
- Intended for ciphers which can encrypt for multiple recipients. For this purpose, a method must be provided which generates the Content Encryption Key.
- Marker trait intended for ciphers which can create MAC tags for multiple recipients.
- Marker trait intended for ciphers which can create signatures for multiple recipients.
- Provides methods to serialize a type into a CBOR map bytestring and back.
Functions
- Decrypts the given
token
andexternal_aad
using thekey
and the cipher given by type parameterT
, returning the decryptedClaimsSet
. - Decrypts the given
token
andexternal_aad
using the Key Encryption Keykek
and the cipher given by type parameterT
, returning the decryptedClaimsSet
. - Encrypts the given
claims
with the given headers andexternal_aad
using thekey
and the cipher given by type parameterT
, returning the token as a serialized bytestring of theCoseEncrypt0
structure. - Encrypts the given
claims
with the given headers andexternal_aad
for each recipient by using thekeys
with the cipher given by type parameterT
, returning the token as a serialized bytestring of theCoseEncrypt
structure. - Returns the headers of the given signed (
CoseSign1
/CoseSign
), MAC tagged (CoseMac0
/CoseMac
), or encrypted (CoseEncrypt0
/CoseEncrypt
) access token. - Signs the given
claims
with the given headers andexternal_aad
using thekey
and the cipher given by type parameterT
, returning the token as a serialized bytestring of theCoseSign1
structure. - Signs the given
claims
with the given headers andexternal_aad
for each recipient by using thekeys
with the cipher given by type parameterT
, returning the token as a serialized bytestring of theCoseSign
structure. - Verifies the given
token
andexternal_aad
with thekey
using the cipher given by type parameterT
, returning an error in case it could not be verified. - Verifies the given
token
andexternal_aad
with thekey
using the cipher given by type parameterT
, returning an error in case it could not be verified.
Type Definitions
- A type intended to be used as a CBOR bytestring, represented as a vector of bytes.