// CreateTimestamp creates a new timestamp. If a prev timestamp is provided, it // is assumed this is the immediately previous one, and the new one will have a // version number one higher than prev. The store is used to lookup the current // snapshot, this function does not save the newly generated timestamp. func CreateTimestamp(gun string, prev *data.SignedTimestamp, snapshot []byte, store storage.MetaStore, cryptoService signed.CryptoService) (*data.Signed, int, error) { algorithm, public, err := store.GetTimestampKey(gun) if err != nil { // owner of gun must have generated a timestamp key otherwise // we won't proceed with generating everything. return nil, 0, err } key := data.NewPublicKey(algorithm, public) sn := &data.Signed{} err = json.Unmarshal(snapshot, sn) if err != nil { // couldn't parse snapshot return nil, 0, err } ts, err := data.NewTimestamp(sn) if err != nil { return nil, 0, err } if prev != nil { ts.Signed.Version = prev.Signed.Version + 1 } sgndTs, err := cjson.Marshal(ts.Signed) if err != nil { return nil, 0, err } out := &data.Signed{ Signatures: ts.Signatures, Signed: sgndTs, } err = signed.Sign(cryptoService, out, key) if err != nil { return nil, 0, err } return out, ts.Signed.Version, nil }
// GetOrCreateSnapshotKey either creates a new snapshot key, or returns // the existing one. Only the PublicKey is returned. The private part // is held by the CryptoService. func GetOrCreateSnapshotKey(gun string, store storage.MetaStore, crypto signed.CryptoService, createAlgorithm string) (data.PublicKey, error) { _, rootJSON, err := store.GetCurrent(gun, data.CanonicalRootRole) if err != nil { // If the error indicates we couldn't find the root, create a new key if _, ok := err.(storage.ErrNotFound); !ok { logrus.Errorf("Error when retrieving root role for GUN %s: %v", gun, err) return nil, err } return crypto.Create(data.CanonicalSnapshotRole, gun, createAlgorithm) } // If we have a current root, parse out the public key for the snapshot role, and return it repoSignedRoot := new(data.SignedRoot) if err := json.Unmarshal(rootJSON, repoSignedRoot); err != nil { logrus.Errorf("Failed to unmarshal existing root for GUN %s to retrieve snapshot key ID", gun) return nil, err } snapshotRole, err := repoSignedRoot.BuildBaseRole(data.CanonicalSnapshotRole) if err != nil { logrus.Errorf("Failed to extract snapshot role from root for GUN %s", gun) return nil, err } // We currently only support single keys for snapshot and timestamp, so we can return the first and only key in the map if the signer has it for keyID := range snapshotRole.Keys { if pubKey := crypto.GetKey(keyID); pubKey != nil { return pubKey, nil } } logrus.Debugf("Failed to find any snapshot keys in cryptosigner from root for GUN %s, generating new key", gun) return crypto.Create(data.CanonicalSnapshotRole, gun, createAlgorithm) }
// 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. // Additionally, if we had to generate a new snapshot for this timestamp, // it is also saved in the store func GetOrCreateTimestamp(gun string, store storage.MetaStore, cryptoService signed.CryptoService) ( *time.Time, []byte, error) { updates := []storage.MetaUpdate{} lastModified, timestampJSON, err := store.GetCurrent(gun, data.CanonicalTimestampRole) if err != nil { logrus.Debug("error retrieving timestamp: ", err.Error()) return nil, nil, err } prev := &data.SignedTimestamp{} if err := json.Unmarshal(timestampJSON, prev); err != nil { logrus.Error("Failed to unmarshal existing timestamp") return nil, nil, err } snapChecksums, err := prev.GetSnapshot() if err != nil || snapChecksums == nil { return nil, nil, err } snapshotSha256Bytes, ok := snapChecksums.Hashes[notary.SHA256] if !ok { return nil, nil, data.ErrMissingMeta{Role: data.CanonicalSnapshotRole} } snapshotSha256Hex := hex.EncodeToString(snapshotSha256Bytes[:]) snapshotTime, snapshot, err := snapshot.GetOrCreateSnapshot(gun, snapshotSha256Hex, store, cryptoService) if err != nil { logrus.Debug("Previous timestamp, but no valid snapshot for GUN ", gun) return nil, nil, err } snapshotRole := &data.SignedSnapshot{} if err := json.Unmarshal(snapshot, snapshotRole); err != nil { logrus.Error("Failed to unmarshal retrieved snapshot") return nil, nil, err } // If the snapshot was generated, we should write it with the timestamp if snapshotTime == nil { updates = append(updates, storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: snapshotRole.Signed.Version, Data: snapshot}) } if !timestampExpired(prev) && !snapshotExpired(prev, snapshot) { return lastModified, timestampJSON, nil } tsUpdate, err := createTimestamp(gun, prev, snapshot, store, cryptoService) if err != nil { logrus.Error("Failed to create a new timestamp") return nil, nil, err } updates = append(updates, *tsUpdate) c := time.Now() // Write the timestamp, and potentially snapshot if err = store.UpdateMany(gun, updates); err != nil { return nil, nil, err } return &c, tsUpdate.Data, nil }
// generateSnapshot generates a new snapshot from the previous one in the store - this assumes all // the other roles except timestamp have already been set on the repo, and will set the generated // snapshot on the repo as well func generateSnapshot(gun string, builder tuf.RepoBuilder, store storage.MetaStore) (*storage.MetaUpdate, error) { var prev *data.SignedSnapshot _, currentJSON, err := store.GetCurrent(gun, data.CanonicalSnapshotRole) if err == nil { prev = new(data.SignedSnapshot) if err = json.Unmarshal(currentJSON, prev); err != nil { logrus.Error("Failed to unmarshal existing snapshot for GUN ", gun) return nil, err } } if _, ok := err.(storage.ErrNotFound); !ok && err != nil { return nil, err } meta, ver, err := builder.GenerateSnapshot(prev) switch err.(type) { case nil: return &storage.MetaUpdate{ Role: data.CanonicalSnapshotRole, Version: ver, Data: meta, }, nil case signed.ErrInsufficientSignatures, signed.ErrNoKeys, signed.ErrRoleThreshold: // If we cannot sign the snapshot, then we don't have keys for the snapshot, // and the client should have submitted a snapshot return nil, validation.ErrBadHierarchy{ Missing: data.CanonicalSnapshotRole, Msg: "no snapshot was included in update and server does not hold current snapshot key for repository"} default: return nil, validation.ErrValidation{Msg: err.Error()} } }
// CreateTimestamp creates a new timestamp. If a prev timestamp is provided, it // is assumed this is the immediately previous one, and the new one will have a // version number one higher than prev. The store is used to lookup the current // snapshot, this function does not save the newly generated timestamp. func createTimestamp(gun string, prev *data.SignedTimestamp, snapshot []byte, store storage.MetaStore, cryptoService signed.CryptoService) (*storage.MetaUpdate, error) { builder := tuf.NewRepoBuilder(gun, cryptoService, trustpinning.TrustPinConfig{}) // load the current root to ensure we use the correct timestamp key. _, root, err := store.GetCurrent(gun, data.CanonicalRootRole) if err != nil { logrus.Debug("Previous timestamp, but no root for GUN ", gun) return nil, err } if err := builder.Load(data.CanonicalRootRole, root, 1, false); err != nil { logrus.Debug("Could not load valid previous root for GUN ", gun) return nil, err } // load snapshot so we can include it in timestamp if err := builder.Load(data.CanonicalSnapshotRole, snapshot, 1, false); err != nil { logrus.Debug("Could not load valid previous snapshot for GUN ", gun) return nil, err } meta, ver, err := builder.GenerateTimestamp(prev) if err != nil { return nil, err } return &storage.MetaUpdate{ Role: data.CanonicalTimestampRole, Version: ver, Data: meta, }, nil }
func getRole(ctx context.Context, store storage.MetaStore, gun, role, checksum string) (*time.Time, []byte, error) { var ( lastModified *time.Time out []byte err error ) if checksum == "" { // the timestamp and snapshot might be server signed so are // handled specially switch role { case data.CanonicalTimestampRole, data.CanonicalSnapshotRole: return getMaybeServerSigned(ctx, store, gun, role) } lastModified, out, err = store.GetCurrent(gun, role) } else { lastModified, out, err = store.GetChecksum(gun, role, checksum) } if err != nil { if _, ok := err.(storage.ErrNotFound); ok { return nil, nil, errors.ErrMetadataNotFound.WithDetail(err) } return nil, nil, errors.ErrUnknown.WithDetail(err) } if out == nil { return nil, nil, errors.ErrMetadataNotFound.WithDetail(nil) } return lastModified, out, nil }
func loadFromStore(gun, roleName string, builder tuf.RepoBuilder, store storage.MetaStore) error { _, metaJSON, err := store.GetCurrent(gun, roleName) if err != nil { return err } if err := builder.Load(roleName, metaJSON, 1, true); err != nil { return err } return nil }
func loadTargetsFromStore(gun, role string, repo *tuf.Repo, store storage.MetaStore) error { tgtJSON, err := store.GetCurrent(gun, role) if err != nil { return err } t := &data.SignedTargets{} err = json.Unmarshal(tgtJSON, t) if err != nil { return err } return repo.SetTargets(role, t) }
func copyTimestampKey(t *testing.T, fromRepo *tuf.Repo, toStore storage.MetaStore, gun string) { role, err := fromRepo.GetBaseRole(data.CanonicalTimestampRole) assert.NoError(t, err) assert.NotNil(t, role, "No timestamp role in the root file") assert.Len(t, role.ListKeyIDs(), 1, fmt.Sprintf( "Expected 1 timestamp key in timestamp role, got %d", len(role.ListKeyIDs()))) pubTimestampKey := role.ListKeys()[0] err = toStore.SetKey(gun, data.CanonicalTimestampRole, pubTimestampKey.Algorithm(), pubTimestampKey.Public()) assert.NoError(t, err) }
func copyTimestampKey(t *testing.T, fromKeyDB *keys.KeyDB, toStore storage.MetaStore, gun string) { role := fromKeyDB.GetRole(data.CanonicalTimestampRole) assert.NotNil(t, role, "No timestamp role in the KeyDB") assert.Len(t, role.KeyIDs, 1, fmt.Sprintf( "Expected 1 timestamp key in timestamp role, got %d", len(role.KeyIDs))) pubTimestampKey := fromKeyDB.GetKey(role.KeyIDs[0]) assert.NotNil(t, pubTimestampKey, "Timestamp key specified by KeyDB role not in KeysDB") err := toStore.SetKey(gun, data.CanonicalTimestampRole, pubTimestampKey.Algorithm(), pubTimestampKey.Public()) assert.NoError(t, err) }
func changefeed(logger ctxu.Logger, store storage.MetaStore, imageName, changeID string, records int64) ([]byte, error) { changes, err := store.GetChanges(changeID, int(records), imageName) if err != nil { logger.Errorf("%d GET could not retrieve records: %s", http.StatusInternalServerError, err.Error()) return nil, errors.ErrUnknown.WithDetail(err) } out, err := json.Marshal(&changefeedResponse{ NumberOfRecords: len(changes), Records: changes, }) if err != nil { logger.Errorf("%d GET could not json.Marshal changefeedResponse", http.StatusInternalServerError) return nil, errors.ErrUnknown.WithDetail(err) } return out, nil }
// getMaybeServerSigned writes the current snapshot or timestamp (based on the // role passed) to the provided writer or returns an error. In retrieving // the timestamp and snapshot, based on the keys held by the server, a new one // might be generated and signed due to expiry of the previous one or updates // to other roles. func getMaybeServerSigned(ctx context.Context, store storage.MetaStore, gun, role string) (*time.Time, []byte, error) { cryptoServiceVal := ctx.Value(notary.CtxKeyCryptoSvc) cryptoService, ok := cryptoServiceVal.(signed.CryptoService) if !ok { return nil, nil, errors.ErrNoCryptoService.WithDetail(nil) } var ( lastModified *time.Time out []byte err error ) if role != data.CanonicalTimestampRole && role != data.CanonicalSnapshotRole { return nil, nil, fmt.Errorf("role %s cannot be server signed", role) } lastModified, out, err = timestamp.GetOrCreateTimestamp(gun, store, cryptoService) if err != nil { switch err.(type) { case *storage.ErrNoKey, storage.ErrNotFound: return nil, nil, errors.ErrMetadataNotFound.WithDetail(err) default: return nil, nil, errors.ErrUnknown.WithDetail(err) } } // If we wanted the snapshot, get it by checksum from the timestamp data if role == data.CanonicalSnapshotRole { ts := new(data.SignedTimestamp) if err := json.Unmarshal(out, ts); err != nil { return nil, nil, err } snapshotChecksums, err := ts.GetSnapshot() if err != nil || snapshotChecksums == nil { return nil, nil, fmt.Errorf("could not retrieve latest snapshot checksum") } if snapshotSHA256Bytes, ok := snapshotChecksums.Hashes[notary.SHA256]; ok { snapshotSHA256Hex := hex.EncodeToString(snapshotSHA256Bytes[:]) return store.GetChecksum(gun, role, snapshotSHA256Hex) } return nil, nil, fmt.Errorf("could not retrieve sha256 snapshot checksum") } return lastModified, out, nil }
func validateRoot(gun string, oldRoot, newRoot []byte, store storage.MetaStore) ( *data.SignedRoot, error) { var parsedOldRoot *data.SignedRoot parsedNewRoot := &data.SignedRoot{} if oldRoot != nil { parsedOldRoot = &data.SignedRoot{} err := json.Unmarshal(oldRoot, parsedOldRoot) if err != nil { // TODO(david): if we can't read the old root should we continue // here to check new root self referential integrity? // This would permit recovery of a repo with a corrupted // root. logrus.Warn("Old root could not be parsed.") } } err := json.Unmarshal(newRoot, parsedNewRoot) if err != nil { return nil, err } // Don't update if a timestamp key doesn't exist. algo, keyBytes, err := store.GetKey(gun, data.CanonicalTimestampRole) if err != nil || algo == "" || keyBytes == nil { return nil, fmt.Errorf("no timestamp key for %s", gun) } timestampKey := data.NewPublicKey(algo, keyBytes) if err := checkRoot(parsedOldRoot, parsedNewRoot, timestampKey); err != nil { // TODO(david): how strict do we want to be here about old signatures // for rotations? Should the user have to provide a flag // which gets transmitted to force a root update without // correct old key signatures. return nil, err } if !data.ValidTUFType(parsedNewRoot.Signed.Type, data.CanonicalRootRole) { return nil, fmt.Errorf("root has wrong type") } return parsedNewRoot, 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 := snapshot.GetOrCreateSnapshot(gun, store, cryptoService) 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 }
// GetOrCreateTimestampKey returns the timestamp key for the gun. It uses the store to // lookup an existing timestamp key and the crypto to generate a new one if none is // found. It attempts to handle the race condition that may occur if 2 servers try to // create the key at the same time by simply querying the store a second time if it // receives a conflict when writing. func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService, fallBackAlgorithm data.KeyAlgorithm) (data.PublicKey, error) { keyAlgorithm, public, err := store.GetTimestampKey(gun) if err == nil { return data.NewPublicKey(keyAlgorithm, public), nil } if _, ok := err.(*storage.ErrNoKey); ok { key, err := crypto.Create("timestamp", fallBackAlgorithm) if err != nil { return nil, err } logrus.Debug("Creating new timestamp key for ", gun, ". With algo: ", key.Algorithm()) err = store.SetTimestampKey(gun, key.Algorithm(), key.Public()) if err == nil { return key, nil } if _, ok := err.(*storage.ErrTimestampKeyExists); ok { keyAlgorithm, public, err = store.GetTimestampKey(gun) if err != nil { return nil, err } return data.NewPublicKey(keyAlgorithm, public), nil } return nil, err } return nil, err }
// GetOrCreateTimestampKey returns the timestamp key for the gun. It uses the store to // lookup an existing timestamp key and the crypto to generate a new one if none is // found. It attempts to handle the race condition that may occur if 2 servers try to // create the key at the same time by simply querying the store a second time if it // receives a conflict when writing. func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService, fallBackAlgorithm data.KeyAlgorithm) (*data.TUFKey, error) { keyAlgorithm, public, err := store.GetTimestampKey(gun) if err == nil { return data.NewTUFKey(keyAlgorithm, public, nil), nil } if _, ok := err.(*storage.ErrNoKey); ok { key, err := crypto.Create("timestamp", fallBackAlgorithm) if err != nil { return nil, err } err = store.SetTimestampKey(gun, key.Algorithm(), key.Public()) if err == nil { return &key.TUFKey, nil } if _, ok := err.(*storage.ErrTimestampKeyExists); ok { keyAlgorithm, public, err = store.GetTimestampKey(gun) if err != nil { return nil, err } return data.NewTUFKey(keyAlgorithm, public, nil), nil } return nil, err } return nil, err }
// GetOrCreateTimestampKey returns the timestamp key for the gun. It uses the store to // lookup an existing timestamp key and the crypto to generate a new one if none is // found. It attempts to handle the race condition that may occur if 2 servers try to // create the key at the same time by simply querying the store a second time if it // receives a conflict when writing. func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService) (*data.TUFKey, error) { cipher, public, err := store.GetTimestampKey(gun) if err == nil { return data.NewTUFKey(cipher, public, nil), nil } if _, ok := err.(*storage.ErrNoKey); ok { key, err := crypto.Create("timestamp") if err != nil { return nil, err } err = store.SetTimestampKey(gun, key.Cipher(), key.Public()) if err == nil { return &key.TUFKey, nil } if _, ok := err.(*storage.ErrTimestampKeyExists); ok { cipher, public, err = store.GetTimestampKey(gun) if err != nil { return nil, err } return data.NewTUFKey(cipher, public, nil), nil } return nil, err } return nil, err }
// GetOrCreateSnapshot either returns the existing latest snapshot, or uses // whatever the most recent snapshot is to generate the next one, only updating // the expiry time and version. Note that this function does not write generated // snapshots to the underlying data store, and will either return the latest snapshot time // or nil as the time modified func GetOrCreateSnapshot(gun, checksum string, store storage.MetaStore, cryptoService signed.CryptoService) ( *time.Time, []byte, error) { lastModified, currentJSON, err := store.GetChecksum(gun, data.CanonicalSnapshotRole, checksum) if err != nil { return nil, nil, err } prev := new(data.SignedSnapshot) if err := json.Unmarshal(currentJSON, prev); err != nil { logrus.Error("Failed to unmarshal existing snapshot for GUN ", gun) return nil, nil, err } if !snapshotExpired(prev) { return lastModified, currentJSON, nil } builder := tuf.NewRepoBuilder(gun, cryptoService, trustpinning.TrustPinConfig{}) // load the current root to ensure we use the correct snapshot key. _, rootJSON, err := store.GetCurrent(gun, data.CanonicalRootRole) if err != nil { logrus.Debug("Previous snapshot, but no root for GUN ", gun) return nil, nil, err } if err := builder.Load(data.CanonicalRootRole, rootJSON, 1, false); err != nil { logrus.Debug("Could not load valid previous root for GUN ", gun) return nil, nil, err } meta, _, err := builder.GenerateSnapshot(prev) if err != nil { return nil, nil, err } return nil, meta, nil }
// createSnapshot uses an existing snapshot to create a new one. // Important things to be aware of: // - It requires that a snapshot already exists. We create snapshots // on upload so there should always be an existing snapshot if this // gets called. // - It doesn't update what roles are present in the snapshot, as those // were validated during upload. func createSnapshot(gun string, sn *data.SignedSnapshot, store storage.MetaStore, cryptoService signed.CryptoService) (*data.Signed, int, error) { algorithm, public, err := store.GetKey(gun, data.CanonicalSnapshotRole) if err != nil { // owner of gun must have generated a snapshot key otherwise // we won't proceed with generating everything. return nil, 0, err } key := data.NewPublicKey(algorithm, public) // update version and expiry sn.Signed.Version = sn.Signed.Version + 1 sn.Signed.Expires = data.DefaultExpires(data.CanonicalSnapshotRole) out, err := sn.ToSigned() if err != nil { return nil, 0, err } err = signed.Sign(cryptoService, out, key) if err != nil { return nil, 0, err } return out, sn.Signed.Version, 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) { d, err := store.GetCurrent(gun, "timestamp") if err != nil { if _, ok := err.(*storage.ErrNotFound); !ok { // If we received an ErrNotFound, we're going to // generate the first timestamp, any other error // should be returned here return nil, err } } 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) { return d, nil } } sgnd, version, err := createTimestamp(gun, ts, 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 }
// generateTimestamp generates a new timestamp from the previous one in the store - this assumes all // the other roles have already been set on the repo, and will set the generated timestamp on the repo as well func generateTimestamp(gun string, builder tuf.RepoBuilder, store storage.MetaStore) (*storage.MetaUpdate, error) { var prev *data.SignedTimestamp _, currentJSON, err := store.GetCurrent(gun, data.CanonicalTimestampRole) switch err.(type) { case nil: prev = new(data.SignedTimestamp) if err := json.Unmarshal(currentJSON, prev); err != nil { logrus.Error("Failed to unmarshal existing timestamp for GUN ", gun) return nil, err } case storage.ErrNotFound: break // this is the first timestamp ever for the repo default: return nil, err } meta, ver, err := builder.GenerateTimestamp(prev) switch err.(type) { case nil: return &storage.MetaUpdate{ Role: data.CanonicalTimestampRole, Version: ver, Data: meta, }, nil case signed.ErrInsufficientSignatures, signed.ErrNoKeys: // If we cannot sign the timestamp, then we don't have keys for the timestamp, // and the client screwed up their root return nil, validation.ErrBadRoot{ Msg: fmt.Sprintf("no timestamp keys exist on the server"), } default: return nil, validation.ErrValidation{Msg: err.Error()} } }
// GetOrCreateSnapshot either returns the exisiting latest snapshot, or uses // whatever the most recent snapshot is to create the next one, only updating // the expiry time and version. func GetOrCreateSnapshot(gun string, store storage.MetaStore, cryptoService signed.CryptoService) ([]byte, error) { d, err := store.GetCurrent(gun, "snapshot") if err != nil { return nil, err } sn := &data.SignedSnapshot{} if d != nil { err := json.Unmarshal(d, sn) if err != nil { logrus.Error("Failed to unmarshal existing snapshot") return nil, err } if !snapshotExpired(sn) { return d, nil } } sgnd, version, err := createSnapshot(gun, sn, store, cryptoService) if err != nil { logrus.Error("Failed to create a new snapshot") return nil, err } out, err := json.Marshal(sgnd) if err != nil { logrus.Error("Failed to marshal new snapshot") return nil, err } err = store.UpdateCurrent(gun, storage.MetaUpdate{Role: "snapshot", Version: version, Data: out}) if err != nil { return nil, err } return out, nil }
func generateSnapshot(gun string, repo *tuf.Repo, store storage.MetaStore) (*storage.MetaUpdate, error) { role, err := repo.GetBaseRole(data.CanonicalSnapshotRole) if err != nil { return nil, validation.ErrBadRoot{Msg: "root did not include snapshot role"} } algo, keyBytes, err := store.GetKey(gun, data.CanonicalSnapshotRole) if err != nil { return nil, validation.ErrBadHierarchy{Msg: "could not retrieve snapshot key. client must provide snapshot"} } foundK := data.NewPublicKey(algo, keyBytes) validKey := false for _, id := range role.ListKeyIDs() { if id == foundK.ID() { validKey = true break } } if !validKey { return nil, validation.ErrBadHierarchy{ Missing: data.CanonicalSnapshotRole, Msg: "no snapshot was included in update and server does not hold current snapshot key for repository"} } currentJSON, err := store.GetCurrent(gun, data.CanonicalSnapshotRole) if err != nil { if _, ok := err.(storage.ErrNotFound); !ok { return nil, validation.ErrValidation{Msg: err.Error()} } } var sn *data.SignedSnapshot if currentJSON != nil { sn = new(data.SignedSnapshot) err := json.Unmarshal(currentJSON, sn) if err != nil { return nil, validation.ErrValidation{Msg: err.Error()} } err = repo.SetSnapshot(sn) if err != nil { return nil, validation.ErrValidation{Msg: err.Error()} } } else { // this will only occurr if no snapshot has ever been created for the repository err := repo.InitSnapshot() if err != nil { return nil, validation.ErrBadSnapshot{Msg: err.Error()} } } sgnd, err := repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) if err != nil { return nil, validation.ErrBadSnapshot{Msg: err.Error()} } sgndJSON, err := json.Marshal(sgnd) if err != nil { return nil, validation.ErrBadSnapshot{Msg: err.Error()} } return &storage.MetaUpdate{ Role: data.CanonicalSnapshotRole, Version: repo.Snapshot.Signed.Version, Data: sgndJSON, }, nil }
// validateUpload checks that the updates being pushed // are semantically correct and the signatures are correct func validateUpdate(gun string, updates []storage.MetaUpdate, store storage.MetaStore) error { kdb := keys.NewDB() repo := tuf.NewRepo(kdb, nil) rootRole := data.RoleName(data.CanonicalRootRole) targetsRole := data.RoleName(data.CanonicalTargetsRole) snapshotRole := data.RoleName(data.CanonicalSnapshotRole) // check that the necessary roles are present: roles := make(map[string]storage.MetaUpdate) for _, v := range updates { roles[v.Role] = v } if err := hierarchyOK(roles); err != nil { logrus.Error("ErrBadHierarchy: ", err.Error()) return ErrBadHierarchy{msg: err.Error()} } logrus.Debug("Successfully validated hierarchy") var root *data.SignedRoot oldRootJSON, err := store.GetCurrent(gun, rootRole) if _, ok := err.(*storage.ErrNotFound); err != nil && !ok { // problem with storage. No expectation we can // write if we can't read so bail. logrus.Error("error reading previous root: ", err.Error()) return err } if rootUpdate, ok := roles[rootRole]; ok { // if root is present, validate its integrity, possibly // against a previous root if root, err = validateRoot(gun, oldRootJSON, rootUpdate.Data); err != nil { logrus.Error("ErrBadRoot: ", err.Error()) return ErrBadRoot{msg: err.Error()} } // setting root will update keys db if err = repo.SetRoot(root); err != nil { logrus.Error("ErrValidation: ", err.Error()) return ErrValidation{msg: err.Error()} } logrus.Debug("Successfully validated root") } else { if oldRootJSON == nil { return ErrValidation{msg: "no pre-existing root and no root provided in update."} } parsedOldRoot := &data.SignedRoot{} if err := json.Unmarshal(oldRootJSON, parsedOldRoot); err != nil { return ErrValidation{msg: "pre-existing root is corrupted and no root provided in update."} } if err = repo.SetRoot(parsedOldRoot); err != nil { logrus.Error("ErrValidation: ", err.Error()) return ErrValidation{msg: err.Error()} } } // TODO: validate delegated targets roles. var t *data.SignedTargets if _, ok := roles[targetsRole]; ok { if t, err = validateTargets(targetsRole, roles, kdb); err != nil { logrus.Error("ErrBadTargets: ", err.Error()) return ErrBadTargets{msg: err.Error()} } repo.SetTargets(targetsRole, t) } logrus.Debug("Successfully validated targets") var oldSnap *data.SignedSnapshot oldSnapJSON, err := store.GetCurrent(gun, snapshotRole) if _, ok := err.(*storage.ErrNotFound); err != nil && !ok { // problem with storage. No expectation we can // write if we can't read so bail. logrus.Error("error reading previous snapshot: ", err.Error()) return err } else if err == nil { oldSnap = &data.SignedSnapshot{} if err := json.Unmarshal(oldSnapJSON, oldSnap); err != nil { oldSnap = nil } } if err := validateSnapshot(snapshotRole, oldSnap, roles[snapshotRole], roles, kdb); err != nil { logrus.Error("ErrBadSnapshot: ", err.Error()) return ErrBadSnapshot{msg: err.Error()} } logrus.Debug("Successfully validated snapshot") return nil }
// validateUpload checks that the updates being pushed // are semantically correct and the signatures are correct // A list of possibly modified updates are returned if all // validation was successful. This allows the snapshot to be // created and added if snapshotting has been delegated to the // server func validateUpdate(cs signed.CryptoService, gun string, updates []storage.MetaUpdate, store storage.MetaStore) ([]storage.MetaUpdate, error) { repo := tuf.NewRepo(cs) rootRole := data.CanonicalRootRole snapshotRole := data.CanonicalSnapshotRole // some delegated targets role may be invalid based on other updates // that have been made by other clients. We'll rebuild the slice of // updates with only the things we should actually update updatesToApply := make([]storage.MetaUpdate, 0, len(updates)) roles := make(map[string]storage.MetaUpdate) for _, v := range updates { roles[v.Role] = v } var root *data.SignedRoot oldRootJSON, err := store.GetCurrent(gun, rootRole) if _, ok := err.(storage.ErrNotFound); err != nil && !ok { // problem with storage. No expectation we can // write if we can't read so bail. logrus.Error("error reading previous root: ", err.Error()) return nil, err } if rootUpdate, ok := roles[rootRole]; ok { // if root is present, validate its integrity, possibly // against a previous root if root, err = validateRoot(gun, oldRootJSON, rootUpdate.Data, store); err != nil { logrus.Error("ErrBadRoot: ", err.Error()) return nil, validation.ErrBadRoot{Msg: err.Error()} } // setting root will update keys db if err = repo.SetRoot(root); err != nil { logrus.Error("ErrValidation: ", err.Error()) return nil, validation.ErrValidation{Msg: err.Error()} } logrus.Debug("Successfully validated root") updatesToApply = append(updatesToApply, rootUpdate) } else { if oldRootJSON == nil { return nil, validation.ErrValidation{Msg: "no pre-existing root and no root provided in update."} } parsedOldRoot := &data.SignedRoot{} if err := json.Unmarshal(oldRootJSON, parsedOldRoot); err != nil { return nil, validation.ErrValidation{Msg: "pre-existing root is corrupted and no root provided in update."} } if err = repo.SetRoot(parsedOldRoot); err != nil { logrus.Error("ErrValidation: ", err.Error()) return nil, validation.ErrValidation{Msg: err.Error()} } } targetsToUpdate, err := loadAndValidateTargets(gun, repo, roles, store) if err != nil { return nil, err } updatesToApply = append(updatesToApply, targetsToUpdate...) // there's no need to load files from the database if no targets etc... // were uploaded because that means they haven't been updated and // the snapshot will already contain the correct hashes and sizes for // those targets (incl. delegated targets) logrus.Debug("Successfully validated targets") // At this point, root and targets must have been loaded into the repo if _, ok := roles[snapshotRole]; ok { var oldSnap *data.SignedSnapshot oldSnapJSON, err := store.GetCurrent(gun, snapshotRole) if _, ok := err.(storage.ErrNotFound); err != nil && !ok { // problem with storage. No expectation we can // write if we can't read so bail. logrus.Error("error reading previous snapshot: ", err.Error()) return nil, err } else if err == nil { oldSnap = &data.SignedSnapshot{} if err := json.Unmarshal(oldSnapJSON, oldSnap); err != nil { oldSnap = nil } } if err := validateSnapshot(snapshotRole, oldSnap, roles[snapshotRole], roles, repo); err != nil { logrus.Error("ErrBadSnapshot: ", err.Error()) return nil, validation.ErrBadSnapshot{Msg: err.Error()} } logrus.Debug("Successfully validated snapshot") updatesToApply = append(updatesToApply, roles[snapshotRole]) } else { // Check: // - we have a snapshot key // - it matches a snapshot key signed into the root.json // Then: // - generate a new snapshot // - add it to the updates update, err := generateSnapshot(gun, repo, store) if err != nil { return nil, err } updatesToApply = append(updatesToApply, *update) } return updatesToApply, nil }