Skip to content

Commit c6592ba

Browse files
dmihalcik-virtrubleggettb-long
authored
✨ Let DPoP draft standard supplant client_signing_key (#10)
### MINOR Update: Explicitly supporting [IETF DPoP Draft Proposal](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) * This proposal aligns our Proof of Possession of the access token with the current IETF DPoP draft proposal. This includes: * Deprecating storing the entire key in the `tdf_claims.client_public_signing_key` subclaim * Store a SHA-256 hash of the key in the `cnf.jkt` field, per the current draft proposal * Submit a DPoP in the `DPoP` header, which will include the complete key and request identifying information * Not included: * To get complete support for the missing functionality we may desire to add a body hash to the DPoP claims list, which will provides an additional check for partial message integrity. A reference implementation of this change is now included with opentdf/backend and opentdf/client-web. If accepted we will port this to the remaining opentdf/client-*. #### Reference Implementation Details ##### Client Implementation For the client, we use [an existing DPoP implementation](https://www.npmjs.com/package/dpop) to generate proofs. To use, we added a parameter (dpopEnabled) to the clients, and if present make this required. Implementation: opentdf/web-sdk#118 ##### Identity Provider Implementation (Access Token Generation) Our reference IdP, Keycloak, provides a java plugin environment for adding claims to access tokens. Our current reference implementation extended our existing plugin, which adds the `tdf_claims` claim, to include the `cnf.jkt` claim when there is a DPoP header in the code exchange. Reference: https://github.com/opentdf/backend/blob/9603538287e23240c6d40ea596cf42f8b26bfcc4/containers/keycloak-protocol-mapper/custom-mapper/src/main/java/com/virtru/keycloak/TdfClaimsMapper.java#L292 ##### Access Point Validation (KAS) KAS currently uses application level validation of the OIDC header. We extended this to include the DPoP check iff the access token includes the `jkt` claim. https://github.com/opentdf/backend/blob/9603538287e23240c6d40ea596cf42f8b26bfcc4/containers/kas/kas_core/tdf3_kas_core/dpop.py#L55 Co-authored-by: Ben Leggett <[email protected]> Co-authored-by: b-long <[email protected]>
1 parent 4a4215a commit c6592ba

File tree

4 files changed

+65
-30
lines changed

4 files changed

+65
-30
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.1.0
1+
4.2.0

protocol/README.md

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Protocol
1+
# Open TDF Protocol
22

33
This document describes the canonical system architecture used to encrypt and decrypt TDF ciphertext.
44

@@ -8,46 +8,43 @@ The canonical architecture contains four major components.
88

99
* *TDF Client* - Initiates and drives the TDF encryption and decryption workflows. Only component with access to the content (ciphertext or plaintext).
1010
* May be entitled as Non-Person Entity acting on behalf of itself, OR on behalf of a Person Entity.
11-
* *OpenID Connect (OIDC) Identity Provider (IdP)* - This system could be any OIDC IdP software. OpenTDF has chosen Keycloak as its reference implementation IdP.
12-
* From Wikipedia: "Keycloak is an open source software product to allow single sign-on with Identity and Access Management aimed at modern applications and services. As of March 2018 this JBoss community project is under the stewardship of Red Hat. Keycloak is licensed under Apache 2.0."
13-
* Any OIDC-compliant IdP software may be used, provided it supports custom claims, and can:
14-
* Read the TDF Client public key from a custom HTTP header sent with the OIDC authentication request.
15-
* Construct and send an Attribute Provider web service request, including the public key in the payload.
16-
* Return the resulting Claims Object in the signed IdP JWT.
17-
* A list of Certified OpenID Connect applications can be found at: https://openid.net/developers/certified/
18-
* *OpenTDF Protocol Mapper* (PM) is OpenTDF's Keycloak-specific reference implementation of the above functionality.
19-
* *Attribute Provider* (AP) - A web service that receives requests which contain information about the authenticated entities from an OIDC IdP with custom claims support (ex: Keycloak with OpenTDF Protocol Mapper), and returns custom TDF OIDC claims in response. It is the responsibility of Attribute Provider to transform incoming 3rd party IdP claims/metadata to a set of outgoing [Attribute Objects](../schema/AttributeObject.md). It returns a TDF [Claims Object](../schema/ClaimsObject.md).
20-
* *Key Access Service* (KAS) - Responsible for authorizing and granting TDF Clients access to rewrapped data key material. If authorized, TDF Clients (on behalf of themselves, or other entities) can use this rewrapped data key to decrypt TDF ciphertext. A valid OIDC token containing [TDF Claims](../schema/ClaimsObject.md) must be used as a bearer token when communicating with KAS. KAS will verify the authenticity of the bearer token, the request signature, and then the policy claims within that bearer token. An otherwise valid and trusted OIDC token without valid TDF Claims will be rejected.
11+
* *OpenID Connect (OIDC) Identity Provider (IdP)* - This system could be any OIDC IdP software. Any OIDC-compliant IdP software may be used, provided it supports custom claims and *Demonstration of Proof of Possession (DPoP)*.
12+
* Supporting this protocol must include:
13+
* Obtaining a session-bound TDF Client public signing key. This may be via a client side channel, or an addditional request. Implements may choose a custom HTTP header sent with the OIDC authentication request, or an identity provider (IdP) web hook that fires during a token or code exchange.
14+
* Constructing and sending a web service request to an ABAC entitlement PDP (e.g. the OpenTDF Entitlement PDP), including sufficient information to identify all entities requesting the token grant, and any additional IdP context data for those entities.
15+
* Including within the returned signed access token both the evidence of the client's public key in the `cnf` claim, and the entitlements in the `tdf_claims`.[^client_public_signing_key]
16+
* A list of Certified OpenID Connect applications can be found at: https://openid.net/developers/certified/ OpenTDF has chosen [Keycloak] as its reference implementation IdP. [^why-keycloak]
17+
* *Entitlement Policy Decision Point (PDP)* (AP) - A web service that receives requests which contain information about the authenticated entities from an OIDC IdP with custom claims support (e.g. Keycloak with OpenTDF Protocol Mapper), and returns custom TDF OIDC claims in response. It is the responsibility of Entitlement PDP to transform incoming 3rd party IdP claims/metadata to a set of outgoing [Attribute Objects](../schema/AttributeObject.md). It returns a TDF [Claims Object](../schema/ClaimsObject.md).
18+
* *Key Access Service* (KAS) - Responsible for authorizing and granting TDF Clients access to rewrapped data key material. If authorized, TDF Clients (on behalf of themselves, or other entities) can use this rewrapped data key to decrypt TDF ciphertext. A valid OIDC token containing [`tdf_claims`](../schema/ClaimsObject.md) and [`dpop` (Demonstration Proof of Possession)](../schema/ProofOfPossession.md) must be used as a bearer token when communicating with KAS. KAS will verify the authenticity of the bearer token, the request signature, and then the policy claims within that bearer token. An otherwise valid and trusted OIDC token without valid TDF Claims will be rejected.
2119

2220
## General Authentication Protocol
2321

24-
OIDC Auth with a PoP scheme is used for **all** TDF Client interactions with backend TDF services:
22+
OIDC Auth with a DPoP (Demonstration Proof of Possession) scheme is used for **all** TDF Client interactions with backend TDF services:
2523

2624
1. The TDF Client requests an OIDC Bearer Token (either on behalf of itself, or another entity)
2725
by first authenticating via the OpenID Connect (OIDC) Identity Provider (IdP) with Custom Claims
28-
support (in this example, Keycloak). As part of this authentication process, the TDF Client **must** convey its signing public key to the IdP.
29-
* If the TDF Client public signing key is rotated or changed, a new OIDC Bearer Token must be obtained from the IdP, containing the TDF Client's new public signing key.
30-
* It should be assumed that the TDF Client's signing keypair is ephemeral, and that the TDF Client's _private_ signing key is known only to the TDF Client.
26+
support (in this example, Keycloak). As part of this authentication process, the TDF Client **must** convey a DPoP key to the IdP.
27+
* To change (or rotate) its DPoP key, a client must obtain a new OIDC access token from the IdP containing its own new public signing key.
28+
* The TDF Client's signing keypair is ephemeral and the _private_ signing key must be known only to the TDF Client.
3129
* Measures should be taken to protect all TDF Client private keys, but the mechanisms for doing so are outside the scope of this spec.
3230

3331
1. If entity authentication succeeds, a
3432
[TDF Claims Object](../schema/ClaimsObject.md) is obtained from
35-
Attribute Provider and signed by the IdP. The signing public key previously conveyed by the TDF Client is embedded within the [TDF Claims Object](../schema/ClaimsObject.md).
33+
Entitlement PDP.
34+
A hash of the signing public key is embedded within the [JWT's `cnf.jkt` claim](../schema/ProofOfPossession.md).
3635
The signed OIDC Bearer Token is then returned to the TDF Client, containing the complete [TDF Claims Object](../schema/ClaimsObject.md).
3736
* The [TDF Claims Object](../schema/ClaimsObject.md) contains one or more [Entitlement Objects](EntitlementObject.md) entitling all entities
3837
involved in the authentication request.
3938

40-
1. The TDF Client must convey the IdP-signed OIDC Bearer Token to backend services with all requests, and in addition, the TDF Client **must** sign all requests to backend services with its _private signing key_
41-
* The request signature should be a signature of the entire request body, sans the request signature itself.
42-
* For HTTPS, it should be considered best practice to insert the request signature itself in the request body rather than send it as a custom header.
43-
44-
1. Backend services are required to:
39+
1. The TDF Client must convey the IdP-signed OIDC Bearer Token as a JWT to backend services with all requests, and a DPoP proof generated from its _private signing key_
40+
* The DPoP proof claims must include a `body_hash` claim, which is a hash of the request body.
41+
1. All backend services are required to _minimally_:
4542
* Validate AuthN:
46-
* Examine the validity of the OIDC Bearer Token signature by contacting the issuing IdP.
47-
* Validate that the [TDF Claims Object](../schema/ClaimsObject.md) contains a TDF Client public signing key.
48-
* Validate that the request signature in the TDF Client's payload **can be validated** with the TDF Client's public signing key embedded in the OIDC Bearer Token associated with the signed request
49-
* Validate AuthZ (if necessary)
50-
* Determine if all the entities entitled in the presented bearer token have all the required Attributes for a given operation, as per service requirements.
43+
* Examine the validity of the OIDC Bearer Token signature and other assertions by contacting the issuing IdP.
44+
* Validate the [`DPoP`](../schema/ProofOfPossession.md) header
45+
1. Backend services acting as Access PEPs must _additionally_ validate AuthZ:
46+
* Validate that the access token contains a valid [TDF Claims Object](../schema/ClaimsObject.md) under the `tdf_claims` key,
47+
* Validate AuthZ by presenting the attributes for all authenticated entities to an Access PDP.
5148

5249
If these requirements are met, a TDF Client may be considered authenticated and authorized.
5350

@@ -61,7 +58,7 @@ _Offline mode_ requires that the TDF Client previously obtained a
6158
long-lived TDF JWT with embedded Claims Object via
6259
online OIDC authentication. Or, the TDF Client has otherwise obtained
6360
a valid signed JWT with embedded Claims Object through offline means
64-
(ex: generated by a script using the IdP signing key). TDF Clients
61+
(e.g. generated by a script using the IdP signing key). TDF Clients
6562
running in this mode commit the [authorization
6663
policy](../schema/PolicyObject.md) out-of-band, or when decrypt is
6764
first performed. This significantly reduces latency at the cost of a
@@ -84,3 +81,8 @@ information must be included).
8481

8582
![IdP Brokered Authentication](../diagrams/OIDC_brokered_auth.png)
8683

84+
[Keycloak]: https://www.keycloak.org/
85+
86+
[^client_public_signing_key]: Clients in version [4..4.2) may include the signing key in the `tdf_claims` object in the `client_public_signing_key` field, instead of the `cnf`.
87+
88+
[^why-keycloak]: [From Wikipedia](https://en.wikipedia.org/wiki/Keycloak): "Keycloak is an open source software product to allow single sign-on with Identity and Access Management aimed at modern applications and services. As of March 2018 this JBoss community project is under the stewardship of Red Hat. Keycloak is licensed under Apache 2.0."

schema/ClaimsObject.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,12 @@ If these requirements are met, the KAS will permit a decrypt of the file.
7474
]
7575
}
7676
],
77-
"client_public_signing_key":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy18Efi6+3vSELpbK58gC\nA9vJxZtoRHR604yi707h6nzTsTSNUg5mNzt/nWswWzloIWCgA7EPNpJy9lYn4h1Z\n6LhxEgf0wFcaux0/C19dC6WRPd6 ... XzNO4J38CoFz/\nwwIDAQAB\n-----END PUBLIC KEY-----",
7877
"tdf_spec_version:":"x.y.z"
7978
}
8079
```
8180

8281
| Parameter | Type | Description | Required? |
8382
|-----------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------------|--------------------|
8483
| `entitlements` | Array | An array of [Entitlement Objects](EntitlementObject.md) for each entity (PE or NPE) involved in the authentication request. | Yes |
85-
| `client_public_signing_key` | String | The TDF Client's public signing key, in a PEM-encoded format. | Yes |
8684
| `tdf_spec_version` | String | Semver version number of the TDF spec. | No |
85+
| `client_public_signing_key` | String | [DEPRECATED] The TDF Client's public signing key, in a PEM-encoded format. Replaced by cnf claim and the DPoP protocol. | No |

schema/ProofOfPossession.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Proof of Possession
2+
3+
In order to allow extended offline flows, and to deal with potential leaks of bearer tokens,
4+
we enforce the use of proof of token possession, as currently described in
5+
[this IETF draft](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop).
6+
7+
## What is this?
8+
9+
The proof of possession works by having the client generate an ephemeral key pair, which it
10+
then associates with an access_token via a code or token exchange. This produces
11+
an access token with an explicit binding (via the `cnf` claim) to the public
12+
key of the value. Then, the client can issue DPoP proofs that will be allow servers
13+
to trust that the client has possession of the private key.
14+
15+
## How does it work?
16+
17+
1. TDF clients may request an OIDC requests an OIDC Bearer Token (either on behalf of itself, or another entity)
18+
by first authenticating via the
19+
OpenID Connect (OIDC) Identity Provider (IdP),
20+
passing along a signing key with the request.
21+
In some scenarios, a key may be added using an exchange or refresh.
22+
23+
2. The client requests a decrypt from the Key Access Server (KAS),
24+
presenting its annotated JWT with PoP proof and entitlement claims in the access token.
25+
26+
## Example
27+
28+
```javascript
29+
TK
30+
```
31+
32+
## DEPRECATED: Validating with the `client_public_signing_key`
33+
34+
Previously (<4.2) we stored the entire public key within the `tdf_claims` as the client_public_signing_key and used that to sign the request body. IdPs *may* include both the `cnf` hash of the public key and the `client_public_signing_key` subclaim for compatibility with older KAS services.

0 commit comments

Comments
 (0)