Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@ configured using the [`tessera.NewAppendOptions`](https://pkg.go.dev/github.com/

This is described above in [Constructing the Appender](#constructing-the-appender).

Note that entries are limited to 64KB in size by the [tlog-tiles][] spec, with the exception that when Tessera is configured for use
with Static CT via the `WithCTLayout` option, entries are then limited to 256KB.

See more details in the [Lifecycle Design: Appender](https://github.com/transparency-dev/tessera/blob/main/docs/design/lifecycle.md#appender).

### Migration Target
Expand Down
28 changes: 28 additions & 0 deletions append_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ const (
DefaultAntispamInMemorySize = 256 << 10
// DefaultWitnessTimeout is the default maximum time to wait for responses from configured witnesses.
DefaultWitnessTimeout = 5 * time.Second

// DefaultEntrySizeLimit is the maximum possible size of data for a single entry, as specified by C2SP tlog-tiles.
DefaultEntrySizeLimit = 1<<16 - 1
)

var (
Expand Down Expand Up @@ -269,6 +272,7 @@ func NewAppender(ctx context.Context, d Driver, opts *AppendOptions) (*Appender,
for i := len(opts.addDecorators) - 1; i >= 0; i-- {
a.Add = opts.addDecorators[i](a.Add)
}
a.Add = entrySizeLimitDecorator(a.Add, opts.maxEntrySize)
sd := &integrationStats{}
a.Add = sd.statsDecorator(a.Add)
for _, f := range opts.followers {
Expand Down Expand Up @@ -299,6 +303,19 @@ func NewAppender(ctx context.Context, d Driver, opts *AppendOptions) (*Appender,
return a, t.Shutdown, r, nil
}

// entrySizeLimitDecorator wraps a delegate AddFn with logic which will return an error if
// it is called with an entry larger than the provided max size.
func entrySizeLimitDecorator(d AddFn, maxSize uint) AddFn {
return func(ctx context.Context, entry *Entry) IndexFuture {
if sz := uint(len(entry.Data())); sz > maxSize {
return func() (Index, error) {
return Index{}, fmt.Errorf("entry data too large (%d > %d)", sz, maxSize)
}
}
return d(ctx, entry)
}
}

// memoizeFuture wraps an AddFn delegate with logic to ensure that the delegate is called at most
// once.
func memoizeFuture(delegate IndexFuture) IndexFuture {
Expand Down Expand Up @@ -543,6 +560,7 @@ func NewAppendOptions() *AppendOptions {
batchMaxSize: DefaultBatchMaxSize,
batchMaxAge: DefaultBatchMaxAge,
entriesPath: layout.EntriesPath,
maxEntrySize: DefaultEntrySizeLimit,
bundleIDHasher: defaultIDHasher,
checkpointInterval: DefaultCheckpointInterval,
checkpointRepublishInterval: DefaultCheckpointRepublishInterval,
Expand All @@ -564,6 +582,16 @@ type AppendOptions struct {

// EntriesPath knows how to format entry bundle paths.
entriesPath func(n uint64, p uint8) string

// maxEntrySize is the maximum permitted size of individual entries to be added.
// By default this will correspond with the limit set out in the C2SP tlog-tiles spec, however
// it will be overridden when Tessera is being used to implement static-ct.
//
// Note that storage implementations MAY enforce their own, lower, limits on entry size due to
// storage-level constraints. This should be handled internally by those storage implementations,
// e.g. in their Add() function implementations.
maxEntrySize uint

// bundleIDHasher knows how to create antispam leaf identities for entries in a serialised bundle.
bundleIDHasher func([]byte) ([][]byte, error)

Expand Down
36 changes: 36 additions & 0 deletions append_lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,42 @@ func TestAppendOptionsValid(t *testing.T) {
}
}

func TestMaxEntrySize(t *testing.T) {
d := func(_ context.Context, e *Entry) IndexFuture {
return func() (Index, error) {
return Index{}, nil
}
}

const limit = 128
add := entrySizeLimitDecorator(d, limit)

for _, test := range []struct {
name string
size uint
wantErr bool
}{
{
name: "< limit",
size: limit - 1,
}, {
name: "== limit",
size: limit,
}, {
name: "> limit",
size: limit + 1,
wantErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
_, err := add(t.Context(), NewEntry(make([]byte, test.size)))()
if gotErr := err != nil; gotErr != test.wantErr {
t.Fatalf("Got err %q, want err? %T", err, test.wantErr)
}
})
}
}

func mustCreateSigner(t *testing.T, k string) note.Signer {
t.Helper()
s, err := note.NewSigner(k)
Expand Down
5 changes: 5 additions & 0 deletions ct_only.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import (
"golang.org/x/crypto/cryptobyte"
)

// ctEntrySizeLimit is the maximum permitted serialized entry size when Tessera is configured for static-ct logs.
// Note that storage implementations MAY impose a lower limit due to infrastructure limitations.
const ctEntrySizeLimit = 256 << 10

// NewCertificateTransparencyAppender returns a function which knows how to add a CT-specific entry type to the log.
//
// This entry point MUST ONLY be used for CT logs participating in the CT ecosystem.
Expand Down Expand Up @@ -60,6 +64,7 @@ func convertCTEntry(e *ctonly.Entry) *Entry {
func (o *AppendOptions) WithCTLayout() *AppendOptions {
o.entriesPath = ctEntriesPath
o.bundleIDHasher = ctBundleIDHasher
o.maxEntrySize = ctEntrySizeLimit
return o
}

Expand Down
Loading