1package client
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"io/ioutil"
8	"net/http"
9	"net/url"
10	"os"
11	"path/filepath"
12	"strings"
13	"time"
14
15	"github.com/sirupsen/logrus"
16	"github.com/docker/notary"
17	"github.com/docker/notary/client/changelist"
18	"github.com/docker/notary/cryptoservice"
19	store "github.com/docker/notary/storage"
20	"github.com/docker/notary/trustmanager"
21	"github.com/docker/notary/trustpinning"
22	"github.com/docker/notary/tuf"
23	"github.com/docker/notary/tuf/data"
24	"github.com/docker/notary/tuf/signed"
25	"github.com/docker/notary/tuf/utils"
26)
27
28func init() {
29	data.SetDefaultExpiryTimes(notary.NotaryDefaultExpiries)
30}
31
32// ErrRepoNotInitialized is returned when trying to publish an uninitialized
33// notary repository
34type ErrRepoNotInitialized struct{}
35
36func (err ErrRepoNotInitialized) Error() string {
37	return "repository has not been initialized"
38}
39
40// ErrInvalidRemoteRole is returned when the server is requested to manage
41// a key type that is not permitted
42type ErrInvalidRemoteRole struct {
43	Role string
44}
45
46func (err ErrInvalidRemoteRole) Error() string {
47	return fmt.Sprintf(
48		"notary does not permit the server managing the %s key", err.Role)
49}
50
51// ErrInvalidLocalRole is returned when the client wants to manage
52// a key type that is not permitted
53type ErrInvalidLocalRole struct {
54	Role string
55}
56
57func (err ErrInvalidLocalRole) Error() string {
58	return fmt.Sprintf(
59		"notary does not permit the client managing the %s key", err.Role)
60}
61
62// ErrRepositoryNotExist is returned when an action is taken on a remote
63// repository that doesn't exist
64type ErrRepositoryNotExist struct {
65	remote string
66	gun    string
67}
68
69func (err ErrRepositoryNotExist) Error() string {
70	return fmt.Sprintf("%s does not have trust data for %s", err.remote, err.gun)
71}
72
73const (
74	tufDir = "tuf"
75)
76
77// NotaryRepository stores all the information needed to operate on a notary
78// repository.
79type NotaryRepository struct {
80	baseDir       string
81	gun           string
82	baseURL       string
83	tufRepoPath   string
84	fileStore     store.MetadataStore
85	CryptoService signed.CryptoService
86	tufRepo       *tuf.Repo
87	invalid       *tuf.Repo // known data that was parsable but deemed invalid
88	roundTrip     http.RoundTripper
89	trustPinning  trustpinning.TrustPinConfig
90}
91
92// repositoryFromKeystores is a helper function for NewNotaryRepository that
93// takes some basic NotaryRepository parameters as well as keystores (in order
94// of usage preference), and returns a NotaryRepository.
95func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper,
96	keyStores []trustmanager.KeyStore, trustPin trustpinning.TrustPinConfig) (*NotaryRepository, error) {
97
98	cryptoService := cryptoservice.NewCryptoService(keyStores...)
99
100	nRepo := &NotaryRepository{
101		gun:           gun,
102		baseDir:       baseDir,
103		baseURL:       baseURL,
104		tufRepoPath:   filepath.Join(baseDir, tufDir, filepath.FromSlash(gun)),
105		CryptoService: cryptoService,
106		roundTrip:     rt,
107		trustPinning:  trustPin,
108	}
109
110	fileStore, err := store.NewFilesystemStore(
111		nRepo.tufRepoPath,
112		"metadata",
113		"json",
114	)
115	if err != nil {
116		return nil, err
117	}
118	nRepo.fileStore = fileStore
119
120	return nRepo, nil
121}
122
123// Target represents a simplified version of the data TUF operates on, so external
124// applications don't have to depend on TUF data types.
125type Target struct {
126	Name   string      // the name of the target
127	Hashes data.Hashes // the hash of the target
128	Length int64       // the size in bytes of the target
129}
130
131// TargetWithRole represents a Target that exists in a particular role - this is
132// produced by ListTargets and GetTargetByName
133type TargetWithRole struct {
134	Target
135	Role string
136}
137
138// NewTarget is a helper method that returns a Target
139func NewTarget(targetName string, targetPath string) (*Target, error) {
140	b, err := ioutil.ReadFile(targetPath)
141	if err != nil {
142		return nil, err
143	}
144
145	meta, err := data.NewFileMeta(bytes.NewBuffer(b), data.NotaryDefaultHashes...)
146	if err != nil {
147		return nil, err
148	}
149
150	return &Target{Name: targetName, Hashes: meta.Hashes, Length: meta.Length}, nil
151}
152
153func rootCertKey(gun string, privKey data.PrivateKey) (data.PublicKey, error) {
154	// Hard-coded policy: the generated certificate expires in 10 years.
155	startTime := time.Now()
156	cert, err := cryptoservice.GenerateCertificate(
157		privKey, gun, startTime, startTime.Add(notary.Year*10))
158	if err != nil {
159		return nil, err
160	}
161
162	x509PublicKey := utils.CertToKey(cert)
163	if x509PublicKey == nil {
164		return nil, fmt.Errorf(
165			"cannot use regenerated certificate: format %s", cert.PublicKeyAlgorithm)
166	}
167
168	return x509PublicKey, nil
169}
170
171// Initialize creates a new repository by using rootKey as the root Key for the
172// TUF repository. The server must be reachable (and is asked to generate a
173// timestamp key and possibly other serverManagedRoles), but the created repository
174// result is only stored on local disk, not published to the server. To do that,
175// use r.Publish() eventually.
176func (r *NotaryRepository) Initialize(rootKeyIDs []string, serverManagedRoles ...string) error {
177	privKeys := make([]data.PrivateKey, 0, len(rootKeyIDs))
178	for _, keyID := range rootKeyIDs {
179		privKey, _, err := r.CryptoService.GetPrivateKey(keyID)
180		if err != nil {
181			return err
182		}
183		privKeys = append(privKeys, privKey)
184	}
185
186	// currently we only support server managing timestamps and snapshots, and
187	// nothing else - timestamps are always managed by the server, and implicit
188	// (do not have to be passed in as part of `serverManagedRoles`, so that
189	// the API of Initialize doesn't change).
190	var serverManagesSnapshot bool
191	locallyManagedKeys := []string{
192		data.CanonicalTargetsRole,
193		data.CanonicalSnapshotRole,
194		// root is also locally managed, but that should have been created
195		// already
196	}
197	remotelyManagedKeys := []string{data.CanonicalTimestampRole}
198	for _, role := range serverManagedRoles {
199		switch role {
200		case data.CanonicalTimestampRole:
201			continue // timestamp is already in the right place
202		case data.CanonicalSnapshotRole:
203			// because we put Snapshot last
204			locallyManagedKeys = []string{data.CanonicalTargetsRole}
205			remotelyManagedKeys = append(
206				remotelyManagedKeys, data.CanonicalSnapshotRole)
207			serverManagesSnapshot = true
208		default:
209			return ErrInvalidRemoteRole{Role: role}
210		}
211	}
212
213	rootKeys := make([]data.PublicKey, 0, len(privKeys))
214	for _, privKey := range privKeys {
215		rootKey, err := rootCertKey(r.gun, privKey)
216		if err != nil {
217			return err
218		}
219		rootKeys = append(rootKeys, rootKey)
220	}
221
222	var (
223		rootRole = data.NewBaseRole(
224			data.CanonicalRootRole,
225			notary.MinThreshold,
226			rootKeys...,
227		)
228		timestampRole data.BaseRole
229		snapshotRole  data.BaseRole
230		targetsRole   data.BaseRole
231	)
232
233	// we want to create all the local keys first so we don't have to
234	// make unnecessary network calls
235	for _, role := range locallyManagedKeys {
236		// This is currently hardcoding the keys to ECDSA.
237		key, err := r.CryptoService.Create(role, r.gun, data.ECDSAKey)
238		if err != nil {
239			return err
240		}
241		switch role {
242		case data.CanonicalSnapshotRole:
243			snapshotRole = data.NewBaseRole(
244				role,
245				notary.MinThreshold,
246				key,
247			)
248		case data.CanonicalTargetsRole:
249			targetsRole = data.NewBaseRole(
250				role,
251				notary.MinThreshold,
252				key,
253			)
254		}
255	}
256	for _, role := range remotelyManagedKeys {
257		// This key is generated by the remote server.
258		key, err := getRemoteKey(r.baseURL, r.gun, role, r.roundTrip)
259		if err != nil {
260			return err
261		}
262		logrus.Debugf("got remote %s %s key with keyID: %s",
263			role, key.Algorithm(), key.ID())
264		switch role {
265		case data.CanonicalSnapshotRole:
266			snapshotRole = data.NewBaseRole(
267				role,
268				notary.MinThreshold,
269				key,
270			)
271		case data.CanonicalTimestampRole:
272			timestampRole = data.NewBaseRole(
273				role,
274				notary.MinThreshold,
275				key,
276			)
277		}
278	}
279
280	r.tufRepo = tuf.NewRepo(r.CryptoService)
281
282	err := r.tufRepo.InitRoot(
283		rootRole,
284		timestampRole,
285		snapshotRole,
286		targetsRole,
287		false,
288	)
289	if err != nil {
290		logrus.Debug("Error on InitRoot: ", err.Error())
291		return err
292	}
293	_, err = r.tufRepo.InitTargets(data.CanonicalTargetsRole)
294	if err != nil {
295		logrus.Debug("Error on InitTargets: ", err.Error())
296		return err
297	}
298	err = r.tufRepo.InitSnapshot()
299	if err != nil {
300		logrus.Debug("Error on InitSnapshot: ", err.Error())
301		return err
302	}
303
304	return r.saveMetadata(serverManagesSnapshot)
305}
306
307// adds a TUF Change template to the given roles
308func addChange(cl *changelist.FileChangelist, c changelist.Change, roles ...string) error {
309
310	if len(roles) == 0 {
311		roles = []string{data.CanonicalTargetsRole}
312	}
313
314	var changes []changelist.Change
315	for _, role := range roles {
316		// Ensure we can only add targets to the CanonicalTargetsRole,
317		// or a Delegation role (which is <CanonicalTargetsRole>/something else)
318		if role != data.CanonicalTargetsRole && !data.IsDelegation(role) && !data.IsWildDelegation(role) {
319			return data.ErrInvalidRole{
320				Role:   role,
321				Reason: "cannot add targets to this role",
322			}
323		}
324
325		changes = append(changes, changelist.NewTUFChange(
326			c.Action(),
327			role,
328			c.Type(),
329			c.Path(),
330			c.Content(),
331		))
332	}
333
334	for _, c := range changes {
335		if err := cl.Add(c); err != nil {
336			return err
337		}
338	}
339	return nil
340}
341
342// AddTarget creates new changelist entries to add a target to the given roles
343// in the repository when the changelist gets applied at publish time.
344// If roles are unspecified, the default role is "targets"
345func (r *NotaryRepository) AddTarget(target *Target, roles ...string) error {
346
347	if len(target.Hashes) == 0 {
348		return fmt.Errorf("no hashes specified for target \"%s\"", target.Name)
349	}
350	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
351	if err != nil {
352		return err
353	}
354	defer cl.Close()
355	logrus.Debugf("Adding target \"%s\" with sha256 \"%x\" and size %d bytes.\n", target.Name, target.Hashes["sha256"], target.Length)
356
357	meta := data.FileMeta{Length: target.Length, Hashes: target.Hashes}
358	metaJSON, err := json.Marshal(meta)
359	if err != nil {
360		return err
361	}
362
363	template := changelist.NewTUFChange(
364		changelist.ActionCreate, "", changelist.TypeTargetsTarget,
365		target.Name, metaJSON)
366	return addChange(cl, template, roles...)
367}
368
369// RemoveTarget creates new changelist entries to remove a target from the given
370// roles in the repository when the changelist gets applied at publish time.
371// If roles are unspecified, the default role is "target".
372func (r *NotaryRepository) RemoveTarget(targetName string, roles ...string) error {
373
374	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
375	if err != nil {
376		return err
377	}
378	logrus.Debugf("Removing target \"%s\"", targetName)
379	template := changelist.NewTUFChange(changelist.ActionDelete, "",
380		changelist.TypeTargetsTarget, targetName, nil)
381	return addChange(cl, template, roles...)
382}
383
384// ListTargets lists all targets for the current repository. The list of
385// roles should be passed in order from highest to lowest priority.
386//
387// IMPORTANT: if you pass a set of roles such as [ "targets/a", "targets/x"
388// "targets/a/b" ], even though "targets/a/b" is part of the "targets/a" subtree
389// its entries will be strictly shadowed by those in other parts of the "targets/a"
390// subtree and also the "targets/x" subtree, as we will defer parsing it until
391// we explicitly reach it in our iteration of the provided list of roles.
392func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, error) {
393	if err := r.Update(false); err != nil {
394		return nil, err
395	}
396
397	if len(roles) == 0 {
398		roles = []string{data.CanonicalTargetsRole}
399	}
400	targets := make(map[string]*TargetWithRole)
401	for _, role := range roles {
402		// Define an array of roles to skip for this walk (see IMPORTANT comment above)
403		skipRoles := utils.StrSliceRemove(roles, role)
404
405		// Define a visitor function to populate the targets map in priority order
406		listVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
407			// We found targets so we should try to add them to our targets map
408			for targetName, targetMeta := range tgt.Signed.Targets {
409				// Follow the priority by not overriding previously set targets
410				// and check that this path is valid with this role
411				if _, ok := targets[targetName]; ok || !validRole.CheckPaths(targetName) {
412					continue
413				}
414				targets[targetName] = &TargetWithRole{
415					Target: Target{
416						Name:   targetName,
417						Hashes: targetMeta.Hashes,
418						Length: targetMeta.Length,
419					},
420					Role: validRole.Name,
421				}
422			}
423			return nil
424		}
425
426		r.tufRepo.WalkTargets("", role, listVisitorFunc, skipRoles...)
427	}
428
429	var targetList []*TargetWithRole
430	for _, v := range targets {
431		targetList = append(targetList, v)
432	}
433
434	return targetList, nil
435}
436
437// GetTargetByName returns a target by the given name. If no roles are passed
438// it uses the targets role and does a search of the entire delegation
439// graph, finding the first entry in a breadth first search of the delegations.
440// If roles are passed, they should be passed in descending priority and
441// the target entry found in the subtree of the highest priority role
442// will be returned.
443// See the IMPORTANT section on ListTargets above. Those roles also apply here.
444func (r *NotaryRepository) GetTargetByName(name string, roles ...string) (*TargetWithRole, error) {
445	if err := r.Update(false); err != nil {
446		return nil, err
447	}
448
449	if len(roles) == 0 {
450		roles = append(roles, data.CanonicalTargetsRole)
451	}
452	var resultMeta data.FileMeta
453	var resultRoleName string
454	var foundTarget bool
455	for _, role := range roles {
456		// Define an array of roles to skip for this walk (see IMPORTANT comment above)
457		skipRoles := utils.StrSliceRemove(roles, role)
458
459		// Define a visitor function to find the specified target
460		getTargetVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
461			if tgt == nil {
462				return nil
463			}
464			// We found the target and validated path compatibility in our walk,
465			// so we should stop our walk and set the resultMeta and resultRoleName variables
466			if resultMeta, foundTarget = tgt.Signed.Targets[name]; foundTarget {
467				resultRoleName = validRole.Name
468				return tuf.StopWalk{}
469			}
470			return nil
471		}
472		// Check that we didn't error, and that we assigned to our target
473		if err := r.tufRepo.WalkTargets(name, role, getTargetVisitorFunc, skipRoles...); err == nil && foundTarget {
474			return &TargetWithRole{Target: Target{Name: name, Hashes: resultMeta.Hashes, Length: resultMeta.Length}, Role: resultRoleName}, nil
475		}
476	}
477	return nil, fmt.Errorf("No trust data for %s", name)
478
479}
480
481// TargetSignedStruct is a struct that contains a Target, the role it was found in, and the list of signatures for that role
482type TargetSignedStruct struct {
483	Role       data.DelegationRole
484	Target     Target
485	Signatures []data.Signature
486}
487
488// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all
489// roles, and returns a list of TargetSignedStructs for each time it finds the specified target.
490// If given an empty string for a target name, it will return back all targets signed into the repository in every role
491func (r *NotaryRepository) GetAllTargetMetadataByName(name string) ([]TargetSignedStruct, error) {
492	if err := r.Update(false); err != nil {
493		return nil, err
494	}
495
496	var targetInfoList []TargetSignedStruct
497
498	// Define a visitor function to find the specified target
499	getAllTargetInfoByNameVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
500		if tgt == nil {
501			return nil
502		}
503		// We found a target and validated path compatibility in our walk,
504		// so add it to our list if we have a match
505		// if we have an empty name, add all targets, else check if we have it
506		var targetMetaToAdd data.Files
507		if name == "" {
508			targetMetaToAdd = tgt.Signed.Targets
509		} else {
510			if meta, ok := tgt.Signed.Targets[name]; ok {
511				targetMetaToAdd = data.Files{name: meta}
512			}
513		}
514
515		for targetName, resultMeta := range targetMetaToAdd {
516			targetInfo := TargetSignedStruct{
517				Role:       validRole,
518				Target:     Target{Name: targetName, Hashes: resultMeta.Hashes, Length: resultMeta.Length},
519				Signatures: tgt.Signatures,
520			}
521			targetInfoList = append(targetInfoList, targetInfo)
522		}
523		// continue walking to all child roles
524		return nil
525	}
526
527	// Check that we didn't error, and that we found the target at least once
528	if err := r.tufRepo.WalkTargets(name, "", getAllTargetInfoByNameVisitorFunc); err != nil {
529		return nil, err
530	}
531	if len(targetInfoList) == 0 {
532		return nil, fmt.Errorf("No valid trust data for %s", name)
533	}
534	return targetInfoList, nil
535}
536
537// GetChangelist returns the list of the repository's unpublished changes
538func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
539	changelistDir := filepath.Join(r.tufRepoPath, "changelist")
540	cl, err := changelist.NewFileChangelist(changelistDir)
541	if err != nil {
542		logrus.Debug("Error initializing changelist")
543		return nil, err
544	}
545	return cl, nil
546}
547
548// RoleWithSignatures is a Role with its associated signatures
549type RoleWithSignatures struct {
550	Signatures []data.Signature
551	data.Role
552}
553
554// ListRoles returns a list of RoleWithSignatures objects for this repo
555// This represents the latest metadata for each role in this repo
556func (r *NotaryRepository) ListRoles() ([]RoleWithSignatures, error) {
557	// Update to latest repo state
558	if err := r.Update(false); err != nil {
559		return nil, err
560	}
561
562	// Get all role info from our updated keysDB, can be empty
563	roles := r.tufRepo.GetAllLoadedRoles()
564
565	var roleWithSigs []RoleWithSignatures
566
567	// Populate RoleWithSignatures with Role from keysDB and signatures from TUF metadata
568	for _, role := range roles {
569		roleWithSig := RoleWithSignatures{Role: *role, Signatures: nil}
570		switch role.Name {
571		case data.CanonicalRootRole:
572			roleWithSig.Signatures = r.tufRepo.Root.Signatures
573		case data.CanonicalTargetsRole:
574			roleWithSig.Signatures = r.tufRepo.Targets[data.CanonicalTargetsRole].Signatures
575		case data.CanonicalSnapshotRole:
576			roleWithSig.Signatures = r.tufRepo.Snapshot.Signatures
577		case data.CanonicalTimestampRole:
578			roleWithSig.Signatures = r.tufRepo.Timestamp.Signatures
579		default:
580			if !data.IsDelegation(role.Name) {
581				continue
582			}
583			if _, ok := r.tufRepo.Targets[role.Name]; ok {
584				// We'll only find a signature if we've published any targets with this delegation
585				roleWithSig.Signatures = r.tufRepo.Targets[role.Name].Signatures
586			}
587		}
588		roleWithSigs = append(roleWithSigs, roleWithSig)
589	}
590	return roleWithSigs, nil
591}
592
593// Publish pushes the local changes in signed material to the remote notary-server
594// Conceptually it performs an operation similar to a `git rebase`
595func (r *NotaryRepository) Publish() error {
596	cl, err := r.GetChangelist()
597	if err != nil {
598		return err
599	}
600	if err = r.publish(cl); err != nil {
601		return err
602	}
603	if err = cl.Clear(""); err != nil {
604		// This is not a critical problem when only a single host is pushing
605		// but will cause weird behaviour if changelist cleanup is failing
606		// and there are multiple hosts writing to the repo.
607		logrus.Warn("Unable to clear changelist. You may want to manually delete the folder ", filepath.Join(r.tufRepoPath, "changelist"))
608	}
609	return nil
610}
611
612// publish pushes the changes in the given changelist to the remote notary-server
613// Conceptually it performs an operation similar to a `git rebase`
614func (r *NotaryRepository) publish(cl changelist.Changelist) error {
615	var initialPublish bool
616	// update first before publishing
617	if err := r.Update(true); err != nil {
618		// If the remote is not aware of the repo, then this is being published
619		// for the first time.  Try to load from disk instead for publishing.
620		if _, ok := err.(ErrRepositoryNotExist); ok {
621			err := r.bootstrapRepo()
622			if err != nil {
623				logrus.Debugf("Unable to load repository from local files: %s",
624					err.Error())
625				if _, ok := err.(store.ErrMetaNotFound); ok {
626					return ErrRepoNotInitialized{}
627				}
628				return err
629			}
630			// Ensure we will push the initial root and targets file.  Either or
631			// both of the root and targets may not be marked as Dirty, since
632			// there may not be any changes that update them, so use a
633			// different boolean.
634			initialPublish = true
635		} else {
636			// We could not update, so we cannot publish.
637			logrus.Error("Could not publish Repository since we could not update: ", err.Error())
638			return err
639		}
640	}
641	// apply the changelist to the repo
642	if err := applyChangelist(r.tufRepo, r.invalid, cl); err != nil {
643		logrus.Debug("Error applying changelist")
644		return err
645	}
646
647	// these are the TUF files we will need to update, serialized as JSON before
648	// we send anything to remote
649	updatedFiles := make(map[string][]byte)
650
651	// check if our root file is nearing expiry or dirty. Resign if it is.  If
652	// root is not dirty but we are publishing for the first time, then just
653	// publish the existing root we have.
654	if nearExpiry(r.tufRepo.Root.Signed.SignedCommon) || r.tufRepo.Root.Dirty {
655		rootJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalRootRole)
656		if err != nil {
657			return err
658		}
659		updatedFiles[data.CanonicalRootRole] = rootJSON
660	} else if initialPublish {
661		rootJSON, err := r.tufRepo.Root.MarshalJSON()
662		if err != nil {
663			return err
664		}
665		updatedFiles[data.CanonicalRootRole] = rootJSON
666	}
667
668	// iterate through all the targets files - if they are dirty, sign and update
669	for roleName, roleObj := range r.tufRepo.Targets {
670		if roleObj.Dirty || (roleName == data.CanonicalTargetsRole && initialPublish) {
671			targetsJSON, err := serializeCanonicalRole(r.tufRepo, roleName)
672			if err != nil {
673				return err
674			}
675			updatedFiles[roleName] = targetsJSON
676		}
677	}
678
679	// if we initialized the repo while designating the server as the snapshot
680	// signer, then there won't be a snapshots file.  However, we might now
681	// have a local key (if there was a rotation), so initialize one.
682	if r.tufRepo.Snapshot == nil {
683		if err := r.tufRepo.InitSnapshot(); err != nil {
684			return err
685		}
686	}
687
688	snapshotJSON, err := serializeCanonicalRole(
689		r.tufRepo, data.CanonicalSnapshotRole)
690
691	if err == nil {
692		// Only update the snapshot if we've successfully signed it.
693		updatedFiles[data.CanonicalSnapshotRole] = snapshotJSON
694	} else if signErr, ok := err.(signed.ErrInsufficientSignatures); ok && signErr.FoundKeys == 0 {
695		// If signing fails due to us not having the snapshot key, then
696		// assume the server is going to sign, and do not include any snapshot
697		// data.
698		logrus.Debugf("Client does not have the key to sign snapshot. " +
699			"Assuming that server should sign the snapshot.")
700	} else {
701		logrus.Debugf("Client was unable to sign the snapshot: %s", err.Error())
702		return err
703	}
704
705	remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
706	if err != nil {
707		return err
708	}
709
710	return remote.SetMulti(updatedFiles)
711}
712
713// bootstrapRepo loads the repository from the local file system (i.e.
714// a not yet published repo or a possibly obsolete local copy) into
715// r.tufRepo.  This attempts to load metadata for all roles.  Since server
716// snapshots are supported, if the snapshot metadata fails to load, that's ok.
717// This assumes that bootstrapRepo is only used by Publish() or RotateKey()
718func (r *NotaryRepository) bootstrapRepo() error {
719	b := tuf.NewRepoBuilder(r.gun, r.CryptoService, r.trustPinning)
720
721	logrus.Debugf("Loading trusted collection.")
722
723	for _, role := range data.BaseRoles {
724		jsonBytes, err := r.fileStore.GetSized(role, store.NoSizeLimit)
725		if err != nil {
726			if _, ok := err.(store.ErrMetaNotFound); ok &&
727				// server snapshots are supported, and server timestamp management
728				// is required, so if either of these fail to load that's ok - especially
729				// if the repo is new
730				role == data.CanonicalSnapshotRole || role == data.CanonicalTimestampRole {
731				continue
732			}
733			return err
734		}
735		if err := b.Load(role, jsonBytes, 1, true); err != nil {
736			return err
737		}
738	}
739
740	tufRepo, _, err := b.Finish()
741	if err == nil {
742		r.tufRepo = tufRepo
743	}
744	return nil
745}
746
747// saveMetadata saves contents of r.tufRepo onto the local disk, creating
748// signatures as necessary, possibly prompting for passphrases.
749func (r *NotaryRepository) saveMetadata(ignoreSnapshot bool) error {
750	logrus.Debugf("Saving changes to Trusted Collection.")
751
752	rootJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalRootRole)
753	if err != nil {
754		return err
755	}
756	err = r.fileStore.Set(data.CanonicalRootRole, rootJSON)
757	if err != nil {
758		return err
759	}
760
761	targetsToSave := make(map[string][]byte)
762	for t := range r.tufRepo.Targets {
763		signedTargets, err := r.tufRepo.SignTargets(t, data.DefaultExpires(data.CanonicalTargetsRole))
764		if err != nil {
765			return err
766		}
767		targetsJSON, err := json.Marshal(signedTargets)
768		if err != nil {
769			return err
770		}
771		targetsToSave[t] = targetsJSON
772	}
773
774	for role, blob := range targetsToSave {
775		parentDir := filepath.Dir(role)
776		os.MkdirAll(parentDir, 0755)
777		r.fileStore.Set(role, blob)
778	}
779
780	if ignoreSnapshot {
781		return nil
782	}
783
784	snapshotJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalSnapshotRole)
785	if err != nil {
786		return err
787	}
788
789	return r.fileStore.Set(data.CanonicalSnapshotRole, snapshotJSON)
790}
791
792// returns a properly constructed ErrRepositoryNotExist error based on this
793// repo's information
794func (r *NotaryRepository) errRepositoryNotExist() error {
795	host := r.baseURL
796	parsed, err := url.Parse(r.baseURL)
797	if err == nil {
798		host = parsed.Host // try to exclude the scheme and any paths
799	}
800	return ErrRepositoryNotExist{remote: host, gun: r.gun}
801}
802
803// Update bootstraps a trust anchor (root.json) before updating all the
804// metadata from the repo.
805func (r *NotaryRepository) Update(forWrite bool) error {
806	c, err := r.bootstrapClient(forWrite)
807	if err != nil {
808		if _, ok := err.(store.ErrMetaNotFound); ok {
809			return r.errRepositoryNotExist()
810		}
811		return err
812	}
813	repo, invalid, err := c.Update()
814	if err != nil {
815		// notFound.Resource may include a checksum so when the role is root,
816		// it will be root or root.<checksum>. Therefore best we can
817		// do it match a "root." prefix
818		if notFound, ok := err.(store.ErrMetaNotFound); ok && strings.HasPrefix(notFound.Resource, data.CanonicalRootRole+".") {
819			return r.errRepositoryNotExist()
820		}
821		return err
822	}
823	// we can be assured if we are at this stage that the repo we built is good
824	// no need to test the following function call for an error as it will always be fine should the repo be good- it is!
825	r.tufRepo = repo
826	r.invalid = invalid
827	warnRolesNearExpiry(repo)
828	return nil
829}
830
831// bootstrapClient attempts to bootstrap a root.json to be used as the trust
832// anchor for a repository. The checkInitialized argument indicates whether
833// we should always attempt to contact the server to determine if the repository
834// is initialized or not. If set to true, we will always attempt to download
835// and return an error if the remote repository errors.
836//
837// Populates a tuf.RepoBuilder with this root metadata (only use
838// TUFClient.Update to load the rest).
839//
840// Fails if the remote server is reachable and does not know the repo
841// (i.e. before the first r.Publish()), in which case the error is
842// store.ErrMetaNotFound, or if the root metadata (from whichever source is used)
843// is not trusted.
844//
845// Returns a TUFClient for the remote server, which may not be actually
846// operational (if the URL is invalid but a root.json is cached).
847func (r *NotaryRepository) bootstrapClient(checkInitialized bool) (*TUFClient, error) {
848	minVersion := 1
849	// the old root on disk should not be validated against any trust pinning configuration
850	// because if we have an old root, it itself is the thing that pins trust
851	oldBuilder := tuf.NewRepoBuilder(r.gun, r.CryptoService, trustpinning.TrustPinConfig{})
852
853	// by default, we want to use the trust pinning configuration on any new root that we download
854	newBuilder := tuf.NewRepoBuilder(r.gun, r.CryptoService, r.trustPinning)
855
856	// Try to read root from cache first. We will trust this root until we detect a problem
857	// during update which will cause us to download a new root and perform a rotation.
858	// If we have an old root, and it's valid, then we overwrite the newBuilder to be one
859	// preloaded with the old root or one which uses the old root for trust bootstrapping.
860	if rootJSON, err := r.fileStore.GetSized(data.CanonicalRootRole, store.NoSizeLimit); err == nil {
861		// if we can't load the cached root, fail hard because that is how we pin trust
862		if err := oldBuilder.Load(data.CanonicalRootRole, rootJSON, minVersion, true); err != nil {
863			return nil, err
864		}
865
866		// again, the root on disk is the source of trust pinning, so use an empty trust
867		// pinning configuration
868		newBuilder = tuf.NewRepoBuilder(r.gun, r.CryptoService, trustpinning.TrustPinConfig{})
869
870		if err := newBuilder.Load(data.CanonicalRootRole, rootJSON, minVersion, false); err != nil {
871			// Ok, the old root is expired - we want to download a new one.  But we want to use the
872			// old root to verify the new root, so bootstrap a new builder with the old builder
873			// but use the trustpinning to validate the new root
874			minVersion = oldBuilder.GetLoadedVersion(data.CanonicalRootRole)
875			newBuilder = oldBuilder.BootstrapNewBuilderWithNewTrustpin(r.trustPinning)
876		}
877	}
878
879	remote, remoteErr := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
880	if remoteErr != nil {
881		logrus.Error(remoteErr)
882	} else if !newBuilder.IsLoaded(data.CanonicalRootRole) || checkInitialized {
883		// remoteErr was nil and we were not able to load a root from cache or
884		// are specifically checking for initialization of the repo.
885
886		// if remote store successfully set up, try and get root from remote
887		// We don't have any local data to determine the size of root, so try the maximum (though it is restricted at 100MB)
888		tmpJSON, err := remote.GetSized(data.CanonicalRootRole, store.NoSizeLimit)
889		if err != nil {
890			// we didn't have a root in cache and were unable to load one from
891			// the server. Nothing we can do but error.
892			return nil, err
893		}
894
895		if !newBuilder.IsLoaded(data.CanonicalRootRole) {
896			// we always want to use the downloaded root if we couldn't load from cache
897			if err := newBuilder.Load(data.CanonicalRootRole, tmpJSON, minVersion, false); err != nil {
898				return nil, err
899			}
900
901			err = r.fileStore.Set(data.CanonicalRootRole, tmpJSON)
902			if err != nil {
903				// if we can't write cache we should still continue, just log error
904				logrus.Errorf("could not save root to cache: %s", err.Error())
905			}
906		}
907	}
908
909	// We can only get here if remoteErr != nil (hence we don't download any new root),
910	// and there was no root on disk
911	if !newBuilder.IsLoaded(data.CanonicalRootRole) {
912		return nil, ErrRepoNotInitialized{}
913	}
914
915	return NewTUFClient(oldBuilder, newBuilder, remote, r.fileStore), nil
916}
917
918// RotateKey removes all existing keys associated with the role, and either
919// creates and adds one new key or delegates managing the key to the server.
920// These changes are staged in a changelist until publish is called.
921func (r *NotaryRepository) RotateKey(role string, serverManagesKey bool) error {
922	// We currently support remotely managing timestamp and snapshot keys
923	canBeRemoteKey := role == data.CanonicalTimestampRole || role == data.CanonicalSnapshotRole
924	// And locally managing root, targets, and snapshot keys
925	canBeLocalKey := (role == data.CanonicalSnapshotRole || role == data.CanonicalTargetsRole ||
926		role == data.CanonicalRootRole)
927
928	switch {
929	case !data.ValidRole(role) || data.IsDelegation(role):
930		return fmt.Errorf("notary does not currently permit rotating the %s key", role)
931	case serverManagesKey && !canBeRemoteKey:
932		return ErrInvalidRemoteRole{Role: role}
933	case !serverManagesKey && !canBeLocalKey:
934		return ErrInvalidLocalRole{Role: role}
935	}
936
937	var (
938		pubKey    data.PublicKey
939		err       error
940		errFmtMsg string
941	)
942	switch serverManagesKey {
943	case true:
944		pubKey, err = rotateRemoteKey(r.baseURL, r.gun, role, r.roundTrip)
945		errFmtMsg = "unable to rotate remote key: %s"
946	default:
947		pubKey, err = r.CryptoService.Create(role, r.gun, data.ECDSAKey)
948		errFmtMsg = "unable to generate key: %s"
949	}
950
951	if err != nil {
952		return fmt.Errorf(errFmtMsg, err)
953	}
954
955	// if this is a root role, generate a root cert for the public key
956	if role == data.CanonicalRootRole {
957		privKey, _, err := r.CryptoService.GetPrivateKey(pubKey.ID())
958		if err != nil {
959			return err
960		}
961		pubKey, err = rootCertKey(r.gun, privKey)
962		if err != nil {
963			return err
964		}
965	}
966
967	cl := changelist.NewMemChangelist()
968	if err := r.rootFileKeyChange(cl, role, changelist.ActionCreate, pubKey); err != nil {
969		return err
970	}
971	return r.publish(cl)
972}
973
974func (r *NotaryRepository) rootFileKeyChange(cl changelist.Changelist, role, action string, key data.PublicKey) error {
975	kl := make(data.KeyList, 0, 1)
976	kl = append(kl, key)
977	meta := changelist.TUFRootData{
978		RoleName: role,
979		Keys:     kl,
980	}
981	metaJSON, err := json.Marshal(meta)
982	if err != nil {
983		return err
984	}
985
986	c := changelist.NewTUFChange(
987		action,
988		changelist.ScopeRoot,
989		changelist.TypeRootRole,
990		role,
991		metaJSON,
992	)
993	return cl.Add(c)
994}
995
996// DeleteTrustData removes the trust data stored for this repo in the TUF cache on the client side
997// Note that we will not delete any private key material from local storage
998func (r *NotaryRepository) DeleteTrustData(deleteRemote bool) error {
999	// Remove the tufRepoPath directory, which includes local TUF metadata files and changelist information
1000	if err := os.RemoveAll(r.tufRepoPath); err != nil {
1001		return fmt.Errorf("error clearing TUF repo data: %v", err)
1002	}
1003	// Note that this will require admin permission in this NotaryRepository's roundtripper
1004	if deleteRemote {
1005		remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
1006		if err != nil {
1007			return err
1008		}
1009		if err := remote.RemoveAll(); err != nil {
1010			return err
1011		}
1012	}
1013	return nil
1014}
1015