diff --git a/concepts/security.md b/concepts/security.md index 10f5720..6cdb81e 100644 --- a/concepts/security.md +++ b/concepts/security.md @@ -13,9 +13,12 @@ While encryption protects confidentiality, it doesn't inherently prevent undetec * **Purpose:** To allow recipients to verify that the encrypted payload has not been altered since its creation. This is especially critical for streamed data. * **Mechanism:** 1. **Segmentation:** The plaintext payload is processed in chunks (segments). - 2. **Segment Hashing/Tagging:** As each segment is encrypted (using AES-GCM, for example), a cryptographic integrity tag (like a GMAC) is generated for that encrypted segment using the *payload encryption key*. This tag is stored (as `hash`) in the corresponding [Segment Object](../schema/OpenTDF/integrity_information.md#encryptioninformationintegrityinformationsegment). - 3. **Root Signature:** All the individual segment tags/hashes are concatenated in order. A final HMAC (e.g., HMAC-SHA256) is calculated over this concatenated string of hashes, again using the *payload encryption key*. This result is stored as the `rootSignature.sig`. -* **Result:** Any modification to even a single bit of the encrypted payload will invalidate the integrity tag of the affected segment *and* consequently invalidate the final `rootSignature`. During decryption, the receiving client MUST verify the integrity tag of each segment and the overall `rootSignature`. Failure indicates tampering. + 2. **Segment Tagging (GMAC for AES-GCM):** For `method.algorithm = AES-256-GCM`, the segment `hash` is the AEAD authentication tag (GMAC) produced during encryption of that segment. It is computed with the payload key, the per-segment nonce, and any AAD, and stored as Base64 in the Segment Object. + 3. **Root Signature (HS256):** Concatenate the raw bytes of each segment tag in order (Base64-decode each `segments[i].hash` and concatenate). Compute `HMAC-SHA256` over that byte stream using the payload key, then Base64-encode the result as `rootSignature.sig`. + 4. **Nonce Requirement:** For streamable AES-GCM, each segment MUST use a unique nonce. The derivation and encoding MUST be specified by the encryption method. Reuse is catastrophic. +* **Result:** Any modification to even a single bit of the encrypted payload will invalidate the integrity tag of the affected segment *and* consequently invalidate the final `rootSignature`. During decryption, the receiving client MUST verify each segment's AEAD tag and the overall `rootSignature`. Failure indicates tampering. + +**Note on plaintext payloads:** `payload.isEncrypted=false` is reserved for future use; integrity rules for plaintext payloads are out of scope. ## 3. Policy Binding @@ -53,4 +56,4 @@ These mechanisms work together: * **Policy Binding** ensures the access policy cannot be decoupled from the key access grant for a specific KAS. * **Key Splitting** enforces multi-party authorization, preventing single points of failure or compromise for key access. -This layered approach provides robust, data-centric security and tamper evidence for data protected by OpenTDF. \ No newline at end of file +This layered approach provides robust, data-centric security and tamper evidence for data protected by OpenTDF. diff --git a/schema/OpenTDF/integrity_information.md b/schema/OpenTDF/integrity_information.md index e7cfb13..0a576e9 100644 --- a/schema/OpenTDF/integrity_information.md +++ b/schema/OpenTDF/integrity_information.md @@ -21,10 +21,10 @@ The `integrityInformation` object, nested within [`encryptionInformation`](./enc | Parameter | Type | Description | Required? | | --------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| rootSignature | Object | Contains a cryptographic signature or HMAC over the combined integrity hashes of all segments, providing overall payload integrity. | Yes | +| rootSignature | Object | Contains a cryptographic integrity value over the combined segment hashes/tags, providing overall payload integrity. For `alg=HS256`, this is an HMAC. | Yes | | rootSignature.alg | String | Algorithm used for the rootSignature.sig. HS256 (HMAC-SHA256 using the payload key) is commonly used. | Yes | -| rootSignature.sig | String | The Base64 encoded signature or HMAC value. Calculated over the concatenation of all segment hashes/tags in order. E.g., Base64(HMAC-SHA256(PayloadKey, Concat(SegmentHash1, SegmentHash2, ...))). | Yes | -| segmentHashAlg | String | The algorithm used to generate the hash for each segment in the segments array. GMAC (using the AES-GCM payload key) is commonly used when method.algorithm is AES-256-GCM. | Yes | +| rootSignature.sig | String | The Base64-encoded integrity value. For `alg=HS256`, this is `Base64(HMAC-SHA256(PayloadKey, Concat(bytes(Hash1), bytes(Hash2), ...)))`, where `bytes(HashN)` are the Base64-decoded segment hashes in order. | Yes | +| segmentHashAlg | String | The algorithm used to generate the hash for each segment in the segments array. For `AES-256-GCM`, `GMAC` (the AEAD tag) is used. | Yes | | segments | Array | An array of [Segment Objects](#encryptionInformation.integrityInformation.segment), one for each chunk of the payload if method.isStreamable is true. Order MUST match payload order. | Yes | | segmentSizeDefault | Number | The default size (in bytes) of the plaintext payload segments. Allows omitting segmentSize in individual segment objects if they match this default. | Yes | | encryptedSegmentSizeDefault | Number | The default size (in bytes) of the encrypted payload segments (including any authentication tag overhead, like from AES-GCM). Allows omitting encryptedSegmentSize in segments. | | @@ -43,6 +43,8 @@ Object containing integrity information about a segment of the payload, includin |Parameter|Type|Description| |---|---|---| -|`hash`|String|A hash generated using the specified `segmentHashAlg`.

`Base64.encode(HMAC(segment, payloadKey))`| +|`hash`|String|A Base64-encoded authentication tag generated using the specified `segmentHashAlg`. For `GMAC`, this is the AES-GCM tag produced during encryption of the segment with the payload key, the per-segment nonce, and any AAD.| + +**Nonce derivation (AES-GCM, streamable):** Each segment must use a unique nonce; the derivation and encoding MUST be specified by the encryption method. Without a defined nonce scheme, GMAC verification is undefined. |`segmentSize`|Number|The size of the segment. This field is optional. The size of the segment is inferred from 'segmentSizeDefault' defined above, but in the event that a segment were modified and re-encrypted, the segment size would change.| -|`encryptedSegmentSize`|Number|The size of the segment (in bytes) after the payload segment has been encrypted.| \ No newline at end of file +|`encryptedSegmentSize`|Number|The size of the segment (in bytes) after the payload segment has been encrypted.| diff --git a/schema/OpenTDF/json-schema/schema.json b/schema/OpenTDF/json-schema/schema.json index b2929ba..441462b 100644 --- a/schema/OpenTDF/json-schema/schema.json +++ b/schema/OpenTDF/json-schema/schema.json @@ -133,7 +133,7 @@ "type": "number" }, "segmentHashAlg": { - "description": "Algorithm used to generate segment hashes", + "description": "Algorithm used to generate segment hashes (e.g., GMAC for AES-GCM AEAD tags)", "type": "string" }, "segments": { diff --git a/schema/OpenTDF/method.md b/schema/OpenTDF/method.md index 61737ca..c52abf7 100644 --- a/schema/OpenTDF/method.md +++ b/schema/OpenTDF/method.md @@ -18,4 +18,4 @@ The `method` object, nested within [`encryptionInformation`](./encryption_inform | ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | | algorithm | String | The symmetric encryption algorithm used. AES-256-GCM is the recommended and commonly implemented algorithm. | Yes | | isStreamable | Boolean | Indicates if the payload was encrypted in segments suitable for streaming decryption. If true, [integrityInformation](./integrity_information.md) MUST contain segment details. | Yes | -| iv | String | The Base64 encoded Initialization Vector (IV) used with the symmetric algorithm. MUST be unique for each TDF encrypted with the same key. For AES-GCM, typically 12 bytes (96 bits). | Yes | +| iv | String | The Base64 encoded Initialization Vector (IV) used with the symmetric algorithm. MUST be unique for each TDF encrypted with the same key. For AES-GCM, MUST be 12 bytes (96 bits). | Yes |