// Unmarshal unmarshals and verifys the raw bytes for a given role's metadata func Unmarshal(b []byte, v interface{}, role string, minVersion int, db *keys.KeyDB) error { s := &data.Signed{} if err := json.Unmarshal(b, s); err != nil { return err } if err := Verify(s, role, minVersion, db); err != nil { return err } return json.Unmarshal(s.Signed, v) }
// UnmarshalTrusted unmarshals and verifies signatures only, not metadata, for a // given role's metadata func UnmarshalTrusted(b []byte, v interface{}, role string, db *keys.KeyDB) error { s := &data.Signed{} if err := json.Unmarshal(b, s); err != nil { return err } if err := VerifySignatures(s, role, db); err != nil { return err } return json.Unmarshal(s.Signed, v) }
func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error { switch c.Action() { case changelist.ActionCreate: td := changelist.TufDelegation{} err := json.Unmarshal(c.Content(), &td) if err != nil { return err } r, err := repo.GetDelegation(c.Scope()) if _, ok := err.(data.ErrNoSuchRole); err != nil && !ok { // error that wasn't ErrNoSuchRole return err } if err == nil { // role existed return data.ErrInvalidRole{ Role: c.Scope(), Reason: "cannot create a role that already exists", } } // role doesn't exist, create brand new r, err = td.ToNewRole(c.Scope()) if err != nil { return err } return repo.UpdateDelegations(r, td.AddKeys) case changelist.ActionUpdate: td := changelist.TufDelegation{} err := json.Unmarshal(c.Content(), &td) if err != nil { return err } r, err := repo.GetDelegation(c.Scope()) if err != nil { return err } // role exists, merge if err := r.AddPaths(td.AddPaths); err != nil { return err } if err := r.AddPathHashPrefixes(td.AddPathHashPrefixes); err != nil { return err } r.RemoveKeys(td.RemoveKeys) r.RemovePaths(td.RemovePaths) r.RemovePathHashPrefixes(td.RemovePathHashPrefixes) return repo.UpdateDelegations(r, td.AddKeys) case changelist.ActionDelete: r := data.Role{Name: c.Scope()} return repo.DeleteDelegation(r) default: return fmt.Errorf("unsupported action against delegations: %s", c.Action()) } }
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) } }
// 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 changeTargetMeta(repo *tuf.Repo, c changelist.Change) error { var err error switch c.Action() { case changelist.ActionCreate: logrus.Debug("changelist add: ", c.Path()) meta := &data.FileMeta{} err = json.Unmarshal(c.Content(), meta) if err != nil { return err } files := data.Files{c.Path(): *meta} err = doWithRoleFallback(c.Scope(), func(role string) error { _, e := repo.AddTargets(role, files) return e }) if err != nil { logrus.Errorf("couldn't add target to %s: %s", c.Scope(), err.Error()) } case changelist.ActionDelete: logrus.Debug("changelist remove: ", c.Path()) err = doWithRoleFallback(c.Scope(), func(role string) error { return repo.RemoveTargets(role, c.Path()) }) if err != nil { logrus.Errorf("couldn't remove target from %s: %s", c.Scope(), err.Error()) } default: logrus.Debug("action not yet supported: ", c.Action()) } return err }
func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) { var rootJSON []byte remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip) if err == nil { // if remote store successfully set up, try and get root from remote rootJSON, err = remote.GetMeta("root", maxSize) } // if remote store couldn't be setup, or we failed to get a root from it // load the root from cache (offline operation) if err != nil { if err, ok := err.(store.ErrMetaNotFound); ok { // if the error was MetaNotFound then we successfully contacted // the store and it doesn't know about the repo. return nil, err } result, cacheErr := r.fileStore.GetMeta("root", maxSize) if cacheErr != nil { // if cache didn't return a root, we cannot proceed - just return // the original error. return nil, err } rootJSON = result logrus.Debugf( "Using local cache instead of remote due to failure: %s", err.Error()) } // can't just unmarshal into SignedRoot because validate root // needs the root.Signed field to still be []byte for signature // validation root := &data.Signed{} err = json.Unmarshal(rootJSON, root) if err != nil { return nil, err } err = r.CertManager.ValidateRoot(root, r.gun) if err != nil { return nil, err } kdb := keys.NewDB() r.tufRepo = tuf.NewRepo(kdb, r.CryptoService) signedRoot, err := data.RootFromSigned(root) if err != nil { return nil, err } err = r.tufRepo.SetRoot(signedRoot) if err != nil { return nil, err } return tufclient.NewClient( r.tufRepo, remote, kdb, r.fileStore, ), nil }
// UnmarshalPrivateKey is used to parse individual private keys in JSON func UnmarshalPrivateKey(data []byte) (PrivateKey, error) { var parsed tufKey err := json.Unmarshal(data, &parsed) if err != nil { return nil, err } return typedPrivateKey(parsed) }
// bootstrapRepo loads the repository from the local file system. This attempts // to load metadata for all roles. Since server snapshots are supported, // if the snapshot metadata fails to load, that's ok. // This can also be unified with some cache reading tools from tuf/client. // This assumes that bootstrapRepo is only used by Publish() func (r *NotaryRepository) bootstrapRepo() error { kdb := keys.NewDB() tufRepo := tuf.NewRepo(kdb, r.CryptoService) logrus.Debugf("Loading trusted collection.") rootJSON, err := r.fileStore.GetMeta("root", 0) if err != nil { return err } root := &data.SignedRoot{} err = json.Unmarshal(rootJSON, root) if err != nil { return err } err = tufRepo.SetRoot(root) if err != nil { return err } targetsJSON, err := r.fileStore.GetMeta("targets", 0) if err != nil { return err } targets := &data.SignedTargets{} err = json.Unmarshal(targetsJSON, targets) if err != nil { return err } tufRepo.SetTargets("targets", targets) snapshotJSON, err := r.fileStore.GetMeta("snapshot", 0) if err == nil { snapshot := &data.SignedSnapshot{} err = json.Unmarshal(snapshotJSON, snapshot) if err != nil { return err } tufRepo.SetSnapshot(snapshot) } else if _, ok := err.(store.ErrMetaNotFound); !ok { return err } r.tufRepo = tufRepo return nil }
// 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 }
func TestSignatureUnmarshalJSON(t *testing.T) { signatureJSON := `{"keyid":"97e8e1b51b6e7cf8720a56b5334bd8692ac5b28233c590b89fab0b0cd93eeedc","method":"RSA","sig":"2230cba525e4f5f8fc744f234221ca9a92924da4cc5faf69a778848882fcf7a20dbb57296add87f600891f2569a9c36706314c240f9361c60fd36f5a915a0e9712fc437b761e8f480868d7a4444724daa0d29a2669c0edbd4046046649a506b3d711d0aa5e70cb9d09dec7381e7de27a3168e77731e08f6ed56fcce2478855e837816fb69aff53412477748cd198dce783850080d37aeb929ad0f81460ebd31e61b772b6c7aa56977c787d4281fa45dbdefbb38d449eb5bccb2702964a52c78811545939712c8280dee0b23b2fa9fbbdd6a0c42476689ace655eba0745b4a21ba108bcd03ad00fdefff416dc74e08486a0538f8fd24989e1b9fc89e675141b7c"}` var sig Signature err := json.Unmarshal([]byte(signatureJSON), &sig) assert.NoError(t, err) // Check that the method string is lowercased assert.Equal(t, sig.Method.String(), "rsa") }
// UnmarshalJSON does a custom unmarshalling of the signature JSON func (s *Signature) UnmarshalJSON(data []byte) error { uSignature := unmarshalledSignature{} err := json.Unmarshal(data, &uSignature) if err != nil { return err } uSignature.Method = SigAlgorithm(strings.ToLower(string(uSignature.Method))) *s = Signature(uSignature) return nil }
// TargetsFromSigned fully unpacks a Signed object into a SignedTargets func TargetsFromSigned(s *Signed) (*SignedTargets, error) { t := Targets{} err := json.Unmarshal(s.Signed, &t) if err != nil { return nil, err } sigs := make([]Signature, len(s.Signatures)) copy(sigs, s.Signatures) return &SignedTargets{ Signatures: sigs, Signed: t, }, nil }
// UnmarshalJSON implements the json.Unmarshaller interface func (ks *KeyList) UnmarshalJSON(data []byte) error { parsed := make([]tufKey, 0, 1) err := json.Unmarshal(data, &parsed) if err != nil { return err } final := make([]PublicKey, 0, len(parsed)) for _, tk := range parsed { final = append(final, typedPublicKey(tk)) } *ks = final return nil }
// UnmarshalJSON implements the json.Unmarshaller interface func (ks *Keys) UnmarshalJSON(data []byte) error { parsed := make(map[string]tufKey) err := json.Unmarshal(data, &parsed) if err != nil { return err } final := make(map[string]PublicKey) for k, tk := range parsed { final[k] = typedPublicKey(tk) } *ks = final return nil }
// RootFromSigned fully unpacks a Signed object into a SignedRoot func RootFromSigned(s *Signed) (*SignedRoot, error) { r := Root{} err := json.Unmarshal(s.Signed, &r) if err != nil { return nil, err } sigs := make([]Signature, len(s.Signatures)) copy(sigs, s.Signatures) return &SignedRoot{ Signatures: sigs, Signed: r, }, nil }
func TimestampFromSigned(s *Signed) (*SignedTimestamp, error) { ts := Timestamp{} err := json.Unmarshal(s.Signed, &ts) if err != nil { return nil, err } sigs := make([]Signature, len(s.Signatures)) copy(sigs, s.Signatures) return &SignedTimestamp{ Signatures: sigs, Signed: ts, }, nil }
func SnapshotFromSigned(s *Signed) (*SignedSnapshot, error) { sp := Snapshot{} err := json.Unmarshal(s.Signed, &sp) if err != nil { return nil, err } sigs := make([]Signature, len(s.Signatures)) copy(sigs, s.Signatures) return &SignedSnapshot{ Signatures: sigs, Signed: sp, }, nil }
func verifyMeta(s *data.Signed, role string, minVersion int) error { sm := &data.SignedCommon{} if err := json.Unmarshal(s.Signed, sm); err != nil { return err } if !data.ValidTUFType(sm.Type, role) { return ErrWrongType } if IsExpired(sm.Expires) { logrus.Errorf("Metadata for %s expired", role) return ErrExpired{Role: role, Expired: sm.Expires.Format("Mon Jan 2 15:04:05 MST 2006")} } if sm.Version < minVersion { return ErrLowVersion{sm.Version, minVersion} } return nil }
func applyRootRoleChange(repo *tuf.Repo, c changelist.Change) error { switch c.Action() { case changelist.ActionCreate: // replaces all keys for a role d := &changelist.TufRootData{} err := json.Unmarshal(c.Content(), d) if err != nil { return err } err = repo.ReplaceBaseKeys(d.RoleName, d.Keys...) if err != nil { return err } default: logrus.Debug("action not yet supported for root: ", c.Action()) } return nil }
// 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 := store.GetCurrent(gun, "snapshot") 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.MarshalCanonical(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 }
func TestValidateSnapshotGenerateWithPrev(t *testing.T) { kdb, repo, cs := testutils.EmptyRepo() store := storage.NewMemStorage() snapRole := kdb.GetRole(data.CanonicalSnapshotRole) for _, id := range snapRole.KeyIDs { k := kdb.GetKey(id) assert.NotNil(t, k) err := store.SetKey("testGUN", data.CanonicalSnapshotRole, k.Algorithm(), k.Public()) assert.NoError(t, err) } r, tg, sn, ts, err := testutils.Sign(repo) assert.NoError(t, err) root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts) assert.NoError(t, err) updates := []storage.MetaUpdate{root, targets} // set the current snapshot in the store manually so we find it when generating // the next version store.UpdateCurrent("testGUN", snapshot) prev, err := data.SnapshotFromSigned(sn) assert.NoError(t, err) copyTimestampKey(t, kdb, store, "testGUN") updates, err = validateUpdate(cs, "testGUN", updates, store) assert.NoError(t, err) for _, u := range updates { if u.Role == data.CanonicalSnapshotRole { curr := &data.SignedSnapshot{} err = json.Unmarshal(u.Data, curr) assert.Equal(t, prev.Signed.Version+1, curr.Signed.Version) assert.Equal(t, u.Version, curr.Signed.Version) } } }
// VerifyRoot checks if a given root file is valid against a known set of keys. // Threshold is always assumed to be 1 func VerifyRoot(s *data.Signed, minVersion int, keys map[string]data.PublicKey) error { if len(s.Signatures) == 0 { return ErrNoSignatures } 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 } for _, sig := range s.Signatures { // 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 for verify root: %s\n", sig.Method) continue } key, ok := keys[sig.KeyID] if !ok { logrus.Debugf("continuing b/c signing key isn't present in keys: %s\n", sig.KeyID) continue } if err := verifier.Verify(key, sig.Signature, msg); err != nil { logrus.Debugf("continuing b/c signature was invalid\n") continue } // threshold of 1 so return on first success return verifyMeta(s, "root", minVersion) } return ErrRoleThreshold{} }
// 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.MarshalCanonical(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 }
// GetKeys returns private keys func (dbs *dbStore) GetKeys(role string) ([]data.PrivateKey, error) { keys := []data.PrivateKey{} var r *sql.Rows var err error sql := "SELECT `key` FROM `keys` WHERE `role` = ? AND `namespace` = ?;" tx, err := dbs.db.Begin() defer tx.Rollback() r, err = tx.Query(sql, role, dbs.imageName) if err != nil { return nil, err } defer r.Close() for r.Next() { var jsonStr string key := new(data.TUFKey) r.Scan(&jsonStr) err := json.Unmarshal([]byte(jsonStr), key) if err != nil { return nil, err } keys = append(keys, key) } return keys, nil }
// Unmarshal unmarshals some JSON bytes func (c canonicalJSON) Unmarshal(from []byte, to interface{}) error { return json.Unmarshal(from, to) }