From 624bcbacb9ca649673b7db843edfd9f119c49e9b Mon Sep 17 00:00:00 2001 From: marinamoore Date: Thu, 17 Sep 2020 12:30:05 -0700 Subject: [PATCH 01/13] Add initial draft of tap introducing snapshot merkle trees Signed-off-by: marinamoore --- candidate-snapshot-merkle-tap.md | 244 +++++++++++++++++++++++++++++++ merkletap-1.jpg | Bin 0 -> 31572 bytes 2 files changed, 244 insertions(+) create mode 100644 candidate-snapshot-merkle-tap.md create mode 100644 merkletap-1.jpg diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md new file mode 100644 index 00000000..8cf49a97 --- /dev/null +++ b/candidate-snapshot-merkle-tap.md @@ -0,0 +1,244 @@ +* TAP: +* Title: Snapshot Merkle Trees +* Version: 0 +* Last-Modified: 17/09/2020 +* Author: Marina Moore, Justin Cappos +* Type: Standardization +* Status: Draft +* Content-Type: markdown +* Created: 14/09/2020 +* +TUF-Version: +* +Post-History: + + # Abstract + + To optimize the snapshot metadata file size for large registries, registries + can use a snapshot Merkle tree to conceptually store version information about + all images in a single snapshot without needing to distribute this entire + snapshot to all clients. First, the client retrieves only a timestamp file, + which changes according to some period p (such as every day or week). Second, + the snapshot file is itself kept as a Merkle tree, with the root stored in + timestamp metadata. This snapshot file is broken into a file for each target + that contains the Merkle tree leaf with information about that target and a + path to the root of the Merkle tree. A new snapshot Merkle tree is generated + every time a new timestamp is generated. To prove that there has not been a + reversion of the snapshot Merkle tree when downloading an image, the client + and third-party auditors download the prior snapshot Merkle trees and check + that the version numbers did not decrease at any point. To make this scalable + as the number of timestamps increases, the client will only download version + information signed by the current timestamp file. Thus, rotating this key + enables the registry to discard old snapshot Merkle tree data. + +The feature described in this TAP does not need to be implemented by all TUF +implementations. It is an option for any adopter who is interested in the +benefits provided by this feature, but may not make sense for implementations +with fewer target files. + +# Motivation + +For very large repositories, the snapshot metadata file could get very large. +This snapshot metadata file must be downloaded on every update cycle, and so +could significantly impact the metadata overhead. For example, if a repository +has 50,000,000 targets, the snapshot metadata will be about 380,000,000 bytes +(https://docs.google.com/spreadsheets/d/18iwWnWvAAZ4In33EWJBgdAWVFE720B_z0eQlB4FpjNc/edit?ts=5ed7d6f4#gid=0). +For this reason, it is necessary to create a more scalable solution for snapshot +metadata that does not significantly impact the security properties of TUF. + +We designed a new approach to snapshot that improves scalability while +achieving similar security properties to the existing snapshot metadata + + +# Rationale + +Snapshot metadata provides a consistent view of the repository in order to +protect against mix-and-match attacks and rollback attacks. In order to provide +these protections, snapshot metadata is responsible for keeping track of the +version number of each target file, ensuring that all targets downloaded are +from the same snapshot, and ensuring that no target file decreases its version +number (except in the case of fast forward attack recovery). Any new solution +we develop must provide these same protections. + +A snapshot Merkle tree manages version information for each target by including +this information in each leaf node. By using a Merkle tree to store these nodes, +this proposal can cryptographically verify that different targets are from the +same snapshot by ensuring that the Merkle tree roots match. Due to the +properties of secure hash functions, any two leaves of a Merkle tree with the +same root are from the same tree. + +In order to prevent rollback attacks between Merkle trees, this proposal +introduces third-party auditors. These auditors are responsible for downloading +all nodes of each Merkle tree to ensure that no version numbers have decreased +between generated trees. This achieves rollback protection without every client +having to store the version information for every target. + +# Specification + +This proposal replaces the single snapshot metadata file with a snapshot Merkle +metadata file for each target. The repository generates these snapshot Merkle +metadata files by building a Merkle tree using all target files and storing the +path to each target in the snapshot Merkle metadata. The root of this Merkle +tree is stored in timestamp metadata to allow for client verification. The +client uses the path stored in the snapshot Merkle metadata for a target, along +with the root of the Merkle tree, to ensure that metadata is from the given +Merkle tree. The details of these files and procedures are described in +this section. + +![Diagram of snapshot Merkle tree](merkletap-1.jpg) + +## Merkle tree generation + +When the repository generates snapshot metadata, instead of putting the version +information for all targets into a single file, it instead uses the version +information to generate a Merkle tree. Each target’s version information forms +a leaf of the tree, then these leaves are used to build a Merkle tree. The +internal nodes of a Merkle tree contain the hash of the leaf nodes. The exact +algorithm for generating this Merkle tree (ie the order of leaves in the hash, +how version information is encoded), is left to the implementer, but this +algorithm should be documented in a POUF so that implementations can be +compatible and correctly verify Merkle tree data. However, all implementations +should meet the following requirements: +* Leaf nodes must be unique. A unique identifier of the target, such as the +filepath or hash must be included in the leaf data to ensure that no two leaf +node hashes are the same. +* The tree must be a Merkle tree. Each internal node must contain a hash that +includes both leaf nodes. + +Once the Merkle tree is generated, the repository must create a snapshot Merkle +metadata file for each target. This file must contain the leaf contents and +the path to the root of the Merkle tree. This path must contain the hashes of +sibling nodes needed to reconstruct the tree during verification (see diagram). +In addition the path should contain direction information so that the client +will know whether each node is a left or right sibling when reconstructing the +tree. + +This information will be included in the following metadata format: +``` +{ “leaf_contents”: {METAFILES}, + “Merkle_path”: {INDEX:HASH} + “path_directions”:{INDEX:DIR} +} +``` + +Where `METAFILES` is the version information as defined for snapshot metadata, +`INDEX` provides the ordering of nodes, `HASH` is the hash of the sibling node, +and `DIR` indicates whether the given node is a left or right sibling. + +In addition, the following optional field will be added to timestamp metadata. +If this field is included, the client should use snapshot Merkle metadata to +verify updates instead: + +``` +("merkle_root": ROOT_HASH) +``` + +Where `ROOT_HASH` is the hash of the Merkle tree root. + +Note that snapshot Merkle metadata files do not need to be signed by a snapshot +key because the path information will be verified based on the Merkle root +provided in timestamp. Removing these signatures will provide additional space +savings for clients. + +## Merkle tree verification + +If a client sees the `merkle_root` field in timestamp metadata, they will use +the snapshot Merkle metadata to check version information. If this field is +present, the client will download the snapshot Merkle metadata file only for +the target the client is attempting to update. The client will verify the +snapshot Merkle metadata file by reconstructing the Merkle tree and comparing +the computed root hash to the hash provided in timestamp metadata. If the +hashes do not match, the snapshot Merkle metadata is invalid. Otherwise, the +client will use the version information in the verified snapshot Merkle +metadata to proceed with the update. + +For additional rollback protection, the client may download previous versions +of the snapshot Merkle metadata for the given target file. After verifying +these files, the client should compare the version information in the previous +Merkle trees to the information in the current Merkle tree to ensure that the +version numbers have never decreased. In order to allow for fast forward attack +recovery (discussed further in Security Analysis), the client should only +download previous versions that were signed with the same timestamp key. + +## Auditing Merkle trees + +In order to ensure the validity of all target version information in the +Merkle tree, third-party auditors should validate the entire tree each time it +is updated. Auditors should download every snapshot Merkle file, verify the +paths, check the root hash against the hash provided in timestamp metadata, +and ensure that the version information has not decreased for each target. +Alternatively, the repository may provide auditors with information about the +contents and ordering of leaf nodes so that the auditors can more efficiently +verify the entire tree. + +Auditors may provide an additional signature for timestamp metadata that +indicates that they have verified the contents of the Merkle tree whose root +is in that timestamp file. Using this signature, clients can check whether a +particular third party has approved the Merkle tree. + +## Garbage collection +When a threshold of timestamp keys are revoked and replaced, the repository no +longer needs to store snapshot Merkle files signed by the previous timestamp +key. Replacing the timestamp key is an opportunity for fast forward attack +recovery, and so all version information from before the replacement is no +longer valid. At this point, the repository may garbage collect all snapshot +Merkle metadata files. + +# Security Analysis + +This proposal impacts the snapshot metadata, so this section will discuss the +attacks that are mitigated by snapshot metadata in TUF. + +## Rollback attack + +In the event that the timestamp key is compromised, an attacker may provide an +invalid Merkle tree that contains a previous version of a target. This attack +is prevented by both the client’s verification and by auditors. When the client +verifies previous versions of the snapshot Merkle metadata for a target, they +ensure that the version number of that target has not decreased. However, if +the attacker controls the timestamp key(s) and the repository, the previous +snapshot Merkle metadata downloaded by the client may also be invalid. To +protect against this case, third party auditors store the previous version of +all metadata, and will detect when the version number decreases in a new +Merkle tree. As long as the client checks for an auditor’s verification, the +client will not install the rolled-back version of the target. + +## Fast forward attack + +If an attacker is able to compromise the timestamp key, they may arbitrarily +increase the version number of a target in the snapshot Merkle metadata. If +they increase it to a sufficiently large number (say the maximum integer value), +the client will not accept any future version of the target as the version +number will be below the previous version. To recover from this attack, +auditors and clients should not check version information from before a +timestamp key replacement. This allows a timestamp key replacement to be used +as a reset after a fast forward attack. The existing system handles fast +forward attack recovery in a similar manner, by instructing clients to delete +stored version information after a timestamp key replacement. + +## Mix and match attack + +A snapshot Merkle tree prevents mix and match attacks by ensuring that all +targets files installed come from the same snapshot Merkle tree. If all targets +have version information in the same snapshot Merkle tree, the properties of +secure hash functions ensure that these versions were part of the same snapshot. + + +# Backwards Compatibility + +This TAP is not backwards compatible. The following table describes +compatibility for clients and repositories. + +| Parties that support snapshot Merkle trees | Result | +| ------------------------------------------ | ------ | +| Client and repository support this TAP | Client and repository are compatible | +| Client supports this TAP, but repository does not | Client and repository are compatible. The timestamp metadata provided by the repository will never contain the `merkle_root` field, and so the client will not look for snapshot Merkle metadata. | +| Repository supports this TAP, but client does not | Client and repository are not compatible. If the repository uses snapshot Merkle metadata, the client will not recognise the `merkle_root` field as valid. | +| Neither client nor repository supports this TAP | Client and repository are compatible | + +# Augmented Reference Implementation + +https://github.com/theupdateframework/tuf/pull/1113/ +TODO: auditor implementation + +# Copyright + +This document has been placed in the public domain. diff --git a/merkletap-1.jpg b/merkletap-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3a6b16cbdf6b3615605c124efa385b8a5478d71f GIT binary patch literal 31572 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 q%=;E(fH3{9kiz_j7V!_u_ Date: Fri, 18 Sep 2020 09:20:04 -0700 Subject: [PATCH 02/13] Use repository instead of registry The TUF spec uses the term repository to refer to the server that stores updates. Signed-off-by: marinamoore --- candidate-snapshot-merkle-tap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md index 8cf49a97..58351ff4 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/candidate-snapshot-merkle-tap.md @@ -12,7 +12,7 @@ # Abstract - To optimize the snapshot metadata file size for large registries, registries + To optimize the snapshot metadata file size for large repositories, repositories can use a snapshot Merkle tree to conceptually store version information about all images in a single snapshot without needing to distribute this entire snapshot to all clients. First, the client retrieves only a timestamp file, From 5d579caffa92bcd090766123ddeeb1cc0dd52b9f Mon Sep 17 00:00:00 2001 From: marinamoore Date: Fri, 18 Sep 2020 09:21:12 -0700 Subject: [PATCH 03/13] Add link to tap 11 Signed-off-by: marinamoore --- candidate-snapshot-merkle-tap.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md index 58351ff4..69446e28 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/candidate-snapshot-merkle-tap.md @@ -27,7 +27,7 @@ that the version numbers did not decrease at any point. To make this scalable as the number of timestamps increases, the client will only download version information signed by the current timestamp file. Thus, rotating this key - enables the registry to discard old snapshot Merkle tree data. + enables the repository to discard old snapshot Merkle tree data. The feature described in this TAP does not need to be implemented by all TUF implementations. It is an option for any adopter who is interested in the @@ -94,7 +94,8 @@ a leaf of the tree, then these leaves are used to build a Merkle tree. The internal nodes of a Merkle tree contain the hash of the leaf nodes. The exact algorithm for generating this Merkle tree (ie the order of leaves in the hash, how version information is encoded), is left to the implementer, but this -algorithm should be documented in a POUF so that implementations can be +algorithm should be documented in a [POUF](https://github.com/theupdateframework/taps/blob/master/tap11.md) +so that implementations can be compatible and correctly verify Merkle tree data. However, all implementations should meet the following requirements: * Leaf nodes must be unique. A unique identifier of the target, such as the From 1d22fbaebb22575bf88a720d22b7f376608886e8 Mon Sep 17 00:00:00 2001 From: marinamoore Date: Fri, 18 Sep 2020 09:31:22 -0700 Subject: [PATCH 04/13] Snapshot holds targets metadata information, not targets information Signed-off-by: marinamoore --- candidate-snapshot-merkle-tap.md | 34 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md index 69446e28..fbad76fd 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/candidate-snapshot-merkle-tap.md @@ -39,8 +39,8 @@ with fewer target files. For very large repositories, the snapshot metadata file could get very large. This snapshot metadata file must be downloaded on every update cycle, and so could significantly impact the metadata overhead. For example, if a repository -has 50,000,000 targets, the snapshot metadata will be about 380,000,000 bytes -(https://docs.google.com/spreadsheets/d/18iwWnWvAAZ4In33EWJBgdAWVFE720B_z0eQlB4FpjNc/edit?ts=5ed7d6f4#gid=0). +has 50,000,000 targets metadata files, the snapshot metadata will be about +380,000,000 bytes (https://docs.google.com/spreadsheets/d/18iwWnWvAAZ4In33EWJBgdAWVFE720B_z0eQlB4FpjNc/edit?ts=5ed7d6f4#gid=0). For this reason, it is necessary to create a more scalable solution for snapshot metadata that does not significantly impact the security properties of TUF. @@ -53,12 +53,12 @@ achieving similar security properties to the existing snapshot metadata Snapshot metadata provides a consistent view of the repository in order to protect against mix-and-match attacks and rollback attacks. In order to provide these protections, snapshot metadata is responsible for keeping track of the -version number of each target file, ensuring that all targets downloaded are +version number of each targets metadata file, ensuring that all targets downloaded are from the same snapshot, and ensuring that no target file decreases its version number (except in the case of fast forward attack recovery). Any new solution we develop must provide these same protections. -A snapshot Merkle tree manages version information for each target by including +A snapshot Merkle tree manages version information for each targets metadata file by including this information in each leaf node. By using a Merkle tree to store these nodes, this proposal can cryptographically verify that different targets are from the same snapshot by ensuring that the Merkle tree roots match. Due to the @@ -69,16 +69,17 @@ In order to prevent rollback attacks between Merkle trees, this proposal introduces third-party auditors. These auditors are responsible for downloading all nodes of each Merkle tree to ensure that no version numbers have decreased between generated trees. This achieves rollback protection without every client -having to store the version information for every target. +having to store the version information for every targets metadata file. # Specification This proposal replaces the single snapshot metadata file with a snapshot Merkle -metadata file for each target. The repository generates these snapshot Merkle -metadata files by building a Merkle tree using all target files and storing the -path to each target in the snapshot Merkle metadata. The root of this Merkle -tree is stored in timestamp metadata to allow for client verification. The -client uses the path stored in the snapshot Merkle metadata for a target, along +metadata file for each targets metadata file. The repository generates these +snapshot Merkle metadata files by building a Merkle tree using all target +metadata files and storing the path to each targets metadata file in the +snapshot Merkle metadata. The root of this Merkle tree is stored in timestamp +metadata to allow for client verification. The client uses the path stored in +the snapshot Merkle metadata for a targets metadata file, along with the root of the Merkle tree, to ensure that metadata is from the given Merkle tree. The details of these files and procedures are described in this section. @@ -88,8 +89,8 @@ this section. ## Merkle tree generation When the repository generates snapshot metadata, instead of putting the version -information for all targets into a single file, it instead uses the version -information to generate a Merkle tree. Each target’s version information forms +information for all targets metadata file into a single file, it instead uses the version +information to generate a Merkle tree. Each targets metadata file's version information forms a leaf of the tree, then these leaves are used to build a Merkle tree. The internal nodes of a Merkle tree contain the hash of the leaf nodes. The exact algorithm for generating this Merkle tree (ie the order of leaves in the hash, @@ -144,7 +145,7 @@ savings for clients. If a client sees the `merkle_root` field in timestamp metadata, they will use the snapshot Merkle metadata to check version information. If this field is present, the client will download the snapshot Merkle metadata file only for -the target the client is attempting to update. The client will verify the +the targets metadata the client is attempting to update. The client will verify the snapshot Merkle metadata file by reconstructing the Merkle tree and comparing the computed root hash to the hash provided in timestamp metadata. If the hashes do not match, the snapshot Merkle metadata is invalid. Otherwise, the @@ -152,7 +153,7 @@ client will use the version information in the verified snapshot Merkle metadata to proceed with the update. For additional rollback protection, the client may download previous versions -of the snapshot Merkle metadata for the given target file. After verifying +of the snapshot Merkle metadata for the given targets metadata file. After verifying these files, the client should compare the version information in the previous Merkle trees to the information in the current Merkle tree to ensure that the version numbers have never decreased. In order to allow for fast forward attack @@ -161,11 +162,11 @@ download previous versions that were signed with the same timestamp key. ## Auditing Merkle trees -In order to ensure the validity of all target version information in the +In order to ensure the validity of all targets metadata version information in the Merkle tree, third-party auditors should validate the entire tree each time it is updated. Auditors should download every snapshot Merkle file, verify the paths, check the root hash against the hash provided in timestamp metadata, -and ensure that the version information has not decreased for each target. +and ensure that the version information has not decreased for each leaf. Alternatively, the repository may provide auditors with information about the contents and ordering of leaf nodes so that the auditors can more efficiently verify the entire tree. @@ -176,6 +177,7 @@ is in that timestamp file. Using this signature, clients can check whether a particular third party has approved the Merkle tree. ## Garbage collection + When a threshold of timestamp keys are revoked and replaced, the repository no longer needs to store snapshot Merkle files signed by the previous timestamp key. Replacing the timestamp key is an opportunity for fast forward attack From e930054f258d482aeb6d36e27c41ee93313327e9 Mon Sep 17 00:00:00 2001 From: marinamoore Date: Fri, 18 Sep 2020 10:21:12 -0700 Subject: [PATCH 05/13] Update security analysis for snapshot merkle TAP Add attack descriptions and comparison to the existing TUF specification Signed-off-by: marinamoore --- candidate-snapshot-merkle-tap.md | 58 ++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md index fbad76fd..04537cd6 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/candidate-snapshot-merkle-tap.md @@ -192,14 +192,26 @@ attacks that are mitigated by snapshot metadata in TUF. ## Rollback attack -In the event that the timestamp key is compromised, an attacker may provide an -invalid Merkle tree that contains a previous version of a target. This attack -is prevented by both the client’s verification and by auditors. When the client -verifies previous versions of the snapshot Merkle metadata for a target, they -ensure that the version number of that target has not decreased. However, if -the attacker controls the timestamp key(s) and the repository, the previous -snapshot Merkle metadata downloaded by the client may also be invalid. To -protect against this case, third party auditors store the previous version of +A rollback attack provides a client with an old, previously valid view of +the repository. Using this attack, an attacker could convince a client to +install a version from before a security patch was released. + +TUF currently protects against rollback attacks by checking the current time +signed by timestamp and ensuring that no version information provided by +snapshot has decreased since the last update. With both of these protections, +the client is secure against a rollback attack to any version released +before the previous update cycle, even if the timestamp and snapshot keys +are compromised. + +Using snapshot Merkle trees, rollback attacks are prevented by both the +client verification and by third party auditors. If no keys are compromised, +the timestamp keys protect against a rollback attack by ensuring a valid +snapshot Merkle tree. If the timestamp key is compromised, the client +verification of previous Merkle trees provides rollback protection for the +individual targets metadata files that are verified. However, if the attacker +controls the repository and timestamp keys, they may provide malicious previous +Merkle trees. For full rollback protection, clients rely on third party +auditors. Third party auditors store the previous version of all metadata, and will detect when the version number decreases in a new Merkle tree. As long as the client checks for an auditor’s verification, the client will not install the rolled-back version of the target. @@ -210,19 +222,37 @@ If an attacker is able to compromise the timestamp key, they may arbitrarily increase the version number of a target in the snapshot Merkle metadata. If they increase it to a sufficiently large number (say the maximum integer value), the client will not accept any future version of the target as the version -number will be below the previous version. To recover from this attack, -auditors and clients should not check version information from before a -timestamp key replacement. This allows a timestamp key replacement to be used -as a reset after a fast forward attack. The existing system handles fast -forward attack recovery in a similar manner, by instructing clients to delete -stored version information after a timestamp key replacement. +number will be below the previous version. + +In the current specification, repositories can recover from a fast forward +attack by replacing a threshold of timestamp keys. If the client sees that +a threshold of timestamp keys were replaced, it deletes the currently trusted +version information. + +Snapshot Merkle trees also reset snapshot information after a replacement of +a threshold of timestamp keys in order to recover from fast forward attacks. +Auditors and clients should not check version information from before a +timestamp key replacement when verifying the Merkle tree. ## Mix and match attack +In a mix and match attack, an attacker combines images from the current +snapshot with images from other snapshots, potentially introducing +vulnerabilities. + +Currently, TUF protects against mix and match attacks by providing a snapshot +metadata file that contains all targets metadata files available on the +repository. Therefore, a mix and match attack is only possible in an +attacker is able to compromise the timestamp and snapshot keys to create +a malicious snapshot metadata file. + A snapshot Merkle tree prevents mix and match attacks by ensuring that all targets files installed come from the same snapshot Merkle tree. If all targets have version information in the same snapshot Merkle tree, the properties of secure hash functions ensure that these versions were part of the same snapshot. +As in the existing specification, a mix and match attack would be possible +if an attacker was able to replace the snapshot Merkle tree using compromised +timestamp keys. # Backwards Compatibility From db0c22900d31c0827b0894cdd4401c3422ea952c Mon Sep 17 00:00:00 2001 From: marinamoore Date: Fri, 18 Sep 2020 13:08:18 -0700 Subject: [PATCH 06/13] Simplify the abstract for snapshot merkle tap Signed-off-by: marinamoore --- candidate-snapshot-merkle-tap.md | 35 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md index 04537cd6..aebe1320 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/candidate-snapshot-merkle-tap.md @@ -10,24 +10,23 @@ * +TUF-Version: * +Post-History: - # Abstract - - To optimize the snapshot metadata file size for large repositories, repositories - can use a snapshot Merkle tree to conceptually store version information about - all images in a single snapshot without needing to distribute this entire - snapshot to all clients. First, the client retrieves only a timestamp file, - which changes according to some period p (such as every day or week). Second, - the snapshot file is itself kept as a Merkle tree, with the root stored in - timestamp metadata. This snapshot file is broken into a file for each target - that contains the Merkle tree leaf with information about that target and a - path to the root of the Merkle tree. A new snapshot Merkle tree is generated - every time a new timestamp is generated. To prove that there has not been a - reversion of the snapshot Merkle tree when downloading an image, the client - and third-party auditors download the prior snapshot Merkle trees and check - that the version numbers did not decrease at any point. To make this scalable - as the number of timestamps increases, the client will only download version - information signed by the current timestamp file. Thus, rotating this key - enables the repository to discard old snapshot Merkle tree data. +# Abstract + +To optimize the snapshot metadata file size for large repositories, repositories +can use a snapshot Merkle tree to conceptually store version information about +all images in a single snapshot without needing to distribute this entire +snapshot to all clients. To do so, it puts version information for each targets +metadata file into a leaf of the Merkle tree, and distributes snapshot Merkle +files containing this leaf information and a path to the root of the Merkle tree. +Clients can then compare this information with a Merkle root provided in the +timestamp metadata. To prove that there has not been a reversion of the +snapshot Merkle tree when downloading an image, the client and third-party +auditors download the prior snapshot Merkle trees and check that the version +numbers did not decrease at any point. + +By replacing a single snapshot metadata file with individual snapshot Merkle +metadata files, this TAP reduces the metadata overhead for repositories with +large numbers of targets metadata files. The feature described in this TAP does not need to be implemented by all TUF implementations. It is an option for any adopter who is interested in the From 56554792a974acbb1c295e5e2101b435574bd253 Mon Sep 17 00:00:00 2001 From: marinamoore Date: Wed, 23 Sep 2020 10:36:13 -0700 Subject: [PATCH 07/13] Clarify client verification and auditing Add clarifications based on feedback on the pr. Signed-off-by: marinamoore --- candidate-snapshot-merkle-tap.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md index aebe1320..de000d23 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/candidate-snapshot-merkle-tap.md @@ -26,7 +26,9 @@ numbers did not decrease at any point. By replacing a single snapshot metadata file with individual snapshot Merkle metadata files, this TAP reduces the metadata overhead for repositories with -large numbers of targets metadata files. +large numbers of targets metadata files. It maintains the security protections +of the single snapshot metadata file in part through the use of third party +auditors that check for rollback attacks anywhere in the Merkle tree. The feature described in this TAP does not need to be implemented by all TUF implementations. It is an option for any adopter who is interested in the @@ -53,12 +55,13 @@ Snapshot metadata provides a consistent view of the repository in order to protect against mix-and-match attacks and rollback attacks. In order to provide these protections, snapshot metadata is responsible for keeping track of the version number of each targets metadata file, ensuring that all targets downloaded are -from the same snapshot, and ensuring that no target file decreases its version +from the same snapshot, and ensuring that no targets metadata file decreases its version number (except in the case of fast forward attack recovery). Any new solution we develop must provide these same protections. -A snapshot Merkle tree manages version information for each targets metadata file by including -this information in each leaf node. By using a Merkle tree to store these nodes, +A snapshot Merkle tree manages version information for each targets metadata +file by including this information in a leaf node for each targets metadata +file. By using a Merkle tree to store these nodes, this proposal can cryptographically verify that different targets are from the same snapshot by ensuring that the Merkle tree roots match. Due to the properties of secure hash functions, any two leaves of a Merkle tree with the @@ -115,7 +118,7 @@ tree. This information will be included in the following metadata format: ``` { “leaf_contents”: {METAFILES}, - “Merkle_path”: {INDEX:HASH} + “merkle_path”: {INDEX:HASH} “path_directions”:{INDEX:DIR} } ``` @@ -139,6 +142,11 @@ key because the path information will be verified based on the Merkle root provided in timestamp. Removing these signatures will provide additional space savings for clients. +Previous versions of snapshot Merkle metadata files using the current timestamp +key must remain available to clients and auditors. The repository may store +snapshot Merkle metadata files using consistent snapshots to facilitate +access to previous Merkle trees. + ## Merkle tree verification If a client sees the `merkle_root` field in timestamp metadata, they will use @@ -152,7 +160,8 @@ client will use the version information in the verified snapshot Merkle metadata to proceed with the update. For additional rollback protection, the client may download previous versions -of the snapshot Merkle metadata for the given targets metadata file. After verifying +of the snapshot Merkle metadata for the given targets metadata file. The client +should perform this check immediately after verifying the current Merkle tree. After verifying these files, the client should compare the version information in the previous Merkle trees to the information in the current Merkle tree to ensure that the version numbers have never decreased. In order to allow for fast forward attack @@ -175,6 +184,13 @@ indicates that they have verified the contents of the Merkle tree whose root is in that timestamp file. Using this signature, clients can check whether a particular third party has approved the Merkle tree. +An auditor should validate all versions of the Merkle tree signed by the +current timestamp key. For fast-forward attack recovery, the auditor should +not check for a rollback attack after the timestamp key +has been replaced. This means that all new auditors should check the Merkle +trees signed with the current timestamp keys before attesting to the validity +of the current Merkle tree. + ## Garbage collection When a threshold of timestamp keys are revoked and replaced, the repository no @@ -198,7 +214,8 @@ install a version from before a security patch was released. TUF currently protects against rollback attacks by checking the current time signed by timestamp and ensuring that no version information provided by snapshot has decreased since the last update. With both of these protections, -the client is secure against a rollback attack to any version released +a client that has a copy of trusted metadata is secure against a rollback +attack to any version released before the previous update cycle, even if the timestamp and snapshot keys are compromised. From f7ef3ab7ff3c122c734eae2ac631955762033534 Mon Sep 17 00:00:00 2001 From: Marina Moore Date: Fri, 22 Jan 2021 09:47:46 -0800 Subject: [PATCH 08/13] Apply edits for clarity from code review Co-authored-by: Joshua Lock --- candidate-snapshot-merkle-tap.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md index de000d23..ecbe8a74 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/candidate-snapshot-merkle-tap.md @@ -77,7 +77,7 @@ having to store the version information for every targets metadata file. This proposal replaces the single snapshot metadata file with a snapshot Merkle metadata file for each targets metadata file. The repository generates these -snapshot Merkle metadata files by building a Merkle tree using all target +snapshot Merkle metadata files by building a Merkle tree using all targets metadata files and storing the path to each targets metadata file in the snapshot Merkle metadata. The root of this Merkle tree is stored in timestamp metadata to allow for client verification. The client uses the path stored in @@ -91,24 +91,24 @@ this section. ## Merkle tree generation When the repository generates snapshot metadata, instead of putting the version -information for all targets metadata file into a single file, it instead uses the version +information for all targets metadata files into a single file, it instead uses the version information to generate a Merkle tree. Each targets metadata file's version information forms a leaf of the tree, then these leaves are used to build a Merkle tree. The -internal nodes of a Merkle tree contain the hash of the leaf nodes. The exact -algorithm for generating this Merkle tree (ie the order of leaves in the hash, -how version information is encoded), is left to the implementer, but this +internal nodes of a Merkle tree contain the hash of their child nodes. The exact +algorithm for generating this Merkle tree (ie the order of nodes in the hash, +how version information is encoded, etc.), is left to the implementer, but this algorithm should be documented in a [POUF](https://github.com/theupdateframework/taps/blob/master/tap11.md) so that implementations can be compatible and correctly verify Merkle tree data. However, all implementations should meet the following requirements: -* Leaf nodes must be unique. A unique identifier of the target, such as the -filepath or hash must be included in the leaf data to ensure that no two leaf +* Leaf nodes must be unique. A unique identifier of the targets metadata – such as the +filepath, filename, or the hash of the content – must be included in the leaf data to ensure that no two leaf node hashes are the same. * The tree must be a Merkle tree. Each internal node must contain a hash that -includes both leaf nodes. +includes both child nodes. Once the Merkle tree is generated, the repository must create a snapshot Merkle -metadata file for each target. This file must contain the leaf contents and +metadata file for each targets metadata file. This file must contain the leaf contents and the path to the root of the Merkle tree. This path must contain the hashes of sibling nodes needed to reconstruct the tree during verification (see diagram). In addition the path should contain direction information so that the client @@ -135,7 +135,7 @@ verify updates instead: ("merkle_root": ROOT_HASH) ``` -Where `ROOT_HASH` is the hash of the Merkle tree root. +Where `ROOT_HASH` is the hash of the Merkle tree's root node. Note that snapshot Merkle metadata files do not need to be signed by a snapshot key because the path information will be verified based on the Merkle root @@ -195,7 +195,7 @@ of the current Merkle tree. When a threshold of timestamp keys are revoked and replaced, the repository no longer needs to store snapshot Merkle files signed by the previous timestamp -key. Replacing the timestamp key is an opportunity for fast forward attack +keys. Replacing the timestamp keys is an opportunity for fast forward attack recovery, and so all version information from before the replacement is no longer valid. At this point, the repository may garbage collect all snapshot Merkle metadata files. From 82891d5cffc64dcb833b6821074df2e20df4e981 Mon Sep 17 00:00:00 2001 From: Marina Moore Date: Fri, 22 Jan 2021 10:12:04 -0800 Subject: [PATCH 09/13] Add clarifications based on review This commit adds clarifications to: * The size of snapshot merkle metadata * Information included in the merkle metadata * Fast forward attack recovery Signed-off-by: Marina Moore --- candidate-snapshot-merkle-tap.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/candidate-snapshot-merkle-tap.md b/candidate-snapshot-merkle-tap.md index ecbe8a74..eaa1b2e0 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/candidate-snapshot-merkle-tap.md @@ -46,7 +46,9 @@ For this reason, it is necessary to create a more scalable solution for snapshot metadata that does not significantly impact the security properties of TUF. We designed a new approach to snapshot that improves scalability while -achieving similar security properties to the existing snapshot metadata +achieving similar security properties to the existing snapshot metadata. +Using this new approach, a repository with 50,000,000 targets metadata files +would only require the user to download about 800 bytes of snapshot metadata (https://docs.google.com/spreadsheets/d/18iwWnWvAAZ4In33EWJBgdAWVFE720B_z0eQlB4FpjNc/edit?ts=5ed7d6f4#gid=924553486). # Rationale @@ -110,9 +112,10 @@ includes both child nodes. Once the Merkle tree is generated, the repository must create a snapshot Merkle metadata file for each targets metadata file. This file must contain the leaf contents and the path to the root of the Merkle tree. This path must contain the hashes of -sibling nodes needed to reconstruct the tree during verification (see diagram). +nodes needed to reconstruct the tree during verification, including the leaf's +sibling (see diagram). In addition the path should contain direction information so that the client -will know whether each node is a left or right sibling when reconstructing the +will know whether each listed node is a left or right sibling when reconstructing the tree. This information will be included in the following metadata format: @@ -166,7 +169,7 @@ these files, the client should compare the version information in the previous Merkle trees to the information in the current Merkle tree to ensure that the version numbers have never decreased. In order to allow for fast forward attack recovery (discussed further in Security Analysis), the client should only -download previous versions that were signed with the same timestamp key. +download previous versions whose root hashes were signed for with the same timestamp key. ## Auditing Merkle trees From 83f989f1f4235f91ee2fb083710db17b1f0274a7 Mon Sep 17 00:00:00 2001 From: Marina Moore Date: Fri, 22 Jan 2021 10:16:18 -0800 Subject: [PATCH 10/13] Assign TAP number to Snapshot Merke Tree TAP Signed-off-by: Marina Moore --- README.md | 1 + candidate-snapshot-merkle-tap.md => tap16.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) rename candidate-snapshot-merkle-tap.md => tap16.md (99%) diff --git a/README.md b/README.md index 4fd788a6..526cf53e 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ * [TAP 12: Improving keyid flexibility](tap12.md) * [TAP 14: Managing TUF Versions](tap14.md) * [TAP 15: Succinct hashed bin delegations](tap15.md) +* [TAP 16: Snapshot Merkle Trees](tap16.md) ## Rejected diff --git a/candidate-snapshot-merkle-tap.md b/tap16.md similarity index 99% rename from candidate-snapshot-merkle-tap.md rename to tap16.md index eaa1b2e0..dbf9fdc9 100644 --- a/candidate-snapshot-merkle-tap.md +++ b/tap16.md @@ -1,7 +1,7 @@ -* TAP: +* TAP: 16 * Title: Snapshot Merkle Trees * Version: 0 -* Last-Modified: 17/09/2020 +* Last-Modified: 22/01/2021 * Author: Marina Moore, Justin Cappos * Type: Standardization * Status: Draft From 2319f9c83709a4a3539be474cb9b8f1c451faf47 Mon Sep 17 00:00:00 2001 From: Marina Moore Date: Fri, 22 Jan 2021 10:29:29 -0800 Subject: [PATCH 11/13] Add summary to security analysis Add a quick comparison to the existing specification to each section of the security analysis to make it clear what the differences are with and without auditors. Signed-off-by: Marina Moore --- tap16.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tap16.md b/tap16.md index dbf9fdc9..8e73a079 100644 --- a/tap16.md +++ b/tap16.md @@ -235,6 +235,10 @@ all metadata, and will detect when the version number decreases in a new Merkle tree. As long as the client checks for an auditor’s verification, the client will not install the rolled-back version of the target. +In summary, without auditors, a client is vulnerable to rollback attacks when an attacker +controls the timestamp key. With auditors, the client has the same rollback +protection as the existing TUF specification. + ## Fast forward attack If an attacker is able to compromise the timestamp key, they may arbitrarily @@ -253,6 +257,10 @@ a threshold of timestamp keys in order to recover from fast forward attacks. Auditors and clients should not check version information from before a timestamp key replacement when verifying the Merkle tree. +Thus, fast forward attack recovery with snapshot Merkle trees is the same +as in the existing specification, but must be performed by both clients and +auditors. + ## Mix and match attack In a mix and match attack, an attacker combines images from the current @@ -273,6 +281,9 @@ As in the existing specification, a mix and match attack would be possible if an attacker was able to replace the snapshot Merkle tree using compromised timestamp keys. +Snapshot Merkle trees provide the same protection against mix and match attacks +as the existing specification. + # Backwards Compatibility From 32d214b8961827012b630b44735183874234c0c6 Mon Sep 17 00:00:00 2001 From: Marina Moore Date: Fri, 22 Jan 2021 10:34:30 -0800 Subject: [PATCH 12/13] Condense the abstract Credit to @joshuagl for helping with the text here. This commit makes the abstract shorter and more in line with the requirements of TAP 1. Signed-off-by: Marina Moore --- tap16.md | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/tap16.md b/tap16.md index 8e73a079..826ea78d 100644 --- a/tap16.md +++ b/tap16.md @@ -12,28 +12,15 @@ # Abstract -To optimize the snapshot metadata file size for large repositories, repositories -can use a snapshot Merkle tree to conceptually store version information about -all images in a single snapshot without needing to distribute this entire -snapshot to all clients. To do so, it puts version information for each targets -metadata file into a leaf of the Merkle tree, and distributes snapshot Merkle -files containing this leaf information and a path to the root of the Merkle tree. -Clients can then compare this information with a Merkle root provided in the -timestamp metadata. To prove that there has not been a reversion of the -snapshot Merkle tree when downloading an image, the client and third-party -auditors download the prior snapshot Merkle trees and check that the version -numbers did not decrease at any point. - -By replacing a single snapshot metadata file with individual snapshot Merkle -metadata files, this TAP reduces the metadata overhead for repositories with -large numbers of targets metadata files. It maintains the security protections -of the single snapshot metadata file in part through the use of third party -auditors that check for rollback attacks anywhere in the Merkle tree. - -The feature described in this TAP does not need to be implemented by all TUF -implementations. It is an option for any adopter who is interested in the -benefits provided by this feature, but may not make sense for implementations -with fewer target files. +Snapshot metadata for repositories with a high number of targets +metadata files (through significant use of delegations), can become +prohibitively large. Due to the need to download the snapshot file on every +update cycle, very large snapshot metadata files can become a significant +overhead for TUF clients. + +This TAP proposes a method for reducing the size of snapshot metadata a client +must download without significantly weakening the security properties of TUF. + # Motivation From b4f7f8051a4eb3f2624e9a7916a6535244f283c0 Mon Sep 17 00:00:00 2001 From: Marina Moore Date: Fri, 22 Jan 2021 10:46:58 -0800 Subject: [PATCH 13/13] Add section for client interaction with auditors Add a section that describes a few options for how clients can verify that a Merkle tree has been verified by an auditor. Signed-off-by: Marina Moore --- tap16.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tap16.md b/tap16.md index 826ea78d..145922b7 100644 --- a/tap16.md +++ b/tap16.md @@ -169,11 +169,6 @@ Alternatively, the repository may provide auditors with information about the contents and ordering of leaf nodes so that the auditors can more efficiently verify the entire tree. -Auditors may provide an additional signature for timestamp metadata that -indicates that they have verified the contents of the Merkle tree whose root -is in that timestamp file. Using this signature, clients can check whether a -particular third party has approved the Merkle tree. - An auditor should validate all versions of the Merkle tree signed by the current timestamp key. For fast-forward attack recovery, the auditor should not check for a rollback attack after the timestamp key @@ -181,6 +176,25 @@ has been replaced. This means that all new auditors should check the Merkle trees signed with the current timestamp keys before attesting to the validity of the current Merkle tree. +## Client interaction with auditors + +Clients must ensure that snapshot Merkle trees have been verified by an auditor. +To do so, implementations may use a few different mechanisms: + +* Auditors may provide an additional signature for timestamp metadata that +indicates that they have verified the contents of the Merkle tree whose root +is in that timestamp file. Using this signature, clients can check whether a +particular third party has approved the Merkle tree. To use this mechanism, +the auditor's key should be included in the root metadata. + +* Auditors may host a list of verified Merkle roots for a given repository, +signed by the auditor's key. Clients may be configured with the auditor's key, +or get it from the root metadata. + +* Clients may use a secure API to verify that a given Merkle root has been +verified by an auditor. This API should provide compromise resilience similar to +TUF's root metadata. + ## Garbage collection When a threshold of timestamp keys are revoked and replaced, the repository no