Skip to content

Commit

Permalink
Merge pull request #273 from tri-adam/deterministic
Browse files Browse the repository at this point in the history
fix: non-deterministic timestamp behaviour
  • Loading branch information
tri-adam authored Mar 9, 2023
2 parents 868c239 + 59c6022 commit 20e4326
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 51 deletions.
19 changes: 13 additions & 6 deletions pkg/integrity/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,19 @@ type Signer struct {
// By default, one digital signature is added per object group in f. To override this behavior,
// consider using OptSignGroup and/or OptSignObjects.
//
// By default, signature, header and descriptor timestamps are set to the current time. To override
// this behavior, consider using OptSignWithTime or OptSignDeterministic.
// By default, signature timestamps are set to the current time. To override this behavior,
// consider using OptSignWithTime.
//
// By default, header and descriptor timestamps are set to the current time for non-deterministic
// images, and unset otherwise. To override this behavior, consider using OptSignWithTime or
// OptSignDeterministic.
func NewSigner(f *sif.FileImage, opts ...SignerOpt) (*Signer, error) {
if f == nil {
return nil, fmt.Errorf("integrity: %w", errNilFileImage)
}

so := signOpts{
timeFunc: time.Now,
ctx: context.Background(),
ctx: context.Background(),
}

// Apply options.
Expand All @@ -346,7 +349,11 @@ func NewSigner(f *sif.FileImage, opts ...SignerOpt) (*Signer, error) {
return nil, fmt.Errorf("integrity: %w", err)
}
case so.e != nil:
en = newClearsignEncoder(so.e, so.timeFunc)
timeFunc := time.Now
if so.timeFunc != nil {
timeFunc = so.timeFunc
}
en = newClearsignEncoder(so.e, timeFunc)
commonOpts = append(commonOpts, optSignGroupFingerprint(so.e.PrimaryKey.Fingerprint))
default:
return nil, fmt.Errorf("integrity: %w", ErrNoKeyMaterial)
Expand Down Expand Up @@ -410,7 +417,7 @@ func (s *Signer) Sign() error {
var opts []sif.AddOpt
if s.opts.deterministic {
opts = append(opts, sif.OptAddDeterministic())
} else {
} else if s.opts.timeFunc != nil {
opts = append(opts, sif.OptAddWithTime(s.opts.timeFunc()))
}

Expand Down
36 changes: 22 additions & 14 deletions pkg/sif/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func createContainer(rw ReadWriter, co createOpts) (*FileImage, error) {
// By default, the image ID is set to a randomly generated value. To override this, consider using
// OptCreateDeterministic or OptCreateWithID.
//
// By default, the image creation time is set to time.Now(). To override this, consider using
// By default, the image creation time is set to the current time. To override this, consider using
// OptCreateDeterministic or OptCreateWithTime.
//
// By default, the image will support a maximum of 48 descriptors. To change this, consider using
Expand Down Expand Up @@ -296,7 +296,7 @@ func CreateContainer(rw ReadWriter, opts ...CreateOpt) (*FileImage, error) {
// By default, the image ID is set to a randomly generated value. To override this, consider using
// OptCreateDeterministic or OptCreateWithID.
//
// By default, the image creation time is set to time.Now(). To override this, consider using
// By default, the image creation time is set to the current time. To override this, consider using
// OptCreateDeterministic or OptCreateWithTime.
//
// By default, the image will support a maximum of 48 descriptors. To change this, consider using
Expand Down Expand Up @@ -393,11 +393,13 @@ func OptAddWithTime(t time.Time) AddOpt {

// AddObject adds a new data object and its descriptor into the specified SIF file.
//
// By default, the image modification time is set to the current time. To override this, consider
// using OptAddDeterministic or OptAddWithTime.
// By default, the image modification time is set to the current time for non-deterministic images,
// and unset otherwise. To override this, consider using OptAddDeterministic or OptAddWithTime.
func (f *FileImage) AddObject(di DescriptorInput, opts ...AddOpt) error {
ao := addOpts{
t: time.Now(),
ao := addOpts{}

if !f.isDeterministic() {
ao.t = time.Now()
}

for _, opt := range opts {
Expand Down Expand Up @@ -506,11 +508,14 @@ var errCompactNotImplemented = errors.New("compact not implemented for non-last
// To zero the data region of the deleted object, use OptDeleteZero. To compact the file following
// object deletion, use OptDeleteCompact.
//
// By default, the image modification time is set to time.Now(). To override this, consider using
// OptDeleteDeterministic or OptDeleteWithTime.
// By default, the image modification time is set to the current time for non-deterministic images,
// and unset otherwise. To override this, consider using OptDeleteDeterministic or
// OptDeleteWithTime.
func (f *FileImage) DeleteObject(id uint32, opts ...DeleteOpt) error {
do := deleteOpts{
t: time.Now(),
do := deleteOpts{}

if !f.isDeterministic() {
do.t = time.Now()
}

for _, opt := range opts {
Expand Down Expand Up @@ -596,11 +601,14 @@ var (

// SetPrimPart sets the specified system partition to be the primary one.
//
// By default, the image/object modification times are set to time.Now(). To override this,
// consider using OptSetDeterministic or OptSetWithTime.
// By default, the image/object modification times are set to the current time for
// non-deterministic images, and unset otherwise. To override this, consider using
// OptSetDeterministic or OptSetWithTime.
func (f *FileImage) SetPrimPart(id uint32, opts ...SetOpt) error {
so := setOpts{
t: time.Now(),
so := setOpts{}

if !f.isDeterministic() {
so.t = time.Now()
}

for _, opt := range opts {
Expand Down
71 changes: 41 additions & 30 deletions pkg/sif/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,17 @@ func TestAddObject(t *testing.T) {
),
wantErr: errPrimaryPartition,
},
{
name: "Deterministic",
createOpts: []CreateOpt{
OptCreateWithID("de170c43-36ab-44a8-bca9-1ea1a070a274"),
OptCreateWithTime(time.Unix(946702800, 0)),
},
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
opts: []AddOpt{
OptAddDeterministic(),
},
},
{
name: "WithTime",
createOpts: []CreateOpt{
Expand All @@ -335,9 +346,6 @@ func TestAddObject(t *testing.T) {
OptCreateDeterministic(),
},
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
opts: []AddOpt{
OptAddDeterministic(),
},
},
{
name: "EmptyNotAligned",
Expand All @@ -347,9 +355,6 @@ func TestAddObject(t *testing.T) {
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce},
OptObjectAlignment(0),
),
opts: []AddOpt{
OptAddDeterministic(),
},
},
{
name: "EmptyAligned",
Expand All @@ -359,9 +364,6 @@ func TestAddObject(t *testing.T) {
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce},
OptObjectAlignment(128),
),
opts: []AddOpt{
OptAddDeterministic(),
},
},
{
name: "NotEmpty",
Expand All @@ -374,9 +376,6 @@ func TestAddObject(t *testing.T) {
di: getDescriptorInput(t, DataPartition, []byte{0xfe, 0xed},
OptPartitionMetadata(FsSquash, PartPrimSys, "386"),
),
opts: []AddOpt{
OptAddDeterministic(),
},
},
{
name: "NotEmptyNotAligned",
Expand All @@ -390,9 +389,6 @@ func TestAddObject(t *testing.T) {
OptPartitionMetadata(FsSquash, PartPrimSys, "386"),
OptObjectAlignment(0),
),
opts: []AddOpt{
OptAddDeterministic(),
},
},
{
name: "NotEmptyAligned",
Expand All @@ -406,9 +402,6 @@ func TestAddObject(t *testing.T) {
OptPartitionMetadata(FsSquash, PartPrimSys, "386"),
OptObjectAlignment(128),
),
opts: []AddOpt{
OptAddDeterministic(),
},
},
}

Expand Down Expand Up @@ -461,7 +454,6 @@ func TestDeleteObject(t *testing.T) {
},
id: 1,
opts: []DeleteOpt{
OptDeleteDeterministic(),
OptDeleteZero(true),
},
},
Expand All @@ -475,7 +467,6 @@ func TestDeleteObject(t *testing.T) {
},
id: 1,
opts: []DeleteOpt{
OptDeleteDeterministic(),
OptDeleteCompact(true),
},
},
Expand All @@ -489,11 +480,24 @@ func TestDeleteObject(t *testing.T) {
},
id: 1,
opts: []DeleteOpt{
OptDeleteDeterministic(),
OptDeleteZero(true),
OptDeleteCompact(true),
},
},
{
name: "Deterministic",
createOpts: []CreateOpt{
OptCreateWithID("de170c43-36ab-44a8-bca9-1ea1a070a274"),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
),
OptCreateWithTime(time.Unix(946702800, 0)),
},
id: 1,
opts: []DeleteOpt{
OptDeleteDeterministic(),
},
},
{
name: "WithTime",
createOpts: []CreateOpt{
Expand All @@ -518,9 +522,6 @@ func TestDeleteObject(t *testing.T) {
),
},
id: 1,
opts: []DeleteOpt{
OptDeleteDeterministic(),
},
},
}

Expand Down Expand Up @@ -563,6 +564,22 @@ func TestSetPrimPart(t *testing.T) {
id: 1,
wantErr: ErrObjectNotFound,
},
{
name: "Deterministic",
createOpts: []CreateOpt{
OptCreateWithID("de170c43-36ab-44a8-bca9-1ea1a070a274"),
OptCreateWithDescriptors(
getDescriptorInput(t, DataPartition, []byte{0xfa, 0xce},
OptPartitionMetadata(FsRaw, PartSystem, "386"),
),
),
OptCreateWithTime(time.Unix(946702800, 0)),
},
id: 1,
opts: []SetOpt{
OptSetDeterministic(),
},
},
{
name: "WithTime",
createOpts: []CreateOpt{
Expand Down Expand Up @@ -592,9 +609,6 @@ func TestSetPrimPart(t *testing.T) {
),
},
id: 1,
opts: []SetOpt{
OptSetDeterministic(),
},
},
{
name: "Two",
Expand All @@ -610,9 +624,6 @@ func TestSetPrimPart(t *testing.T) {
),
},
id: 2,
opts: []SetOpt{
OptSetDeterministic(),
},
},
}

Expand Down
8 changes: 7 additions & 1 deletion pkg/sif/sif.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2022, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
// Copyright (c) 2017, SingularityWare, LLC. All rights reserved.
// Copyright (c) 2017, Yannick Cote <[email protected]> All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
Expand Down Expand Up @@ -402,3 +402,9 @@ func (f *FileImage) DataSize() int64 { return f.h.DataSize }
func (f *FileImage) GetHeaderIntegrityReader() io.Reader {
return f.h.GetIntegrityReader()
}

// isDeterministic returns true if the UUID and timestamps in the header of f are set to
// deterministic values.
func (f *FileImage) isDeterministic() bool {
return f.h.ID == uuid.Nil && f.CreatedAt().IsZero() && f.ModifiedAt().IsZero()
}
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 20e4326

Please sign in to comment.