@@ -177,33 +177,38 @@ func (c *Client) Update() (data.TargetFiles, error) {
177177 return nil , err
178178 }
179179
180- // Get timestamp.json, extract snapshot.json file meta and save the
181- // timestamp.json locally
180+ // Load trusted metadata files, if any, and verify them against the latest root
181+ c .getLocalMeta ()
182+
183+ // 5.4.1 - Download the timestamp metadata
182184 timestampJSON , err := c .downloadMetaUnsafe ("timestamp.json" , defaultTimestampDownloadLimit )
183185 if err != nil {
184186 return nil , err
185187 }
188+ // 5.4.(2,3 and 4) - Verify timestamp against various attacks
189+ // Returns the extracted snapshot metadata
186190 snapshotMeta , err := c .decodeTimestamp (timestampJSON )
187191 if err != nil {
188192 return nil , err
189193 }
194+ // 5.4.5 - Persist the timestamp metadata
190195 if err := c .local .SetMeta ("timestamp.json" , timestampJSON ); err != nil {
191196 return nil , err
192197 }
193198
194- // Get snapshot.json, then extract file metas.
195- // root.json meta should not be stored in the snapshot, if it is,
196- // the root will be checked, re-downloaded
199+ // 5.5.1 - Download snapshot metadata
200+ // 5.5.2 and 5.5.4 - Check against timestamp role's snapshot hash and version
197201 snapshotJSON , err := c .downloadMetaFromTimestamp ("snapshot.json" , snapshotMeta )
198202 if err != nil {
199203 return nil , err
200204 }
205+ // 5.5.(3,5 and 6) - Verify snapshot against various attacks
206+ // Returns the extracted metadata files
201207 snapshotMetas , err := c .decodeSnapshot (snapshotJSON )
202208 if err != nil {
203209 return nil , err
204210 }
205-
206- // Save the snapshot.json
211+ // 5.5.7 - Persist snapshot metadata
207212 if err := c .local .SetMeta ("snapshot.json" , snapshotJSON ); err != nil {
208213 return nil , err
209214 }
@@ -213,14 +218,18 @@ func (c *Client) Update() (data.TargetFiles, error) {
213218 var updatedTargets data.TargetFiles
214219 targetsMeta := snapshotMetas ["targets.json" ]
215220 if ! c .hasMetaFromSnapshot ("targets.json" , targetsMeta ) {
221+ // 5.6.1 - Download the top-level targets metadata file
222+ // 5.6.2 and 5.6.4 - Check against snapshot role's targets hash and version
216223 targetsJSON , err := c .downloadMetaFromSnapshot ("targets.json" , targetsMeta )
217224 if err != nil {
218225 return nil , err
219226 }
227+ // 5.6.(3 and 5) - Verify signatures and check against freeze attack
220228 updatedTargets , err = c .decodeTargets (targetsJSON )
221229 if err != nil {
222230 return nil , err
223231 }
232+ // 5.6.6 - Persist targets metadata
224233 if err := c .local .SetMeta ("targets.json" , targetsJSON ); err != nil {
225234 return nil , err
226235 }
@@ -393,44 +402,69 @@ func (c *Client) UpdateRoots() error {
393402// getLocalMeta decodes and verifies metadata from local storage.
394403// The verification of local files is purely for consistency, if an attacker
395404// has compromised the local storage, there is no guarantee it can be trusted.
405+ // Before trying to load the metadata files, it clears the in-memory copy of the local metadata.
406+ // This is to insure that all of the loaded metadata files at the end are indeed verified by the latest root.
407+ // If some of the metadata files fail to load it will proceed with trying to load the rest,
408+ // but still return an error at the end, if such occurred. Otherwise returns nil.
396409func (c * Client ) getLocalMeta () error {
410+ var retErr error
411+ loadFailed := false
412+ // Clear the in-memory copy of the local metadata. The goal is to reload and take into account
413+ // only the metadata files that are verified by the latest root. Otherwise, their content should
414+ // be ignored.
415+ c .localMeta = make (map [string ]json.RawMessage )
416+
417+ // Load the latest root meta
397418 if err := c .loadAndVerifyLocalRootMeta ( /*ignoreExpiredCheck=*/ false ); err != nil {
398419 return err
399420 }
400421
422+ // Load into memory the existing meta, if any, from the local storage
401423 meta , err := c .local .GetMeta ()
402424 if err != nil {
403425 return nil
404426 }
405427
428+ // Verify the top-level metadata (timestamp, snapshot and targets) against the latest root and load it, if okay
406429 if timestampJSON , ok := meta ["timestamp.json" ]; ok {
407430 timestamp := & data.Timestamp {}
408431 if err := c .db .UnmarshalTrusted (timestampJSON , timestamp , "timestamp" ); err != nil {
409- return err
432+ loadFailed = true
433+ retErr = err
434+ } else {
435+ c .localMeta ["timestamp.json" ] = meta ["timestamp.json" ]
436+ c .timestampVer = timestamp .Version
410437 }
411- c .timestampVer = timestamp .Version
412438 }
413439
414440 if snapshotJSON , ok := meta ["snapshot.json" ]; ok {
415441 snapshot := & data.Snapshot {}
416442 if err := c .db .UnmarshalTrusted (snapshotJSON , snapshot , "snapshot" ); err != nil {
417- return err
443+ loadFailed = true
444+ retErr = err
445+ } else {
446+ c .localMeta ["snapshot.json" ] = meta ["snapshot.json" ]
447+ c .snapshotVer = snapshot .Version
418448 }
419- c .snapshotVer = snapshot .Version
420449 }
421450
422451 if targetsJSON , ok := meta ["targets.json" ]; ok {
423452 targets := & data.Targets {}
424453 if err := c .db .UnmarshalTrusted (targetsJSON , targets , "targets" ); err != nil {
425- return err
454+ loadFailed = true
455+ retErr = err
456+ } else {
457+ c .localMeta ["targets.json" ] = meta ["targets.json" ]
458+ c .targetsVer = targets .Version
459+ // FIXME(TUF-0.9) temporarily support files with leading path separators.
460+ // c.targets = targets.Targets
461+ c .loadTargets (targets .Targets )
426462 }
427- c .targetsVer = targets .Version
428- // FIXME(TUF-0.9) temporarily support files with leading path separators.
429- // c.targets = targets.Targets
430- c .loadTargets (targets .Targets )
431463 }
432-
433- c .localMeta = meta
464+ if loadFailed {
465+ // If any of the metadata failed to be verified, return the reason for that failure
466+ return retErr
467+ }
434468 return nil
435469}
436470
@@ -660,6 +694,7 @@ func (c *Client) downloadMetaFromSnapshot(name string, m data.SnapshotFileMeta)
660694 if err != nil {
661695 return nil , err
662696 }
697+ // 5.6.2 and 5.6.4 - Check against snapshot role's targets hash and version
663698 if err := util .SnapshotFileMetaEqual (meta , m ); err != nil {
664699 return nil , ErrDownloadFailed {name , err }
665700 }
@@ -676,6 +711,7 @@ func (c *Client) downloadMetaFromTimestamp(name string, m data.TimestampFileMeta
676711 if err != nil {
677712 return nil , err
678713 }
714+ // 5.5.2 and 5.5.4 - Check against timestamp role's snapshot hash and version
679715 if err := util .TimestampFileMetaEqual (meta , m ); err != nil {
680716 return nil , ErrDownloadFailed {name , err }
681717 }
@@ -697,20 +733,53 @@ func (c *Client) decodeRoot(b json.RawMessage) error {
697733// root and targets file meta.
698734func (c * Client ) decodeSnapshot (b json.RawMessage ) (data.SnapshotFiles , error ) {
699735 snapshot := & data.Snapshot {}
736+ // 5.5.(3 and 6) - Verify it's signed correctly and it's not expired
700737 if err := c .db .Unmarshal (b , snapshot , "snapshot" , c .snapshotVer ); err != nil {
701738 return data.SnapshotFiles {}, ErrDecodeFailed {"snapshot.json" , err }
702739 }
703- c .snapshotVer = snapshot .Version
740+ // 5.5.5 - Check for top-level targets rollback attack
741+ // Verify explicitly that current targets meta version is less than or equal to the new one
742+ if snapshot .Meta ["targets.json" ].Version < c .targetsVer {
743+ return data.SnapshotFiles {}, verify.ErrLowVersion {Actual : snapshot .Meta ["targets.json" ].Version , Current : c .targetsVer }
744+ }
745+
746+ // 5.5.5 - Get the local/trusted snapshot metadata, if any, and check all target metafiles against rollback attack
747+ // In case the local snapshot metadata was not verified by the keys in the latest root during getLocalMeta(),
748+ // snapshot.json won't be present in c.localMeta and thus this check will not be processed.
749+ if snapshotJSON , ok := c .localMeta ["snapshot.json" ]; ok {
750+ currentSnapshot := & data.Snapshot {}
751+ if err := c .db .UnmarshalTrusted (snapshotJSON , currentSnapshot , "snapshot" ); err != nil {
752+ return data.SnapshotFiles {}, err
753+ }
754+ // 5.5.5 - Check for rollback attacks in both top-level and delegated targets roles (note that the Meta object includes both)
755+ for path , local := range currentSnapshot .Meta {
756+ if newMeta , ok := snapshot .Meta [path ]; ok {
757+ // 5.5.5 - Check for rollback attack
758+ if newMeta .Version < local .Version {
759+ return data.SnapshotFiles {}, verify.ErrLowVersion {Actual : newMeta .Version , Current : local .Version }
760+ }
761+ } else {
762+ // 5.5.5 - Abort the update if a target file has been removed from the new snapshot file
763+ return data.SnapshotFiles {}, verify .ErrMissingTargetFile
764+ }
765+ }
766+ }
767+ // At this point we can trust the new snapshot, the top-level targets, and any delegated targets versions it refers to
768+ // so we can update the client's trusted versions and proceed with persisting the new snapshot metadata
769+ // c.snapshotVer was already set when we verified the timestamp metadata
770+ c .targetsVer = snapshot .Meta ["targets.json" ].Version
704771 return snapshot .Meta , nil
705772}
706773
707774// decodeTargets decodes and verifies targets metadata, sets c.targets and
708775// returns updated targets.
709776func (c * Client ) decodeTargets (b json.RawMessage ) (data.TargetFiles , error ) {
710777 targets := & data.Targets {}
778+ // 5.6.(3 and 5) - Verify signatures and check against freeze attack
711779 if err := c .db .Unmarshal (b , targets , "targets" , c .targetsVer ); err != nil {
712780 return nil , ErrDecodeFailed {"targets.json" , err }
713781 }
782+ // Generate a list with the updated targets
714783 updatedTargets := make (data.TargetFiles )
715784 for path , meta := range targets .Targets {
716785 if local , ok := c .targets [path ]; ok {
@@ -720,7 +789,7 @@ func (c *Client) decodeTargets(b json.RawMessage) (data.TargetFiles, error) {
720789 }
721790 updatedTargets [path ] = meta
722791 }
723- c .targetsVer = targets . Version
792+ // c.targetsVer was already updated when we verified the snapshot metadata
724793 // FIXME(TUF-0.9) temporarily support files with leading path separators.
725794 // c.targets = targets.Targets
726795 c .loadTargets (targets .Targets )
@@ -734,7 +803,15 @@ func (c *Client) decodeTimestamp(b json.RawMessage) (data.TimestampFileMeta, err
734803 if err := c .db .Unmarshal (b , timestamp , "timestamp" , c .timestampVer ); err != nil {
735804 return data.TimestampFileMeta {}, ErrDecodeFailed {"timestamp.json" , err }
736805 }
806+ // 5.4.3.2 - Check for snapshot rollback attack
807+ // Verify that the current snapshot meta version is less than or equal to the new one
808+ if timestamp .Meta ["snapshot.json" ].Version < c .snapshotVer {
809+ return data.TimestampFileMeta {}, verify.ErrLowVersion {Actual : timestamp .Meta ["snapshot.json" ].Version , Current : c .snapshotVer }
810+ }
811+ // At this point we can trust the new timestamp and the snaphost version it refers to
812+ // so we can update the client's trusted versions and proceed with persisting the new timestamp
737813 c .timestampVer = timestamp .Version
814+ c .snapshotVer = timestamp .Meta ["snapshot.json" ].Version
738815 return timestamp .Meta ["snapshot.json" ], nil
739816}
740817
0 commit comments