Skip to content

Commit

Permalink
Merge branch 'main' into feat/bulk-rewrap
Browse files Browse the repository at this point in the history
  • Loading branch information
biscoe916 authored Jan 17, 2025
2 parents 9a0c5a1 + 456639e commit 0e6de7f
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 8 deletions.
116 changes: 116 additions & 0 deletions examples/cmd/isvalid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package cmd

import (
_ "embed"
"errors"
"io"
"testing"

"github.com/opentdf/platform/sdk"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)

type mockReadSeeker struct {
data []byte
pos int
}

func (m *mockReadSeeker) Read(p []byte) (n int, err error) {
if m.pos >= len(m.data) {
return 0, io.EOF
}
n = copy(p, m.data[m.pos:])
m.pos += n
return n, nil
}

func (m *mockReadSeeker) Seek(offset int64, whence int) (int64, error) {
var newPos int
switch whence {
case io.SeekStart:
newPos = int(offset)
case io.SeekCurrent:
newPos = m.pos + int(offset)
case io.SeekEnd:
newPos = len(m.data) + int(offset)
default:
return 0, errors.New("invalid whence")
}
if newPos < 0 || newPos > len(m.data) {
return 0, errors.New("invalid seek position")
}
m.pos = newPos
return int64(newPos), nil
}

//go:embed testdata/nano-valid.ntdf
var sampleNanoValid []byte

//go:embed testdata/tdf-filewatcher-old.tdf
var sampleTDFFileWatcherOld []byte

//go:embed testdata/tdf-multikas.tdf
var sampleTDFMultiKas []byte

//go:embed testdata/tdf-valid.tdf
var sampleTDFValid []byte

func TestIsValid(t *testing.T) {
tests := []struct {
name string
data []byte
isValidTdf bool
isValidNano bool
tdfType sdk.TdfType
}{
{
name: "Valid TDF3",
data: sampleTDFValid,
isValidTdf: true,
isValidNano: false,
tdfType: sdk.Standard,
},
{
name: "Valid NanoTDF",
data: sampleNanoValid,
isValidTdf: false,
isValidNano: true,
tdfType: sdk.Nano,
},
{
name: "Invalid All",
data: []byte("invalid data"),
isValidTdf: false,
isValidNano: false,
tdfType: sdk.Invalid,
},
{
name: "Valid TDF3 (filewatcher)",
data: sampleTDFFileWatcherOld,
isValidTdf: true,
isValidNano: false,
tdfType: sdk.Standard,
},
{
name: "Valid TDF3 (multikas)",
data: sampleTDFMultiKas,
isValidTdf: true,
isValidNano: false,
tdfType: sdk.Standard,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
in := &mockReadSeeker{data: tt.data}
cmd := &cobra.Command{}
typeInfos := isValid(cmd, in)

assert.Len(t, typeInfos, 3)
assert.Equal(t, tt.isValidTdf, typeInfos[0].Valid)
assert.Equal(t, tt.isValidNano, typeInfos[1].Valid)
assert.Equal(t, tt.tdfType.String(), typeInfos[2].Type)
})
}
}
Binary file added examples/cmd/testdata/nano-valid.ntdf
Binary file not shown.
Binary file added examples/cmd/testdata/tdf-filewatcher-old.tdf
Binary file not shown.
Binary file added examples/cmd/testdata/tdf-multikas.tdf
Binary file not shown.
Binary file added examples/cmd/testdata/tdf-valid.tdf
Binary file not shown.
3 changes: 3 additions & 0 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ require (
github.com/opentdf/platform/protocol/go v0.2.20
github.com/opentdf/platform/sdk v0.3.23
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
google.golang.org/grpc v1.66.0
google.golang.org/protobuf v1.34.2
)

require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.1-20240508200655-46a4cf4ba109.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/google/uuid v1.6.0 // indirect
Expand All @@ -27,6 +29,7 @@ require (
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
Expand Down
8 changes: 4 additions & 4 deletions sdk/schema/manifest.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"tdf_spec_version": {
"description": "Semver version number of the TDF spec.",
"type": "string"
"type": ["string", "null"]
}
},
"required": ["type", "url", "protocol", "isEncrypted"]
Expand Down Expand Up @@ -69,7 +69,7 @@
},
"sid": {
"description": "A unique identifier for a single key split. In some complex policies, multiple key access objects may exist that share a specific key split. Using a splitId allows software to more efficiently operate by not reusing key material unnecessarily. ",
"type": "string"
"type": ["string", "null"]
},
"kid": {
"description": "A UUID for the specific keypair used for wrapping the symmetric key.",
Expand Down Expand Up @@ -98,7 +98,7 @@
},
"encryptedMetadata": {
"description": "Metadata associated with the TDF, and the request. The contents of the metadata are freeform, and are used to pass information from the client, and any plugins that may be in use by the KAS. The metadata stored here should not be used for primary access decisions. Base64.",
"type": "string"
"type": ["string", "null"]
}
}
},
Expand Down Expand Up @@ -198,7 +198,7 @@
},
"appliesToState": {
"description": "Used to indicate if the statement metadata applies to 'encrypted' or 'unencrypted' data.",
"type": "string"
"type": ["string", "null"]
},
"statement": {
"description": "Intended for access, rights, and/or handling instructions that apply to the scope of the assertion.",
Expand Down
7 changes: 5 additions & 2 deletions sdk/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ func GetTdfType(reader io.ReadSeeker) TdfType {
return Invalid
}

// Indicates JSON Schema validation failed for the manifest or header of the TDF file.
// Some invalid manifests are still usable, so this file may still be usable.
var ErrInvalidPerSchema = errors.New("manifest was not valid")

//go:embed schema/manifest.schema.json
var manifestSchema []byte

Expand All @@ -356,7 +360,6 @@ var manifestSchema []byte
// 'required', older TDF versions will fail despite being valid. So each time we release an update to
// the TDF spec, we'll need to include the respective schema in the schema directory, then update this code
// to validate against all previously known schema versions.

func IsValidTdf(reader io.ReadSeeker) (bool, error) {
// create tdf reader
tdfReader, err := archive.NewTDFReader(reader)
Expand All @@ -379,7 +382,7 @@ func IsValidTdf(reader io.ReadSeeker) (bool, error) {
}

if !result.Valid() {
return false, fmt.Errorf("manifest was not valid: %v", result.Errors())
return false, fmt.Errorf("%w: %v", ErrInvalidPerSchema, result.Errors())
}

return true, nil
Expand Down
18 changes: 16 additions & 2 deletions sdk/sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,16 +184,30 @@ func TestNew_ShouldValidateStandardTdf(t *testing.T) {
}

func TestNew_ShouldNotValidateBadStandardTdf(t *testing.T) {
// This TDF is missing the "type" field in the key access object, making it invalid.
// This zip file is invalid, with a bad central directory header.
badStandardTdf := "UEsDBC0ACAAAAJ2TFTEAAAAAAAAAAAAAAAAJAAAAMC5wYXlsb2Fktu4m+vdwl0mtjhY3U5e7TG2o1s8ifK+RAhFNjRjGTLJ7V3w5UEsHCGiY7skkAAAAJAAAAFBLAwQtAAgAAACdkxUxAAAAAAAAAAAAAAAADwAAADAubWFuaWZlc3QuanNvbnsiZW5jcnlwdGlvbkluZm9ybWF0aW9uIjp7InR5cGUiOiJzcGxpdCIsInBvbGljeSI6ImV5SjFkV2xrSWpvaU1HTTFORGsyWlRZdE5EYzRaaTB4TVdWbUxXSXlOakV0WWpJMVl6UmhORE14TjJFM0lpd2lZbTlrZVNJNmV5SmtZWFJoUVhSMGNtbGlkWFJsY3lJNlczc2lZWFIwY21saWRYUmxJam9pYUhSMGNITTZMeTlsZUdGdGNHeGxMbU52YlM5aGRIUnlMMkYwZEhJeEwzWmhiSFZsTDNaaGJIVmxNU0lzSW1ScGMzQnNZWGxPWVcxbElqb2lJaXdpYVhORVpXWmhkV3gwSWpwbVlXeHpaU3dpY0hWaVMyVjVJam9pSWl3aWEyRnpWVkpNSWpvaUluMWRMQ0prYVhOelpXMGlPbHRkZlgwPSIsImtleUFjY2VzcyI6W3sidXJsIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwIiwicHJvdG9jb2wiOiJrYXMiLCJ3cmFwcGVkS2V5IjoidFVTL1BPU2lQbThlejhoci9nTFRjemNZTk9La3FDRHJWYkEwVnR2Z2tvUGxwdDNQQ2VaU3QzZ3Z5UDVWSmVwTTJjanVQYVFiSVBpcjI5VnVSdk9UV2ZkM0VIdSs4MlQrRTRFWWxKQTNuVWw3RkE0TFBmYVBLV1pNc0xMR1JCSVVMWU9FYTFiZmtTL1JvV29BMCtvN1pRRVZDYWJnSTdiRUQySldkNmhtcmptYlJzNndJcDlRVzVLOEN3SVo2VWY5RjFxMEQ1Yk5pa2xsR2grYmlSbFdTbnBMMWxwT2hXb2tYMXVCbFNFUUgzbzNibVVxUzVVWlI0ZmFMbk1ucThkdG0vMGJyY041MGhTYitMU05WZHdnWkxLM000R05sRHhnc3A5MWNFbmI2aGZLS3pnUmNFQktLTEExdW9wVzR3QkRvQWphbllqZUJWVU92QWRCOXpOOU93PT0iLCJwb2xpY3lCaW5kaW5nIjp7ImFsZyI6IkhTMjU2IiwiaGFzaCI6IlpqQXpNR1l5WXpJeFpUQm1Nek5tTWpoaE1qRmpaakkyWkROaFpUazJOREUzWkRCaFpUazNOREkyTURBMU56VTFNVFUxTVdGaU0yUmpPRFExWm1NMllnPT0ifSwia2lkIjoicjEifV0sIm1ldGhvZCI6eyJhbGdvcml0aG0iOiJBRVMtMjU2LUdDTSIsIml2IjoiIiwiaXNTdHJlYW1hYmxlIjp0cnVlfSwiaW50ZWdyaXR5SW5mb3JtYXRpb24iOnsicm9vdFNpZ25hdHVyZSI6eyJhbGciOiJIUzI1NiIsInNpZyI6IlpHVmhZbUZrTURobE1EQm1NVFZtWXpCbU1XRTBNMkpoTmpoa05qQTFaVGsxTVRkbVpqaGtaRE5rTXpJNE5XWmtNV1F4TlRWbFl6YzBNRFV4T0RNd05nPT0ifSwic2VnbWVudEhhc2hBbGciOiJHTUFDIiwic2VnbWVudFNpemVEZWZhdWx0IjoyMDk3MTUyLCJlbmNyeXB0ZWRTZWdtZW50U2l6ZURlZmF1bHQiOjIwOTcxODAsInNlZ21lbnRzIjpbeyJoYXNoIjoiTWpJM1kyRm1PVEV3TWpFeE5HUTRaREU0WXpZMFkySXlOMkkxTnpkak16az0iLCJzZWdtZW50U2l6ZSI6OCwiZW5jcnlwdGVkU2VnbWVudFNpemUiOjM2fV19fSwicGF5bG9hZCI6eyJ0eXBlIjoicmVmZXJlbmNlIiwidXJsIjoiMC5wYXlsb2FkIiwicHJvdG9jb2wiOiJ6aXAiLCJtaW1lVHlwZSI6ImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsImlzRW5jcnlwdGVkIjp0cnVlfX1QSwcIMKRTpa8FAACvBQAAUEsBAi0ALQAIAAAAnZMVMWiY7skkAAAAJAAAAAkAAAAAAAAAAAAAAAAAAAAAADAucGF5bG9hZFBLAQItAC0ACAAAAJ2TFTEwpFOlrwUAAK8FAAAPAAAAAAAAAAAAAAAAAFsAAAAwLm1hbmlmZXN0Lmpzb25QSwUGAAAAAAIAAgB0AAAARwYAAAAA"
badDecodedData, err := base64.StdEncoding.DecodeString(badStandardTdf)
in := bytes.NewReader(badDecodedData)

require.NoError(t, err)
// Decode the base64 string
isValid, _ := sdk.IsValidTdf(in)
isValid, err := sdk.IsValidTdf(in)
// Error is ok here, as it acts as a sort of reason for the nanotdf not being valid
assert.False(t, isValid)
require.Error(t, err)
}
func TestIsInvalid_MissingRequiredManifestPayloadField(t *testing.T) {
// This is a valid ZTDF, but missing the manifest.payload.type entry.
badStandardTdf := "UEsDBC0ACAAAAN2oTzIAAAAAAAAAAAAAAAAJAAAAMC5wYXlsb2FkD/U+BGl0DU+fwM4j8f6FgpXSaKlOvzGgSK/AnAzDUiID+S97s7fGqV7ajuc9uFBLBwgYUH8dLgAAAC4AAABQSwMELQAIAAAA3ahPMgAAAAAAAAAAAAAAAA8AAAAwLm1hbmlmZXN0Lmpzb257ImVuY3J5cHRpb25JbmZvcm1hdGlvbiI6eyJ0eXBlIjoic3BsaXQiLCJwb2xpY3kiOiJleUoxZFdsa0lqb2lPRFpqTW1ZNU16SXRaRE00TkMweE1XVm1MV0V3Tm1JdFpXRmtNR1F3TjJJeFpHUm1JaXdpWW05a2VTSTZleUprWVhSaFFYUjBjbWxpZFhSbGN5STZXM3NpWVhSMGNtbGlkWFJsSWpvaWFIUjBjSE02THk5bGVHRnRjR3hsTG1OdmJTOWhkSFJ5TDJGMGRISXhMM1poYkhWbEwzWmhiSFZsTVNJc0ltUnBjM0JzWVhsT1lXMWxJam9pSWl3aWFYTkVaV1poZFd4MElqcG1ZV3h6WlN3aWNIVmlTMlY1SWpvaUlpd2lhMkZ6VlZKTUlqb2lJbjFkTENKa2FYTnpaVzBpT2x0ZGZYMD0iLCJrZXlBY2Nlc3MiOlt7InR5cGUiOiJ3cmFwcGVkIiwidXJsIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwIiwicHJvdG9jb2wiOiJrYXMiLCJ3cmFwcGVkS2V5IjoiazR4ci9UZkpMbGpkOUM4VXR5Q3pQaG5JUkRBek9RUzA0bTZreDVlUmh1VEtmZkErNEIxajJIWjBTUDRwb2tRSGo2bDAycU1aalBkVEd4eDRnNm5wemN2QkdoeFhyQyt4Q3dCTHcrWjViTVI4VG1uOVpsOUhrUmJWUXNpb01QbWk2NURaY0RwOHZPTUxob0ZrblA4WlNsQ3ZuNHBXMWN2eEpGN3N5MGpvNDBlZDhhUVp6RnU1b0J4alpCbDhUcnpSZ1NGK3VwTGp1cEdKUHF0VVpmd0FQZ3JGTFdRMmFXQ0laQ2VkTDlCSXozTFhRS3lGU1VEUmZiTyszT3dPZHV6aWpNbVUrWnJPSGFkNXRqRklxS0swZHlRMkFDR3RoajNIUEJDUGc0UDJoZ0tGeEgzQWUrMTVFVnV5QWpGOVk4NDlDU2Q4NGJMS0NzTjZ4Mjl0L2dpVGJnPT0iLCJwb2xpY3lCaW5kaW5nIjp7ImFsZyI6IkhTMjU2IiwiaGFzaCI6Ik1UWTFZelExTjJRNU5qRTVPREprWXpjM056QTRaVFZpWlRWaE56QTNNMlpoWldNNU16azVaR1U1WmpVd1kySmhNakJsTVRBeE5HWmhaRGhrWlRFMk5RPT0ifSwia2lkIjoicjEifV0sIm1ldGhvZCI6eyJhbGdvcml0aG0iOiJBRVMtMjU2LUdDTSIsIml2IjoiIiwiaXNTdHJlYW1hYmxlIjp0cnVlfSwiaW50ZWdyaXR5SW5mb3JtYXRpb24iOnsicm9vdFNpZ25hdHVyZSI6eyJhbGciOiJIUzI1NiIsInNpZyI6Ik1ESTNaREUyTmpBek1qUXhOMkUxWlRRNU1qVTFPREF5WW1abE5UQmtaak5rTWpBeU1qa3pNbUkwTUdRd01EWTNNakkzTm1VeFptTmhPRGd4TlRVellnPT0ifSwic2VnbWVudEhhc2hBbGciOiJHTUFDIiwic2VnbWVudFNpemVEZWZhdWx0IjoyMDk3MTUyLCJlbmNyeXB0ZWRTZWdtZW50U2l6ZURlZmF1bHQiOjIwOTcxODAsInNlZ21lbnRzIjpbeyJoYXNoIjoiTlRJeU1qQXpaamt5WmpkaVlqTmlOMk0yWVRrMVpXUmhPR1ZsTnpOa1lqZz0iLCJzZWdtZW50U2l6ZSI6MTgsImVuY3J5cHRlZFNlZ21lbnRTaXplIjo0Nn1dfX0sInBheWxvYWQiOnsidXJsIjoiMC5wYXlsb2FkIiwicHJvdG9jb2wiOiJ6aXAiLCJtaW1lVHlwZSI6ImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSIsImlzRW5jcnlwdGVkIjp0cnVlfX1QSwcI2lLjxJ0FAACdBQAAUEsBAi0ALQAIAAAA3ahPMhhQfx0uAAAALgAAAAkAAAAAAAAAAAAAAAAAAAAAADAucGF5bG9hZFBLAQItAC0ACAAAAN2oTzLaUuPEnQUAAJ0FAAAPAAAAAAAAAAAAAAAAAGUAAAAwLm1hbmlmZXN0Lmpzb25QSwUGAAAAAAIAAgB0AAAAPwYAAAAA"
badDecodedData, err := base64.StdEncoding.DecodeString(badStandardTdf)
in := bytes.NewReader(badDecodedData)

require.NoError(t, err)
// Decode the base64 string
isValid, err := sdk.IsValidTdf(in)
// Error is ok here, as it acts as a sort of reason for the nanotdf not being valid
assert.False(t, isValid)
require.ErrorIs(t, err, sdk.ErrInvalidPerSchema)
}

func TestNew_ShouldHaveSameMethods(t *testing.T) {
Expand Down

0 comments on commit 0e6de7f

Please sign in to comment.