1package client 2 3import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "time" 8 9 "github.com/sirupsen/logrus" 10 "github.com/theupdateframework/notary/client/changelist" 11 store "github.com/theupdateframework/notary/storage" 12 "github.com/theupdateframework/notary/tuf" 13 "github.com/theupdateframework/notary/tuf/data" 14 "github.com/theupdateframework/notary/tuf/signed" 15 "github.com/theupdateframework/notary/tuf/utils" 16) 17 18// Use this to initialize remote HTTPStores from the config settings 19func getRemoteStore(baseURL string, gun data.GUN, rt http.RoundTripper) (store.RemoteStore, error) { 20 s, err := store.NewHTTPStore( 21 baseURL+"/v2/"+gun.String()+"/_trust/tuf/", 22 "", 23 "json", 24 "key", 25 rt, 26 ) 27 if err != nil { 28 return store.OfflineStore{}, err 29 } 30 return s, nil 31} 32 33func applyChangelist(repo *tuf.Repo, invalid *tuf.Repo, cl changelist.Changelist) error { 34 it, err := cl.NewIterator() 35 if err != nil { 36 return err 37 } 38 index := 0 39 for it.HasNext() { 40 c, err := it.Next() 41 if err != nil { 42 return err 43 } 44 isDel := data.IsDelegation(c.Scope()) || data.IsWildDelegation(c.Scope()) 45 switch { 46 case c.Scope() == changelist.ScopeTargets || isDel: 47 err = applyTargetsChange(repo, invalid, c) 48 case c.Scope() == changelist.ScopeRoot: 49 err = applyRootChange(repo, c) 50 default: 51 return fmt.Errorf("scope not supported: %s", c.Scope().String()) 52 } 53 if err != nil { 54 logrus.Debugf("error attempting to apply change #%d: %s, on scope: %s path: %s type: %s", index, c.Action(), c.Scope(), c.Path(), c.Type()) 55 return err 56 } 57 index++ 58 } 59 logrus.Debugf("applied %d change(s)", index) 60 return nil 61} 62 63func applyTargetsChange(repo *tuf.Repo, invalid *tuf.Repo, c changelist.Change) error { 64 switch c.Type() { 65 case changelist.TypeTargetsTarget: 66 return changeTargetMeta(repo, c) 67 case changelist.TypeTargetsDelegation: 68 return changeTargetsDelegation(repo, c) 69 case changelist.TypeWitness: 70 return witnessTargets(repo, invalid, c.Scope()) 71 default: 72 return fmt.Errorf("only target meta and delegations changes supported") 73 } 74} 75 76func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error { 77 switch c.Action() { 78 case changelist.ActionCreate: 79 td := changelist.TUFDelegation{} 80 err := json.Unmarshal(c.Content(), &td) 81 if err != nil { 82 return err 83 } 84 85 // Try to create brand new role or update one 86 // First add the keys, then the paths. We can only add keys and paths in this scenario 87 err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, []string{}, td.NewThreshold) 88 if err != nil { 89 return err 90 } 91 return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, []string{}, false) 92 case changelist.ActionUpdate: 93 td := changelist.TUFDelegation{} 94 err := json.Unmarshal(c.Content(), &td) 95 if err != nil { 96 return err 97 } 98 if data.IsWildDelegation(c.Scope()) { 99 return repo.PurgeDelegationKeys(c.Scope(), td.RemoveKeys) 100 } 101 102 delgRole, err := repo.GetDelegationRole(c.Scope()) 103 if err != nil { 104 return err 105 } 106 107 // We need to translate the keys from canonical ID to TUF ID for compatibility 108 canonicalToTUFID := make(map[string]string) 109 for tufID, pubKey := range delgRole.Keys { 110 canonicalID, err := utils.CanonicalKeyID(pubKey) 111 if err != nil { 112 return err 113 } 114 canonicalToTUFID[canonicalID] = tufID 115 } 116 117 removeTUFKeyIDs := []string{} 118 for _, canonID := range td.RemoveKeys { 119 removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID]) 120 } 121 122 err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, removeTUFKeyIDs, td.NewThreshold) 123 if err != nil { 124 return err 125 } 126 return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, td.RemovePaths, td.ClearAllPaths) 127 case changelist.ActionDelete: 128 return repo.DeleteDelegation(c.Scope()) 129 default: 130 return fmt.Errorf("unsupported action against delegations: %s", c.Action()) 131 } 132 133} 134 135func changeTargetMeta(repo *tuf.Repo, c changelist.Change) error { 136 var err error 137 switch c.Action() { 138 case changelist.ActionCreate: 139 logrus.Debug("changelist add: ", c.Path()) 140 meta := &data.FileMeta{} 141 err = json.Unmarshal(c.Content(), meta) 142 if err != nil { 143 return err 144 } 145 files := data.Files{c.Path(): *meta} 146 147 // Attempt to add the target to this role 148 if _, err = repo.AddTargets(c.Scope(), files); err != nil { 149 logrus.Errorf("couldn't add target to %s: %s", c.Scope(), err.Error()) 150 } 151 152 case changelist.ActionDelete: 153 logrus.Debug("changelist remove: ", c.Path()) 154 155 // Attempt to remove the target from this role 156 if err = repo.RemoveTargets(c.Scope(), c.Path()); err != nil { 157 logrus.Errorf("couldn't remove target from %s: %s", c.Scope(), err.Error()) 158 } 159 160 default: 161 err = fmt.Errorf("action not yet supported: %s", c.Action()) 162 } 163 return err 164} 165 166func applyRootChange(repo *tuf.Repo, c changelist.Change) error { 167 var err error 168 switch c.Type() { 169 case changelist.TypeBaseRole: 170 err = applyRootRoleChange(repo, c) 171 default: 172 err = fmt.Errorf("type of root change not yet supported: %s", c.Type()) 173 } 174 return err // might be nil 175} 176 177func applyRootRoleChange(repo *tuf.Repo, c changelist.Change) error { 178 switch c.Action() { 179 case changelist.ActionCreate: 180 // replaces all keys for a role 181 d := &changelist.TUFRootData{} 182 err := json.Unmarshal(c.Content(), d) 183 if err != nil { 184 return err 185 } 186 err = repo.ReplaceBaseKeys(d.RoleName, d.Keys...) 187 if err != nil { 188 return err 189 } 190 default: 191 return fmt.Errorf("action not yet supported for root: %s", c.Action()) 192 } 193 return nil 194} 195 196func nearExpiry(r data.SignedCommon) bool { 197 plus6mo := time.Now().AddDate(0, 6, 0) 198 return r.Expires.Before(plus6mo) 199} 200 201func warnRolesNearExpiry(r *tuf.Repo) { 202 //get every role and its respective signed common and call nearExpiry on it 203 //Root check 204 if nearExpiry(r.Root.Signed.SignedCommon) { 205 logrus.Warn("root is nearing expiry, you should re-sign the role metadata") 206 } 207 //Targets and delegations check 208 for role, signedTOrD := range r.Targets { 209 //signedTOrD is of type *data.SignedTargets 210 if nearExpiry(signedTOrD.Signed.SignedCommon) { 211 logrus.Warn(role, " metadata is nearing expiry, you should re-sign the role metadata") 212 } 213 } 214 //Snapshot check 215 if nearExpiry(r.Snapshot.Signed.SignedCommon) { 216 logrus.Warn("snapshot is nearing expiry, you should re-sign the role metadata") 217 } 218 //do not need to worry about Timestamp, notary signer will re-sign with the timestamp key 219} 220 221// Fetches a public key from a remote store, given a gun and role 222func getRemoteKey(role data.RoleName, remote store.RemoteStore) (data.PublicKey, error) { 223 rawPubKey, err := remote.GetKey(role) 224 if err != nil { 225 return nil, err 226 } 227 228 pubKey, err := data.UnmarshalPublicKey(rawPubKey) 229 if err != nil { 230 return nil, err 231 } 232 233 return pubKey, nil 234} 235 236// Rotates a private key in a remote store and returns the public key component 237func rotateRemoteKey(role data.RoleName, remote store.RemoteStore) (data.PublicKey, error) { 238 rawPubKey, err := remote.RotateKey(role) 239 if err != nil { 240 return nil, err 241 } 242 243 pubKey, err := data.UnmarshalPublicKey(rawPubKey) 244 if err != nil { 245 return nil, err 246 } 247 248 return pubKey, nil 249} 250 251// signs and serializes the metadata for a canonical role in a TUF repo to JSON 252func serializeCanonicalRole(tufRepo *tuf.Repo, role data.RoleName, extraSigningKeys data.KeyList) (out []byte, err error) { 253 var s *data.Signed 254 switch { 255 case role == data.CanonicalRootRole: 256 s, err = tufRepo.SignRoot(data.DefaultExpires(role), extraSigningKeys) 257 case role == data.CanonicalSnapshotRole: 258 s, err = tufRepo.SignSnapshot(data.DefaultExpires(role)) 259 case tufRepo.Targets[role] != nil: 260 s, err = tufRepo.SignTargets( 261 role, data.DefaultExpires(data.CanonicalTargetsRole)) 262 default: 263 err = fmt.Errorf("%s not supported role to sign on the client", role) 264 } 265 266 if err != nil { 267 return 268 } 269 270 return json.Marshal(s) 271} 272 273func getAllPrivKeys(rootKeyIDs []string, cryptoService signed.CryptoService) ([]data.PrivateKey, error) { 274 if cryptoService == nil { 275 return nil, fmt.Errorf("no crypto service available to get private keys from") 276 } 277 278 privKeys := make([]data.PrivateKey, 0, len(rootKeyIDs)) 279 for _, keyID := range rootKeyIDs { 280 privKey, _, err := cryptoService.GetPrivateKey(keyID) 281 if err != nil { 282 return nil, err 283 } 284 privKeys = append(privKeys, privKey) 285 } 286 if len(privKeys) == 0 { 287 var rootKeyID string 288 rootKeyList := cryptoService.ListKeys(data.CanonicalRootRole) 289 if len(rootKeyList) == 0 { 290 rootPublicKey, err := cryptoService.Create(data.CanonicalRootRole, "", data.ECDSAKey) 291 if err != nil { 292 return nil, err 293 } 294 rootKeyID = rootPublicKey.ID() 295 } else { 296 rootKeyID = rootKeyList[0] 297 } 298 privKey, _, err := cryptoService.GetPrivateKey(rootKeyID) 299 if err != nil { 300 return nil, err 301 } 302 privKeys = append(privKeys, privKey) 303 } 304 305 return privKeys, nil 306} 307