blob: 1502ef7873993236f28474c6a1380eaca66badbe [file] [log] [blame]
package tuf
import (
"encoding/json"
"errors"
"io"
"time"
"github.com/flynn/go-tuf/data"
"github.com/flynn/go-tuf/keys"
"github.com/flynn/go-tuf/signed"
)
var (
ErrLatest = errors.New("tuf: the current version is the latest")
ErrNotFound = errors.New("tuf: file not found")
ErrWrongSize = errors.New("tuf: unexpected file size")
ErrNoRootKeys = errors.New("tuf: no root keys found in local meta store")
)
type LocalStore interface {
GetMeta() (map[string]json.RawMessage, error)
SetMeta(map[string]json.RawMessage) error
}
type RemoteStore interface {
Get(name string, size int64) (io.ReadCloser, error)
}
type Repo struct {
local LocalStore
remote RemoteStore
db *keys.DB
meta map[string]json.RawMessage
root *data.Root
snapshot *data.Snapshot
targets *data.Targets
timestamp *data.Timestamp
}
func NewRepo(local LocalStore, remote RemoteStore) *Repo {
return &Repo{
local: local,
remote: remote,
db: keys.NewDB(),
}
}
func (r *Repo) getLocalMeta() error {
if r.meta == nil {
var err error
r.meta, err = r.local.GetMeta()
if err != nil {
return err
}
}
if rootKeyJSON, ok := r.meta["root-keys"]; ok {
var rootKeys []*data.Key
if err := json.Unmarshal(rootKeyJSON, &rootKeys); err != nil {
return err
}
if len(rootKeys) == 0 {
return ErrNoRootKeys
}
rootKeyIDs := make([]string, len(rootKeys))
for i, k := range rootKeys {
id := k.ID()
rootKeyIDs[i] = id
if err := r.db.AddKey(id, k); err != nil {
return err
}
}
r.db.AddRole("root", &data.Role{Threshold: 1, KeyIDs: rootKeyIDs})
} else {
return ErrNoRootKeys
}
if rootJSON, ok := r.meta["root"]; ok {
s := &data.Signed{}
if err := json.Unmarshal(rootJSON, s); err != nil {
return err
}
if err := r.decodeRoot(s); err != nil {
return err
}
}
if snapshotJSON, ok := r.meta["snapshot"]; ok {
s := &data.Signed{}
if err := json.Unmarshal(snapshotJSON, s); err != nil {
return err
}
if err := r.decodeSnapshot(s); err != nil {
return err
}
}
if targetsJSON, ok := r.meta["targets"]; ok {
s := &data.Signed{}
if err := json.Unmarshal(targetsJSON, s); err != nil {
return err
}
if err := r.decodeTargets(s); err != nil {
return err
}
}
if timestampJSON, ok := r.meta["timestamp"]; ok {
s := &data.Signed{}
if err := json.Unmarshal(timestampJSON, s); err != nil {
return err
}
if err := r.decodeTimestamp(s); err != nil {
return err
}
}
return nil
}
func (r *Repo) decodeRoot(s *data.Signed) error {
var minVer int
if r.root != nil {
minVer = r.root.Version
}
root := &data.Root{}
if err := signed.Unmarshal(s, root, "root", minVer, r.db); err != nil {
return err
}
for id, k := range root.Keys {
if err := r.db.AddKey(id, k); err != nil {
return err
}
}
for name, role := range root.Roles {
if err := r.db.AddRole(name, role); err != nil {
return err
}
}
r.root = root
return nil
}
func (r *Repo) decodeSnapshot(s *data.Signed) error {
snapshot := &data.Snapshot{}
if err := signed.Unmarshal(s, snapshot, "snapshot", r.root.Version, r.db); err != nil {
return err
}
r.snapshot = snapshot
return nil
}
func (r *Repo) decodeTargets(s *data.Signed) error {
targets := &data.Targets{}
if err := signed.Unmarshal(s, targets, "targets", r.root.Version, r.db); err != nil {
return err
}
r.targets = targets
return nil
}
func (r *Repo) decodeTimestamp(s *data.Signed) error {
timestamp := &data.Timestamp{}
if err := signed.Unmarshal(s, timestamp, "timestamp", r.root.Version, r.db); err != nil {
return err
}
r.timestamp = timestamp
return nil
}
func (r *Repo) Update() error {
if err := r.getLocalMeta(); err != nil {
return err
}
// check for root keys
// if no meta, get root.json
// if meta, get timestamp.json
// if new, get snapshot.json
// if new root.json, restart
// if new targets.json update
// fully check all signatures
/*
If at any point in the following process there is a problem (e.g., only expired
metadata can be retrieved), the Root file is downloaded and the process starts
over. Optionally, the software update system using the framework can decide how
to proceed rather than automatically downloading a new Root file.
TUF downloads and verifies timestamp.json.
If timestamp.json indicates that snapshot.json has changed, TUF downloads
and verifies snapshot.json.
TUF determines which metadata files listed in snapshot.json differ from
those described in the last snapshot.json that TUF has seen. If root.json
has changed, the update process starts over using the new root.json.
TUF provides the software update system with a list of available files
according to targets.json.
*/
return nil
}
func (r *Repo) Expires() time.Time {
return time.Time{}
}
func (r *Repo) Version() int {
return 0
}
func (r *Repo) Files() data.Files {
return nil
}
type Destination interface {
io.Writer
Size() (int, error)
Delete() error
}
func (r *Repo) Download(name string, dest Destination) error {
/*
The software update system instructs TUF to download a specific target file.
TUF downloads and verifies the file and then makes the file available to the
software update system.
*/
return nil
}