Skip to content

Commit e418a4b

Browse files
feat(sdk): adds configurable max manifest sizes (#2906)
### Proposed Changes * Allows configurable max manifest size so large TDFs can be decrypted. ### Checklist - [x] I have added or updated unit tests - [x] I have added or updated integration tests (if appropriate) - [x] I have added or updated documentation ### Testing Instructions
1 parent de9a76e commit e418a4b

File tree

4 files changed

+100
-18
lines changed

4 files changed

+100
-18
lines changed

sdk/internal/archive/tdf3_reader.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import (
55
)
66

77
type TDFReader struct {
8-
archiveReader Reader
8+
archiveReader Reader
9+
manifestMaxSize int64
910
}
1011

1112
const (
@@ -14,22 +15,33 @@ const (
1415
manifestMaxSize = 1024 * 1024 * 10 // 10 MB
1516
)
1617

18+
type TDFReaderOptions func(*TDFReader)
19+
20+
func WithTDFManifestMaxSize(size int64) TDFReaderOptions {
21+
return func(tdfReader *TDFReader) {
22+
tdfReader.manifestMaxSize = size
23+
}
24+
}
25+
1726
// NewTDFReader Create tdf reader instance.
18-
func NewTDFReader(readSeeker io.ReadSeeker) (TDFReader, error) {
27+
func NewTDFReader(readSeeker io.ReadSeeker, opt ...TDFReaderOptions) (TDFReader, error) {
1928
archiveReader, err := NewReader(readSeeker)
2029
if err != nil {
2130
return TDFReader{}, err
2231
}
2332

24-
tdfArchiveReader := TDFReader{}
33+
tdfArchiveReader := TDFReader{manifestMaxSize: manifestMaxSize}
2534
tdfArchiveReader.archiveReader = archiveReader
35+
for _, o := range opt {
36+
o(&tdfArchiveReader)
37+
}
2638

2739
return tdfArchiveReader, nil
2840
}
2941

3042
// Manifest Return the manifest of the tdf.
3143
func (tdfReader TDFReader) Manifest() (string, error) {
32-
fileContent, err := tdfReader.archiveReader.ReadAllFileData(TDFManifestFileName, manifestMaxSize)
44+
fileContent, err := tdfReader.archiveReader.ReadAllFileData(TDFManifestFileName, tdfReader.manifestMaxSize)
3345
if err != nil {
3446
return "", err
3547
}

sdk/tdf.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -758,8 +758,13 @@ func allowListFromKASRegistry(ctx context.Context, logger *slog.Logger, kasRegis
758758

759759
// LoadTDF loads the tdf and prepare for reading the payload from TDF
760760
func (s SDK) LoadTDF(reader io.ReadSeeker, opts ...TDFReaderOption) (*Reader, error) {
761+
config, err := newTDFReaderConfig(opts...)
762+
if err != nil {
763+
return nil, fmt.Errorf("newTDFReaderConfig failed: %w", err)
764+
}
765+
761766
// create tdf reader
762-
tdfReader, err := archive.NewTDFReader(reader)
767+
tdfReader, err := archive.NewTDFReader(reader, archive.WithTDFManifestMaxSize(config.maxManifestSize))
763768
if err != nil {
764769
return nil, fmt.Errorf("archive.NewTDFReader failed: %w", err)
765770
}
@@ -768,11 +773,6 @@ func (s SDK) LoadTDF(reader io.ReadSeeker, opts ...TDFReaderOption) (*Reader, er
768773
opts = append([]TDFReaderOption{withSessionKey(s.kasSessionKey)}, opts...)
769774
}
770775

771-
config, err := newTDFReaderConfig(opts...)
772-
if err != nil {
773-
return nil, fmt.Errorf("newAssertionConfig failed: %w", err)
774-
}
775-
776776
useGlobalFulfillableObligations := len(config.fulfillableObligationFQNs) == 0 && len(s.fulfillableObligationFQNs) > 0
777777
if useGlobalFulfillableObligations {
778778
config.fulfillableObligationFQNs = s.fulfillableObligationFQNs

sdk/tdf_config.go

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ import (
1212
)
1313

1414
const (
15-
tdf3KeySize = 2048
16-
defaultSegmentSize = 2 * 1024 * 1024 // 2mb
17-
maxSegmentSize = defaultSegmentSize * 2
18-
minSegmentSize = 16 * 1024
19-
DefaultRSAKeySize = 2048
20-
ECKeySize256 = 256
21-
ECKeySize384 = 384
22-
ECKeySize521 = 521
15+
tdf3KeySize = 2048
16+
defaultMaxManifestSize = 10 * 1024 * 1024 // 10 MB
17+
defaultSegmentSize = 2 * 1024 * 1024 // 2mb
18+
maxSegmentSize = defaultSegmentSize * 2
19+
minSegmentSize = 16 * 1024
20+
DefaultRSAKeySize = 2048
21+
ECKeySize256 = 256
22+
ECKeySize384 = 384
23+
ECKeySize521 = 521
2324
)
2425

2526
type TDFFormat = int
@@ -271,6 +272,7 @@ type TDFReaderConfig struct {
271272
kasAllowlist AllowList // KAS URLs that are allowed to be used for reading TDFs
272273
ignoreAllowList bool // If true, the kasAllowlist will be ignored, and all KAS URLs will be allowed
273274
fulfillableObligationFQNs []string
275+
maxManifestSize int64
274276
}
275277

276278
type AllowList map[string]bool
@@ -344,6 +346,7 @@ func (a AllowList) Add(kasURL string) error {
344346
func newTDFReaderConfig(opt ...TDFReaderOption) (*TDFReaderConfig, error) {
345347
c := &TDFReaderConfig{
346348
disableAssertionVerification: false,
349+
maxManifestSize: defaultMaxManifestSize,
347350
}
348351

349352
for _, o := range opt {
@@ -364,6 +367,21 @@ func newTDFReaderConfig(opt ...TDFReaderOption) (*TDFReaderConfig, error) {
364367
return c, nil
365368
}
366369

370+
// WithMaxManifestSize sets the maximum allowed manifest size for the TDF reader.
371+
// By default, the maximum manifest size is 10 MB.
372+
// The manifest size is proportional to the sum of the sizes of the policy and the number of segments in the payload.
373+
// Setting this limit helps prevent denial of service attacks due to large policies or overly segmented files.
374+
// Use this option to override the default limit; the size parameter specifies the maximum size in bytes.
375+
func WithMaxManifestSize(size int64) TDFReaderOption {
376+
return func(c *TDFReaderConfig) error {
377+
if size <= 0 {
378+
return errors.New("max manifest size must be greater than 0")
379+
}
380+
c.maxManifestSize = size
381+
return nil
382+
}
383+
}
384+
367385
func WithAssertionVerificationKeys(keys AssertionVerificationKeys) TDFReaderOption {
368386
return func(c *TDFReaderConfig) error {
369387
c.verifiers = keys

sdk/tdf_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"github.com/opentdf/platform/protocol/go/policy/kasregistry/kasregistryconnect"
4343
wellknownpb "github.com/opentdf/platform/protocol/go/wellknownconfiguration"
4444
wellknownconnect "github.com/opentdf/platform/protocol/go/wellknownconfiguration/wellknownconfigurationconnect"
45+
"github.com/opentdf/platform/sdk/internal/archive"
4546
"google.golang.org/grpc/codes"
4647
"google.golang.org/grpc/resolver"
4748
"google.golang.org/grpc/status"
@@ -3100,3 +3101,54 @@ func TestGetKasErrorToReturn(t *testing.T) {
31003101
require.Equal(t, defaultError, result)
31013102
})
31023103
}
3104+
3105+
func (s *TDFSuite) Test_LargeManifest_WithMaxManifest() {
3106+
const maxManifestSize = 1024 * 1024 // 1MB
3107+
3108+
// Helper to create a large manifest JSON string
3109+
createLargeManifest := func(size int) []byte {
3110+
manifest := map[string]interface{}{
3111+
"payload": map[string]interface{}{
3112+
"data": string(bytes.Repeat([]byte{'a'}, size)),
3113+
},
3114+
"tdf_spec_version": TDFSpecVersion,
3115+
}
3116+
b, err := json.Marshal(manifest)
3117+
s.Require().NoError(err)
3118+
return b
3119+
}
3120+
3121+
// Helper to create a TDF file in memory for testing
3122+
createTestTDF := func(manifest []byte, payload []byte) *bytes.Reader {
3123+
tdfBuffer := new(bytes.Buffer)
3124+
tdfWriter := archive.NewTDFWriter(tdfBuffer)
3125+
3126+
// Add payload
3127+
err := tdfWriter.SetPayloadSize(int64(len(payload)))
3128+
s.Require().NoError(err)
3129+
err = tdfWriter.AppendPayload(payload)
3130+
s.Require().NoError(err)
3131+
3132+
// Add manifest
3133+
err = tdfWriter.AppendManifest(string(manifest))
3134+
s.Require().NoError(err)
3135+
3136+
_, err = tdfWriter.Finish()
3137+
s.Require().NoError(err)
3138+
3139+
return bytes.NewReader(tdfBuffer.Bytes())
3140+
}
3141+
3142+
// Case 1: Manifest just below the limit
3143+
manifestBelow := createLargeManifest(maxManifestSize - 100)
3144+
tdfBelow := createTestTDF(manifestBelow, []byte("payload"))
3145+
_, err := s.sdk.LoadTDF(tdfBelow, WithMaxManifestSize(maxManifestSize))
3146+
s.Require().NoError(err, "Manifest below max size should load successfully")
3147+
3148+
// Case 2: Manifest just above the limit
3149+
manifestAbove := createLargeManifest(maxManifestSize + 100)
3150+
tdfAbove := createTestTDF(manifestAbove, []byte("payload"))
3151+
_, err = s.sdk.LoadTDF(tdfAbove, WithMaxManifestSize(maxManifestSize))
3152+
s.Require().Error(err, "Manifest above max size should fail to load")
3153+
s.Require().ErrorContains(err, "size too large")
3154+
}

0 commit comments

Comments
 (0)