CABE Key Access Protocol (CKAP)
This document specifies the CABE Key Access Protocol (CKAP).
Introduction
This document specifies the Concise Attribute-Bound Encapsulation (CABE) Key Access Protocol (CKAP). It comprises one of the core specifications of the CABE Architecture, as defined in the CABE Architecture Specification.
Definitions
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY” and “OPTIONAL” in this document are to be interpreted as specified in BCP 14 when, and only when, they appear in all capitals, as shown here.
All definitions given in the CABE Architecture Specification are reused for the purposes of this document.
Key Schedule
Overview
To support the objectives given in CABE-ARCH, a Key Server maintains multiple Key Series and multiple Key Epochs. Key Series represents the temporal and contiguous sequence of Keys maintained for a given Attribute Set. A Key Epoch represents one contiguous unit of time for which a given Key Series uses a given Key as its current Key.
A Key Series rolls over into a new Key Epoch, causing rotation of the current Key in that Key Series, for one or more of the following reasons:
-
Periodically, as part of routine key rotation.
-
When a new Principal is authorized to access the Envelope Set associated with a Key Series. This ensures that when the new Key is disclosed to the newly authorized Principal, it does not enable the Principal to access Envelopes created before it was so authorized.
-
When a presently authorized Principal is revoked from having access to the Envelope Set associated with a Key Series. This ensures that the revoked Principal does not have access to Envelopes created after revocation.
-
At operator discretion.
Derivation Tree
This specification intentionally does not constrain or define the internal storage architecture of a Key Server, which is left unspecified. A Key Server may adopt whatever internal Key storage mechanisms it desires so long as it correctly implements this specification. Nonetheless, this section discusses suggested key storage architectures likely to be suitable for many implementations.
A Key Server can ensure it is able to reproduce a specific Key either by storing it or, in the case of a Derived Key, by re-deriving it as needed from another Key.
There are always at least two levels in the derivation tree: Set Keys and Lease Keys, which necessarily are derived from Set Keys. The number of levels in the derivation tree above Key Series and their associated Set Keys is implementation-specific.
As regards the design of the key derivation tree, several strategies are available:
Single Root Key (SRK)
A Key Server can, if desired, be implemented with only a single Non-Derived Key which never changes, and use it to derive all other Keys. This has the advantage that the storage requirements of the Key Server are O(1) with respect to the number of Envelope Sets in play; however, it also carries the associated disadvantage that new entropy is never injected into the system, and there is therefore no temporal recovery from the compromise of the single Non-Derived Key, compromise of which catastrophically and permanently compromises the entire Domain. Therefore, implementations SHOULD NOT use this approach.
Single Temporal Root Key (STRK)
In this approach, a Key Server maintains a single Root Key Series of Internal, Non-Derived Keys. All Keys are derived, directly or indirectly, from the current (or a previous) temporal Internal Key. Periodically, either on a schedule or on demand as deemed warranted by an operator, a Key Server chooses to roll the Root Key, creating a new Root Key Epoch with a new Root Key in the Root Key Series. Each Root Key is a Non-Derived Key which incorporates new entropy, and must be securely stored by the Key Server.
This approach has the advantage that the storage requirements of the Key Server are O(N) with respect to the amount of time the Key Server has been operating, but do not scale with regards to the number of Envelope Sets in play.
This approach allows a Key Server to recover from a point-in-time compromise of its key storage by causing subsequent Key Epochs to be derived from new entropy post compromise.
Captive v. Non-Captive Keys
For Access Keys used by a Client, Keys can either be kept Non-Captive and their full image can be distributed to a Client securely, or be kept Captive, in which a Key Server does not disclose the Key but performs operations using it on behalf of a Client (“glovebox operation”).
Similar tradeoffs can be made by a Key Server’s own internal Key Schedule. For example, an implementation could use a purely software, in-process implementation of STRK Root Key storage (Non-Captive Keys), or could choose to store Root Keys in an external cryptographic device such as a TPM or HSM from which exportation of the key material is restricted. Where a captive key storage device exhibits significant performance restrictions, careful design of a key derivation schedule can ameliorate this, at the cost of in-memory duplication of temporal keys with the associated risks.
Captive 2-Level Root Key (C2LRK)
This is an example of a hybrid approach which uses Captive Keys and temporal injection of new entropy but which places a load on a key storage device which is fixed and does not scale with the load placed on the Key Server by Clients or number of Message Sets in play.
A key storage device maintains a single Root Key Series, each Key of which is a non-derived Key. Because each new Key in the Root Key Series must be permanently stored, the Root Key Epoch has a relatively long duration, such as a few days, or even longer.
The current Root Key is then used to derive a Sub-Root Key which can be stored in-memory and used as needed. Because the Sub-Root Key is a Derived Key, the Sub-Root Key Epoch can be much narrower, on the order of minutes or hours. A Key Server can re-derive a Sub-Root Key as needed by communicating with the key storage device, and maintain an in-memory cache of Sub-Root Keys which can be evicted as needed without consequence.
Rollover Handling
When a Key Series is rolled over, causing creation of a new Key Epoch for that Key Series, a new Derived or Non-Derived, Captive or Non-Captive Set Key is created for that Key Series-Key Epoch intersection, and the Key Server begins using that Set Key for all future Prograde Key Resolution requests for the relevant Envelope Set.
Clients do not necessarily transition to use of the new Set Key (or more specifically a new derived Lease Key) for a given Envelope Set instantaneously, for several reasons.
Firstly, because encapsulating a large number of Messages within a given Message Set would cause excess load on a Key Server if every such encapsulation operation required a separate Prograde Key Resolution request to be made to the Key Server, the caching of Key Mapping decisions for a given Client is a practical necessity. Thus, each Prograde Key Resolution request results In creation of a cacheable Lease object.
Some events causing key rollover (such as periodic scheduled rollover) are predictable, and the invalidation of cache entries can be handled by placing an expiry timestamp in key resolution results indicating when the resolution decision is expected to no longer be current for a given Envelope Set.
However, this is not entirely effective as clocks may not be synchronized between client and server. Further, Key Series rollover may happen not just on a schedule (“Synchronous Rollover”) but also unpredictably and without warning, for example if requested by an operator, or if an authorized principal is added or removed (“Asynchronous Rollover”).
Therefore, the possibility of a Client encapsulating a message using a non-current Lease Key (that is, a Lease Key derived from a non-current Set Key) cannot be eliminated. For this reason, the correct Set Key to de-encapsulate a previously encapsulated Envelope cannot be determined solely from the Envelope’s timestamp. Therefore, CBES incorporates header information adequate both to determine the applicable Envelope Set but also the specific Lease Key which was used to create it, independently of any timestamp information the Envelope may contain.
The period in which Clients are liable to encapsulate messages using non-current Lease Keys is referred to as the Rollover Period. It is desirable to minimise the duration of this period, as system invariants (such as newly authorized Principals not being able to access old Messages) are not fully enforced during it.
This is accomplished via the following means:
-
Lease objects returned from Prograde Key Resolution results contain an expiry time. This can be set to the time at which routine Key Series rollover is anticipated; however, it can also be set to a shorter time (such as a few seconds) to minimise the duration of any Rollover Period in the event of an asynchronous rollover, while still facilitating some level of caching.
-
Explicit cache invalidation is provided by an asynchronous, publish-subscribe event notification mechanism which clients SHOULD implement, defined below.
Abstract Service Interface
This section defines the abstract interface between a Client and a Key Server, which is independent of the specific transport used to realise a CKAP service. Abstractly, a CKAP service provides five core operations:
- GetSelf, which allows a Client to obtain information about its own
identity as the Key Server perceives it (similar to shell commands such as
idorwhoami); - Prograde, which implements Prograde Key Resolution and creates a Lease;
- Retrograde, which implements Retrograde Key Resolution;
- AssistedEncapsulate, which facilitates Key Access to a Captive Key by wrapping a CEK;
- AssistedDecapsulate, which facilitates Key Access to a Captive Key by unwrapping a CEK.
Each operation is defined in terms of a Request structure named xRequest and
a Response structure xResponse for some operation named x. Each operation
can result in an xResponse structure or in the Error structure. All
structures are CBOR data items defined in terms of CDDL (RFC
8610).
Common Structures
This section defines CBOR structures common to several operations.
AttributeSet = {
/ MUST conform to the restrictions on Attribute Sets
as defined in CABE-ARCH /
* text => any
}
Error
The Error structure may be returned instead of any xResponse structure.
The following fields are required:
-
errorCode, an integral error code as defined in Annex A. -
summary, a brief one-line textual error string describing the error.
More details about an error can be included by including the optional details
field. This field must be a CBOR map, but its specific usage is unspecified.
Error = {
errorCode: int,
summary: text,
? details: { * text => any }
}
GetSelf
The GetSelf operation takes no arguments and allows a Client to determine information about the Principal it acts as in the perspective of the Key Server.
The information returned includes:
-
A URI which uniquely identifies the Principal, which MUST be reported by the Key Server;
-
The Claims attached to the Principal, which the Key Server MAY choose to report.
Claims = {
* text => any
}
GetSelfRequest = {
kind: "GetSelfRequest"
}
GetSelfResponse = {
kind: "GetSelfResponse",
/ Information about the Principal. /
principal: {
uri: tstr,
? claims: Claims
},
/ Unspecified server information. /
serverInfo: {
* text => any
}
}
Prograde
The Prograde operation implements Prograde Key Resolution.
ProgradeRequest = {
kind: "ProgradeRequest",
attributeSet: AttributeSet,
/ Optional ARIN token to subscribe the created lease into an ARIN stream. /
? arinToken: bstr
}
ProgradeResponse = {
kind: "ProgradeResponse",
/ The created lease. /
lease: Lease
}
Lease = {
/ A lease identifier used solely for purposes of invalidation. It does not need
to be unique beyond the scope of the Key Server process's lifespan.
It MUST be present if an ARIN Token was quoted in the ProgradeRequest
and MAY be omitted otherwise. /
? leaseID: tstr,
/ The opaque Lease Reference. /
leaseRef: bstr,
/ The Key Server may optionally report back the Attribute Set pertaining to the lease. /
? attributeSet: AttributeSet,
/ The LKAI for the Lease Key. /
lkai: LKAI,
/ The expiry time as a CBOR integral UNIX timestamp (UTC). /
expiry: time
}
LKAI = {
/ There MUST be exactly one of these fields present.
A Client MUST reject a LKAI with both present, or with any other field present. /
? nonCaptive: LKAI_NonCaptive,
? captive: LKAI_Captive
}
LKAI_NonCaptive = {
/ COSE Key object with symmetric 'k' /
leaseKey: COSE_Key
}
LKAI_Captive = {
/ The Lease Key Access Token for use with the Assisted Encapsulate
and Decapsulate operations. /
leaseKeyAccessToken: bstr
}
Retrograde
RetrogradeRequest = {
kind: "RetrogradeRequest",
/ The Attribute Set of the Envelope causing the Retrograde request. /
attributeSet: AttributeSet,
/ The Lease Reference quoted in the Envelope causing the Retrograde request. /
leaseRef: bstr
}
RetrogradeResponse = {
kind: "RetrogradeResponse",
/ The Key Server may optionally report back the Attribute Set pertaining to
the retrograde request. /
? attributeSet: AttributeSet,
/ The LKAI for the Lease Key. /
lkai: LKAI
}
AssistedEncapsulate
AssistedEncapsulateRequest = {
kind: "AssistedEncapsulateRequest",
/ The Lease Key Access Token in an LKAI_Captive structure. /
leaseKeyAccessToken: bstr,
/ The unencrypted Content Encryption Key to be wrapped. /
cek: bstr
}
AssistedEncapsulateResponse = {
kind: "AssistedEncapsulateResponse",
/ The newly wrapped Content Encryption Key. /
wrappedCEK: bstr
}
AssistedDecapsulate
AssistedDecapsulateRequest = {
kind: "AssistedDecapsulateRequest",
/ The Lease Key Access Token in an LKAI_Captive structure. /
leaseKeyAccessToken: bstr,
/ The wrapped Content Encryption Key to be unwrapped. /
wrappedCEK: bstr
}
AssistedDecapsulateResponse = {
kind: "AssistedDecapsulateResponse",
/ The unencrypted Content Encryption Key. /
cek: bstr
}
HTTP Transport
Requirements
CKAP is currently defined with an HTTP-based transport. Other transports could be defined in the future. The HTTP transport is independent of any specific HTTP protocol but can use any transport compliant with HTTP semantics (e.g. HTTP/1.1, HTTP/2, HTTP/3).
Due to the sensitive nature of CKAP operations, and since CKAP is used to transport key material, Transport Layer Security (TLS) or a comparable channel security mechanism MUST be used.
A CKAP server has a base URL which is used to access it, such as
https://example.com/ckap/. Except where otherwise specified, operations in
the abstract service interface defined in the previous section are uniformly
converted to HTTP requests as follows:
-
The request method shall be
POST; -
The URL shall be formed by appending a ’/’ (if not already present) to the base URL and the name of the operation;
-
The request
Content-Typeheader andAcceptheader MUST both be specified asapplication/ckap+cbor; -
An informative
User-Agentheader SHOULD be specified; -
The request body of the request MUST be the
xRequeststructure, serialized as CBOR;
The Key Server MUST respond with a HTTP response constructed as follows:
-
The response
Content-Typeheader MUST beapplication/ckap+cbor, whether or not the response code is a success (2xx) code or a failure code. -
The response body is the CBOR serialization of either
-
the
xResponsestructure directly corresponding to thexRequeststructure which was sent; or -
the
Errorstructure.
-
If an error occurs during processing of an operation and the Key Server
responds with an Error structure, the HTTP status code MUST be an error
status code. If the Key Server responds with an xResponse structure, the HTTP
status code MUST be 200.
Example
An example HTTP/1.1 request is shown below:
POST /ckap/Prograde HTTP/1.1
Authorization: ...
Accept: application/ckap+cbor
Content-Type: application/ckap+cbor
...CBOR-encoded request message...
HTTP/1.1 200 OK
Content-Type: application/ckap+cbor
...CBOR-encoded response message...
In the event of an error, an HTTP error status code is returned. The response body is a CBOR-serialized Error message.
The serialization of Request and Response messages has a field kind inserted
to ensure that all valid byte strings under the application/ckap+cbor content
type are unambiguous. This field MUST be set to the name of the structure (e.g.
GetSelfRequest, Error).
Client Identity
It is REQUIRED that a Key Server implement a means of authenticating Clients and assigning Principals to Clients based on a Client’s authenticated identity.
CKAP does not define a specific mechanism for Client authentication. Key
Servers MAY use a mechanism of their choice to authenticate Clients and
associate Principals with them. In the case of the HTTP transport, this may
include client certificates presented during establishment of a TLS connection,
or a defined HTTP authentication scheme presented in the Authorization
header, any other mechanism, or some combination thereof.
HTTP Asynchronous Resolution Invalidation Notification (ARIN)
A Client using the HTTP transport SHOULD implement Asynchronous Resolution Invalidation Notification (ARIN). This is an HTTP Server-Sent Events (SSE) mechanism by which a Client can be notified that a previously returned Lease is no longer valid.
Initialization
A Client initializes ARIN by obtaining an ARIN Token by issuing a GET request
for the ARINToken resource under the CKAP Base URL. The Accept header MUST
be set to application/ckap+cbor. For example:
GET /ckap/ARINToken HTTP/1.1
Accept: application/ckap+cbor
If the Key Server does not support ARIN, it MAY return a status code of 404 or 501 to indicate this.
If the Key Server supports ARIN, it generates a new ARIN Token and returns it
in the following CBOR-serialized structure. The Content-Type header MUST be
set to application/ckap+cbor:
ARINTokenResponse = {
arinToken: bstr
}
An ARIN Token is an opaque byte string generated by a Key Server to cross reference an ARIN Session with Prograde Key Resolution requests and the Leases produced by them.
An ARIN Token is a reference to an individual event stream (an ARIN Stream) which can recover from connection loss. If an event is sent to an ARIN Stream while no Client is connected to the SSE endpoint, it is buffered until reconnection occurs or until the ARIN Stream and associated ARIN Token expires.
No events are sent to an ARIN Stream by default. In order to make use of ARIN, the ARIN Token must be quoted in a Prograde Key Resolution request. If the request is successful and results in a response, the created Lease has the given ARIN Stream attached to it. If the Lease becomes expired or is otherwise invalidated due to Synchronous or Asynchronous Rollover, an invalidation event is sent to the associated ARIN Stream.
If multiple concurrent requests are made to the SSE endpoint for a given ARIN Token, the Key Server MUST attempt to send invalidation events for the given ARIN Stream to all ongoing SSE streams for it.
If an ARIN Stream and associated Token expires due to disuse, this does not impact the lifespan of any related Lease; rather, it is simply detached from any Leases it was previously attached to.
Streaming
Having obtained an ARIN Token, a Client streams ARIN events by retrieving an
HTTP SSE resource. The URL is the resource ARIN beneath the CABE Base URL,
with an additional query string parameter token set to the ARIN Token, for
example:
GET /ckap/ARIN?token={arinToken} HTTP/1.1
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Each event returned by the event stream is one of the following event types.
The event type name corresponds to the event: field of HTTP SSE.
Every event sent by the Key Server MUST have an id: field as per the SSE
specification. A Client MUST use the Last-Event-ID mechanism when
reconnecting and a Key Server MUST implement this to replay lost events. A Key
Server MAY drop events reconnection does not occur for an extended period. This
interval SHOULD be chosen according to the expiration times issued in Leases to
avoid the possibility of missed invalidations.
If a Client attempts to begin an ARIN stream using an ARIN Token which is
invalid or expired, the Key Server MUST respond with an HTTP error status code
and MUST NOT stream a SSE response. In this case, the response SHOULD have a
Content-Type of application/ckap+cbor and a response body which is the CBOR
serialization of the Error structure defined in this specification.
The event types are:
invalidate
This event type specifies the invalidation of a Lease. The data: field is
simply the leaseID which was returned with the Lease.
There is no requirement for invalidate events to be sent in any particular
order, or to only be sent once, including during replay when reconnecting using
Last-Event-ID. invalidate events MUST be idempotent. For a given ARIN
Stream used by a Client, a Client is expected to track the Leases that it has
received by Lease IDs. Clients MUST ignore invalidate events for Lease IDs it
does not recognise or it has already processed invalidation for. Lease IDs are
unique within the context of a given ARIN Token and ARIN Stream and MUST NOT be
reused within the context of a given ARIN Token or Stream.
References
Normative References
- BCP 14: Best Current Practice 14
- CABE-ARCH: CABE Architecture Specification
- RFC 8610: Concise Data Definition Language (CDDL)
- RFC 8949: Concise Binary Object Representation (CBOR)
- RFC 9052: CBOR Object Signing and Encryption (COSE): Structures and Process
- RFC 9053: CBOR Object Signing and Encryption (COSE): Initial Algorithms
- HTML-SSE: HTML Standard — Server-Sent Events
Colophon
Author
Hugo Landau