From d9df0fde01b2fa249e410b29a2726bed0f083680 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Mon, 31 Aug 2026 22:07:27 -0400 Subject: [PATCH 1/2] chore(sdk): alias experimental/tdf manifest and assertion types `sdk/experimental/tdf` carried its own copies of the manifest and assertion types -- `Manifest`, `Segment`, `KeyAccess`, `Assertion`, `Statement`, `AssertionKey` and the rest -- structurally identical to the ones in `sdk` but distinct to the type system, so anything crossing the boundary needed conversion. Two copies of the JWT signing and verification logic also had to be kept in step by hand. Replaces both files' definitions with type aliases. `sdk` owns the definitions; this package re-exports them. Every exported name and every method survives: `Assertion.Sign` / `Verify` / `GetHash`, `Statement.UnmarshalJSON`, `AssertionKey.IsEmpty` / `Algorithm`, `AssertionVerificationKeys.Get` / `IsEmpty` and the five `String()` methods all come along with the aliased types, so importers compile unchanged. A manifest produced here can now be handed to the stable SDK without conversion, which is what the follow-up delegation needs. Two deliberate non-aliases: `Policy`, `PolicyBody` and `PolicyAttribute` stay local. `sdk.PolicyObject` is itself exported and could be aliased, but it declares `Body` as an anonymous struct over the unexported `attributeObject`, so `PolicyBody` and `PolicyAttribute` have no sdk names to alias to, and the local ones are not assignable to `Body`. Aliasing `Policy` alone would break every caller that names the other two. For the alias to become possible `sdk` would have to both export the attribute type and give `Body` a named one; that is a separate change. `IntegrityAlgorithm` stays a distinct `int` type. `sdk.IntegrityAlgorithm` is itself `= int`, so no method can be attached to it, and aliasing would silently drop `String()` from this package's public API. The underlying values match, so the two convert freely. `kSplitKeyType`, `kPolicyBindingAlg` and `kGMACPayloadLength` are retained verbatim: this package still builds its own manifests and they have callers in `writer.go` and `key_access.go`. The change that removes those callers must delete these constants too, or they are left unused. (`calculateSignature` was part of this set until #4030 removed it; the integrity helpers here are now `hmacIntegrity`, `segmentIntegrity` and `rootIntegrity`.) No behavior change. The package's existing tests pass unmodified. Signed-off-by: Dave Mihalcik --- sdk/assertion.go | 2 +- sdk/experimental/tdf/assertion.go | 511 +++++++----------------------- sdk/experimental/tdf/doc.go | 11 + sdk/experimental/tdf/manifest.go | 153 +++++---- 4 files changed, 203 insertions(+), 474 deletions(-) diff --git a/sdk/assertion.go b/sdk/assertion.go index 4c3a9244bd..da7bca9d47 100644 --- a/sdk/assertion.go +++ b/sdk/assertion.go @@ -362,7 +362,7 @@ type AssertionVerificationKeys struct { } // Get returns the key for the given assertion ID or the default key if the key is not found. -// If the default key is not set, it returns error. +// If the default key is not set, it returns an empty key. func (k AssertionVerificationKeys) Get(assertionID string) (AssertionKey, error) { if key, ok := k.Keys[assertionID]; ok { return key, nil diff --git a/sdk/experimental/tdf/assertion.go b/sdk/experimental/tdf/assertion.go index db417a5479..fe5e08cab4 100644 --- a/sdk/experimental/tdf/assertion.go +++ b/sdk/experimental/tdf/assertion.go @@ -3,422 +3,125 @@ package tdf import ( - "encoding/json" - "errors" - "fmt" - - "github.com/gowebpki/jcs" - "github.com/lestrrat-go/jwx/v2/cert" - "github.com/lestrrat-go/jwx/v2/jwa" - "github.com/lestrrat-go/jwx/v2/jws" - "github.com/lestrrat-go/jwx/v2/jwt" - "github.com/opentdf/platform/lib/ocrypto" + "github.com/opentdf/platform/sdk" ) +// The JWT claim keys sdk writes into an assertion binding. Signing moved to +// sdk with the aliases below, so these have no caller here beyond the tests +// that assert against sdk's claim names. const ( - // SystemMetadataAssertionID is the standard ID for system metadata assertions - SystemMetadataAssertionID = "system-metadata" - // SystemMetadataSchemaV1 defines the schema version for system metadata - SystemMetadataSchemaV1 = "system-metadata-v1" - // kAssertionSignature is the JWT claim key for assertion signatures kAssertionSignature = "assertionSig" - // kAssertionHash is the JWT claim key for assertion hashes - kAssertionHash = "assertionHash" + kAssertionHash = "assertionHash" ) -// AssertionConfig defines an assertion to be included in the TDF during creation. -// -// AssertionConfig extends Assertion with a signing key, enabling creation -// of cryptographically signed assertions. The signing key is used during -// TDF creation but is not stored in the final TDF. -// -// Required fields: -// - ID: Unique identifier for the assertion -// - Type: The kind of assertion (BaseAssertion, HandlingAssertion) -// - Scope: What the assertion applies to (PayloadScope, TrustedDataObjScope) -// - AppliesToState: When the assertion is relevant (Encrypted, Unencrypted) -// - Statement: The assertion content and metadata -// -// Optional fields: -// - SigningKey: Custom signing key (defaults to DEK with HS256) -// -// Example: -// -// assertion := AssertionConfig{ -// ID: "retention-policy", -// Type: HandlingAssertion, -// Scope: PayloadScope, -// AppliesToState: Unencrypted, -// Statement: Statement{ -// Format: "json", -// Schema: "retention-v1", -// Value: `{"retain_days": 90, "auto_delete": true}`, -// }, -// } -type AssertionConfig struct { - ID string `validate:"required"` - Type AssertionType `validate:"required"` - Scope Scope `validate:"required"` - AppliesToState AppliesToState `validate:"required"` - Statement Statement - SigningKey AssertionKey -} - -// Assertion represents a cryptographically signed assertion in the TDF manifest. -// -// Assertions provide integrity verification and handling instructions that are -// cryptographically bound to the TDF. They cannot be modified or copied to -// another TDF without detection due to the cryptographic binding. -// -// The assertion structure includes: -// - Metadata: ID, type, scope, and state applicability -// - Statement: The actual assertion content in structured format -// - Binding: Cryptographic signature ensuring integrity -// -// Assertions are verified during TDF reading to ensure they haven't been -// tampered with since TDF creation. -type Assertion struct { - ID string `json:"id"` - Type AssertionType `json:"type"` - Scope Scope `json:"scope"` - AppliesToState AppliesToState `json:"appliesToState,omitempty"` - Statement Statement `json:"statement"` - Binding Binding `json:"binding,omitempty"` -} - -var errAssertionVerifyKeyFailure = errors.New("assertion: failed to verify with provided key") - -// Sign signs the assertion with the given hash and signature using the key. -// It returns an error if the signing fails. -// The assertion binding is updated with the method and the signature. -func (a *Assertion) Sign(hash, sig string, key AssertionKey) error { - tok := jwt.New() - if err := tok.Set(kAssertionHash, hash); err != nil { - return fmt.Errorf("failed to set assertion hash: %w", err) - } - if err := tok.Set(kAssertionSignature, sig); err != nil { - return fmt.Errorf("failed to set assertion signature: %w", err) - } - - // sign the hash and signature - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.KeyAlgorithmFrom(key.Alg.String()), key.Key)) - if err != nil { - return fmt.Errorf("signing assertion failed: %w", err) - } - - // set the binding - a.Binding.Method = JWS.String() - a.Binding.Signature = string(signedTok) - - return nil -} - -// Verify checks the binding signature of the assertion and -// returns the hash and the signature. It returns an error if the verification fails. -func (a Assertion) Verify(key AssertionKey) (string, string, error) { - parseOptions := []jwt.ParseOption{ - jwt.WithKey(jwa.KeyAlgorithmFrom(key.Alg.String()), key.Key), - } - - // Try to get a key from the JWS protected header if it exists - if decodedSig, err := jws.Parse([]byte(a.Binding.Signature)); err == nil && len(decodedSig.Signatures()) > 0 { //nolint:nestif // many keys - sig := decodedSig.Signatures()[0] - // First, try to get a JWK from the header - if jwkKey := sig.ProtectedHeaders().JWK(); jwkKey != nil { - var rawKey interface{} - if err := jwkKey.Raw(&rawKey); err == nil { - parseOptions = append(parseOptions, jwt.WithKey(sig.ProtectedHeaders().Algorithm(), rawKey)) - } - } - // Also try to get a key from x5c (certificate chain) header - if x5cChain := sig.ProtectedHeaders().X509CertChain(); x5cChain != nil && x5cChain.Len() > 0 { - // Get the first certificate (the signing certificate) - certBytes, ok := x5cChain.Get(0) - if ok { - // Parse the certificate to extract the public key - x509Cert, err := cert.Parse(certBytes) - if err == nil && x509Cert != nil { - parseOptions = append(parseOptions, jwt.WithKey(sig.ProtectedHeaders().Algorithm(), x509Cert.PublicKey)) - } - } - } - } - - tok, err := jwt.Parse([]byte(a.Binding.Signature), parseOptions...) - if err != nil { - return "", "", fmt.Errorf("%w: %w", errAssertionVerifyKeyFailure, err) - } - hashClaim, found := tok.Get(kAssertionHash) - if !found { - return "", "", errors.New("hash claim not found") - } - hash, ok := hashClaim.(string) - if !ok { - return "", "", errors.New("hash claim is not a string") - } - - sigClaim, found := tok.Get(kAssertionSignature) - if !found { - return "", "", errors.New("signature claim not found") - } - sig, ok := sigClaim.(string) - if !ok { - return "", "", errors.New("signature claim is not a string") - } - return hash, sig, nil -} - -// GetHash returns the hash of the assertion in hex format. -func (a Assertion) GetHash() ([]byte, error) { - // Clear out the binding - a.Binding = Binding{} - - // Marshal the assertion to JSON - assertionJSON, err := json.Marshal(a) - if err != nil { - return nil, fmt.Errorf("json.Marshal failed: %w", err) - } - - // Unmarshal the JSON into a map to manipulate it - var jsonObject map[string]interface{} - if err := json.Unmarshal(assertionJSON, &jsonObject); err != nil { - return nil, fmt.Errorf("json.Unmarshal failed: %w", err) - } - - // Remove the binding key - delete(jsonObject, "binding") - - // Marshal the map back to JSON - assertionJSON, err = json.Marshal(jsonObject) - if err != nil { - return nil, fmt.Errorf("json.Marshal failed: %w", err) - } - - // Transform the JSON using JCS - transformedJSON, err := jcs.Transform(assertionJSON) - if err != nil { - return nil, fmt.Errorf("jcs.Transform failed: %w", err) - } - - return ocrypto.SHA256AsHex(transformedJSON), nil -} - -func (s *Statement) UnmarshalJSON(data []byte) error { - // Define a custom struct for deserialization - type Alias Statement - aux := &struct { - Value json.RawMessage `json:"value,omitempty"` - *Alias - }{ - Alias: (*Alias)(s), - } - - if err := json.Unmarshal(data, &aux); err != nil { - return err - } - - // Attempt to decode Value as an object - var temp map[string]interface{} - if json.Unmarshal(aux.Value, &temp) == nil { - // Re-encode the object as a string and assign to Value - objAsString, err := json.Marshal(temp) - if err != nil { - return err - } - s.Value = string(objAsString) - } else { - // Assign raw string to Value - var str string - if err := json.Unmarshal(aux.Value, &str); err != nil { - return fmt.Errorf("value is neither a valid JSON object nor a string: %s", string(aux.Value)) - } - s.Value = str - } - - return nil -} - -// Statement includes information applying to the scope of the assertion. -// It could contain rights, handling instructions, or general metadata. -type Statement struct { - // Format describes the payload encoding format. (e.g. json) - Format string `json:"format,omitempty" validate:"required"` - // Schema describes the schema of the payload. (e.g. tdf) - Schema string `json:"schema,omitempty" validate:"required"` - // Value is the payload of the assertion. - Value string `json:"value,omitempty" validate:"required"` -} - -// Binding enforces cryptographic integrity of the assertion. -// So the can't be modified or copied to another tdf. -type Binding struct { - // Method used to bind the assertion. (e.g. jws) - Method string `json:"method,omitempty"` - // Signature of the assertion. - Signature string `json:"signature,omitempty"` -} - -// AssertionType represents the category of assertion being made. -// -// Different assertion types serve different purposes in TDF handling: -// - HandlingAssertion: Instructions for data processing, retention, deletion -// - BaseAssertion: General-purpose assertions including metadata, audit info -type AssertionType string - -const ( - // HandlingAssertion provides instructions for data handling and processing. - // Examples: retention policies, deletion schedules, processing requirements - HandlingAssertion AssertionType = "handling" - // BaseAssertion is a general-purpose assertion type for metadata and other content. - // Examples: audit information, system metadata, custom business logic - BaseAssertion AssertionType = "other" +// Aliases onto [github.com/opentdf/platform/sdk], which owns the +// implementation. See the package doc for why. +type ( + // AssertionConfig defines an assertion to be included in the TDF during + // creation. It extends [Assertion] with a signing key, which is used + // during TDF creation but is not stored in the final TDF. An empty + // SigningKey defaults to the TDF's DEK with HS256. + // + // See [sdk.AssertionConfig]. + AssertionConfig = sdk.AssertionConfig + + // Assertion represents a cryptographically signed assertion in the TDF + // manifest. Assertions provide integrity verification and handling + // instructions that are cryptographically bound to the TDF, so they + // cannot be modified or copied to another TDF without detection. + // + // See [sdk.Assertion]. + Assertion = sdk.Assertion + + // Statement includes information applying to the scope of the assertion. + // It could contain rights, handling instructions, or general metadata. + // + // See [sdk.Statement]. + Statement = sdk.Statement + + // Binding enforces cryptographic integrity of the assertion, so it + // cannot be modified or copied to another TDF. + // + // See [sdk.Binding]. + Binding = sdk.Binding + + // AssertionType represents the category of assertion being made. + // + // See [sdk.AssertionType]. + AssertionType = sdk.AssertionType + + // Scope defines what component of the TDF the assertion applies to. + // + // See [sdk.Scope]. + Scope = sdk.Scope + + // AppliesToState declares whether the assertion applies to encrypted or + // unencrypted data. It is a marker for consumers; nothing in the SDK + // dispatches on it. + // + // See [sdk.AppliesToState]. + AppliesToState = sdk.AppliesToState + + // BindingMethod represents the cryptographic method used to bind + // assertions to the TDF. + // + // See [sdk.BindingMethod]. + BindingMethod = sdk.BindingMethod + + // AssertionKeyAlg represents the cryptographic algorithm for assertion + // signing keys. + // + // See [sdk.AssertionKeyAlg]. + AssertionKeyAlg = sdk.AssertionKeyAlg + + // AssertionKey represents a cryptographic key for signing and verifying + // assertions. For RS256 the Key is an RSA private key, a jwk.Key, or a + // [crypto.Signer] for hardware-backed keys; for HS256 it is the shared + // secret bytes. + // + // See [sdk.AssertionKey]. + AssertionKey = sdk.AssertionKey + + // AssertionVerificationKeys represents the verification keys for + // assertions, with an optional default for unlisted assertion IDs. Get + // returns an empty key, not an error, when neither is set. + // + // See [sdk.AssertionVerificationKeys]. + AssertionVerificationKeys = sdk.AssertionVerificationKeys ) -// String returns the string representation of the assertion type. -func (at AssertionType) String() string { - return string(at) -} - -// Scope defines what component of the TDF the assertion applies to. -// -// Scope determines which part of the TDF structure the assertion governs: -// - TrustedDataObjScope: Assertion applies to the entire TDF object -// - PayloadScope: Assertion applies only to the encrypted payload data -type Scope string - const ( - // TrustedDataObjScope indicates the assertion applies to the complete TDF object. - // This includes manifest, key access objects, and payload. - TrustedDataObjScope Scope = "tdo" + // SystemMetadataAssertionID is the standard ID for system metadata assertions. + SystemMetadataAssertionID = sdk.SystemMetadataAssertionID + // SystemMetadataSchemaV1 defines the schema version for system metadata. + SystemMetadataSchemaV1 = sdk.SystemMetadataSchemaV1 + + // HandlingAssertion provides instructions for data handling and processing. + // Examples: retention policies, deletion schedules, processing requirements. + HandlingAssertion = sdk.HandlingAssertion + // BaseAssertion is a general-purpose assertion type for metadata and other + // content. Examples: audit information, system metadata, custom business logic. + BaseAssertion = sdk.BaseAssertion + + // TrustedDataObjScope indicates the assertion applies to the complete TDF + // object, including manifest, key access objects, and payload. + TrustedDataObjScope = sdk.TrustedDataObjScope // PayloadScope indicates the assertion applies only to the payload data. // This is the most common scope for data handling assertions. - PayloadScope Scope = "payload" -) - -// String returns the string representation of the scope. -func (s Scope) String() string { - return string(s) -} - -// AppliesToState indicates when the assertion is relevant in the TDF lifecycle. -// -// This determines whether the assertion should be processed before or after -// decryption, enabling different handling patterns: -// - Encrypted: Process before decryption (e.g., access logging) -// - Unencrypted: Process after decryption (e.g., content filtering) -type AppliesToState string - -const ( - // Encrypted means the assertion should be processed before payload decryption. - // Used for access control, audit logging, and pre-processing requirements. - Encrypted AppliesToState = "encrypted" - // Unencrypted means the assertion should be processed after payload decryption. - // Used for content analysis, post-processing, and data handling requirements. - Unencrypted AppliesToState = "unencrypted" -) - -// String returns the string representation of the applies to state. -func (ats AppliesToState) String() string { - return string(ats) -} + PayloadScope = sdk.PayloadScope -// BindingMethod represents the cryptographic method used to bind assertions to the TDF. -// -// The binding method ensures assertions cannot be modified or transferred -// to other TDFs without detection. -type BindingMethod string + // Encrypted marks the assertion as applying to the encrypted payload. + Encrypted = sdk.Encrypted + // Unencrypted marks the assertion as applying to the decrypted payload. + Unencrypted = sdk.Unencrypted -const ( // JWS (JSON Web Signature) is the standard method for assertion binding. - // Uses JWT-based cryptographic signatures for tamper detection. - JWS BindingMethod = "jws" + JWS = sdk.JWS + + // AssertionKeyAlgRS256 uses RSA-SHA256 for assertion signatures. Suitable + // when assertions must be verified without access to the signing key. + AssertionKeyAlgRS256 = sdk.AssertionKeyAlgRS256 + // AssertionKeyAlgHS256 uses HMAC-SHA256 for assertion signatures. More + // efficient, and lets the TDF's DEK double as the signing key. + AssertionKeyAlgHS256 = sdk.AssertionKeyAlgHS256 ) - -// String returns the string representation of the binding method. -func (bm BindingMethod) String() string { - return string(bm) -} - -// AssertionKeyAlg represents the cryptographic algorithm for assertion signing keys. -// -// Different algorithms provide different security and compatibility characteristics: -// - RS256: RSA-based signatures, widely supported, good for public key scenarios -// - HS256: HMAC-based signatures, simpler, good for shared key scenarios -type AssertionKeyAlg string - -const ( - // AssertionKeyAlgRS256 uses RSA-SHA256 for assertion signatures. - // Suitable when assertions need to be verified by parties without access to signing keys. - AssertionKeyAlgRS256 AssertionKeyAlg = "RS256" - // AssertionKeyAlgHS256 uses HMAC-SHA256 for assertion signatures. - // More efficient, suitable when the same key used for TDF encryption can sign assertions. - AssertionKeyAlgHS256 AssertionKeyAlg = "HS256" -) - -// String returns the string representation of the algorithm. -func (a AssertionKeyAlg) String() string { - return string(a) -} - -// AssertionKey represents a cryptographic key for signing and verifying assertions. -// -// The key can be either RSA or HMAC-based depending on the algorithm: -// - RS256: Key should be an RSA private key (*rsa.PrivateKey or jwk.Key) -// - HS256: Key should be a byte slice containing the shared secret -// -// Example usage: -// -// // HMAC key using TDF's Data Encryption Key -// hmacKey := AssertionKey{ -// Alg: AssertionKeyAlgHS256, -// Key: dek, // 32-byte AES key -// } -// -// // RSA key for public key scenarios -// rsaKey := AssertionKey{ -// Alg: AssertionKeyAlgRS256, -// Key: privateKey, // *rsa.PrivateKey -// } -type AssertionKey struct { - // Alg specifies the cryptographic algorithm for this key - Alg AssertionKeyAlg - // Key contains the actual key material (type depends on algorithm). - // Can be raw key material or a crypto.Signer for hardware-backed keys (HSM/KMS). - Key interface{} -} - -// Algorithm returns the cryptographic algorithm of the key. -func (k AssertionKey) Algorithm() AssertionKeyAlg { - return k.Alg -} - -// IsEmpty returns true if the key has no algorithm or key material configured. -// Used to check if a default signing key should be used instead. -func (k AssertionKey) IsEmpty() bool { - return k.Key == nil && k.Alg == "" -} - -// AssertionVerificationKeys represents the verification keys for assertions. -type AssertionVerificationKeys struct { - // Default key to use if the key for the assertion ID is not found. - DefaultKey AssertionKey - // Map of assertion ID to key. - Keys map[string]AssertionKey -} - -// Get returns the key for the given assertion ID or the default key if the key is not found. -// If the default key is not set, it returns an empty key. -func (k AssertionVerificationKeys) Get(assertionID string) (AssertionKey, error) { - if key, ok := k.Keys[assertionID]; ok { - return key, nil - } - if k.DefaultKey.IsEmpty() { - return AssertionKey{}, nil - } - return k.DefaultKey, nil -} - -// IsEmpty returns true if the default key and the keys map are empty. -func (k AssertionVerificationKeys) IsEmpty() bool { - return k.DefaultKey.IsEmpty() && len(k.Keys) == 0 -} diff --git a/sdk/experimental/tdf/doc.go b/sdk/experimental/tdf/doc.go index b149c0212d..a4102fecca 100644 --- a/sdk/experimental/tdf/doc.go +++ b/sdk/experimental/tdf/doc.go @@ -140,6 +140,17 @@ // - Standard ZIP tools (for archive structure inspection) // - TDF specification version 4.3.0 // +// # Relationship to the Stable SDK +// +// The manifest and assertion types this package exports are aliases onto +// [github.com/opentdf/platform/sdk], which owns the definitions. A manifest +// produced here is therefore the same Go type the stable SDK produces and +// needs no conversion at the boundary. The aliases exist so existing importers +// compile unchanged; prefer the sdk-scoped names in new code. +// +// Policy, PolicyBody, and PolicyAttribute are the exception: they remain +// local. See their declarations in manifest.go for why. +// // # Error Handling // // The package uses structured error reporting with operation context: diff --git a/sdk/experimental/tdf/manifest.go b/sdk/experimental/tdf/manifest.go index 4171368f89..cf6bd14501 100644 --- a/sdk/experimental/tdf/manifest.go +++ b/sdk/experimental/tdf/manifest.go @@ -7,70 +7,90 @@ import ( "fmt" "github.com/opentdf/platform/lib/ocrypto" + "github.com/opentdf/platform/sdk" ) +// kGMACPayloadLength and kSplitKeyType copy sdk's identically-named unexported +// constants; kPolicyBindingAlg has no sdk counterpart, but matches the value +// sdk writes there via hmacIntegrityAlgorithm. All three stay only while this +// package still builds manifests itself. The follow-up that delegates manifest +// construction must delete them along with their callers, or they become +// unused. const ( kGMACPayloadLength = 16 kSplitKeyType = "split" kPolicyBindingAlg = "HS256" ) -type RootSignature struct { - Algorithm string `json:"alg"` - Signature string `json:"sig"` -} - -type IntegrityInformation struct { - RootSignature `json:"rootSignature"` - SegmentHashAlgorithm string `json:"segmentHashAlg"` - DefaultSegmentSize int64 `json:"segmentSizeDefault"` - DefaultEncryptedSegSize int64 `json:"encryptedSegmentSizeDefault"` - Segments []Segment `json:"segments"` -} - -type KeyAccess struct { - KeyType string `json:"type"` - KasURL string `json:"url"` - Protocol string `json:"protocol"` - WrappedKey string `json:"wrappedKey"` - PolicyBinding interface{} `json:"policyBinding"` - EncryptedMetadata string `json:"encryptedMetadata,omitempty"` - KID string `json:"kid,omitempty"` - SplitID string `json:"sid,omitempty"` - SchemaVersion string `json:"schemaVersion,omitempty"` - EphemeralPublicKey string `json:"ephemeralPublicKey,omitempty"` -} - -type Method struct { - Algorithm string `json:"algorithm"` - IV string `json:"iv"` - IsStreamable bool `json:"isStreamable"` -} - -type Payload struct { - Type string `json:"type"` - URL string `json:"url"` - Protocol string `json:"protocol"` - MimeType string `json:"mimeType"` - IsEncrypted bool `json:"isEncrypted"` - // IntegrityInformation IntegrityInformation `json:"integrityInformation"` -} - -type EncryptionInformation struct { - KeyAccessType string `json:"type"` - Policy string `json:"policy"` - KeyAccessObjs []KeyAccess `json:"keyAccess"` - Method Method `json:"method"` - IntegrityInformation `json:"integrityInformation"` -} - -type Manifest struct { - EncryptionInformation `json:"encryptionInformation"` - Payload `json:"payload"` - Assertions []Assertion `json:"assertions,omitempty"` - TDFVersion string `json:"schemaVersion,omitempty"` -} +// Aliases onto [github.com/opentdf/platform/sdk], which owns the definitions. +// See the package doc for why. +type ( + // RootSignature is the HMAC-SHA256 over the concatenated segment hashes, + // in manifest order, keyed by the payload key. HS256 is the only accepted + // algorithm. + // + // See [sdk.RootSignature]. + RootSignature = sdk.RootSignature + + // IntegrityInformation describes segment layout and the hashes that + // protect the payload. + // + // See [sdk.IntegrityInformation]. + IntegrityInformation = sdk.IntegrityInformation + + // KeyAccess is one wrapped key share addressed to a single KAS. Its + // PolicyBinding field is an interface{}: usually a [PolicyBinding], but a + // bare string in legacy manifests. + // + // See [sdk.KeyAccess]. + KeyAccess = sdk.KeyAccess + + // Method describes the payload encryption algorithm, IV, and whether the + // payload is streamable. + // + // See [sdk.Method]. + Method = sdk.Method + + // Payload describes the payload entry in the archive: its type, URL, mime + // type, and whether it is encrypted. + // + // See [sdk.Payload]. + Payload = sdk.Payload + + // EncryptionInformation carries the policy, key access objects, and + // integrity information for a TDF. + // + // See [sdk.EncryptionInformation]. + EncryptionInformation = sdk.EncryptionInformation + + // Manifest is the TDF manifest, written to the archive as + // 0.manifest.json. + // + // See [sdk.Manifest]. + Manifest = sdk.Manifest + + // Segment is one encrypted chunk of the payload plus its integrity hash. + // Size and EncryptedSize may be omitted when they equal the manifest + // defaults; see [sdk.Segment] for the defaulting rule, which matters when + // writing segments. + Segment = sdk.Segment + + // PolicyBinding is the {alg, hash} record binding a key share to the + // policy, where hash is the HMAC keyed by that split's symmetric key. + // + // See [sdk.PolicyBinding]. + PolicyBinding = sdk.PolicyBinding + + // EncryptedMetadata is the AES-GCM envelope for a key access object's + // opaque metadata. + // + // See [sdk.EncryptedMetadata]. + EncryptedMetadata = sdk.EncryptedMetadata +) +// PolicyAttribute is one attribute entry in a [PolicyBody]. It mirrors sdk's +// unexported attributeObject field-for-field; see [Policy] for why it is not +// an alias. type PolicyAttribute struct { Attribute string `json:"attribute"` DisplayName string `json:"displayName"` @@ -79,30 +99,25 @@ type PolicyAttribute struct { KasURL string `json:"kasURL"` } +// Policy is the attribute policy, serialized into +// EncryptionInformation.Policy. It is JSON-identical to [sdk.PolicyObject] but +// deliberately not aliased onto it: sdk declares PolicyObject.Body as an +// anonymous struct over the unexported attributeObject, so [PolicyBody] and +// [PolicyAttribute] have no sdk names to alias, and the local ones are not +// assignable to Body. Aliasing Policy alone would break every caller naming +// the other two. For the alias to become possible, sdk would have to both +// export the attribute type and give Body a named one. type Policy struct { UUID string `json:"uuid"` Body PolicyBody `json:"body"` } +// PolicyBody carries the attributes and dissem list of a [Policy]. type PolicyBody struct { DataAttributes []PolicyAttribute `json:"dataAttributes"` Dissem []string `json:"dissem"` } -type Segment struct { - Hash string `json:"hash"` - Size int64 `json:"segmentSize"` - EncryptedSize int64 `json:"encryptedSegmentSize"` -} -type PolicyBinding struct { - Alg string `json:"alg"` - Hash string `json:"hash"` -} -type EncryptedMetadata struct { - Cipher string `json:"ciphertext"` - Iv string `json:"iv"` -} - var ( // ErrUnsupportedRootIntegrityAlgorithm rejects any root signature // algorithm other than HS256. From 83d88316879bb4c8d252d705260b9c2da61f9166 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 22 Sep 2026 09:06:39 -0400 Subject: [PATCH 2/2] Remove comment relevant to reviewer only --- sdk/experimental/tdf/assertion.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/sdk/experimental/tdf/assertion.go b/sdk/experimental/tdf/assertion.go index fe5e08cab4..26ed3be522 100644 --- a/sdk/experimental/tdf/assertion.go +++ b/sdk/experimental/tdf/assertion.go @@ -6,9 +6,6 @@ import ( "github.com/opentdf/platform/sdk" ) -// The JWT claim keys sdk writes into an assertion binding. Signing moved to -// sdk with the aliases below, so these have no caller here beyond the tests -// that assert against sdk's claim names. const ( kAssertionSignature = "assertionSig" kAssertionHash = "assertionHash"