1package data 2 3import ( 4 "errors" 5 "fmt" 6 "path" 7 8 "github.com/docker/go/canonical/json" 9) 10 11// SignedTargets is a fully unpacked targets.json, or target delegation 12// json file 13type SignedTargets struct { 14 Signatures []Signature 15 Signed Targets 16 Dirty bool 17} 18 19// Targets is the Signed components of a targets.json or delegation json file 20type Targets struct { 21 SignedCommon 22 Targets Files `json:"targets"` 23 Delegations Delegations `json:"delegations,omitempty"` 24} 25 26// isValidTargetsStructure returns an error, or nil, depending on whether the content of the struct 27// is valid for targets metadata. This does not check signatures or expiry, just that 28// the metadata content is valid. 29func isValidTargetsStructure(t Targets, roleName RoleName) error { 30 if roleName != CanonicalTargetsRole && !IsDelegation(roleName) { 31 return ErrInvalidRole{Role: roleName} 32 } 33 34 // even if it's a delegated role, the metadata type is "Targets" 35 expectedType := TUFTypes[CanonicalTargetsRole] 36 if t.Type != expectedType { 37 return ErrInvalidMetadata{ 38 role: roleName, msg: fmt.Sprintf("expected type %s, not %s", expectedType, t.Type)} 39 } 40 41 if t.Version < 1 { 42 return ErrInvalidMetadata{role: roleName, msg: "version cannot be less than one"} 43 } 44 45 for _, roleObj := range t.Delegations.Roles { 46 if !IsDelegation(roleObj.Name) || path.Dir(roleObj.Name.String()) != roleName.String() { 47 return ErrInvalidMetadata{ 48 role: roleName, msg: fmt.Sprintf("delegation role %s invalid", roleObj.Name)} 49 } 50 if err := isValidRootRoleStructure(roleName, roleObj.Name, roleObj.RootRole, t.Delegations.Keys); err != nil { 51 return err 52 } 53 } 54 return nil 55} 56 57// NewTargets intiializes a new empty SignedTargets object 58func NewTargets() *SignedTargets { 59 return &SignedTargets{ 60 Signatures: make([]Signature, 0), 61 Signed: Targets{ 62 SignedCommon: SignedCommon{ 63 Type: TUFTypes["targets"], 64 Version: 0, 65 Expires: DefaultExpires("targets"), 66 }, 67 Targets: make(Files), 68 Delegations: *NewDelegations(), 69 }, 70 Dirty: true, 71 } 72} 73 74// GetMeta attempts to find the targets entry for the path. It 75// will return nil in the case of the target not being found. 76func (t SignedTargets) GetMeta(path string) *FileMeta { 77 for p, meta := range t.Signed.Targets { 78 if p == path { 79 return &meta 80 } 81 } 82 return nil 83} 84 85// GetValidDelegations filters the delegation roles specified in the signed targets, and 86// only returns roles that are direct children and restricts their paths 87func (t SignedTargets) GetValidDelegations(parent DelegationRole) []DelegationRole { 88 roles := t.buildDelegationRoles() 89 result := []DelegationRole{} 90 for _, r := range roles { 91 validRole, err := parent.Restrict(r) 92 if err != nil { 93 continue 94 } 95 result = append(result, validRole) 96 } 97 return result 98} 99 100// BuildDelegationRole returns a copy of a DelegationRole using the information in this SignedTargets for the specified role name. 101// Will error for invalid role name or key metadata within this SignedTargets. Path data is not validated. 102func (t *SignedTargets) BuildDelegationRole(roleName RoleName) (DelegationRole, error) { 103 for _, role := range t.Signed.Delegations.Roles { 104 if role.Name == roleName { 105 pubKeys := make(map[string]PublicKey) 106 for _, keyID := range role.KeyIDs { 107 pubKey, ok := t.Signed.Delegations.Keys[keyID] 108 if !ok { 109 // Couldn't retrieve all keys, so stop walking and return invalid role 110 return DelegationRole{}, ErrInvalidRole{ 111 Role: roleName, 112 Reason: "role lists unknown key " + keyID + " as a signing key", 113 } 114 } 115 pubKeys[keyID] = pubKey 116 } 117 return DelegationRole{ 118 BaseRole: BaseRole{ 119 Name: role.Name, 120 Keys: pubKeys, 121 Threshold: role.Threshold, 122 }, 123 Paths: role.Paths, 124 }, nil 125 } 126 } 127 return DelegationRole{}, ErrNoSuchRole{Role: roleName} 128} 129 130// helper function to create DelegationRole structures from all delegations in a SignedTargets, 131// these delegations are read directly from the SignedTargets and not modified or validated 132func (t SignedTargets) buildDelegationRoles() []DelegationRole { 133 var roles []DelegationRole 134 for _, roleData := range t.Signed.Delegations.Roles { 135 delgRole, err := t.BuildDelegationRole(roleData.Name) 136 if err != nil { 137 continue 138 } 139 roles = append(roles, delgRole) 140 } 141 return roles 142} 143 144// AddTarget adds or updates the meta for the given path 145func (t *SignedTargets) AddTarget(path string, meta FileMeta) { 146 t.Signed.Targets[path] = meta 147 t.Dirty = true 148} 149 150// AddDelegation will add a new delegated role with the given keys, 151// ensuring the keys either already exist, or are added to the map 152// of delegation keys 153func (t *SignedTargets) AddDelegation(role *Role, keys []*PublicKey) error { 154 return errors.New("Not Implemented") 155} 156 157// ToSigned partially serializes a SignedTargets for further signing 158func (t *SignedTargets) ToSigned() (*Signed, error) { 159 s, err := defaultSerializer.MarshalCanonical(t.Signed) 160 if err != nil { 161 return nil, err 162 } 163 signed := json.RawMessage{} 164 err = signed.UnmarshalJSON(s) 165 if err != nil { 166 return nil, err 167 } 168 sigs := make([]Signature, len(t.Signatures)) 169 copy(sigs, t.Signatures) 170 return &Signed{ 171 Signatures: sigs, 172 Signed: &signed, 173 }, nil 174} 175 176// MarshalJSON returns the serialized form of SignedTargets as bytes 177func (t *SignedTargets) MarshalJSON() ([]byte, error) { 178 signed, err := t.ToSigned() 179 if err != nil { 180 return nil, err 181 } 182 return defaultSerializer.Marshal(signed) 183} 184 185// TargetsFromSigned fully unpacks a Signed object into a SignedTargets, given 186// a role name (so it can validate the SignedTargets object) 187func TargetsFromSigned(s *Signed, roleName RoleName) (*SignedTargets, error) { 188 t := Targets{} 189 if err := defaultSerializer.Unmarshal(*s.Signed, &t); err != nil { 190 return nil, err 191 } 192 if err := isValidTargetsStructure(t, roleName); err != nil { 193 return nil, err 194 } 195 sigs := make([]Signature, len(s.Signatures)) 196 copy(sigs, s.Signatures) 197 return &SignedTargets{ 198 Signatures: sigs, 199 Signed: t, 200 }, nil 201} 202