Commit graph

819 commits

Author SHA1 Message Date
Lukas Puehringer
3e249f5bdd Make Metadata a container class (WIP)
This commit performs restructuring on the recently added metadata
class model architecture, which shall be part of a new simple TUF
API.

The key change is that the Metadata class is now used as container
for inner TUF metadata (Root, Timestamp, Snapshot, Targets) instead
of serving as base class for these, that means we use 'composition'
instead of 'inheritance'. Still, in order to aggregate common
attributes of the inner Metadata (expires, version, spec_version),
we use a new baseclass 'Signed', which also corresponds to the
signed field of the outer metadata container.

Based on prior observations in TUF's sister project in-toto, this
architecture seems to more closely represent the metadata model as
it is defined in the specification (see in-toto/in-toto#98 and
in-toto/in-toto#142 for related discussions).

Note that the proposed changes require us to now access some
attributes/methods via the signed attribute of a Metadata object
and not directly on the Metadata object, but it would be possible
to add short-cuts. (see todo notes in doc header).

Further changes include:
 - Add minimal doc header with TODO notes

 - Make attributes that correspond to fields in TUF JSON metadata
public again. There doesn't seem to be a good reason to protect
them with leading underscores and use setters/getters instead, it
just adds more code.

 - Generally try to reduce code.

 - Remove keyring and consistent_snapshot attributes from metadata
   class. As discussed in #1060 they are a better fit for extra
   management code (also see #660)

- Remove sslib schema checks (see TODO notes about validation in
  doc header)

 - Drop usage of build_dict_conforming_to_schema, it seems a lot
   simpler and more explicit to just code this here.

 - ... same goes for make_metadata_fileinfo

 - Adapt tests accordingly

TODO: Document!!!
Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-07-10 16:05:53 +02:00
Trishank Karthik Kuppusamy
76cb560a46
minor edits
* classmethod for init RAMKey from file
* private class variables
* more typing for methods
* better names for arguments

Signed-off-by: Trishank Karthik Kuppusamy <trishank.kuppusamy@datadoghq.com>
2020-07-07 23:55:22 -04:00
Joshua Lock
42372872d2 Test Metadata.bump_expiration() with relativedelta
dateutil provides an interface which is much easier to reason about for
users, i.e. it provides an interface for year deltas which automatically
handles leap years. Add some tests to try and ensure that, even though it
uses standard library functionality, the metadata API can accept
dateutil.relativedelta and do the right thing.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-07-07 10:35:08 +01:00
Joshua Lock
54e1f9c03b tuf.api: drop use of dateutil
All of the functionality we need is available from the standard library
which reduces our dependency footprint. Having minimal dependencies is
especially important for update clients which often have to vendor their
dependencies.

However, dateutil.relativedelta is richer than timedelta and helps to
provide a clearer API. For example, with relativedelta it's possible
to specify a delta in years *and* dateutil will do the right thing for
leap years.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-07-07 10:35:08 +01:00
Joshua Lock
fd5732a024 tuf.api: treat all datetime's as UTC
We don't capture timezone information in the metadata, therefore we should
not capture it in the interfaces. Ensure we remove timezone information
from any datetime objects when they are assigned to the expiration
property of a Metadata object.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-07-07 10:33:40 +01:00
Teodora Sechkova
3c5e312e60 WIP added tuf.api keys tests
Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2020-07-06 15:23:26 +01:00
Joshua Lock
34a0680947 More tests tuf.api and verify data!
Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-07-03 14:42:54 +01:00
Trishank Karthik Kuppusamy
f2861bfd41
much simpler keys
Signed-off-by: Trishank Karthik Kuppusamy <trishank.kuppusamy@datadoghq.com>
2020-07-01 18:39:35 -04:00
Joshua Lock
57c98d45ac WIP tests for tuf.api.metadata
Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-07-01 17:17:33 +01:00
Teodora Sechkova
2553dff276
Update test_load_repository
Extend test_load_repository to check if targets file info is loaded
correctly.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2020-06-09 16:48:53 +03:00
Joshua Lock
5d40ffa3c4
Merge pull request #1034 from joshuagl/joshuagl/abstract-files-fixes
Fix and better test abstract files and directories support
2020-06-05 13:40:21 +01:00
Jesús Castro
f4121e8f75
Remove unused imports
Those imports are marked as a non used libraries.

Signed-off-by: Jesús Castro <x51v4n@gmail.com>
2020-06-04 19:18:33 -05:00
Joshua Lock
5e5c598769 Support abstract storage for timestamp metadata
This was erroneously absent in PR 1024, which added support for abstract
files and directories. Resolve by adding a storage_backend argument to
generate_timestamp_metadata() and using it so that the fileinfo (hashes
and length) for the snapshot file can be generated for a snapshot
metadata file on any supported storage.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-06-03 14:16:47 +01:00
Joshua Lock
d9ec10e894 Test abstract storage backend support
Add a class implementing StorageBackendInterface for testhing which
mutates filenames on put()/get(), such that trying to read the expected
file paths for TUF metadata from the local filesystem doesn't find the
files.

Use this class when creating a repository and writing metadata to test
abstract files and directories support for metadata writing.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-06-03 14:16:47 +01:00
William Woodruff
1e532e825a
tests: Fill in more returned role name use
Signed-off-by: William Woodruff <william@trailofbits.com>
2020-06-01 14:22:29 -04:00
William Woodruff
4327a980cd
tests: Use newly returned role name
Signed-off-by: William Woodruff <william@trailofbits.com>
2020-06-01 14:01:46 -04:00
Joshua Lock
4e7b7b40ea Allow generating targets metadata for non-local storage
Utilise the abstract files and directories support to enable generating
targets metadata for files which aren't necessarily locally accessible,
rather than requiring that metadata for non-local files be provided via
existing fileinfo structures.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-05-19 22:36:17 +01:00
Joshua Lock
a187377533 Make absence of fundamental roles fatal
The specification lists four fundamental roles: root, targets, snapshot
and timestamp. Loading a repository where those roles are not present
should not be supported, therefore convert debug messages on the absence
of metadata files for these fundamental roles into a RepositoryError
exception.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-05-19 22:36:17 +01:00
Joshua Lock
0c0aaa97eb Port to new securesystemslib w abstract filesystem
Switch to using the new abstract files and directories support in
securesystemslib by taking an object which implements
securesystemslib.storage.StorageBackendInterface in the Repository
constructor, passed in by tuf.repository_tool.create_new_repository() and
tuf.repository_tool.load_repository()

The Updater class in tuf.client.updater does not specify a storage backend
and instead allows the functions in securesystemslib to perform the
default action of instantiating a LocalFilesystemBackend, that is the
updater does not currently support abstract filesystem backends and always
defaults to using local storage.

Finally we drop support for tuf.settings.CONSISTENT_METHOD as it's not as
clear how different copying modes should work when the details of the
underlying storage are abstracted away.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-05-12 22:16:50 +01:00
Joshua Lock
4487a98020 Remove redundant test logic
Support for compressed files was removed in tuf v0.10.x leaving behind
some vestiges like the test logic in test_repository_lib, which is
duplicated below and carries a redundant comment, and setting compression
on in generate_project_data.py

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-05-12 22:16:38 +01:00
Martin Vrachev
c7f878b2dc Remove six.PY2 and platform checks and add warning
After a discussion with Joshua Lock, we agreed that for
Windows users it would be good to provide the option to use
SimpleHTTPRequestHandler, but still leave a warning about it,
knowing that this caused an error before.
See: 7dbb30ae10

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2020-05-07 18:06:09 +03:00
Martin Vrachev
b42ca297e5 Remove PIPE arg and make QuiteHandler the default
Passing a pipe to the subprocess, but not reading from it
conceals helpful error messages.
As the code redirects all of the stderr from the subprocess
to nowhere, the error output of the process is never read.
If we remove the PIPEs from the tests we should see some
error messages on the console/logger that can
help us understand what went wrong.

On another hand, when we stop passing stderr=subprocess.PIPE arg
to the subprocess.Popen function call there are a lot of
HTTP messages together with the helpful error messages.
One decision is to make QuietHTTPRequestHandler
the default. That way we receive the helpful error messages
without the HTTP messages.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2020-05-07 15:53:01 +03:00
Martin Vrachev
dae1a1ba66 Fix [Errno 111] Connection refused
Fixes issue: https://github.com/theupdateframework/tuf/issues/1010

When running the tests this error appears
"[Errno 111] Connection refused". After some digging Lukas
found another error "No module named tests.simple_server"
which is the root case for error 111.

With the help of Joshua Lock, we found out that the simple_server.py
was not being found because subprocess.Popen was being passed
a cwd kwarg which moved the current working directory
away from tuf/tests.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2020-05-07 15:35:33 +03:00
Teodora Sechkova
f035cf4e34
Replace os.sep with '/' in _check_path()
Use a hard-coded unix separator ('/') so that an
exception is also raised for paths starting with '/'
when executing on Windows systems.

Update test_check_path to explicitly test invalid paths
starting with Windows style separator.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2020-04-08 18:58:27 +03:00
Teodora Sechkova
a71b3c2b67
Update TUTORIAL and test_tutorial
Improve the coding style in TUTORIAL in the case
where absolute path to a file is needed to perform file system
access and at the same time is rejected by Targets methods.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2020-04-08 18:58:27 +03:00
Teodora Sechkova
57b40518c8
Update test_delegate_hashed_bins()
Test is updated to include checks for incorrect target paths.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2020-04-08 18:58:26 +03:00
Teodora Sechkova
77f5f730f3
Add and update tests for _check_path() method
- add a test for _check_path() method of Targets class.
- update all tests calling _check_path() respectively
- update test_tutorial

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2020-04-08 18:58:26 +03:00
Joshua Lock
c8174f5a9f Use consistent default value for num of hashed bins
delegate_hashed_bins() has a number_of_bins parameter which defaults to
1024. add_target_to_bin() and remove_target_from_bin() both have a
number_of_bins parameter with no default. This means that in the
(somewhat) unlikely case that someone is using the default
number_of_bins when creating hashed bins they will need know what that
default value is and pass it to add_target_to_bin() and
remove_target_from_bin().

In order to be consistent and simpler to use define the default number
of bins as a module level constant and use it as the default value for
the number_of_bins argument for each of:
* delegate_hashed_bins()
* add_target_to_bin()
* remove_target_from_bin()

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-04-02 21:26:24 +01:00
Joshua Lock
62e4364c59 Add a test for add_target_to_bin that adds a fileinfo
Add some additional checks to test_add_target_to_bin to ensure the code
to add a target passing a fileinfo is tested.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-04-01 12:10:58 +01:00
Joshua Lock
694da236ba Test at least one delegated bin has path_hash_prefixes
When testing delegate_hashed_bins to ensure that hash_path_prefixes
map to the generated name of the bin, also check to ensure that at least
one of the delegations contains one or more path_hash_prefixes.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-04-01 12:09:27 +01:00
Joshua Lock
01498049da Add test for repo without access to target files
Test the newly added functionality to:
* add a target to the repository without access to the target file on disk
* write targets metadata without access to target files on disk, by using
  the existing fileinfo data from the roledb

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-04-01 12:09:27 +01:00
Joshua Lock
085c5480a7 Only call logger once for hashed bin info
Merge the logger calls reporting information about the hashed bin
delegations into a single logger.info() call to ensure the messages
will be grouped together even when integrated into a logging system
with multiple parallel sources.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-04-01 12:09:27 +01:00
Joshua Lock
6c69daefa2 Enable passing a fileinfo to add_target[_to_bin]()
Add an additional optional parameter to add_target() and
add_target_to_bin() which is a fileinfo object matching
tuf.formats.FILEINFO_OBJECT

This parameter and the custom parameter are mutually exclusive and
thus cannot be passed at the same time.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-04-01 12:09:18 +01:00
Joshua Lock
095fd40dcb Do not check target file exists in add_target[s]()
The file isn't strictly needed on-disk at the time add_target() and
add_targets() are called and this duplicates the check for the file's
presence in write[_all]()

By removing this check we allow extra versatility in adding targets.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-04-01 12:06:50 +01:00
Joshua Lock
ab788e306a Refactor add_target_to_bin & remove_target_from_bin
Vastly simplify the implementation, using the _get_hash() and
_find_bin_for_hash() helpers added in earlier commits.

Furthermore, enable passing of the custom parameter to
add_target_to_bin() to better match add_target()

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-04-01 12:06:50 +01:00
Joshua Lock
40d1dcfa6c Add _find_bin_for_hash() helper to repository_tool
Add a helper function to determine the name of a bin that a hashed
targetfile will be delegated to.

Based sketches by Lukas Puehringer in issues #994 & #995

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-03-30 19:03:52 +01:00
Joshua Lock
e2407a1f66 Simplify delegated hash bin tests
As we are adding and removing items from the hashed bins and checking
for their presence/absence it's simplest if we being with the hashed
bins initially empty.

If we pass a list of targets when we call delgate_hashed_bins() the
delegated roles have an initial set of targets delegated to them,
which complicates testing of adding then removing a target to a
delegated bin.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-03-26 11:10:56 +00:00
Joshua Lock
790f21fefe Test to ensure delegated bin names match prefixes
Add test to ensure delegated bin names are consistent with the hash
prefixes that are delegated to the role.

This is an implicit assumption of the current implementation, the
testing of which will enable us to modify the code with greater
confidence.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-03-26 11:10:50 +00:00
lukpueh
1cf085a360
Merge pull request #988 from joshuagl/joshuagl/issue-933
Remove root from snapshot
2020-03-11 14:34:09 +01:00
lukpueh
256aef8695
Merge pull request #989 from joshuagl/logger
Use __name__ for loggers, per convention
2020-03-11 14:33:35 +01:00
Joshua Lock
3720b2358e Re-generate repository and client test metadata
Re-generate metadata to adopt the change that root.json is no longer
listed in snapshot.json

```
 # Remove repository and client data
cd tests/repository_data && rm -rf repository client
 # Generate metadata
python generate.py
 # Duplicate metadata files
cp -r client/test_repository1 client/test_repository2
 # Recover non-signed file
git checkout client/map.json
```

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-03-11 11:35:37 +00:00
Joshua Lock
4bd9b5ef6b Improve determinism of test repository generator
One of the created target files has its file permissions encoded in the
targets metadata via the custom attribute of the add_target() function.
On Linux-based OS the umask value of the environment the script is run
in can result in different octal permissions for the created file, i.e.
on Fedora the default umask is 0002 (default permissions 664) whereas
on Debian/Ubuntu the default umask is 0022 (default permissions 644).

Explicitly chown 'file1' to octal permissions 644 so that the generated
data has the same custom attributes for targets regardless of which
Linux host they are generated on.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-03-11 11:35:37 +00:00
Joshua Lock
a134db0a43 Update test repository data generator
* Fix the path referenced in the Purpose
* Change add_target() calls to pass file paths relative to targets dir

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-03-11 11:35:37 +00:00
Joshua Lock
8f13fe5add Update tests for removal of root.json from snapshot.json
Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-03-11 11:15:06 +00:00
Joshua Lock
292b18926b Use __name__ for loggers, per convention
Replace hard-coded logger names with __name__. For the most part this just uses
the standard conventions to create the same logger hierarchy as existed before.
The only real difference is that loggers created for printing during tests are
no longer part of the 'tuf' hierarchy.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-03-03 10:36:39 +00:00
Lukas Puehringer
842f843210 Remove duplicate testing simple_server.py
tests/simple_server.py was copied to tuf/scripts/ to "make testing
easier" (cf84d3f51f), although with
the current test setup the original (and recently patched to fix an
Windows/Py2 test issue) test simple_server.py can be used just as
well.

This commit:
- removes tuf/scripts/simple_server.py
  Note: that version slightly differed from the original test
  server, probably due to demands by the linter that is only executed
  on the tuf core code and not on the tests. However, for the testing
  purposes of simple_server.py these changes (i.e., `SystemRandom()`,
  `if __name__ =='__main__':`) are not necessary.
- updates the tests that used tuf.scripts.simple_server to instead
  use tests.simple_server,
- updates setup.py to not install the simple_server module as
  script, when installing tuf, as it is only a testing script and
  not meant for end-user usage.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-02-25 14:55:36 +01:00
Lukas Puehringer
7dbb30ae10 Fix failing AppVeyor Python2.7 tests
Since #885 the tests in TestUpdater and TestKeyRevocation fail on
Appveyor Python 2.7 builds. After some live debugging, it turns out
that the tests fail due to the extra amount of http requests to
the simple http server (see tests/simple_server.py) that were
added in #885.

The simple server runs in a subprocess and is re-used for the
entire TestCase. After a certain amount of requests it becomes
unresponsive. Note that neither the subprocess exits (ps -W), nor
does the port get closed (netstat -a). It just doesn't serve the
request, making it time out and fail the test.

The following script can be used to reproduce the issue (run in
tests directory):

```python
import subprocess
import requests
import random

counter = 0

port = random.randint(30000, 45000)
command = ['python', 'simple_server.py', str(port)]
server_process = subprocess.Popen(command, stderr=subprocess.PIPE)
url = 'http://localhost:'+str(port) + '/'

sess = requests.Session()

try:
  while True:
    sess.get(url, timeout=3)
    counter +=1

finally:
  print(counter)
  server_process.kill()
```

It fails repeatedly on the 69th request, but only if
`stderr=subprocess.PIPE` is passed to Popen. Given that for each
request the simple server writes about ~60 characters to stderr,
e.g. ...
```
127.0.0.1 - - [24/Feb/2020 12:01:23] "GET / HTTP/1.1" 200 -
```
... it looks a lot like a full pipe buffer of size 4096. Note that the
`bufsize` argument to Popen does not change anything.

As a simple work around we silence the test server on
Windows/Python2 to not fill the buffer.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-02-24 16:32:26 +01:00
Lukas Puehringer
1a826cb4b0 Fix tests that use non-public sslib module
Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-02-06 17:40:29 +01:00
Lukas Puehringer
67a3a7ab92 Update docs and comments in sig.py and test_sig.py
Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-01-10 11:26:07 +01:00
Lukas Puehringer
a0397c7c82 Fix signature threshold verification
Prior to this commit metadadata signature verification as provided
by `tuf.sig.verify()` and used e.g. in `tuf.client.updater` counted
multiple signatures with identical authorized keyids each
separately towards the threshold. This behavior practically
subverts the signature thresholds check.

This commit fixes the issue by counting identical authorized keyids
only once towards the threshold.

The commit further clarifies the behavior of the relevant functions
in the `sig` module, i.e. `get_signature_status` and `verify` in
their respective docstrings. And adds tests for those functions and
also for the client updater.

---

NOTE: With this commit signatures with different authorized keyids
still each count separately towards the threshold, even if the
keyids identify the same key. If this behavior is not desired, I
propose the following fix instead. It verifies uniqueness of keys
(and not keyids):

```
diff --git a/tuf/sig.py b/tuf/sig.py
index ae9bae15..5392e596 100755
--- a/tuf/sig.py
+++ b/tuf/sig.py
@@ -303,7 +303,14 @@ def verify(signable, role, repository_name='default', threshold=None,
   if threshold is None or threshold <= 0: #pragma: no cover
     raise securesystemslib.exceptions.Error("Invalid threshold: " + repr(threshold))

-  return len(good_sigs) >= threshold
+  # Different keyids might point to the same key
+  # To be safe, check against unique public key values
+  unique_good_sig_keys = set()
+  for keyid in good_sigs:
+    key = tuf.keydb.get_key(keyid, repository_name)
+    unique_good_sig_keys.add(key["keyval"]["public"])
+
+  return len(unique_good_sig_keys) >= threshold

```

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-01-09 18:54:47 +01:00