func TestCreateSnapshotNoKeyInCrypto(t *testing.T) { store := storage.NewMemStorage() repo, _, err := testutils.EmptyRepo("gun") require.NoError(t, err) sgnd, err := repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) rootJSON, err := json.Marshal(sgnd) require.NoError(t, err) // create an expired snapshot sgnd, err = repo.SignSnapshot(time.Now().AddDate(-1, -1, -1)) require.True(t, repo.Snapshot.Signed.Expires.Before(time.Now())) require.NoError(t, err) snapshotJSON, err := json.Marshal(sgnd) require.NoError(t, err) // set all the metadata so we know the failure to sign is just because of the key require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: rootJSON})) require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: snapshotJSON})) hashBytes := sha256.Sum256(snapshotJSON) hashHex := hex.EncodeToString(hashBytes[:]) // pass it a new cryptoservice without the key _, _, err = GetOrCreateSnapshot("gun", hashHex, store, signed.NewEd25519()) require.Error(t, err) require.IsType(t, signed.ErrInsufficientSignatures{}, err) }
// NewSnapshot initilizes a SignedSnapshot with a given top level root // and targets objects func NewSnapshot(root *Signed, targets *Signed) (*SignedSnapshot, error) { logrus.Debug("generating new snapshot...") targetsJSON, err := json.Marshal(targets) if err != nil { logrus.Debug("Error Marshalling Targets") return nil, err } rootJSON, err := json.Marshal(root) if err != nil { logrus.Debug("Error Marshalling Root") return nil, err } rootMeta, err := NewFileMeta(bytes.NewReader(rootJSON), NotaryDefaultHashes...) if err != nil { return nil, err } targetsMeta, err := NewFileMeta(bytes.NewReader(targetsJSON), NotaryDefaultHashes...) if err != nil { return nil, err } return &SignedSnapshot{ Signatures: make([]Signature, 0), Signed: Snapshot{ SignedCommon: SignedCommon{ Type: TUFTypes[CanonicalSnapshotRole], Version: 0, Expires: DefaultExpires(CanonicalSnapshotRole), }, Meta: Files{ CanonicalRootRole: rootMeta, CanonicalTargetsRole: targetsMeta, }, }, }, nil }
// If the root is missing or corrupt, no snapshot can be generated func TestCannotMakeNewSnapshotIfNoRoot(t *testing.T) { repo, crypto, err := testutils.EmptyRepo("gun") require.NoError(t, err) // create an expired snapshot _, err = repo.SignSnapshot(time.Now().AddDate(-1, -1, -1)) require.True(t, repo.Snapshot.Signed.Expires.Before(time.Now())) require.NoError(t, err) snapshotJSON, err := json.Marshal(repo.Snapshot) require.NoError(t, err) for _, rootJSON := range [][]byte{nil, []byte("invalid JSON")} { store := storage.NewMemStorage() if rootJSON != nil { require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: rootJSON})) } require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 1, Data: snapshotJSON})) hashBytes := sha256.Sum256(snapshotJSON) hashHex := hex.EncodeToString(hashBytes[:]) _, _, err := GetOrCreateSnapshot("gun", hashHex, store, crypto) require.Error(t, err, "GetSnapshot errored") if rootJSON == nil { // missing metadata require.IsType(t, storage.ErrNotFound{}, err) } else { require.IsType(t, &json.SyntaxError{}, err) } } }
// If there is no previous snapshot or the previous snapshot is corrupt, then // even if everything else is in place, getting the snapshot fails func TestGetSnapshotNoPreviousSnapshot(t *testing.T) { repo, crypto, err := testutils.EmptyRepo("gun") require.NoError(t, err) sgnd, err := repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) rootJSON, err := json.Marshal(sgnd) require.NoError(t, err) for _, snapshotJSON := range [][]byte{nil, []byte("invalid JSON")} { store := storage.NewMemStorage() // so we know it's not a failure in getting root require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: rootJSON})) if snapshotJSON != nil { require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: snapshotJSON})) } hashBytes := sha256.Sum256(snapshotJSON) hashHex := hex.EncodeToString(hashBytes[:]) _, _, err = GetOrCreateSnapshot("gun", hashHex, store, crypto) require.Error(t, err, "GetSnapshot should have failed") if snapshotJSON == nil { require.IsType(t, storage.ErrNotFound{}, err) } else { require.IsType(t, &json.SyntaxError{}, err) } } }
func TestCreateTimestampNoKeyInCrypto(t *testing.T) { store := storage.NewMemStorage() repo, _, err := testutils.EmptyRepo("gun") require.NoError(t, err) meta, err := testutils.SignAndSerialize(repo) require.NoError(t, err) // create an expired timestamp _, err = repo.SignTimestamp(time.Now().AddDate(-1, -1, -1)) require.True(t, repo.Timestamp.Signed.Expires.Before(time.Now())) require.NoError(t, err) timestampJSON, err := json.Marshal(repo.Timestamp) require.NoError(t, err) // set all the metadata so we know the failure to sign is just because of the key require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: meta[data.CanonicalRootRole]})) require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: meta[data.CanonicalSnapshotRole]})) require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalTimestampRole, Version: 1, Data: timestampJSON})) // pass it a new cryptoservice without the key _, _, err = GetOrCreateTimestamp("gun", store, signed.NewEd25519()) require.Error(t, err) require.IsType(t, signed.ErrInsufficientSignatures{}, err) }
// We cannot validate a new root if the old root is corrupt, because there might // have been a root key rotation. func TestValidateOldRootCorruptRootRole(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) // so a valid root, but missing the root role signedRoot, err := data.RootFromSigned(r) require.NoError(t, err) delete(signedRoot.Signed.Roles, data.CanonicalRootRole) badRootJSON, err := json.Marshal(signedRoot) require.NoError(t, err) badRoot := storage.MetaUpdate{ Version: root.Version, Role: root.Role, Data: badRootJSON, } store.UpdateCurrent(gun, badRoot) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, data.ErrInvalidMetadata{}, err) }
func TestGetTimestampOldTimestampExpired(t *testing.T) { store := storage.NewMemStorage() repo, crypto, err := testutils.EmptyRepo("gun") require.NoError(t, err) meta, err := testutils.SignAndSerialize(repo) require.NoError(t, err) // create an expired timestamp _, err = repo.SignTimestamp(time.Now().AddDate(-1, -1, -1)) require.True(t, repo.Timestamp.Signed.Expires.Before(time.Now())) require.NoError(t, err) timestampJSON, err := json.Marshal(repo.Timestamp) require.NoError(t, err) // set all the metadata require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: meta[data.CanonicalRootRole]})) require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: meta[data.CanonicalSnapshotRole]})) require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalTimestampRole, Version: 1, Data: timestampJSON})) _, gottenTimestamp, err := GetOrCreateTimestamp("gun", store, crypto) require.NoError(t, err, "GetTimestamp errored") require.False(t, bytes.Equal(timestampJSON, gottenTimestamp), "Timestamp was not regenerated when old one was expired") signedMeta := &data.SignedMeta{} require.NoError(t, json.Unmarshal(gottenTimestamp, signedMeta)) // the new metadata is not expired require.True(t, signedMeta.Signed.Expires.After(time.Now())) }
// Serialize takes the Signed objects for the 4 top level roles and serializes them all to JSON func Serialize(sRoot, sTargets, sSnapshot, sTimestamp *data.Signed) (root, targets, snapshot, timestamp []byte, err error) { root, err = json.Marshal(sRoot) if err != nil { return nil, nil, nil, nil, err } targets, err = json.Marshal(sTargets) if err != nil { return nil, nil, nil, nil, err } snapshot, err = json.Marshal(sSnapshot) if err != nil { return nil, nil, nil, nil, err } timestamp, err = json.Marshal(sTimestamp) if err != nil { return nil, nil, nil, nil, err } return }
// If the root or snapshot is missing or corrupt, no timestamp can be generated func TestCannotMakeNewTimestampIfNoRootOrSnapshot(t *testing.T) { repo, crypto, err := testutils.EmptyRepo("gun") require.NoError(t, err) meta, err := testutils.SignAndSerialize(repo) require.NoError(t, err) // create an expired timestamp _, err = repo.SignTimestamp(time.Now().AddDate(-1, -1, -1)) require.True(t, repo.Timestamp.Signed.Expires.Before(time.Now())) require.NoError(t, err) timestampJSON, err := json.Marshal(repo.Timestamp) require.NoError(t, err) rootJSON := meta[data.CanonicalRootRole] snapJSON := meta[data.CanonicalSnapshotRole] invalids := []struct { test map[string][]byte err error }{ { test: map[string][]byte{data.CanonicalRootRole: rootJSON, data.CanonicalSnapshotRole: []byte("invalid JSON")}, err: storage.ErrNotFound{}, }, { test: map[string][]byte{data.CanonicalRootRole: []byte("invalid JSON"), data.CanonicalSnapshotRole: snapJSON}, err: &json.SyntaxError{}, }, { test: map[string][]byte{data.CanonicalRootRole: rootJSON}, err: storage.ErrNotFound{}, }, { test: map[string][]byte{data.CanonicalSnapshotRole: snapJSON}, err: storage.ErrNotFound{}, }, } for _, test := range invalids { dataToSet := test.test store := storage.NewMemStorage() for roleName, jsonBytes := range dataToSet { require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: roleName, Version: 0, Data: jsonBytes})) } require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalTimestampRole, Version: 1, Data: timestampJSON})) _, _, err := GetOrCreateTimestamp("gun", store, crypto) require.Error(t, err, "GetTimestamp errored") require.IsType(t, test.err, err) } }
func TestGetSnapshotOldSnapshotExpired(t *testing.T) { store := storage.NewMemStorage() repo, crypto, err := testutils.EmptyRepo("gun") require.NoError(t, err) sgnd, err := repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) rootJSON, err := json.Marshal(sgnd) require.NoError(t, err) // create an expired snapshot sgnd, err = repo.SignSnapshot(time.Now().AddDate(-1, -1, -1)) require.True(t, repo.Snapshot.Signed.Expires.Before(time.Now())) require.NoError(t, err) snapshotJSON, err := json.Marshal(sgnd) require.NoError(t, err) // set all the metadata require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: rootJSON})) require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: snapshotJSON})) hashBytes := sha256.Sum256(snapshotJSON) hashHex := hex.EncodeToString(hashBytes[:]) _, gottenSnapshot, err := GetOrCreateSnapshot("gun", hashHex, store, crypto) require.NoError(t, err, "GetSnapshot errored") require.False(t, bytes.Equal(snapshotJSON, gottenSnapshot), "Snapshot was not regenerated when old one was expired") signedMeta := &data.SignedMeta{} require.NoError(t, json.Unmarshal(gottenSnapshot, signedMeta)) // the new metadata is not expired require.True(t, signedMeta.Signed.Expires.After(time.Now())) }
// GenerateTimestamp generates a new timestamp given a previous (optional) timestamp // We can't just load the previous timestamp, because it may have been signed by a different // timestamp key (maybe from a previous root version) func (rb *repoBuilder) GenerateTimestamp(prev *data.SignedTimestamp) ([]byte, int, error) { switch { case rb.repo.cryptoService == nil: return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate timestamp without a cryptoservice"} case rb.IsLoaded(data.CanonicalTimestampRole): return nil, 0, ErrInvalidBuilderInput{msg: "timestamp has already been loaded"} } // SignTimestamp always serializes the loaded snapshot and signs in the data, so we must always // have the snapshot loaded first if err := rb.checkPrereqsLoaded([]string{data.CanonicalRootRole, data.CanonicalSnapshotRole}); err != nil { return nil, 0, err } switch prev { case nil: if err := rb.repo.InitTimestamp(); err != nil { rb.repo.Timestamp = nil return nil, 0, err } default: if err := data.IsValidTimestampStructure(prev.Signed); err != nil { return nil, 0, err } rb.repo.Timestamp = prev } sgnd, err := rb.repo.SignTimestamp(data.DefaultExpires(data.CanonicalTimestampRole)) if err != nil { rb.repo.Timestamp = nil return nil, 0, err } sgndJSON, err := json.Marshal(sgnd) if err != nil { rb.repo.Timestamp = nil return nil, 0, err } // The snapshot should have been loaded (and not checksummed, since a timestamp // cannot have been loaded), so it is awaiting checksumming. Since this // timestamp was generated using the snapshot awaiting checksumming, we can // remove it from rb.loadedNotChecksummed. There should be no other items // awaiting checksumming now since loading/generating a snapshot should have // cleared out everything else in `loadNotChecksummed`. delete(rb.loadedNotChecksummed, data.CanonicalSnapshotRole) return sgndJSON, rb.repo.Timestamp.Signed.Version, nil }
// If it's a 400, translateStatusToError attempts to parse the body into // an error. If successful (and a recognized error) that error is returned. func TestTranslateErrorsParse400Errors(t *testing.T) { origErr := validation.ErrBadRoot{Msg: "bad"} serialObj, err := validation.NewSerializableError(origErr) require.NoError(t, err) serialization, err := json.Marshal(serialObj) require.NoError(t, err) errorBody := bytes.NewBuffer([]byte(fmt.Sprintf( `{"errors": [{"otherstuff": "what", "detail": %s}]}`, string(serialization)))) errorResp := http.Response{ StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(errorBody), } finalError := translateStatusToError(&errorResp, "") require.Equal(t, origErr, finalError) }
// InvalidateMetadataSignatures signs with the right key(s) but wrong hash func (m *MetadataSwizzler) InvalidateMetadataSignatures(role string) error { signedThing, err := signedFromStore(m.MetadataCache, role) if err != nil { return err } sigs := make([]data.Signature, len(signedThing.Signatures)) for i, origSig := range signedThing.Signatures { sigs[i] = data.Signature{ KeyID: origSig.KeyID, Signature: []byte("invalid signature"), Method: origSig.Method, } } signedThing.Signatures = sigs metaBytes, err := json.Marshal(signedThing) if err != nil { return err } return m.MetadataCache.Set(role, metaBytes) }
// NewTimestamp initializes a timestamp with an existing snapshot func NewTimestamp(snapshot *Signed) (*SignedTimestamp, error) { snapshotJSON, err := json.Marshal(snapshot) if err != nil { return nil, err } snapshotMeta, err := NewFileMeta(bytes.NewReader(snapshotJSON), "sha256") if err != nil { return nil, err } return &SignedTimestamp{ Signatures: make([]Signature, 0), Signed: Timestamp{ Type: TUFTypes["timestamp"], Version: 0, Expires: DefaultExpires("timestamp"), Meta: Files{ CanonicalSnapshotRole: snapshotMeta, }, }, }, nil }
// GetOrCreateTimestamp returns the current timestamp for the gun. This may mean // a new timestamp is generated either because none exists, or because the current // one has expired. Once generated, the timestamp is saved in the store. func GetOrCreateTimestamp(gun string, store storage.MetaStore, cryptoService signed.CryptoService) ([]byte, error) { snapshot, err := store.GetCurrent(gun, "snapshot") if err != nil { return nil, err } d, err := store.GetCurrent(gun, "timestamp") if err != nil { if _, ok := err.(storage.ErrNotFound); !ok { logrus.Error("error retrieving timestamp: ", err.Error()) return nil, err } logrus.Debug("No timestamp found, will proceed to create first timestamp") } ts := &data.SignedTimestamp{} if d != nil { err := json.Unmarshal(d, ts) if err != nil { logrus.Error("Failed to unmarshal existing timestamp") return nil, err } if !timestampExpired(ts) && !snapshotExpired(ts, snapshot) { return d, nil } } sgnd, version, err := CreateTimestamp(gun, ts, snapshot, store, cryptoService) if err != nil { logrus.Error("Failed to create a new timestamp") return nil, err } out, err := json.Marshal(sgnd) if err != nil { logrus.Error("Failed to marshal new timestamp") return nil, err } err = store.UpdateCurrent(gun, storage.MetaUpdate{Role: "timestamp", Version: version, Data: out}) if err != nil { return nil, err } return out, nil }
// If there WAS a pre-existing snapshot, and it is not expired, then just return it (it doesn't // load any other metadata that it doesn't need) func TestGetSnapshotReturnsPreviousSnapshotIfUnexpired(t *testing.T) { store := storage.NewMemStorage() repo, crypto, err := testutils.EmptyRepo("gun") require.NoError(t, err) // create an expired snapshot sgnd, err := repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) require.NoError(t, err) snapshotJSON, err := json.Marshal(sgnd) require.NoError(t, err) require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: snapshotJSON})) hashBytes := sha256.Sum256(snapshotJSON) hashHex := hex.EncodeToString(hashBytes[:]) // test when db is missing the role data (no root) _, gottenSnapshot, err := GetOrCreateSnapshot("gun", hashHex, store, crypto) require.NoError(t, err, "GetSnapshot should not have failed") require.True(t, bytes.Equal(snapshotJSON, gottenSnapshot)) }
func TestGetSnapshotKeyExistingMetadata(t *testing.T) { repo, crypto, err := testutils.EmptyRepo("gun") require.NoError(t, err) sgnd, err := repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) rootJSON, err := json.Marshal(sgnd) require.NoError(t, err) store := storage.NewMemStorage() require.NoError(t, store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: rootJSON})) snapshotRole, err := repo.Root.BuildBaseRole(data.CanonicalSnapshotRole) require.NoError(t, err) key, ok := snapshotRole.Keys[repo.Root.Signed.Roles[data.CanonicalSnapshotRole].KeyIDs[0]] require.True(t, ok) k, err := GetOrCreateSnapshotKey("gun", store, crypto, data.ED25519Key) require.Nil(t, err, "Expected nil error") require.NotNil(t, k, "Key should not be nil") require.Equal(t, key, k, "Did not receive same key when attempting to recreate.") require.NotNil(t, k, "Key should not be nil") k2, err := GetOrCreateSnapshotKey("gun", store, crypto, data.ED25519Key) require.Nil(t, err, "Expected nil error") require.Equal(t, k, k2, "Did not receive same key when attempting to recreate.") require.NotNil(t, k2, "Key should not be nil") // try wiping out the cryptoservice data, and ensure we create a new key because the signer doesn't hold the key specified by TUF crypto = signed.NewEd25519() k3, err := GetOrCreateSnapshotKey("gun", store, crypto, data.ED25519Key) require.Nil(t, err, "Expected nil error") require.NotEqual(t, k, k3, "Received same key when attempting to recreate.") require.NotEqual(t, k2, k3, "Received same key when attempting to recreate.") require.NotNil(t, k3, "Key should not be nil") }
// NewTimestamp initializes a timestamp with an existing snapshot func NewTimestamp(snapshot *Signed) (*SignedTimestamp, error) { snapshotJSON, err := json.Marshal(snapshot) if err != nil { return nil, err } snapshotMeta, err := NewFileMeta(bytes.NewReader(snapshotJSON), NotaryDefaultHashes...) if err != nil { return nil, err } return &SignedTimestamp{ Signatures: make([]Signature, 0), Signed: Timestamp{ SignedCommon: SignedCommon{ Type: TUFTypes[CanonicalTimestampRole], Version: 0, Expires: DefaultExpires(CanonicalTimestampRole), }, Meta: Files{ CanonicalSnapshotRole: snapshotMeta, }, }, }, nil }
// signs the new metadata, replacing whatever signature was there func serializeMetadata(cs signed.CryptoService, s *data.Signed, role string, pubKeys ...data.PublicKey) ([]byte, error) { // delete the existing signatures s.Signatures = []data.Signature{} if len(pubKeys) < 1 { return nil, ErrNoKeyForRole{role} } if err := signed.Sign(cs, s, pubKeys, 1, nil); err != nil { if _, ok := err.(signed.ErrInsufficientSignatures); ok { return nil, ErrNoKeyForRole{Role: role} } return nil, err } metaBytes, err := json.Marshal(s) if err != nil { return nil, err } return metaBytes, nil }
// TestTUFSQLGetCurrent asserts that GetCurrent walks from the current timestamp metadata // to the snapshot specified in the checksum, to potentially other role metadata by checksum func TestTUFSQLGetCurrent(t *testing.T) { tempBaseDir, err := ioutil.TempDir("", "notary-test-") gormDB, tufDBStore := SetupTUFSQLite(t, tempBaseDir) defer os.RemoveAll(tempBaseDir) defer gormDB.Close() initialRootTUFFile := SampleTUF(1) ConsistentEmptyGetCurrentTest(t, tufDBStore, initialRootTUFFile) // put an initial piece of root metadata data in the database, // there isn't enough state to retrieve it since we require a timestamp and snapshot in our walk query := gormDB.Create(&initialRootTUFFile) require.NoError(t, query.Error, "Creating a row in an empty DB failed.") ConsistentMissingTSAndSnapGetCurrentTest(t, tufDBStore, initialRootTUFFile) // Note that get by checksum succeeds, since it does not try to walk timestamp/snapshot _, _, err = tufDBStore.GetChecksum("testGUN", "root", initialRootTUFFile.Sha256) require.NoError(t, err) // Now setup a valid TUF repo and use it to ensure we walk correctly validTUFRepo, _, err := testutils.EmptyRepo("testGUN") require.NoError(t, err) // Add the timestamp, snapshot, targets, and root to the database tufData, err := json.Marshal(validTUFRepo.Timestamp) require.NoError(t, err) tsTUF := SampleCustomTUF(data.CanonicalTimestampRole, "testGUN", tufData, validTUFRepo.Timestamp.Signed.Version) query = gormDB.Create(&tsTUF) require.NoError(t, query.Error, "Creating a row for timestamp in DB failed.") tufData, err = json.Marshal(validTUFRepo.Snapshot) require.NoError(t, err) snapTUF := SampleCustomTUF(data.CanonicalSnapshotRole, "testGUN", tufData, validTUFRepo.Snapshot.Signed.Version) query = gormDB.Create(&snapTUF) require.NoError(t, query.Error, "Creating a row for snapshot in DB failed.") tufData, err = json.Marshal(validTUFRepo.Targets[data.CanonicalTargetsRole]) require.NoError(t, err) targetsTUF := SampleCustomTUF(data.CanonicalTargetsRole, "testGUN", tufData, validTUFRepo.Targets[data.CanonicalTargetsRole].Signed.Version) query = gormDB.Create(&targetsTUF) require.NoError(t, query.Error, "Creating a row for targets in DB failed.") tufData, err = json.Marshal(validTUFRepo.Root) require.NoError(t, err) rootTUF := SampleCustomTUF(data.CanonicalRootRole, "testGUN", tufData, validTUFRepo.Root.Signed.Version) query = gormDB.Create(&rootTUF) require.NoError(t, query.Error, "Creating a row for root in DB failed.") // GetCurrent on all of these roles should succeed ConsistentGetCurrentFoundTest(t, tufDBStore, tsTUF) ConsistentGetCurrentFoundTest(t, tufDBStore, snapTUF) ConsistentGetCurrentFoundTest(t, tufDBStore, targetsTUF) ConsistentGetCurrentFoundTest(t, tufDBStore, rootTUF) // Delete snapshot query = gormDB.Delete(&snapTUF) require.NoError(t, query.Error, "Deleting a row for snapshot in DB failed.") // GetCurrent snapshot lookup should still succeed because of caching ConsistentGetCurrentFoundTest(t, tufDBStore, snapTUF) // targets and root lookup on GetCurrent should also still succeed because of caching ConsistentGetCurrentFoundTest(t, tufDBStore, targetsTUF) ConsistentGetCurrentFoundTest(t, tufDBStore, rootTUF) // add another orphaned root, but ensure that we still get the previous root // since the new root isn't in a timestamp/snapshot chain orphanedRootTUF := SampleCustomTUF(data.CanonicalRootRole, "testGUN", []byte("orphanedRoot"), 9000) query = gormDB.Create(&orphanedRootTUF) require.NoError(t, query.Error, "Creating a row for root in DB failed.") // a GetCurrent for this gun and root gets us the previous root, which is linked in timestamp and snapshot ConsistentGetCurrentFoundTest(t, tufDBStore, rootTUF) // the orphaned root fails on a GetCurrent even though it's in the underlying store ConsistentTSAndSnapGetDifferentCurrentTest(t, tufDBStore, orphanedRootTUF) }
// Marshal returns the regular non-canonical JSON form of a thing func (c canonicalJSON) Marshal(from interface{}) ([]byte, error) { return json.Marshal(from) }
// GenerateSnapshot generates a new snapshot given a previous (optional) snapshot // We can't just load the previous snapshot, because it may have been signed by a different // snapshot key (maybe from a previous root version). Note that we need the root role and // targets role to be loaded, because we need to generate metadata for both (and we need // the root to be loaded so we can get the snapshot role to sign with) func (rb *repoBuilder) GenerateSnapshot(prev *data.SignedSnapshot) ([]byte, int, error) { switch { case rb.repo.cryptoService == nil: return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate snapshot without a cryptoservice"} case rb.IsLoaded(data.CanonicalSnapshotRole): return nil, 0, ErrInvalidBuilderInput{msg: "snapshot has already been loaded"} case rb.IsLoaded(data.CanonicalTimestampRole): return nil, 0, ErrInvalidBuilderInput{msg: "cannot generate snapshot if timestamp has already been loaded"} } if err := rb.checkPrereqsLoaded([]string{data.CanonicalRootRole}); err != nil { return nil, 0, err } // If there is no previous snapshot, we need to generate one, and so the targets must // have already been loaded. Otherwise, so long as the previous snapshot structure is // valid (it has a targets meta), we're good. switch prev { case nil: if err := rb.checkPrereqsLoaded([]string{data.CanonicalTargetsRole}); err != nil { return nil, 0, err } if err := rb.repo.InitSnapshot(); err != nil { rb.repo.Snapshot = nil return nil, 0, err } default: if err := data.IsValidSnapshotStructure(prev.Signed); err != nil { return nil, 0, err } rb.repo.Snapshot = prev } sgnd, err := rb.repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) if err != nil { rb.repo.Snapshot = nil return nil, 0, err } sgndJSON, err := json.Marshal(sgnd) if err != nil { rb.repo.Snapshot = nil return nil, 0, err } // loadedNotChecksummed should currently contain the root awaiting checksumming, // since it has to have been loaded. Since the snapshot was generated using // the root and targets data (there may not be any) that that have been loaded, // remove all of them from rb.loadedNotChecksummed for tgtName := range rb.repo.Targets { delete(rb.loadedNotChecksummed, tgtName) } delete(rb.loadedNotChecksummed, data.CanonicalRootRole) // The timestamp can't have been loaded yet, so we want to cache the snapshot // bytes so we can validate the checksum when a timestamp gets generated or // loaded later. rb.loadedNotChecksummed[data.CanonicalSnapshotRole] = sgndJSON return sgndJSON, rb.repo.Snapshot.Signed.Version, nil }