diff --git a/tests/repository_data/repository/metadata/root_sequence/1.root.json b/tests/repository_data/repository/metadata/root_sequence/1.root.json new file mode 100644 index 0000000000..214d8db01b --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/1.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "a337d6375fedd2eabfcd6c2ef6c8a9c3bb85dc5a857715f6a6bd41123e7670c4972d8548bcd7248154f3d864bf25f1823af59d74c459f41ea09a02db057ca1245612ebbdb97e782c501dc3e094f7fa8aa1402b03c6ed0635f565e2a26f9f543a89237e15a2faf0c267e2b34c3c38f2a43a28ddcdaf8308a12ead8c6dc47d1b762de313e9ddda8cc5bc25aea1b69d0e5b9199ca02f5dda48c3bff615fd12a7136d00634b9abc6e75c3256106c4d6f12e6c43f6195071355b2857bbe377ce028619b58837696b805040ce144b393d50a472531f430fadfb68d3081b6a8b5e49337e328c9a0a3f11e80b0bc8eb2dc6e78d1451dd857e6e6e6363c3fd14c590aa95e083c9bfc77724d78af86eb7a7ef635eeddaa353030c79f66b3ba9ea11fab456cfe896a826fdfb50a43cd444f762821aada9bcd7b022c0ee85b8768f960343d5a1d3d76374cc0ac9e12a500de0bf5d48569e5398cadadadab045931c398e3bcb6cec88af2437ba91959f956079cbed159fed3938016e6c3b5e446131f81cc5981" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 1 + } +} \ No newline at end of file diff --git a/tests/repository_data/repository/metadata/root_sequence/2.root.json b/tests/repository_data/repository/metadata/root_sequence/2.root.json new file mode 100644 index 0000000000..0f16a65ac9 --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/2.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "932485fe7318ab48317dde790ef6cb22cce3e44525b1e8e06a5f8e2c61ec2517ae745993df97c2dfe0024a61bb762e0f5405b2125f49005ea17596706957bf504582f3abf5187662c27fb475aaedcfa84eea31709ec325e17023a66bd6cc5ec32b5f669d677516804dbbf9f8599f166f2699af8f2b6ba1f21bf3ddbbfefa5e16266c5b6ef9d4d72926d5a7cf35b8da1fba4bc057df32127d7058254f6c45f5ca2fb6486c1737d0bc9d698e8f00edb9ed5b2abcc091c9909f61cedc69dca4df4a3ba6e2b8958725eac519c486dfe1dde4928d47cf826ee0065a2169ad4d511b11c6c454b73165e6f1ad904f445a6e9c3e502a0d785b34c4ad52a99733a81ab80664618cd500ee87832e5185b2f08c278603a5d56114cfdc9a6e171df73b8f074745d97851ee0a55be11265b9480aa830583ca3cfed70bf6371976fbc48d77bcef40f2a6aacdc3c4b9fabace5835a85904e921eb1a5cae4f2409e3b15f693497cbd1a82c6cd3fc6d22c159e3af0e742d4da417dd54347ec189eede78e08664beb1" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 2 + } +} \ No newline at end of file diff --git a/tests/repository_data/repository/metadata/root_sequence/3.root.json b/tests/repository_data/repository/metadata/root_sequence/3.root.json new file mode 100644 index 0000000000..42ea1c5bd8 --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/3.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "bc0c34f49f4c6fa7a0c5f5760ef55b1b16c768701189d3376843657f339c39bb587d2d48d6a1d6c973dfcaf4a53e2fad6fc0a74147527662856aac738c2c2ecbe8794d0ed175bea94546c563957274e07a07fc5c11e0b193a711dd6ee9f7f288aca720194137b99acf145a2e5ab4daf8fb0e575098c09614a9a4ea500c20f31ab062e4c622578d82a4f874dbb9ca8a39314a575883adbd6a8cf548754fc720b3353d835af525c887335480edf84b6c9210438f97140432dfe099a9a6b5ab4e134c13979f74b9c38899aa5e9624b040a3a96a1298b2ef8c75996e7ce5ffefb9396bd8be90deb4ad0508af8806212b8316161c44d6f117b98506d73116399a278075b3271e106ba7854dc5ef759f3b7823e3b015f72a65cbab22ec818cf7f784cbaffdbeecc6c0e1e6f5c5b8dd60fb10e2760208ba6786ddb685b26fa505ab5ce58df00cfb4aa5db9e96cd964c80ebc59c3be9f6cc0e24a79288918ff1c4527db724c35170df416d385008ac67b452bda49b04530a5aebbcecd8a7e0f550f2d436" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 3 + } +} \ No newline at end of file diff --git a/tests/repository_data/repository/metadata/root_sequence/4.root.json b/tests/repository_data/repository/metadata/root_sequence/4.root.json new file mode 100644 index 0000000000..9db9511791 --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/4.root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "3e7d8139549f3ceb25d9eebc870df1eaf47114b098aa1f3e916a9a173278e2ba5e400e228fc6aedb6f346cbdb29ce4246869bc146863bb223d95a3fcc4fe6793ef851ee8d50458b286c1b1b95868e08cd6f5be4b588b6fc45ffc22971326ff47b7a7d32ad42f19ed31e127e3c6f1f477acc29711f68ba774c05aeb436539148e3d5a022234f21d397dccccc336edda1cf36c40101e17509fd655f83a704b0afbf851c56db717965148943684894d0908faf427e800c8e88b0dc7eaa956cd0ca4bd047d4b6e09edc66fd090f520bd56abd14ffc6e0a101d0febb8ce84a9d621bb84a816b2a0f9b0544ba9bdd91e96f314397966624dfe2e2dbb26ff3693c782ea5b81df7868a81db12cf0e968e61096fc1e66ad4b0a9552ce2e16d837da7f69d488bee291520c135d9d22eb3d4b0936a9a7fd2f87dce632b8368a2fdf79308c01085ac97abe7dc57b5d65d05529fb780d9cf3784583acc6aa72b3fa308dff9da1314747cd09be78f43014c8fd2efbd0916daa81f012224c4b80e6adfcea1cf9ef" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "1.0.0", + "version": 4 + } +} \ No newline at end of file diff --git a/tests/repository_data/repository/metadata/root_sequence/broken-root.json b/tests/repository_data/repository/metadata/root_sequence/broken-root.json new file mode 100644 index 0000000000..2ff92755ad --- /dev/null +++ b/tests/repository_data/repository/metadata/root_sequence/broken-root.json @@ -0,0 +1,87 @@ +{ + "signatures": [ + { + "keyid": "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb", + "sig": "3e7d8139549f3ceb25d9eebc870df1eaf47114b098aa1f3e916a9a173278e2ba5e400e228fc6aedb6f346cbdb29ce4246869bc146863bb223d95a3fcc4fe6793ef851ee8d50458b286c1b1b95868e08cd6f5be4b588b6fc45ffc22971326ff47b7a7d32ad42f19ed31e127e3c6f1f477acc29711f68ba774c05aeb436539148e3d5a022234f21d397dccccc336edda1cf36c40101e17509fd655f83a704b0afbf851c56db717965148943684894d0908faf427e800c8e88b0dc7eaa956cd0ca4bd047d4b6e09edc66fd090f520bd56abd14ffc6e0a101d0febb8ce84a9d621bb84a816b2a0f9b0544ba9bdd91e96f314397966624dfe2e2dbb26ff3693c782ea5b81df7868a81db12cf0e968e61096fc1e66ad4b0a9552ce2e16d837da7f69d488bee291520c135d9d22eb3d4b0936a9a7fd2f87dce632b8368a2fdf79308c01085ac97abe7dc57b5d65d05529fb780d9cf3784583acc6aa72b3fa308dff9da1314747cd09be78f43014c8fd2efbd0916daa81f012224c4b80e6adfcea1cf9ef" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": false, + "expires": "2030-01-01T00:00:00Z", + "keys": { + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0GjPoVrjS9eCqzoQ8VRe\nPkC0cI6ktiEgqPfHESFzyxyjC490Cuy19nuxPcJuZfN64MC48oOkR+W2mq4pM51i\nxmdG5xjvNOBRkJ5wUCc8fDCltMUTBlqt9y5eLsf/4/EoBU+zC4SW1iPU++mCsity\nfQQ7U6LOn3EYCyrkH51hZ/dvKC4o9TPYMVxNecJ3CL1q02Q145JlyjBTuM3Xdqsa\nndTHoXSRPmmzgB/1dL/c4QjMnCowrKW06mFLq9RAYGIaJWfM/0CbrOJpVDkATmEc\nMdpGJYDfW/sRQvRdlHNPo24ZW7vkQUCqdRxvnTWkK5U81y7RtjLt1yskbWXBIbOV\nz94GXsgyzANyCT9qRjHXDDz2mkLq+9I2iKtEqaEePcWRu3H6RLahpM/TxFzw684Y\nR47weXdDecPNxWyiWiyMGStRFP4Cg9trcwAGnEm1w8R2ggmWphznCd5dXGhPNjfA\na82yNFY8ubnOUVJOf0nXGg3Edw9iY3xyjJb2+nrsk5f3AgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd" + }, + "scheme": "ed25519" + }, + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "89f28bd4ede5ec3786ab923fd154f39588d20881903e69c7b08fb504c6750815" + }, + "scheme": "ed25519" + }, + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ed25519", + "keyval": { + "public": "82ccf6ac47298ff43bfa0cd639868894e305a99c723ff0515ae2e9856eb5bbf4" + }, + "scheme": "ed25519" + } + }, + "roles": { + "root": { + "keyids": [ + "4e777de0d275f9d28588dd9a1606cc748e548f9e22b6795b7cb3f63f98035fcb" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "65171251a9aff5a8b3143a813481cb07f6e0de4eb197c767837fe4491b739093" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "8a1c4a3ac2d515dec982ba9910c5fd79b91ae57f625b9cff25d06bf0a61c1758" + ], + "threshold": 1 + } + }, + "spec_version": "0.0.0", + "version": 4 + } + } \ No newline at end of file diff --git a/tests/test_endless_data_attack.py b/tests/test_endless_data_attack.py index 34928a93ef..e9cad94377 100755 --- a/tests/test_endless_data_attack.py +++ b/tests/test_endless_data_attack.py @@ -269,8 +269,8 @@ def test_with_tuf(self): self.repository_updater.refresh() except tuf.exceptions.NoWorkingMirrorError as exception: - for mirror_url, mirror_error in six.iteritems(exception.mirror_errors): - self.assertTrue(isinstance(mirror_error, securesystemslib.exceptions.Error)) + for _, mirror_error in six.iteritems(exception.mirror_errors): + self.assertTrue(isinstance(mirror_error, tuf.exceptions.InvalidMetadataJSONError)) else: self.fail('TUF did not prevent an endless data attack.') diff --git a/tests/test_repository_tool.py b/tests/test_repository_tool.py index 9f955f72a6..5bb26a2f80 100755 --- a/tests/test_repository_tool.py +++ b/tests/test_repository_tool.py @@ -502,6 +502,13 @@ def test_get_filepaths_in_directory(self): self.assertEqual(sorted(expected_files), sorted(metadata_files)) + for i in range(1, 5): + root_filename = str(i) + ".root.json" + expected_files.append(os.path.abspath(os.path.join('repository_data', + 'repository', 'metadata', 'root_sequence', root_filename))) + + expected_files.append(os.path.abspath(os.path.join('repository_data', + 'repository', 'metadata', 'root_sequence', 'broken-root.json'))) # Test when the 'recursive_walk' argument is True. # In this case, recursive walk should yield the same results as the diff --git a/tests/test_updater.py b/tests/test_updater.py index a9cf90b384..19b93e99ad 100755 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -1027,6 +1027,41 @@ def test_5_all_targets(self): + def test_5__chain_of_root_trust(self): + # Test normal cases. + bootstrap_root_path = os.path.join(self.repository_directory, 'metadata', + 'root_sequence', '1.root.json') + self.repository_updater._set_boostrap_root(bootstrap_root_path) + + self.repository_updater._chain_of_root_trust(current_root_version=1) + self.repository_updater._chain_of_root_trust(current_root_version=4) + + # Broken cases + + # Test with a bootstrap root file which cannot be verified. + broken_root = os.path.join(self.repository_directory, 'metadata', + 'root_sequence', 'broken-root.json') + # Give root with unsupported specification version. + self.assertRaises(tuf.exceptions.UnsupportedSpecificationError, + self.repository_updater._set_boostrap_root, broken_root) + + # Test with a current root version lower than bootstrap root version. + self.assertRaises(tuf.exceptions.BadVersionNumberError, + self.repository_updater._chain_of_root_trust, 0) + + # Test with a bootstrap root file which is not a json file. + self.assertRaises(tuf.exceptions.InvalidMetadataJSONError, + self.repository_updater._set_boostrap_root, __file__) + + # Test with a nonexistent path. + # FileNotFoundError is not available on Python2, + # instead use one of its base classes - EnvironmentError. + self.assertRaises(EnvironmentError, + self.repository_updater._set_boostrap_root, 'nonexistent_path') + + # Unset the booststrap root, so it won't be used for other tests. + self.repository_updater._set_boostrap_root(bootstrap_root_path=None) + def test_5_targets_of_role(self): @@ -1760,10 +1795,58 @@ def test_11__verify_metadata_file(self): self.assertRaises(tuf.exceptions.InvalidMetadataJSONError, self.repository_updater._verify_metadata_file, - metadata_file_object, 'root') + metadata_file_object, 'root', None) + + + + def test_12__validate_metadata_version(self): + # Test for valid metadata version with expected_version. + self.repository_updater._validate_metadata_version( + expected_version=1, metadata_role='root', version_downloaded=1) + + # Test for valid metadata version without expected_version. + self.repository_updater._validate_metadata_version( + expected_version=None, metadata_role='root', version_downloaded=1) + # Test for expected_version different than version downloaded. + self.assertRaises(tuf.exceptions.BadVersionNumberError, + self.repository_updater._validate_metadata_version, + expected_version=2, metadata_role='root', version_downloaded=1) - def test_12__get_file(self): + # Test without expected_version and version_downloaded < current_version. + self.assertRaises(tuf.exceptions.ReplayedMetadataError, + self.repository_updater._validate_metadata_version, + expected_version=None, metadata_role='root', version_downloaded=0) + + + + def test_13__validate_spec_version(self): + # Tests when metadata spec ver is compatible with tuf.SPECIFICATION_VERSION + + # metadata spec ver = tuf.SPECIFICATION_VERSION + self.repository_updater._validate_spec_version(tuf.SPECIFICATION_VERSION) + + code_spec_ver_split = tuf.SPECIFICATION_VERSION.split('.') + code_spec_major = int(code_spec_ver_split[0]) + code_spec_minor= int(code_spec_ver_split[1]) + + # metadata major ver is the same as tuf.SPECIFICATION_VERSION major ver + # but metadata minor ver != tuf.SPECIFICATION_VERSION minor ver + metadata_spec = [str(code_spec_major), str(code_spec_minor + 1), '0'] + separator = '.' + metadata_spec = separator.join(metadata_spec) + self.repository_updater._validate_spec_version(metadata_spec) + + # Test when metadata spec ver is NOT compatible + # with tuf.SPECIFICATION_VERSION + metadata_spec = [str(code_spec_major + 1), str(code_spec_minor), '0'] + metadata_spec = separator.join(metadata_spec) + self.assertRaises(tuf.exceptions.UnsupportedSpecificationError, + self.repository_updater._validate_spec_version, metadata_spec) + + + + def test_14__get_file(self): # Test for an "unsafe" download, where the file is downloaded up to # a required length (and no more). The "safe" download approach # downloads an exact required length. @@ -1783,7 +1866,9 @@ def verify_target_file(targets_path): self.repository_updater._get_file('targets.json', verify_target_file, file_type, file_size, download_safely=False).close() - def test_13__targets_of_role(self): + + + def test_15__targets_of_role(self): # Test case where a list of targets is given. By default, the 'targets' # parameter is None. targets = [{'filepath': 'file1.txt', 'fileinfo': {'length': 1, 'hashes': {'sha256': 'abc'}}}] diff --git a/tuf/client/updater.py b/tuf/client/updater.py index ccab75305b..cbdf710872 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -619,7 +619,8 @@ class Updater(object): http://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables """ - def __init__(self, repository_name, repository_mirrors): + def __init__(self, repository_name, repository_mirrors, + bootstrap_root_path=None): """ Constructor. Instantiating an updater object causes all the metadata @@ -658,6 +659,13 @@ def __init__(self, repository_name, repository_mirrors): 'metadata_path': 'metadata', 'targets_path': 'targets', 'confined_target_dirs': ['']}} + bootstrap_root_path: + Optional path to the bootstrap root metadata file. + If provided, on each refresh() call a chain of trust will be + established from the bootstrap root up to the current root file. + The bootstrap_root_path parent folder will be used to store + all root versions marked as "previous" on _update_metadata() call. + Default is None. securesystemslib.exceptions.FormatError: @@ -668,8 +676,8 @@ def __init__(self, repository_name, repository_mirrors): as a missing 'root.json' file. - Th metadata files (e.g., 'root.json', 'targets.json') for the top- level - roles are read from disk and stored in dictionaries. In addition, the + The metadata files (e.g., 'root.json', 'targets.json') for the top-level + roles are read from disk and stored in dictionaries. In addition, the key and roledb modules are populated with 'repository_name' entries. @@ -763,9 +771,39 @@ def __init__(self, repository_name, repository_mirrors): raise tuf.exceptions.RepositoryError('No root of trust!' ' Could not find the "root.json" file.') + self._set_boostrap_root(bootstrap_root_path) + def _set_boostrap_root(self, bootstrap_root_path): + """ + Internal function. Setup boostrap_root in a separate function + to provide flexibilty needed for testing. + """ + + self.bootstrap_root = bootstrap_root_path + self.bootstrap_root_folder = None + if bootstrap_root_path: + path = bootstrap_root_path + # Open as byte file, so it could be decoded in _verify_metadata_file. + with open(bootstrap_root_path, "rb") as bootstrap_root: + try: + self._verify_metadata_file(bootstrap_root, "root", + expected_version=None, ensure_not_expired=False) + + bootstrap_root.seek(0) + bootstrap_metadata = bootstrap_root.read().decode('utf-8') + + self.bootstap_signable = securesystemslib.util.load_json_string( + bootstrap_metadata) + except Exception: + filename = os.path.basename(path) + logger.warning("Failed to setup " + filename + " as boostrap root!") + raise + + self.bootstrap_root_folder = os.path.dirname(path) + + def __str__(self): """ @@ -1062,6 +1100,9 @@ def refresh(self, unsafely_update_root_if_necessary=True): logger.info('An expired Root metadata was loaded and must be updated.') raise + if self.bootstrap_root: + self._chain_of_root_trust(self.metadata['current']['root']['version']) + # Update the root metadata and verify it by building a chain of trusted root # keys from the current trusted root metadata file self._update_root_metadata(root_metadata) @@ -1087,6 +1128,47 @@ def refresh(self, unsafely_update_root_if_necessary=True): + def _chain_of_root_trust(self, current_root_version): + """ + Establishes a chain of trust between the bootstrap root + and the current root. + Raises an exception if the current_root_version is lower than + the bootstrap root version or the validation of one of + the intermediate root files failed somewhere. + """ + + low = self.bootstap_signable['signed']['version'] + up = current_root_version + + if low > up: + raise tuf.exceptions.BadVersionNumberError('Bootstrap root ver ' \ + + str(low) + ' is higher than the curr root ver ' + str(up) + '!') + + # We don't need to verify self.bootstrap_root again. + # It was verified in __init__. + # We don't need to verify the current_root. + # It was verified when _update_root_metadata was called. + for prev_version in range(low + 1, up): + prev_root_filename = str(prev_version) + ".root.json" + prev_root_path = os.path.join(self.bootstrap_root_folder, + prev_root_filename) + logger.debug('Verifying root file: ' + prev_root_filename) + + try: + # Open as byte file, so it could be decoded in _verify_metadata_file. + with open(prev_root_path, 'rb') as prev_root: + self._verify_metadata_file(prev_root, 'root', prev_version, + ensure_not_expired=False) + + except Exception: + logger.warning("Failed to verify " + prev_root_filename) + raise + + logger.info('The chain of root trust between ' + str(low) + ' and ' \ + + str(up) + ' root versions has been successfully established!') + + + def _update_root_metadata(self, current_root_metadata): """ @@ -1412,9 +1494,81 @@ def _verify_root_self_signed(self, signable): + def _validate_metadata_version(self, expected_version, metadata_role, + version_downloaded): + """ + Validates the metadata version number. + If the version number is unspecified, ensure that the version number + downloaded is greater than the currently trusted version number for + 'metadata_role'. + """ + + if expected_version is not None: + if version_downloaded != expected_version: + raise tuf.exceptions.BadVersionNumberError('Downloaded' + ' version number: ' + repr(version_downloaded) + '. Version' + ' number MUST be: ' + repr(expected_version)) + + # The caller does not know which version to download. + # Verify that the downloaded version is at least greater + # than the one locally available. + else: + try: + current_version = self.metadata['current'][metadata_role]['version'] + + if version_downloaded < current_version: + raise tuf.exceptions.ReplayedMetadataError(metadata_role, + version_downloaded, current_version) + + except KeyError: + logger.info(metadata_role + ' not available locally.') + + + + def _validate_spec_version(self, metadata_spec_version): + """ + Validates if the specification version number is supported. + It is assumed that "spec_version" is in (major.minor.fix) format, + (for example: "1.4.3") and that releases with the same major version + number maintain backward compatibility. + Consequently, if the major version number of new metadata equals our + expected major version number, the new metadata is safe to parse. + """ + + try: + metadata_spec_version_split = metadata_spec_version.split('.') + metadata_spec_major_version = int(metadata_spec_version_split[0]) + metadata_spec_minor_version = int(metadata_spec_version_split[1]) + + code_spec_version_split = tuf.SPECIFICATION_VERSION.split('.') + code_spec_major_version = int(code_spec_version_split[0]) + code_spec_minor_version = int(code_spec_version_split[1]) + + if metadata_spec_major_version != code_spec_major_version: + raise tuf.exceptions.UnsupportedSpecificationError( + 'Downloaded metadata that specifies an unsupported spec_version. ' + 'This code supports major version number: ' + + repr(code_spec_major_version) + '; however,' + 'metadata spec version is: ' + str(metadata_spec_version)) + + # report to user if minor versions do not match, continue with update + if metadata_spec_minor_version != code_spec_minor_version: + logger.info("Downloaded metadata that specifies a different minor " + + "spec_version.") + logger.info("This code has version " + tuf.SPECIFICATION_VERSION + + " and the metadata lists version number " + + str(metadata_spec_version) + ".") + logger.info("The update will continue as the major versions match.") + + except (ValueError, TypeError) as error: + six.raise_from(securesystemslib.exceptions.FormatError('Improperly' + ' formatted spec_version, which must be in major.minor.fix format'), + error) + + def _verify_metadata_file(self, metadata_file_object, - metadata_role): + metadata_role, expected_version, ensure_not_expired=True): """ Non-public method that verifies a metadata file. An exception is @@ -1429,6 +1583,15 @@ def _verify_metadata_file(self, metadata_file_object, The role name of the metadata (e.g., 'root', 'targets', 'unclaimed'). + expected_version: + An integer representing the expected and required version number + of the 'metadata_role' file downloaded. + + ensure_not_expired: + A boolean flag indicating do we need to ensure + that the metadata has not_expired. + Default is True. + securesystemslib.exceptions.FormatError: In case the metadata file is valid JSON, but not valid TUF metadata. @@ -1468,8 +1631,14 @@ def _verify_metadata_file(self, metadata_file_object, # 'securesystemslib.exceptions.FormatError' if not. tuf.formats.check_signable_object_format(metadata_signable) - # Is 'metadata_signable' expired? - self._ensure_not_expired(metadata_signable['signed'], metadata_role) + self._validate_spec_version(metadata_signable['signed']['spec_version']) + + self._validate_metadata_version(expected_version, metadata_role, + metadata_signable['signed']['version']) + + if ensure_not_expired: + # Is 'metadata_signable' expired? + self._ensure_not_expired(metadata_signable['signed'], metadata_role) # We previously verified version numbers in this function, but have since # moved version number verification to the functions that retrieve @@ -1521,8 +1690,8 @@ def _get_metadata_file(self, metadata_role, remote_filename, downloaded. expected_version: - The expected and required version number of the 'metadata_role' file - downloaded. 'expected_version' is an integer. + An integer representing the expected and required version number + of the 'metadata_role' file downloaded. tuf.exceptions.NoWorkingMirrorError: @@ -1549,85 +1718,9 @@ def _get_metadata_file(self, metadata_role, remote_filename, try: file_object = tuf.download.unsafe_download(file_mirror, upperbound_filelength) - file_object.seek(0) - - # Verify 'file_object' according to the callable function. - # 'file_object' is also verified if decompressed above (i.e., the - # uncompressed version). - metadata_signable = \ - securesystemslib.util.load_json_string(file_object.read().decode('utf-8')) - - # Determine if the specification version number is supported. It is - # assumed that "spec_version" is in (major.minor.fix) format, (for - # example: "1.4.3") and that releases with the same major version - # number maintain backwards compatibility. Consequently, if the major - # version number of new metadata equals our expected major version - # number, the new metadata is safe to parse. - try: - metadata_spec_version = metadata_signable['signed']['spec_version'] - metadata_spec_version_split = metadata_spec_version.split('.') - metadata_spec_major_version = int(metadata_spec_version_split[0]) - metadata_spec_minor_version = int(metadata_spec_version_split[1]) - - code_spec_version_split = tuf.SPECIFICATION_VERSION.split('.') - code_spec_major_version = int(code_spec_version_split[0]) - code_spec_minor_version = int(code_spec_version_split[1]) - - if metadata_spec_major_version != code_spec_major_version: - raise tuf.exceptions.UnsupportedSpecificationError( - 'Downloaded metadata that specifies an unsupported ' - 'spec_version. This code supports major version number: ' + - repr(code_spec_major_version) + '; however, the obtained ' - 'metadata lists version number: ' + str(metadata_spec_version)) - - #report to user if minor versions do not match, continue with update - if metadata_spec_minor_version != code_spec_minor_version: - logger.info("Downloaded metadata that specifies a different minor " + - "spec_version. This code has version " + - str(tuf.SPECIFICATION_VERSION) + - " and the metadata lists version number " + - str(metadata_spec_version) + - ". The update will continue as the major versions match.") - - except (ValueError, TypeError) as error: - six.raise_from(securesystemslib.exceptions.FormatError('Improperly' - ' formatted spec_version, which must be in major.minor.fix format'), - error) - - # If the version number is unspecified, ensure that the version number - # downloaded is greater than the currently trusted version number for - # 'metadata_role'. - version_downloaded = metadata_signable['signed']['version'] - - if expected_version is not None: - # Verify that the downloaded version matches the version expected by - # the caller. - if version_downloaded != expected_version: - raise tuf.exceptions.BadVersionNumberError('Downloaded' - ' version number: ' + repr(version_downloaded) + '. Version' - ' number MUST be: ' + repr(expected_version)) - - # The caller does not know which version to download. Verify that the - # downloaded version is at least greater than the one locally - # available. - else: - # Verify that the version number of the locally stored - # 'timestamp.json', if available, is less than what was downloaded. - # Otherwise, accept the new timestamp with version number - # 'version_downloaded'. - - try: - current_version = \ - self.metadata['current'][metadata_role]['version'] - if version_downloaded < current_version: - raise tuf.exceptions.ReplayedMetadataError(metadata_role, - version_downloaded, current_version) - - except KeyError: - logger.info(metadata_role + ' not available locally.') - - self._verify_metadata_file(file_object, metadata_role) + self._verify_metadata_file(file_object, metadata_role, + expected_version) except Exception as exception: # Remember the error from this mirror, and "reset" the target file. @@ -1829,6 +1922,12 @@ def _update_metadata(self, metadata_role, upperbound_filelength, version=None): if os.path.exists(current_filepath): # Previous metadata might not exist, say when delegations are added. securesystemslib.util.ensure_parent_dir(previous_filepath) + + if metadata_role == "root" and self.bootstrap_root_folder: + # Make sure we don't delete the old root metadata files. + # We will need them to establish chain of root trust. + previous_filepath = self.bootstrap_root_folder + shutil.move(current_filepath, previous_filepath) # Next, move the verified updated metadata file to the 'current' directory.