OpenTDF Protocols
This section describes the interaction protocols between OpenTDF clients (SDKs) and Key Access Servers (KAS) for securely managing access to the Data Encryption Keys (DEKs) used to protect TDF payloads.
Protocol Selection and Crypto-Agility
A core design principle of OpenTDF is crypto-agility. The specific cryptographic algorithms and protocols used for key wrapping and KAS communication are not rigidly fixed by the core TDF structure. Instead, each Key Access Object within a TDF's manifest specifies:
- The URL of the responsible KAS.
- The protocol identifier (e.g.,
wrapped
) indicating how to interact with that KAS. - A Key Identifier (
kid
) referencing a specific KAS public key. - A Split Identifier (
sid
) uniquely identifying a key share when the DEK is split across multiple KAS instances for multi-party access control
This allows different keyAccess
objects within the same TDF to potentially use different key wrapping mechanisms (e.g., one KAS using RSA, another using ECIES based on Elliptic Curve Cryptography) or evolve independently to adopt new algorithms, such as post-quantum cryptography, without breaking the overall TDF format.
The client SDK MUST interpret the details within a specific keyAccess
object to determine how to interact with the corresponding KAS.
General Interaction Flow
While specific protocols vary, the high-level interaction generally follows these phases:
- TDF Creation: The SDK encrypts the payload with a generated DEK, defines the access policy, wraps the DEK using the target KAS's public key(s) according to the chosen protocol(s), calculates policy bindings, and constructs the manifest.
- Access Request: A client SDK parses the TDF manifest, identifies the relevant
keyAccess
object(s), and sends a request to the specified KAS URL(s). This request includes the wrapped key(s), policy binding information, the policy itself, and client authentication/authorization context (including the client's public key for rewrapping). - KAS Verification: The KAS authenticates the client, decrypts the wrapped DEK share(s) using its private key, validates the policy binding against the provided policy and the decrypted DEK share, and performs the authorization check (evaluating the policy against the client's authenticated attributes).
- Key Rewrap & Response: If all checks pass, the KAS re-encrypts ("rewraps") the DEK share using the client's public key provided in the request and returns it. If any check fails, the KAS returns an error.
- Payload Decryption: The client SDK decrypts the rewrapped DEK share(s) using its private key, reconstructs the full DEK (if key splitting was used), and uses the DEK to decrypt the TDF payload, verifying payload integrity simultaneously.
Example Protocol: RSA Key Wrapping
The OpenTDF reference implementation (opentdf/platform) demonstrates specific protocols. Below is a detailed example flow using RSA for wrapping the DEK. This assumes a scenario with a single KAS for simplicity.
TDF Creation (Encryption Flow)
Executed by the OpenTDF Client/SDK
- Generate DEK: Generate a cryptographically strong symmetric Data Encryption Key (DEK) (e.g., AES-256).
- Encrypt Payload: Encrypt the original payload data using the DEK and an authenticated encryption mode (e.g., AES-256-GCM), generating an Initialization Vector (IV) and integrity tags per segment. Store the IV and segment information.
- Define Policy: Construct the Policy Object JSON defining the required attributes (
dataAttributes
) and dissemination list (dissem
). Base64 encode this JSON string. - Generate Policy Binding: Calculate the policy binding hash:
HMAC(DEK, Base64(policyJSON))
using a standard algorithm like HMAC-SHA256. Base64 encode the resulting hash. - Prepare Optional Metadata: If client-specific metadata needs to be passed securely to the KAS during decryption, prepare this data.
- Encrypt Optional Metadata: Encrypt the prepared metadata using the DEK (e.g., AES-GCM). Base64 encode the ciphertext. The encypted metadata is always passed to the KAS for processing, but from the perspective of a developer using the SDK, it is optional.
- Fetch KAS Public Key: Obtain the target KAS's RSA public key (identified by the KAS URL and potentially a
kid
). This might involve a separate discovery step or be pre-configured. - Wrap DEK: Encrypt the plaintext DEK using the KAS's RSA public key (e.g., using RSAES-OAEP). Base64 encode the resulting ciphertext.
- Construct Key Access Object: Create the Key Access Object including:
type
: "wrapped"url
: KAS URLprotocol
: "kas" (or a more specific identifier if needed)kid
: Identifier of the KAS key used.wrappedKey
: Base64 encoded wrapped DEK from step 8.policyBinding
: Object containingalg
(e.g., "HS256") and the Base64 encodedhash
from step 4.encryptedMetadata
: (Optional) Base64 encoded encrypted metadata from step 6.
- Construct Manifest: Assemble the full
manifest.json
including thepayload
,encryptionInformation
(containing thekeyAccess
object(s),method
,integrityInformation
, andpolicy
string), and anyassertions
. - Package TDF: Create the Zip archive containing
manifest.json
and the encrypted payload file. - Securely Discard DEK: Erase the plaintext DEK from memory immediately after it has been wrapped and used for metadata encryption/bindings. It should never be stored persistently by the encrypting client.
TDF Access (Decryption Flow)
Involves interaction between Client/SDK and KAS
Phase 1: Client Preparation & Request (Executed by SDK)
- Parse Manifest: Read the TDF's
manifest.json
. - Identify KAS Target(s): Select the appropriate Key Access Object(s) based on desired KAS or required key shares (
sid
if splitting). - Extract Information: From the selected
keyAccess
object(s), extract the KASurl
,wrappedKey
,policyBinding
object, and optionallyencryptedMetadata
. Extract the Base64policy
string fromencryptionInformation
. - Prepare Client Context: Obtain the client's authentication credentials (e.g., OAuth token) and the client's public key (corresponding to the private key the client will use for decryption).
- Construct Rewrap Request: Create a request payload (typically JSON) containing:
- The
wrappedKey
to be rewrapped. - The
policyBinding
object (alg
andhash
). - The Base64
policy
string. - (Optional) The
encryptedMetadata
. - The client's public key (for the KAS to rewrap the DEK).
- Client authentication/authorization information (e.g., in HTTP headers).
- The
- Send Request: POST the request payload to the KAS endpoint (e.g.,
{KAS_URL}/v1/rewrap
).
Phase 2: KAS Processing & Verification (Executed by KAS)
- Authenticate Client: Verify the client's authentication credentials. If invalid, return an authentication error.
- Decrypt DEK Share: Use the KAS's private RSA key (corresponding to the public key used for wrapping, identified by
kid
) to decrypt thewrappedKey
provided in the request, yielding the plaintext DEK share. - Validate Policy Binding:
- Recalculate the HMAC:
HMAC(DEK share, policy string from request)
using the algorithm specified in the request'spolicyBinding.alg
. - Compare the recalculated HMAC hash with the
policyBinding.hash
provided in the request. - If they do not match, return a policy binding error (indicates tampering or mismatch).
- Recalculate the HMAC:
- (Optional) Decrypt Metadata: If
encryptedMetadata
was provided, decrypt it using the plaintext DEK share. This metadata might inform policy decisions or logging. - Perform Authorization Check:
- Retrieve the requesting client's validated attributes/entitlements (based on their authenticated identity).
- Parse the
policy
string from the request to get the requireddataAttributes
anddissem
list. - Evaluate the policy rules (potentially retrieving external Attribute Definitions) against the client's attributes.
- Check if the client is in the
dissem
list (if applicable). - If authorization fails (policy requirements not met), return an authorization error.
- Rewrap DEK Share: If all checks pass, encrypt the plaintext DEK share using the client's public key provided in the request (e.g., using RSAES-OAEP if the client key is RSA). Base64 encode the result.
- Send Response: Return a success response containing the Base64 encoded, rewrapped DEK share.
Phase 3: Client Decryption (Executed by SDK)
- Receive Response: Get the response from the KAS. Check for errors (authentication, binding, authorization failures).
- Decrypt DEK Share: If the request was successful, use the client's private key to decrypt the rewrapped DEK share received from the KAS.
- (If Key Splitting) Reconstruct DEK: If multiple shares were required (
sid
was used), combine the decrypted shares (e.g., via XOR) to reconstruct the full plaintext DEK. - Decrypt Payload: Use the plaintext DEK and the parameters from
encryptionInformation.method
(IV) to decrypt the TDF payload. During decryption (especially with AES-GCM or streaming), simultaneously verify the integrity of each segment using thehash
from the Segment Object and finally verify therootSignature
fromintegrityInformation
. If any integrity check fails, abort decryption and report an error. - Securely Discard DEK: Once the payload is decrypted or decryption fails, securely erase the plaintext DEK and any intermediate shares from memory.
Error Handling
KAS implementations SHOULD return standard HTTP error codes and informative error messages (without revealing sensitive internal state) for failed requests, clearly distinguishing between:
- Authentication failures (401/403)
- Policy Binding validation failures (e.g., 400 Bad Request or 403 Forbidden)
- Authorization failures (policy denied) (403 Forbidden)
- Invalid input or malformed requests (400 Bad Request)
- Internal server errors (500)
Clients MUST handle these errors appropriately.