Beispiel #1
0
// 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
}
Beispiel #2
0
// 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)
}
Beispiel #3
0
// 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
}
Beispiel #4
0
// 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()}
	}
}
Beispiel #5
0
// 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
}
Beispiel #6
0
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
}
Beispiel #7
0
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
}
Beispiel #8
0
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)
}
Beispiel #10
0
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)
}
Beispiel #11
0
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
}
Beispiel #12
0
// 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
}
Beispiel #13
0
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
}
Beispiel #14
0
// 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
}
Beispiel #15
0
// 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
}
Beispiel #16
0
// 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
}
Beispiel #17
0
// 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
}
Beispiel #18
0
// 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
}
Beispiel #19
0
// 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
}
Beispiel #20
0
// 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
}
Beispiel #21
0
// 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()}
	}
}
Beispiel #22
0
// 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
}
Beispiel #23
0
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
}
Beispiel #24
0
// 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
}
Beispiel #25
0
// 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
}