// NewSnapshot initilizes a SignedSnapshot with a given top level root // and targets objects func NewSnapshot(root *Signed, targets *Signed) (*SignedSnapshot, error) { logrus.Debug("generating new snapshot...") targetsJSON, err := json.MarshalCanonical(targets) if err != nil { logrus.Debug("Error Marshalling Targets") return nil, err } rootJSON, err := json.MarshalCanonical(root) if err != nil { logrus.Debug("Error Marshalling Root") return nil, err } rootMeta, err := NewFileMeta(bytes.NewReader(rootJSON), "sha256") if err != nil { return nil, err } targetsMeta, err := NewFileMeta(bytes.NewReader(targetsJSON), "sha256") if err != nil { return nil, err } return &SignedSnapshot{ Signatures: make([]Signature, 0), Signed: Snapshot{ Type: TUFTypes["snapshot"], Version: 0, Expires: DefaultExpires("snapshot"), Meta: Files{ CanonicalRootRole: rootMeta, CanonicalTargetsRole: targetsMeta, }, }, }, nil }
func TestGetTimestampNewSnapshot(t *testing.T) { store := storage.NewMemStorage() crypto := signed.NewEd25519() snapshot := data.SignedSnapshot{} snapshot.Signed.Version = 0 snapJSON, _ := json.MarshalCanonical(snapshot) store.UpdateCurrent("gun", storage.MetaUpdate{Role: "snapshot", Version: 0, Data: snapJSON}) // create a key to be used by GetTimestamp _, err := GetOrCreateTimestampKey("gun", store, crypto, data.ED25519Key) assert.Nil(t, err, "GetKey errored") ts1, err := GetOrCreateTimestamp("gun", store, crypto) assert.Nil(t, err, "GetTimestamp errored") snapshot = data.SignedSnapshot{} snapshot.Signed.Version = 1 snapJSON, _ = json.MarshalCanonical(snapshot) store.UpdateCurrent("gun", storage.MetaUpdate{Role: "snapshot", Version: 1, Data: snapJSON}) ts2, err := GetOrCreateTimestamp("gun", store, crypto) assert.NoError(t, err, "GetTimestamp errored") assert.NotEqual(t, ts1, ts2, "Timestamp was not regenerated when snapshot changed") }
func writeRepo(t *testing.T, dir string, repo *Repo) { err := os.MkdirAll(dir, 0755) assert.NoError(t, err) signedRoot, err := repo.SignRoot(data.DefaultExpires("root")) assert.NoError(t, err) rootJSON, _ := json.MarshalCanonical(signedRoot) ioutil.WriteFile(dir+"/root.json", rootJSON, 0755) for r := range repo.Targets { signedTargets, err := repo.SignTargets(r, data.DefaultExpires("targets")) assert.NoError(t, err) targetsJSON, _ := json.MarshalCanonical(signedTargets) p := path.Join(dir, r+".json") parentDir := filepath.Dir(p) os.MkdirAll(parentDir, 0755) ioutil.WriteFile(p, targetsJSON, 0755) } signedSnapshot, err := repo.SignSnapshot(data.DefaultExpires("snapshot")) assert.NoError(t, err) snapshotJSON, _ := json.MarshalCanonical(signedSnapshot) ioutil.WriteFile(dir+"/snapshot.json", snapshotJSON, 0755) signedTimestamp, err := repo.SignTimestamp(data.DefaultExpires("timestamp")) assert.NoError(t, err) timestampJSON, _ := json.MarshalCanonical(signedTimestamp) ioutil.WriteFile(dir+"/timestamp.json", timestampJSON, 0755) }
func TestValidateTargetsParentInUpdate(t *testing.T) { _, baseRepo, cs := testutils.EmptyRepo() store := storage.NewMemStorage() k, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) r, err := data.NewRole("targets/level1", 1, []string{k.ID()}, []string{""}, nil) assert.NoError(t, err) baseRepo.UpdateDelegations(r, []data.PublicKey{k}) // no targets file is created for the new delegations, so force one baseRepo.InitTargets("targets/level1") targets, err := baseRepo.SignTargets("targets", data.DefaultExpires(data.CanonicalTargetsRole)) tgtsJSON, err := json.MarshalCanonical(targets) assert.NoError(t, err) update := storage.MetaUpdate{ Role: data.CanonicalTargetsRole, Version: 1, Data: tgtsJSON, } store.UpdateCurrent("gun", update) del, err := baseRepo.SignTargets("targets/level1", data.DefaultExpires(data.CanonicalTargetsRole)) assert.NoError(t, err) delJSON, err := json.MarshalCanonical(del) assert.NoError(t, err) delUpdate := storage.MetaUpdate{ Role: "targets/level1", Version: 1, Data: delJSON, } roles := map[string]storage.MetaUpdate{ "targets/level1": delUpdate, "targets": update, } kdb := keys.NewDB() valRepo := tuf.NewRepo(kdb, nil) valRepo.SetRoot(baseRepo.Root) // because we sort the roles, the list of returned updates // will contain shallower roles first, in this case "targets", // and then "targets/level1" updates, err := loadAndValidateTargets("gun", valRepo, roles, kdb, store) assert.NoError(t, err) assert.Len(t, updates, 2) assert.Equal(t, "targets", updates[0].Role) assert.Equal(t, tgtsJSON, updates[0].Data) assert.Equal(t, "targets/level1", updates[1].Role) assert.Equal(t, delJSON, updates[1].Data) }
func TestValidateTargetsLoadParent(t *testing.T) { _, baseRepo, cs := testutils.EmptyRepo() store := storage.NewMemStorage() k, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) r, err := data.NewRole("targets/level1", 1, []string{k.ID()}, []string{""}, nil) assert.NoError(t, err) err = baseRepo.UpdateDelegations(r, []data.PublicKey{k}) assert.NoError(t, err) // no targets file is created for the new delegations, so force one baseRepo.InitTargets("targets/level1") // we're not going to validate things loaded from storage, so no need // to sign the base targets, just Marshal it and set it into storage tgtsJSON, err := json.MarshalCanonical(baseRepo.Targets["targets"]) assert.NoError(t, err) update := storage.MetaUpdate{ Role: data.CanonicalTargetsRole, Version: 1, Data: tgtsJSON, } store.UpdateCurrent("gun", update) // generate the update object we're doing to use to call loadAndValidateTargets del, err := baseRepo.SignTargets("targets/level1", data.DefaultExpires(data.CanonicalTargetsRole)) assert.NoError(t, err) delJSON, err := json.MarshalCanonical(del) assert.NoError(t, err) delUpdate := storage.MetaUpdate{ Role: "targets/level1", Version: 1, Data: delJSON, } roles := map[string]storage.MetaUpdate{"targets/level1": delUpdate} kdb := keys.NewDB() valRepo := tuf.NewRepo(kdb, nil) valRepo.SetRoot(baseRepo.Root) updates, err := loadAndValidateTargets("gun", valRepo, roles, kdb, store) assert.NoError(t, err) assert.Len(t, updates, 1) assert.Equal(t, "targets/level1", updates[0].Role) assert.Equal(t, delJSON, updates[0].Data) }
func TestGetSnapshotCurrValid(t *testing.T) { store := storage.NewMemStorage() crypto := signed.NewEd25519() _, err := GetOrCreateSnapshotKey("gun", store, crypto, data.ED25519Key) newData := []byte{2} currMeta, err := data.NewFileMeta(bytes.NewReader(newData), "sha256") assert.NoError(t, err) snapshot := &data.SignedSnapshot{ Signed: data.Snapshot{ Expires: data.DefaultExpires(data.CanonicalSnapshotRole), Meta: data.Files{ data.CanonicalRootRole: currMeta, }, }, } snapJSON, _ := json.MarshalCanonical(snapshot) // test when db is missing the role data store.UpdateCurrent("gun", storage.MetaUpdate{Role: "snapshot", Version: 0, Data: snapJSON}) _, err = GetOrCreateSnapshot("gun", store, crypto) assert.NoError(t, err) // test when db has the role data store.UpdateCurrent("gun", storage.MetaUpdate{Role: "root", Version: 0, Data: newData}) _, err = GetOrCreateSnapshot("gun", store, crypto) assert.NoError(t, err) // test when db role data is expired store.UpdateCurrent("gun", storage.MetaUpdate{Role: "root", Version: 1, Data: []byte{3}}) _, err = GetOrCreateSnapshot("gun", store, crypto) assert.NoError(t, err) }
// 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.GetKey(gun, data.CanonicalTimestampRole) 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 := json.MarshalCanonical(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 }
func TestDuplicateSigs(t *testing.T) { cs := NewEd25519() k, err := cs.Create("root", data.ED25519Key) assert.NoError(t, err) r, err := data.NewRole( "root", 2, []string{k.ID()}, nil, nil, ) assert.NoError(t, err) db := keys.NewDB() assert.NoError(t, err) db.AddKey(k) err = db.AddRole(r) assert.NoError(t, err) meta := &data.SignedCommon{Type: "Root", Version: 1, Expires: data.DefaultExpires("root")} b, err := json.MarshalCanonical(meta) assert.NoError(t, err) s := &data.Signed{Signed: b} Sign(cs, s, k) s.Signatures = append(s.Signatures, s.Signatures[0]) err = Verify(s, "root", 1, db) assert.IsType(t, ErrRoleThreshold{}, err) }
func TestMoreThanEnoughSigs(t *testing.T) { cs := NewEd25519() k1, err := cs.Create("root", data.ED25519Key) assert.NoError(t, err) k2, err := cs.Create("root", data.ED25519Key) assert.NoError(t, err) r, err := data.NewRole( "root", 1, []string{k1.ID(), k2.ID()}, nil, nil, ) assert.NoError(t, err) db := keys.NewDB() assert.NoError(t, err) db.AddKey(k1) db.AddKey(k2) err = db.AddRole(r) assert.NoError(t, err) meta := &data.SignedCommon{Type: "Root", Version: 1, Expires: data.DefaultExpires("root")} b, err := json.MarshalCanonical(meta) assert.NoError(t, err) s := &data.Signed{Signed: b} Sign(cs, s, k1, k2) assert.Equal(t, 2, len(s.Signatures)) err = Verify(s, "root", 1, db) assert.NoError(t, err) }
func (r *NotaryRepository) rootFileKeyChange(role, action string, key data.PublicKey) error { cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() kl := make(data.KeyList, 0, 1) kl = append(kl, key) meta := changelist.TufRootData{ RoleName: role, Keys: kl, } metaJSON, err := json.MarshalCanonical(meta) if err != nil { return err } c := changelist.NewTufChange( action, changelist.ScopeRoot, changelist.TypeRootRole, role, metaJSON, ) err = cl.Add(c) if err != nil { return err } return nil }
// initialize a repo with keys, so they can be rotated func setUpRepo(t *testing.T, tempBaseDir, gun string, ret passphrase.Retriever) ( *httptest.Server, map[string]string) { // server that always returns 200 (and a key) key, err := trustmanager.GenerateECDSAKey(rand.Reader) assert.NoError(t, err) pubKey := data.PublicKeyFromPrivate(key) jsonBytes, err := json.MarshalCanonical(&pubKey) assert.NoError(t, err) keyJSON := string(jsonBytes) ts := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, keyJSON) })) repo, err := client.NewNotaryRepository( tempBaseDir, gun, ts.URL, http.DefaultTransport, ret) assert.NoError(t, err, "error creating repo: %s", err) rootPubKey, err := repo.CryptoService.Create("root", data.ECDSAKey) assert.NoError(t, err, "error generating root key: %s", err) err = repo.Initialize(rootPubKey.ID()) assert.NoError(t, err) return ts, repo.CryptoService.ListAllKeys() }
// AddDelegation creates a new changelist entry to add a delegation to the repository // when the changelist gets applied at publish time. This does not do any validation // other than checking the name of the delegation to add - all that will happen // at publish time. func (r *NotaryRepository) AddDelegation(name string, threshold int, delegationKeys []data.PublicKey, paths []string) error { if !data.IsDelegation(name) { return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"} } cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() logrus.Debugf(`Adding delegation "%s" with threshold %d, and %d keys\n`, name, threshold, len(delegationKeys)) tdJSON, err := json.MarshalCanonical(&changelist.TufDelegation{ NewThreshold: threshold, AddKeys: data.KeyList(delegationKeys), AddPaths: paths, }) if err != nil { return err } template := changelist.NewTufChange( changelist.ActionCreate, name, changelist.TypeTargetsDelegation, "", // no path tdJSON, ) return addChange(cl, template, name) }
// VerifySignatures checks the we have sufficient valid signatures for the given role func VerifySignatures(s *data.Signed, role string, db *keys.KeyDB) error { if len(s.Signatures) == 0 { return ErrNoSignatures } roleData := db.GetRole(role) if roleData == nil { return ErrUnknownRole } if roleData.Threshold < 1 { return ErrRoleThreshold{} } logrus.Debugf("%s role has key IDs: %s", role, strings.Join(roleData.KeyIDs, ",")) var decoded map[string]interface{} if err := json.Unmarshal(s.Signed, &decoded); err != nil { return err } msg, err := json.MarshalCanonical(decoded) if err != nil { return err } valid := make(map[string]struct{}) for _, sig := range s.Signatures { logrus.Debug("verifying signature for key ID: ", sig.KeyID) if !roleData.ValidKey(sig.KeyID) { logrus.Debugf("continuing b/c keyid was invalid: %s for roledata %s\n", sig.KeyID, roleData) continue } key := db.GetKey(sig.KeyID) if key == nil { logrus.Debugf("continuing b/c keyid lookup was nil: %s\n", sig.KeyID) continue } // method lookup is consistent due to Unmarshal JSON doing lower case for us. method := sig.Method verifier, ok := Verifiers[method] if !ok { logrus.Debugf("continuing b/c signing method is not supported: %s\n", sig.Method) continue } if err := verifier.Verify(key, sig.Signature, msg); err != nil { logrus.Debugf("continuing b/c signature was invalid\n") continue } valid[sig.KeyID] = struct{}{} } if len(valid) < roleData.Threshold { return ErrRoleThreshold{} } return nil }
// Serialize takes the Signed objects for the 4 top level roles and serializes them all to JSON func Serialize(sRoot, sTargets, sSnapshot, sTimestamp *data.Signed) (root, targets, snapshot, timestamp []byte, err error) { root, err = json.MarshalCanonical(sRoot) if err != nil { return nil, nil, nil, nil, err } targets, err = json.MarshalCanonical(sTargets) if err != nil { return nil, nil, nil, nil, err } snapshot, err = json.MarshalCanonical(sSnapshot) if err != nil { return nil, nil, nil, nil, err } timestamp, err = json.MarshalCanonical(sTimestamp) if err != nil { return nil, nil, nil, nil, err } return }
func (k *TUFKey) ID() string { if k.id == "" { pubK := NewPublicKey(k.Algorithm(), k.Public()) data, err := json.MarshalCanonical(&pubK) if err != nil { logrus.Error("Error generating key ID:", err) } digest := sha256.Sum256(data) k.id = hex.EncodeToString(digest[:]) } return k.id }
func TestGetSnapshotCurrCorrupt(t *testing.T) { store := storage.NewMemStorage() crypto := signed.NewEd25519() _, err := GetOrCreateSnapshotKey("gun", store, crypto, data.ED25519Key) snapshot := &data.SignedSnapshot{} snapJSON, _ := json.MarshalCanonical(snapshot) store.UpdateCurrent("gun", storage.MetaUpdate{Role: "snapshot", Version: 0, Data: snapJSON[1:]}) _, err = GetOrCreateSnapshot("gun", store, crypto) assert.Error(t, err) }
// UpdateSnapshot updates the FileMeta for the given role based on the Signed object func (tr *Repo) UpdateSnapshot(role string, s *data.Signed) error { jsonData, err := json.MarshalCanonical(s) if err != nil { return err } meta, err := data.NewFileMeta(bytes.NewReader(jsonData), "sha256") if err != nil { return err } tr.Snapshot.Signed.Meta[role] = meta tr.Snapshot.Dirty = true return nil }
// UpdateTimestamp updates the snapshot meta in the timestamp based on the Signed object func (tr *Repo) UpdateTimestamp(s *data.Signed) error { jsonData, err := json.MarshalCanonical(s) if err != nil { return err } meta, err := data.NewFileMeta(bytes.NewReader(jsonData), "sha256") if err != nil { return err } tr.Timestamp.Signed.Meta["snapshot"] = meta tr.Timestamp.Dirty = true return nil }
func TestHTTPStoreGetMeta(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(testRoot)) } server := httptest.NewServer(http.HandlerFunc(handler)) defer server.Close() store, err := NewHTTPStore( server.URL, "metadata", "txt", "targets", "key", &http.Transport{}, ) if err != nil { t.Fatal(err) } j, err := store.GetMeta("root", 4801) if err != nil { t.Fatal(err) } p := &data.Signed{} err = json.Unmarshal(j, p) if err != nil { t.Fatal(err) } rootKey, err := base64.StdEncoding.DecodeString(testRootKey) assert.NoError(t, err) k := data.NewPublicKey("ecdsa-x509", rootKey) sigBytes := p.Signatures[0].Signature if err != nil { t.Fatal(err) } var decoded map[string]interface{} if err := json.Unmarshal(p.Signed, &decoded); err != nil { t.Fatal(err) } msg, err := json.MarshalCanonical(decoded) if err != nil { t.Fatal(err) } method := p.Signatures[0].Method err = signed.Verifiers[method].Verify(k, sigBytes, msg) if err != nil { t.Fatal(err) } }
func TestGetTimestamp(t *testing.T) { store := storage.NewMemStorage() crypto := signed.NewEd25519() snapshot := &data.SignedSnapshot{} snapJSON, _ := json.MarshalCanonical(snapshot) store.UpdateCurrent("gun", storage.MetaUpdate{Role: "snapshot", Version: 0, Data: snapJSON}) // create a key to be used by GetTimestamp _, err := GetOrCreateTimestampKey("gun", store, crypto, data.ED25519Key) assert.Nil(t, err, "GetKey errored") _, err = GetOrCreateTimestamp("gun", store, crypto) assert.Nil(t, err, "GetTimestamp errored") }
func TestDownloadSnapshotHappy(t *testing.T) { kdb, repo, _ := testutils.EmptyRepo() localStorage := store.NewMemoryStore(nil, nil) remoteStorage := store.NewMemoryStore(nil, nil) client := NewClient(repo, remoteStorage, kdb, localStorage) // create and "upload" sample snapshot and timestamp signedOrig, err := repo.SignSnapshot(data.DefaultExpires("snapshot")) assert.NoError(t, err) orig, err := json.MarshalCanonical(signedOrig) assert.NoError(t, err) err = remoteStorage.SetMeta("snapshot", orig) assert.NoError(t, err) signedOrig, err = repo.SignTimestamp(data.DefaultExpires("timestamp")) assert.NoError(t, err) orig, err = json.MarshalCanonical(signedOrig) assert.NoError(t, err) err = remoteStorage.SetMeta("timestamp", orig) assert.NoError(t, err) err = client.downloadSnapshot() assert.NoError(t, err) }
func TestChecksumMatch(t *testing.T) { repo := tuf.NewRepo(nil, nil) localStorage := store.NewMemoryStore(nil, nil) remoteStorage := store.NewMemoryStore(nil, nil) client := NewClient(repo, remoteStorage, nil, localStorage) sampleTargets := data.NewTargets() orig, err := json.MarshalCanonical(sampleTargets) origSha256 := sha256.Sum256(orig) assert.NoError(t, err) remoteStorage.SetMeta("targets", orig) _, _, err = client.downloadSigned("targets", int64(len(orig)), origSha256[:]) assert.NoError(t, err) }
// ToSigned partially serializes a SignedTargets for further signing func (t SignedTargets) ToSigned() (*Signed, error) { s, err := json.MarshalCanonical(t.Signed) if err != nil { return nil, err } signed := json.RawMessage{} err = signed.UnmarshalJSON(s) if err != nil { return nil, err } sigs := make([]Signature, len(t.Signatures)) copy(sigs, t.Signatures) return &Signed{ Signatures: sigs, Signed: signed, }, nil }
// ID efficiently generates if necessary, and caches the ID of the key func (k *tufKey) ID() string { if k.id == "" { pubK := tufKey{ Type: k.Algorithm(), Value: KeyPair{ Public: k.Public(), Private: nil, }, } data, err := json.MarshalCanonical(&pubK) if err != nil { logrus.Error("Error generating key ID:", err) } digest := sha256.Sum256(data) k.id = hex.EncodeToString(digest[:]) } return k.id }
func TestDownloadTargetsHappy(t *testing.T) { kdb, repo, _ := testutils.EmptyRepo() localStorage := store.NewMemoryStore(nil, nil) remoteStorage := store.NewMemoryStore(nil, nil) client := NewClient(repo, remoteStorage, kdb, localStorage) signedOrig, err := repo.SignTargets("targets", data.DefaultExpires("targets")) assert.NoError(t, err) orig, err := json.MarshalCanonical(signedOrig) assert.NoError(t, err) err = remoteStorage.SetMeta("targets", orig) assert.NoError(t, err) // call repo.SignSnapshot to update the targets role in the snapshot repo.SignSnapshot(data.DefaultExpires("snapshot")) err = client.downloadTargets("targets") assert.NoError(t, err) }
// TestDownloadTargetsNoSnapshot: it's never valid to download any targets // role (incl. delegations) when a checksum is not available. func TestDownloadTargetsNoSnapshot(t *testing.T) { kdb, repo, _ := testutils.EmptyRepo() localStorage := store.NewMemoryStore(nil, nil) remoteStorage := store.NewMemoryStore(nil, nil) client := NewClient(repo, remoteStorage, kdb, localStorage) // create and "upload" sample targets signedOrig, err := repo.SignTargets("targets", data.DefaultExpires("targets")) assert.NoError(t, err) orig, err := json.MarshalCanonical(signedOrig) assert.NoError(t, err) err = remoteStorage.SetMeta("targets", orig) assert.NoError(t, err) repo.Snapshot = nil err = client.downloadTargets("targets") assert.IsType(t, ErrMissingMeta{}, err) }
func TestDownloadSnapshotNoChecksum(t *testing.T) { kdb, repo, _ := testutils.EmptyRepo() localStorage := store.NewMemoryStore(nil, nil) remoteStorage := store.NewMemoryStore(nil, nil) client := NewClient(repo, remoteStorage, kdb, localStorage) // create and "upload" sample snapshot and timestamp signedOrig, err := repo.SignSnapshot(data.DefaultExpires("snapshot")) assert.NoError(t, err) orig, err := json.MarshalCanonical(signedOrig) assert.NoError(t, err) err = remoteStorage.SetMeta("snapshot", orig) assert.NoError(t, err) delete(repo.Timestamp.Signed.Meta["snapshot"].Hashes, "sha256") err = client.downloadSnapshot() assert.IsType(t, ErrMissingMeta{}, err) }
func (r *NotaryRepository) saveMetadata(ignoreSnapshot bool) error { logrus.Debugf("Saving changes to Trusted Collection.") rootJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalRootRole) if err != nil { return err } err = r.fileStore.SetMeta(data.CanonicalRootRole, rootJSON) if err != nil { return err } targetsToSave := make(map[string][]byte) for t := range r.tufRepo.Targets { signedTargets, err := r.tufRepo.SignTargets(t, data.DefaultExpires("targets")) if err != nil { return err } targetsJSON, err := json.MarshalCanonical(signedTargets) if err != nil { return err } targetsToSave[t] = targetsJSON } for role, blob := range targetsToSave { parentDir := filepath.Dir(role) os.MkdirAll(parentDir, 0755) r.fileStore.SetMeta(role, blob) } if ignoreSnapshot { return nil } snapshotJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalSnapshotRole) if err != nil { return err } return r.fileStore.SetMeta(data.CanonicalSnapshotRole, snapshotJSON) }
// AddTarget creates new changelist entries to add a target to the given roles // in the repository when the changelist gets appied at publish time. // If roles are unspecified, the default role is "targets". func (r *NotaryRepository) AddTarget(target *Target, roles ...string) error { cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist")) if err != nil { return err } defer cl.Close() logrus.Debugf("Adding target \"%s\" with sha256 \"%x\" and size %d bytes.\n", target.Name, target.Hashes["sha256"], target.Length) meta := data.FileMeta{Length: target.Length, Hashes: target.Hashes} metaJSON, err := json.MarshalCanonical(meta) if err != nil { return err } template := changelist.NewTufChange( changelist.ActionCreate, "", changelist.TypeTargetsTarget, target.Name, metaJSON) return addChange(cl, template, roles...) }
func TestBootstrapDownloadRootHappy(t *testing.T) { kdb, repo, _ := testutils.EmptyRepo() localStorage := store.NewMemoryStore(nil, nil) remoteStorage := store.NewMemoryStore(nil, nil) client := NewClient(repo, remoteStorage, kdb, localStorage) // create and "upload" sample root signedOrig, err := repo.SignRoot(data.DefaultExpires("root")) assert.NoError(t, err) orig, err := json.MarshalCanonical(signedOrig) assert.NoError(t, err) err = remoteStorage.SetMeta("root", orig) assert.NoError(t, err) // unset snapshot as if we're bootstrapping from nothing repo.Snapshot = nil err = client.downloadRoot() assert.NoError(t, err) }