Skip to content

Commit

Permalink
Support creating and modifying repos
Browse files Browse the repository at this point in the history
Signed-off-by: Lewis Marshall <[email protected]>
  • Loading branch information
lmars committed Dec 26, 2014
1 parent 0a53aeb commit 2555384
Show file tree
Hide file tree
Showing 15 changed files with 536 additions and 26 deletions.
213 changes: 199 additions & 14 deletions client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package tuf

import (
"bytes"
"crypto/sha256"
"encoding/json"
"errors"
"io"
Expand All @@ -18,9 +20,17 @@ var (
ErrNoRootKeys = errors.New("tuf: no root keys found in local meta store")
)

var defaultThresholds = map[string]int{
"root": 1,
"targets": 1,
"snapshot": 1,
"timestamp": 1,
}

type LocalStore interface {
GetMeta() (map[string]json.RawMessage, error)
SetMeta(map[string]json.RawMessage) error
GetTarget(string) (io.ReadCloser, int64, error)
}

type RemoteStore interface {
Expand All @@ -31,9 +41,7 @@ type Repo struct {
local LocalStore
remote RemoteStore

db *keys.DB
meta map[string]json.RawMessage

db *keys.DB
root *data.Root
snapshot *data.Snapshot
targets *data.Targets
Expand All @@ -48,16 +56,82 @@ func NewRepo(local LocalStore, remote RemoteStore) *Repo {
}
}

func (r *Repo) getLocalMeta() error {
if r.meta == nil {
var err error
r.meta, err = r.local.GetMeta()
if err != nil {
return err
func Create(local LocalStore, remote RemoteStore, thresholds map[string]int) (*Repo, map[string][]*keys.Key, error) {
if thresholds == nil {
thresholds = defaultThresholds
}
for role, defaultThreshold := range defaultThresholds {
if _, ok := thresholds[role]; !ok {
thresholds[role] = defaultThreshold
}
if thresholds[role] < 1 {
return nil, nil, keys.ErrInvalidThreshold
}
}

roleKeys, err := keys.NewKeys(thresholds)
if err != nil {
return nil, nil, err
}

r := NewRepo(local, remote)

allKeys := make(map[string]*data.Key)
roles := make(map[string]*data.Role, len(thresholds))
for name, threshold := range thresholds {
keys := roleKeys[name]
role := &data.Role{
Threshold: threshold,
KeyIDs: make([]string, len(keys)),
}
for i, key := range keys {
if err := r.db.AddKey(key.ID, key.Serialize()); err != nil {
return nil, nil, err
}
role.KeyIDs[i] = key.ID
allKeys[key.ID] = key.Serialize()
}
if err := r.db.AddRole(name, role); err != nil {
return nil, nil, err
}
roles[name] = role
}

r.root = &data.Root{
Type: "root",
Version: 1,
Expires: time.Now().AddDate(1, 0, 0),
Keys: allKeys,
Roles: roles,
}
r.targets = &data.Targets{
Type: "targets",
Version: 1,
Expires: time.Now().AddDate(0, 3, 0),
Targets: data.Files{},
}
r.snapshot = &data.Snapshot{
Type: "snapshot",
Version: 1,
Expires: time.Now().AddDate(0, 0, 7),
Meta: data.Files{},
}
r.timestamp = &data.Timestamp{
Type: "timestamp",
Version: 1,
Expires: time.Now().AddDate(0, 0, 1),
Meta: data.Files{},
}
return r, roleKeys, nil
}

func (r *Repo) getLocalMeta() error {
meta, err := r.local.GetMeta()
if err != nil {
return err
}

if rootKeyJSON, ok := r.meta["root-keys"]; ok {
if rootKeyJSON, ok := meta["root-keys.json"]; ok {
var rootKeys []*data.Key
if err := json.Unmarshal(rootKeyJSON, &rootKeys); err != nil {
return err
Expand All @@ -79,7 +153,7 @@ func (r *Repo) getLocalMeta() error {
return ErrNoRootKeys
}

if rootJSON, ok := r.meta["root"]; ok {
if rootJSON, ok := meta["root.json"]; ok {
s := &data.Signed{}
if err := json.Unmarshal(rootJSON, s); err != nil {
return err
Expand All @@ -89,7 +163,7 @@ func (r *Repo) getLocalMeta() error {
}
}

if snapshotJSON, ok := r.meta["snapshot"]; ok {
if snapshotJSON, ok := meta["snapshot.json"]; ok {
s := &data.Signed{}
if err := json.Unmarshal(snapshotJSON, s); err != nil {
return err
Expand All @@ -99,7 +173,7 @@ func (r *Repo) getLocalMeta() error {
}
}

if targetsJSON, ok := r.meta["targets"]; ok {
if targetsJSON, ok := meta["targets.json"]; ok {
s := &data.Signed{}
if err := json.Unmarshal(targetsJSON, s); err != nil {
return err
Expand All @@ -109,7 +183,7 @@ func (r *Repo) getLocalMeta() error {
}
}

if timestampJSON, ok := r.meta["timestamp"]; ok {
if timestampJSON, ok := meta["timestamp.json"]; ok {
s := &data.Signed{}
if err := json.Unmarshal(timestampJSON, s); err != nil {
return err
Expand Down Expand Up @@ -173,6 +247,91 @@ func (r *Repo) decodeTimestamp(s *data.Signed) error {
return nil
}

func (r *Repo) Persist(signingKeys map[string][]*keys.Key) (err error) {
// TODO: only sign changed metadata (e.g. root rarely changes)
// TODO: check all necessary keys present
// TODO: support gzip compression
meta := make(map[string]json.RawMessage, 5)

// add root keys
root := r.db.GetRole("root")
if root == nil {
return ErrNoRootKeys
}
rootKeys := make([]*data.Key, 0, len(root.KeyIDs))
for id := range root.KeyIDs {
key, err := r.db.GetKey(id)
if err != nil {
return err
}
rootKeys = append(rootKeys, key.Serialize())
}
meta["root-keys.json"], err = json.Marshal(rootKeys)
if err != nil {
return err
}

// generate root metdata
s, err := signed.Marshal(r.root, signingKeys["root"]...)
if err != nil {
return err
}
meta["root.json"], err = json.Marshal(s)
if err != nil {
return err
}

// generate targets metadata
s, err = signed.Marshal(r.targets, signingKeys["targets"]...)
if err != nil {
return err
}
meta["targets.json"], err = json.Marshal(s)
if err != nil {
return err
}

// generate snapshot metadata
for _, name := range []string{"root.json", "targets.json"} {
h, err := hash256(bytes.NewReader(meta[name]))
if err != nil {
return err
}
r.snapshot.Meta[name] = data.FileMeta{
Length: int64(len(meta[name])),
Hashes: map[string]data.HexBytes{"sha256": h},
}
}
s, err = signed.Marshal(r.snapshot, signingKeys["snapshot"]...)
if err != nil {
return err
}
meta["snapshot.json"], err = json.Marshal(s)
if err != nil {
return err
}

// generate timestamp metadata
h, err := hash256(bytes.NewReader(meta["snapshot.json"]))
if err != nil {
return err
}
r.timestamp.Meta["snapshot.json"] = data.FileMeta{
Length: int64(len(meta["snapshot.json"])),
Hashes: map[string]data.HexBytes{"sha256": h},
}
s, err = signed.Marshal(r.timestamp, signingKeys["timestamp"]...)
if err != nil {
return err
}
meta["timestamp.json"], err = json.Marshal(s)
if err != nil {
return err
}

return r.local.SetMeta(meta)
}

func (r *Repo) Update() error {
if err := r.getLocalMeta(); err != nil {
return err
Expand Down Expand Up @@ -235,3 +394,29 @@ func (r *Repo) Download(name string, dest Destination) error {
*/
return nil
}

func (r *Repo) AddTarget(path string, custom map[string]interface{}) error {
target, size, err := r.local.GetTarget(path)
if err != nil {
return err
}
defer target.Close()
h, err := hash256(target)
if err != nil {
return err
}
r.targets.Targets[path] = data.FileMeta{
Length: size,
Hashes: map[string]data.HexBytes{"sha256": h},
Custom: custom,
}
return nil
}

func hash256(r io.Reader) ([]byte, error) {
h := sha256.New()
if _, err := io.Copy(h, r); err != nil {
return nil, err
}
return h.Sum(nil), nil
}
Loading

0 comments on commit 2555384

Please sign in to comment.