Commit graph

53 commits

Author SHA1 Message Date
Teodora Sechkova
22facb7753
Update test_api
Add tests for hash and length verification.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-06-11 11:59:09 +03:00
Jussi Kukkonen
de78251b00
Merge pull request #1423 from jku/verify-in-key
Metadata API: implement sig verification in Key, store id in key
2021-06-09 10:14:45 +03:00
Jussi Kukkonen
414dfc829f Metadata API: Move signature verification to Key
This is likely not needed by users of the API (as they are interested
in the higher level functionality "verify delegate metadata with
threshold of signatures").

Moving verify to Key makes the API cleaner because including both
"verify myself" and "verify a delegate with threshold" can look awkward
in Metadata, and because the ugly Securesystemslib integration is now
Key class implementation detail (see Key.to_securesystemslib_key()).

Also raise on verify failure instead of returning false: this was found
to confuse API users (and was arguably not a pythonic way to handle it).

* Name the function verify_signature() to make it clear what is being
  verified.
* Assume only one signature per keyid exists: see #1422
* Raise only UnsignedMetadataError (when no signatures or verify failure),
  the remaining lower level errors will be handled in #1351
* Stop using a "keystore" in tests for the public keys: everything we
  need is in metadata already

This changes API, but also should not be something API users want to
call in the future when "verify a delegate with threshold" exists.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-06-07 13:20:26 +03:00
Jussi Kukkonen
41a6daca75 Metadata API: Add id to Key
This simplifies life for API users as usually a key needs its
identifier: this is already visible in how update() becomes simpler
in the API.

The downside is that 'from_dict()' now has two arguments (so arguably
the name is not great anymore but it still does _mostly_ the same job
as other from_dicts).

This is an API change, if a minor one.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-06-07 13:20:22 +03:00
Martin Vrachev
737c249067 new API: make sure targets in Targets can be empty
This change is relevant to the new metadata class Targets.

In the specification, when describing the Targets metadata file format
and more precisely "TARGETPATH" (or targets containing the actual
target files) it's said:
"It is allowed to have a TARGETS object with no TARGETPATH elements.
This can be used to indicate that no target files are available."

If there is no "TARGETPATH" keys for the dictionary "targets", this
would mean that "Targets.targets" is {}.
Make sure we test for that.

See: https://theupdateframework.github.io/specification/latest/#targetpath

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-25 13:15:00 +03:00
Martin Vrachev
15bf88231d Remove testing "update()" with "version" only
We have tests which make sure we can use `Timestamp.update()` and
`Snapshot.update()` with MetaFile instance storing only version
(because length and hashes are optional).
Those tests were created to make sure that we are actually supporting
optional hashes and length when we call `update` for those classes, but
after we changed the `update()` signature to accept `MetaFile` instance
the tests are obsolete.
The reason is that length and hashes can be optional because of the
MetaFile implementation, no the update function itself and we have
other tests validating creating a MetaFie instance without hashes and
length.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-19 13:21:32 +03:00
Martin Vrachev
37de69050a Change "update()" argument types
Currently, when we call Targets/Snapshot/Timestamp.update() we are
passing all of the necessary values to create MetaFile/Targets File
respectively.
This is not needed, given that one of the reasons we have created
MetaFile and TargetFile is to make the API easier to use.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-19 13:21:32 +03:00
Martin Vrachev
408732f4ff Add MetaFile/TargetFile specific tests
Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-19 13:21:32 +03:00
Martin Vrachev
60bbb165a8 New API: Add TargetFile class
In the top-level metadata classes, there are complex attributes such as
"meta" in Targets and Snapshot, "key" and "roles" in Root etc.
We want to represent those complex attributes with a class to allow
easier verification and support for metadata with unrecognized fields.
For more context read ADR 0004 and ADR 0008 in the docs/adr folder.

As written in the spec "targets" in "targets.json" has defined the
"custom" field serving the same purpose as "unrecognized_fields" in the
implementation.
That's why to conform against the spec and support "custom" and allow
"unrecognized_fields" everywhere where it's not sensitive we can define
custom as property which actually access data stored in
unrecognized_fields.
For context read ADR 8 in tuf/docs/adr.

Additionally, after adding the TargetFile class, when we create a
Targets an object we are now calling from dict twice - one for the main
Targets class and one for each of the complex attributes
TargetFile.from_dict() and Delegations.from_dict().
Given that the "from_dict" methods have the side effect of destroying
the given dictionary, we would need to start using deepcopy()
for our tests.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-19 13:21:31 +03:00
Martin Vrachev
3771a77ffe New API: Add MetaFile class
In the top-level metadata classes, there are complex attributes such as
"meta" in Targets and Snapshot, "key" and "roles" in Root etc.
We want to represent those complex attributes with a class to allow
easier verification and support for metadata with unrecognized fields.
For more context read ADR 0004 and ADR 0008 in the docs/adr folder.

Additionally, after adding the MetaFile class, when we create an object
we are now calling from dict twice - one for the main class (Timestamp,
Snapshot) and one for the pacticular complex attribute -
MetaFile.from_dict(). Given that the "from_dict" methods have the
side effect of destroying the given dictionary, we would need to
start using deepcopy() for our tests.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-19 13:04:24 +03:00
Jussi Kukkonen
8348523b77
Merge pull request #1394 from MVrachev/optional-consistent-snapshot
Metadata API: Make consistent_snapshot optional
2021-05-18 21:11:57 +03:00
Martin Vrachev
de2644f3d0 Breaking: new API: consistent_snapshot optional
NOTE: making consistent_snapshot optional requires using a default value
for the argument in __init__ in Root and thus consistent_snapshot should
be rearranged in the end.
Read more: https://github.com/theupdateframework/tuf/pull/1394#issuecomment-842134961

From chapter 7 in the spec (version 1.0.17)
"Finally, the root metadata should write the Boolean
"consistent_snapshot" attribute at the root level of its keys of
attributes.
If consistent snapshots are not written by the repository,
then the attribute may either be left unspecified or be set to the
False value. Otherwise, it must be set to the True value."

We want to make sure we support repositories
without consistent_snapshot set.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-17 11:41:52 +03:00
Jussi Kukkonen
14f5957eee Metadata API: Don't do equality comparisons on containers
Use either "if X is not None:" or a try-except instead of a "if X:".

I believe Targets.from_dict() was not really broken with previous code
but it looks suspicious and did fail the added test with a strange
exception: I expect the from_dict() methods to mainly fail with
KeyErrors, ValueErrors or AttributeErrors if file format structure
is incorrect.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-05-14 16:12:50 +03:00
Jussi Kukkonen
aa480b1280 Metadata API: Fix DelegatedRole serialization issue
A DelegatedRole with paths=[] fails to serialize correctly (paths is not
included in the output json).

Fix the issue, modify tests to notice a regression.

Fixes #1389

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-05-14 14:02:29 +03:00
Teodora Sechkova
167e1793d0 Fix Root.add_key() argument's type
After the implementation of a Key class representing
the public portion of a key, the method add_key() should
take an argument of type Key, instead of a dictionary.

Test cases are updated accordingly.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-05-13 11:23:15 +03:00
Martin Vrachev
42b3269ac1 Remove forgotten debug line from a test
Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-11 16:04:25 +03:00
Martin Vrachev
f98f4f750a Add Delegation/DelegatednRole specific tests
Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-11 16:04:25 +03:00
Martin Vrachev
8e4d3b9a84 Add DelegatedRole and Delegations
In the top level metadata classes, there are complex attributes such as
"meta" in Targets and Snapshot, "key" and "roles" in Root etc.
We want to represent those complex attributes with a class to allow
easier verification and support for metadata with unrecognized fields.
For more context read ADR 0004 and ADR 0008 in the docs/adr folder.

DelegatedRole shares a couple of fields with the Role class and that's
why it inherits it.
I decided to use a separate Delegations class because I thought it will
make it easier to read, verify and add additional helper functions.
Also, I tried to make sure that I test each level of the delegations
representation for support of storing unrecognized fields.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-11 16:03:56 +03:00
Jussi Kukkonen
17121bad28 Metadata API: remove_key() improvements
We should not do multiple lookups through data structures if one is
enough (here we have extra lookups on both roles and keyids).

Also in this case raising on missing key seems like the preferable
alternative so even a try-except is not needed.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-05-10 17:02:12 +03:00
Martin Vrachev
139bfc0ea9 BREAKING CHANGE: Make delegations optional
According to the spec, delegations in targets are marked as optional:
https://theupdateframework.github.io/specification/latest/#file-formats-targets
and a pr, clarifying that even more, is approved:
https://github.com/theupdateframework/specification/pull/157.

This is a possible breaking change.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-10 15:27:37 +03:00
Martin Vrachev
71c4992cea Make length and hashes optional in Timestamp
As per the specification (v1.0.1) length and hashes fields
in timestamp and snapshot metadata are optional.
We have implement this in the older API
(see https://github.com/theupdateframework/tuf/pull/1031) and we should
implement it in the new API.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-10 15:27:37 +03:00
Martin Vrachev
c0d72d460c New API: Add root use case in couple of tests
Add a use case for the root class to be tested in test_generic_read
and test_read_write_read_compare tests in test_apy.py

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-05-10 15:27:37 +03:00
Martin Vrachev
1ce94b95cb keyid: verify adding an existing key is ignored
Verify that adding an already existing key to keyid for a particular
role in Root won't create duplicate key.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-04-29 12:26:40 +03:00
Martin Vrachev
54a535e4c3 New API: Add Key/Role specific tests
Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-04-27 15:26:41 +03:00
Martin Vrachev
eab8c318f0 Add a Role class and integrate it into Root
In the top level metadata classes, there are complex attributes such as
"meta" in Targets and Snapshot, "key" and "roles" in Root etc.
We want to represent those complex attributes with a class to allow
easier verification and support for metadata with unrecognized fields.
For more context read ADR 0004 and ADR 0008 in the docs/adr folder.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-04-27 15:13:40 +03:00
Martin Vrachev
11e0a87bde Add Key class and integrate it into Root
In the top level metadata classes, there are complex attributes such as
"meta" in Targets and Snapshot, "key" and "roles" in Root etc.
We want to represent those complex attributes with a class to allow
easier verification and support for metadata with unrecognized fields.
For more context read ADR 0004 and ADR 0008 in the docs/adr folder.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-04-27 15:09:48 +03:00
Jussi Kukkonen
feb340f8da
Merge pull request #1345 from MVrachev/implement-adr-8
New metadata API: add support for ADR 0008
2021-04-22 21:54:32 +03:00
Martin Vrachev
79391f1d85 New API: accept metadata with unrecognized fields
In order to support ADR 0008 we would want to accept unrecognized
fields in all metadata classes.
Input that contains unknown fields in the 'signed' dictionary should
successfully deserialize into a Metadata object, and that object should
successfully serialize with the unknown fields intact.

Also, we should test that we support unrecognized fields when adding
new classes or modifying existing ones to make sure we support
ADR 0008.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-04-22 17:11:05 +03:00
Jussi Kukkonen
4e8738ffa5 api: Add Metadata.from_bytes()
This is essentially short-hand for
    JSONDeserializer().deserialize(data)
but seems much easier for the API user so may be worth it.

Metadata.from_file() now uses Metadata.from_bytes() internally.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-04-21 09:36:34 +03:00
Velichka Atanasova
1eaef0093b Add is_expired method to the Signed class
Checks metadata expiration against a reference time (a naive datetime in UTC).
If not provided, checks against the current UTC date and time.
Returns True if expiration time is less than the reference time.

Signed-off-by: Velichka Atanasova <avelichka@vmware.com>
2021-04-14 13:28:36 +03:00
Martin Vrachev
49aa0fc167 Make new API compatible with the Signing interface
In the securesystemslib pr https://github.com/secure-systems-lab/securesystemslib/pull/319
I added a new Signer interface with the purpose of supporting multiple
signing implementations.
Additionally, I added the SSlibSigner implementation of that interface
which implements the signing operation for rsa, ed25519 and ecdsa
schemes.
With this commit, I integrate the SSlibSigner into the new API in tuf.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-03-10 10:57:45 +01:00
Lukas Puehringer
8e9afc96f9 Revert "Move to/from_dict metadata API methods..."
Revert an earlier commit that moved to/from_dict metadata class
model methods to a util module of the serialization sub-package.

We keep to/from_dict methods on the metadata classes because:
- It seems **idiomatic** (see e.g. 3rd-party libaries such as attrs,
pydantic, marshmallow, or built-ins that provide default or
customizable dict representation for higher-level objects).
The idiomatic choice should make usage more intuitive.
- It feels better **structured** when each method is encapsulated
within the corresponding class, which in turn should make
maintaining/modifying/extending the class model easier.
- It allows us to remove function-scope imports (see subsequent
commit).

Caveat:
Now that "the meat" of the sub-packaged JSON serializer is
implemented on the class, it might make it harder to create a
non-dict based serializer by copy-paste-amending the JSON
serializer.

However, the benefits from above seem to outweigh the disadvantage.

See option 5 of ADR0006 for further details (#1270).

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2021-03-04 12:46:16 +01:00
Lukas Puehringer
e1be085c3c Move to/from_dict metadata API methods to util
Add tuf.api.serialization.util module with functions to
convert between TUF metadata class model and the corresponding
dictionary representation. These functions replace the
corresponding to/from_dict classmethods.

Configure api/pylintrc to exempt '_type' from protected member
access warning, because the underscore prefix here is only used to
avoid name shadowing.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2021-03-04 12:33:28 +01:00
Lukas Puehringer
240fb547af Use custom errors in serializer.json sub-package
Re-raise all errors that happen during de/serialization as custom
De/SerializationError.

Whilelist 'e', which is idiomatic for error, in api/pylintrc, and
inline exempt broad-except, which are okay if re-raised.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2021-03-04 12:33:28 +01:00
Lukas Puehringer
499f1c858e Adopt serialization sub-package in metadata API
- Rename Metadata methods:
  - to_json_file -> to_file
  - from_json_file -> from_file
- Remove Metadata.from_json/to_json
- Remove Signed.to_canonical_bytes
- Accept optional de/serializer arguments:
  - from_file (default: JSONDeserializer)
  - to_file (default: JSONSerializer)
  - sign, verify (default: CanonicalJSONSerializer)
- inline disable pylint cyclic-import checks

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2021-03-04 12:33:18 +01:00
Joshua Lock
d144141ec7 tests: remove check for python >= 3.6 in test_api
Signed-off-by: Joshua Lock <jlock@vmware.com>
2021-03-03 09:38:39 +00:00
Martin Vrachev
028d1bc9f7 Make "utils" import more definite
Currently, we are importing the "utils" module in tests/utils
with "import utils".
This could become a problem when there is another module with
the same general name "utils" and could lead to import mistakes.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2020-11-23 22:17:31 +02:00
Teodora Sechkova
5bfd9dd94c
Add tests for root and targets metadata
Add test cases for Root(Signed) and Targets(Signed) classes.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2020-11-09 11:38:26 +02:00
Teodora Sechkova
fe439d2014
Fix passing dictionaries by reference in TestMetadata
Use deepcopy to ensure that the dictionaries with expected data
are not referencing the same memory as the tested ones.

Add a check asserting that metadata is not equal prior to its
update.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2020-11-09 11:38:25 +02:00
Joshua Lock
38a86393c2 Make unit test files executable
It's convenient to be able to run unit test scripts directly, rather than
having to pass them as arguments to Python. This is already possible for
several of our unit tests, make it possible for all by setting the execute
bit.

Signed-off-by: Joshua Lock <jlock@vmware.com>
2020-10-05 10:36:31 +01:00
Jussi Kukkonen
03b15fb4be tests: Configure logging for all test files
all test_*.py files now accept zero or more '-v' to increase tuf
logging level. The default is now ERROR.

default: ERROR
"-v":    ERROR, but unittest prints test names
"-vv":   WARNING
"-vvv":  INFO
"-vvvv": DEBUG

Example to run a single test with DEBUG level:
  python3 test_updater.py -vvvv TestUpdater.test_4_refresh

Also make test_log.py restore the log level it modifies during test.

Fixes #1093

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2020-09-15 21:36:50 +03:00
Lukas Puehringer
73dd72d54d Raise on bad signature count in Metadata.verify
Change Metadata.verify(key) behavior to raise an exception if
none or multiple signatures for the passed key are found on the
Metadata object.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 16:18:28 +02:00
Lukas Puehringer
387169fc11 Add from_json metadata convenience wrapper
Add convenience wrapper that takes a json string and passes it
to from_dict to create a Metadata object.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 16:18:28 +02:00
Lukas Puehringer
f63dce6ddd Refactor metadata constructors and add factory
This commit better separates the Metadata class model from the
Metadata wireline format, by tailoring the constructors
towards class-based parameters and adding an additional
factory classmethod that creates Metadata objects based on the
wireline json/dictionary metadata representation. (pythonic
way of constructor overloading).

This 'from_dict' factory method recurses into the 'from_dict'
methods of each contained complex field/attribute that is also
represented by a class. Currently 'signed' is the only such
attribute.

This commit further:
- Changes optional constructor keyword arguments to mandatory
positional arguments: Reduces code and simplifies usage by
restricting it. For now, users are unlikely to call
constructor directly anyway, but the 'from_dict' factory (or
its 'from_json_file' wrapper) instead.

- Removes Signed.__expiration (datetime) vs. Signed.expires
(datestring) dichotomy: Keeping only one representation of the
same attribute in memory makes the interface simpler and less
ambiguous. We choose the datetime object, because it is more
convenient to modify. Transformation from and to the string
format required by the tuf wireline format is performed in the
corresponding metadata de/serialization methods, i.e.
('to_dict' and 'from_dict').

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 16:18:28 +02:00
Lukas Puehringer
f738ea0273 Rename tuf metadata interface methods
Consistenly rename de/serialization interface methods, using
a 'from_' and 'to_' prefix.

read_from_json -> from_json_file
write_to_json  -> to_json_file
as_json        -> to_json
as_dict        -> to_dict
signed_bytes   -> to_canonical_bytes

The latter is also changed from a property to a method for
consistency with the other serialization methods.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 16:18:28 +02:00
Lukas Puehringer
e61ae1bea3 Remove Signed.read_from_json metadata method
Remove metadata factory on Signed class, for the sake of API
simplicity/non-ambiguity, i.e. it's enough to have one
way of loading any Metadata, that is:
Metadata.read_from_json

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 16:18:28 +02:00
Lukas Puehringer
21de660b66 Remove comments and unify quotes in api tests
Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 16:18:28 +02:00
Lukas Puehringer
08bdc171e4 Add simple sign + verify Metadata methods (+tests)
Add simple methods to create or verify signatures of the
canonical_signed property of a Metadata object.

See corresponding docstrings for behavior and design
considerations.

The commit also adds tests and updates the test setup to load
some test keys into memory.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 16:18:19 +02:00
Lukas Puehringer
5cc73353fa Add metadata model class and method docstrings
Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 16:09:22 +02:00
Lukas Puehringer
088e94055f Replace _get_written_metadata with as_json method.
Add simple as_json Metadata method and use it instead of repository
lib's internal _get_written_metadata function in write_to_json.

This commit further adds code documentation and the possibility to
write compact json by excluding whitespace to write_to_json, and
also removes a call to the sign method from write_to_json.

The commit also adds tests.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
2020-09-10 15:59:10 +02:00