@@ -177,33 +177,38 @@ func (c *Client) Update() (data.TargetFiles, error) {
177
177
return nil , err
178
178
}
179
179
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
182
184
timestampJSON , err := c .downloadMetaUnsafe ("timestamp.json" , defaultTimestampDownloadLimit )
183
185
if err != nil {
184
186
return nil , err
185
187
}
188
+ // 5.4.(2,3 and 4) - Verify timestamp against various attacks
189
+ // Returns the extracted snapshot metadata
186
190
snapshotMeta , err := c .decodeTimestamp (timestampJSON )
187
191
if err != nil {
188
192
return nil , err
189
193
}
194
+ // 5.4.5 - Persist the timestamp metadata
190
195
if err := c .local .SetMeta ("timestamp.json" , timestampJSON ); err != nil {
191
196
return nil , err
192
197
}
193
198
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
197
201
snapshotJSON , err := c .downloadMetaFromTimestamp ("snapshot.json" , snapshotMeta )
198
202
if err != nil {
199
203
return nil , err
200
204
}
205
+ // 5.5.(3,5 and 6) - Verify snapshot against various attacks
206
+ // Returns the extracted metadata files
201
207
snapshotMetas , err := c .decodeSnapshot (snapshotJSON )
202
208
if err != nil {
203
209
return nil , err
204
210
}
205
-
206
- // Save the snapshot.json
211
+ // 5.5.7 - Persist snapshot metadata
207
212
if err := c .local .SetMeta ("snapshot.json" , snapshotJSON ); err != nil {
208
213
return nil , err
209
214
}
@@ -213,14 +218,18 @@ func (c *Client) Update() (data.TargetFiles, error) {
213
218
var updatedTargets data.TargetFiles
214
219
targetsMeta := snapshotMetas ["targets.json" ]
215
220
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
216
223
targetsJSON , err := c .downloadMetaFromSnapshot ("targets.json" , targetsMeta )
217
224
if err != nil {
218
225
return nil , err
219
226
}
227
+ // 5.6.(3 and 5) - Verify signatures and check against freeze attack
220
228
updatedTargets , err = c .decodeTargets (targetsJSON )
221
229
if err != nil {
222
230
return nil , err
223
231
}
232
+ // 5.6.6 - Persist targets metadata
224
233
if err := c .local .SetMeta ("targets.json" , targetsJSON ); err != nil {
225
234
return nil , err
226
235
}
@@ -393,44 +402,69 @@ func (c *Client) UpdateRoots() error {
393
402
// getLocalMeta decodes and verifies metadata from local storage.
394
403
// The verification of local files is purely for consistency, if an attacker
395
404
// 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.
396
409
func (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
397
418
if err := c .loadAndVerifyLocalRootMeta ( /*ignoreExpiredCheck=*/ false ); err != nil {
398
419
return err
399
420
}
400
421
422
+ // Load into memory the existing meta, if any, from the local storage
401
423
meta , err := c .local .GetMeta ()
402
424
if err != nil {
403
425
return nil
404
426
}
405
427
428
+ // Verify the top-level metadata (timestamp, snapshot and targets) against the latest root and load it, if okay
406
429
if timestampJSON , ok := meta ["timestamp.json" ]; ok {
407
430
timestamp := & data.Timestamp {}
408
431
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
410
437
}
411
- c .timestampVer = timestamp .Version
412
438
}
413
439
414
440
if snapshotJSON , ok := meta ["snapshot.json" ]; ok {
415
441
snapshot := & data.Snapshot {}
416
442
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
418
448
}
419
- c .snapshotVer = snapshot .Version
420
449
}
421
450
422
451
if targetsJSON , ok := meta ["targets.json" ]; ok {
423
452
targets := & data.Targets {}
424
453
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 )
426
462
}
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 )
431
463
}
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
+ }
434
468
return nil
435
469
}
436
470
@@ -660,6 +694,7 @@ func (c *Client) downloadMetaFromSnapshot(name string, m data.SnapshotFileMeta)
660
694
if err != nil {
661
695
return nil , err
662
696
}
697
+ // 5.6.2 and 5.6.4 - Check against snapshot role's targets hash and version
663
698
if err := util .SnapshotFileMetaEqual (meta , m ); err != nil {
664
699
return nil , ErrDownloadFailed {name , err }
665
700
}
@@ -676,6 +711,7 @@ func (c *Client) downloadMetaFromTimestamp(name string, m data.TimestampFileMeta
676
711
if err != nil {
677
712
return nil , err
678
713
}
714
+ // 5.5.2 and 5.5.4 - Check against timestamp role's snapshot hash and version
679
715
if err := util .TimestampFileMetaEqual (meta , m ); err != nil {
680
716
return nil , ErrDownloadFailed {name , err }
681
717
}
@@ -697,20 +733,53 @@ func (c *Client) decodeRoot(b json.RawMessage) error {
697
733
// root and targets file meta.
698
734
func (c * Client ) decodeSnapshot (b json.RawMessage ) (data.SnapshotFiles , error ) {
699
735
snapshot := & data.Snapshot {}
736
+ // 5.5.(3 and 6) - Verify it's signed correctly and it's not expired
700
737
if err := c .db .Unmarshal (b , snapshot , "snapshot" , c .snapshotVer ); err != nil {
701
738
return data.SnapshotFiles {}, ErrDecodeFailed {"snapshot.json" , err }
702
739
}
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
704
771
return snapshot .Meta , nil
705
772
}
706
773
707
774
// decodeTargets decodes and verifies targets metadata, sets c.targets and
708
775
// returns updated targets.
709
776
func (c * Client ) decodeTargets (b json.RawMessage ) (data.TargetFiles , error ) {
710
777
targets := & data.Targets {}
778
+ // 5.6.(3 and 5) - Verify signatures and check against freeze attack
711
779
if err := c .db .Unmarshal (b , targets , "targets" , c .targetsVer ); err != nil {
712
780
return nil , ErrDecodeFailed {"targets.json" , err }
713
781
}
782
+ // Generate a list with the updated targets
714
783
updatedTargets := make (data.TargetFiles )
715
784
for path , meta := range targets .Targets {
716
785
if local , ok := c .targets [path ]; ok {
@@ -720,7 +789,7 @@ func (c *Client) decodeTargets(b json.RawMessage) (data.TargetFiles, error) {
720
789
}
721
790
updatedTargets [path ] = meta
722
791
}
723
- c .targetsVer = targets . Version
792
+ // c.targetsVer was already updated when we verified the snapshot metadata
724
793
// FIXME(TUF-0.9) temporarily support files with leading path separators.
725
794
// c.targets = targets.Targets
726
795
c .loadTargets (targets .Targets )
@@ -734,7 +803,15 @@ func (c *Client) decodeTimestamp(b json.RawMessage) (data.TimestampFileMeta, err
734
803
if err := c .db .Unmarshal (b , timestamp , "timestamp" , c .timestampVer ); err != nil {
735
804
return data.TimestampFileMeta {}, ErrDecodeFailed {"timestamp.json" , err }
736
805
}
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
737
813
c .timestampVer = timestamp .Version
814
+ c .snapshotVer = timestamp .Meta ["snapshot.json" ].Version
738
815
return timestamp .Meta ["snapshot.json" ], nil
739
816
}
740
817
0 commit comments