Commit graph

153 commits

Author SHA1 Message Date
Jussi Kukkonen
e1ec782f19
Merge pull request #1520 from avelichka/sslib-key
Add Key.from_securesystemslib_key
2021-08-31 09:42:39 +03:00
Jussi Kukkonen
7d77eeec31
Merge pull request #1512 from MVrachev/glob-pattern-matching
Implement glob-like pattern matching
2021-08-31 09:42:27 +03:00
Martin Vrachev
34e7546255 Remove lstrip & os.sep on targetpath & pathpattern
For targetpath: we don't want to support corner cases such as
file paths starting with separator.
Why this case should be threated specially than any other case where
you have multiple "/" for example "foo//bar/tar.gz"?

For pathpattern: it's recommended that the separator in the pathpattern
should be "/":
see https://theupdateframework.github.io/specification/latest/#targetpath
I believe it could lead to issues for a client implementation if it
supports arbitrary separators - every implementation needs to choose one
and stick with it.
Then, if we decide that "/" is our separator using lstrip on "os.sep" is
wrong, because the os separator from the server could be different that
the one used in the client.

Because of the above arguments, it makes sense to just remove
lstrip on os separators.

Additionally, document that the target_filepath and the DelegatedRole
paths are expected to be in their canonical forms and only "/" is
supported as target path separator.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>

in the public API that we only support "/" as a
separator and don't handle corner cases such as leading separators
in either pathpattern or target_filepath.
2021-08-30 19:04:02 +03:00
Teodora Sechkova
4f57ae43f8
Denote expected type of Metadata.signed
By explicitly denoting the expected type of Metadata.signed
we help mypy understand our intentions and correctly figure
out types. This is entirely a typing feature and has no
runtime effect.

Modify the return type of Metadata.from_dict to match the
other factory methods (from_*).

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-08-27 11:54:45 +03:00
Teodora Sechkova
b6e02bde47
Replace BinaryIO with IO[bytes] in metadata.py
Needed in order to be compatible with the return type of
download_file (TemporaryFile is typed as IO[bytes]).
BinaryIO is a subclass of IO[bytes].

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-08-27 11:49:50 +03:00
Jussi Kukkonen
7731738590
Merge pull request #1514 from MVrachev/filename-in-targetfile
Metadata API: include target target name in TargetFile
2021-08-27 11:02:49 +03:00
Martin Vrachev
b18176db9b Implement glob-like pattern matching
According to the recently updated version of the specification the shell
style wildcard matching is glob-like (see https://github.com/theupdateframework/specification/pull/174),
and therefore a path separator in a path should not be matched by a
wildcard in the PATHPATTERN.

That's not what happens with `fnmatch.fnmatch()` which doesn't
see "/" separator as a special symbol.
For example: fnmatch.fnmatch("targets/foo.tgz", "*.tgz") will return
True which is not what glob-like implementation will do.

We should make sure that target_path and the pathpattern contain the
same number of directories and because each part of the pathpattern
could include a glob pattern we should check that fnmatch.fnmatch() is
true on each target and pathpattern directory fragment separated by "/".

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-08-26 19:57:52 +03:00
Martin Vrachev
9229a405e3 Remove filename argument from Targets.update()
After the addition of "path" argument in the TargetFile class the
filename argument in Targets.update() became redundant.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-08-26 19:48:46 +03:00
Martin Vrachev
91b0c59602 Metadata API: include target path in targetfile
Currently, TargetFile instances do not contain the path relative URL of
the file they represent. The API itself does not need it but it could be
useful for users of the API.

As an example, the current client returns a dict for
get_one_valid_targetinfo(): that dict contains a filepath field and
a targetinfo field (essentially TargetFile).
We would like to keep a similar API, but avoid hand-crafted dicts.
It would be much nicer to return a TargetFile that would contain the
full "metadata" of the targetfile.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-08-26 19:48:45 +03:00
Velichka Atanasova
c875b7ed04 Add Key.from_securesystemslib_key
The securesystemslib key dictionary representation includes
the private key in keyval. TUF key doesn't handle it in any way,
but considering that we allow unrecognized symbols in the format,
we should exclude the private key otherwise this could lead to
misuse.
A call to securesystemslib.keys.format_keyval_to_metadata
with the default private=False would do exactly that.

Signed-off-by: Velichka Atanasova <avelichka@vmware.com>
2021-08-26 15:37:25 +03:00
Teodora Sechkova
bfb509aa53
docs: Fix DelegatedRole rendering
Improve class DelegatedRole docstring in order to be rendered
correctly in the documentation built with sphinx and autodoc.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-08-26 11:07:03 +03:00
Teodora Sechkova
d1329762b6
Fix sphinx-build warnings
Fix docstrings which triggered warnings from sphinx-build.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-08-23 18:25:08 +03:00
Jussi Kukkonen
d3441f056a
Merge pull request #1457 from jku/use-generics-to-improve-signed-typing
Improve signed typing
2021-08-18 12:01:33 +03:00
Jussi Kukkonen
13e20e9954 Metadata API: Make Metadata Generic
When we use Metadata, it is helpful if the specific signed type (and all of
the signed types attribute types are correctly annotated. Currently this is
not possible.

Making Metadata Generic with constraint T, where

    T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets")

allows these annotations. Using Generic annotations is completely
optional so all existing code still works -- the changes in test code
are done to make IDE annotations more useful in the test code, not
because they are required.

Examples:

    md = Metadata[Root].from_bytes(data)
    md:Metadata[Root] = Metadata.from_bytes(data)

In both examples md.signed is now statically typed as "Root" allowing IDE
annotations and static type checking by mypy.

Note that it's not possible to validate that "data" actually contains a
root metadata at runtime in these examples as the annotations are _not_
visible at runtime at all: new constructors would have to be added for that.

from_file() is now a class method like from_bytes() to make sure both
have the same definition of "T" when from_file() calls from_bytes():
This makes mypy happy.

Partially fixes #1433

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-08-16 16:38:21 +03:00
Teodora Sechkova
76d4633600
Raise if none of paths and path_hash_prefixes is set
The specification does not state clearly what is the
behaviour when none of delegation's "paths" and
"path_hash_prefixes" is set. See #1497.

Until this issue is clarified, copy current
Updater which raises an error in such case.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-08-10 09:02:37 +03:00
Teodora Sechkova
790aceb129
Remove _get_filepath_hash
Remove _get_filepath_hash and call sslib_hash.digest
directly instead.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-08-10 09:00:35 +03:00
Teodora Sechkova
f5b81be405
Rename and simplify visit_child_role
Rename to DelegatedRole.is_delegated_path and
return a boolean flag instead of the role's name.

Minor comments and code style improvements.

Some code simplification.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-08-10 09:00:06 +03:00
Teodora Sechkova
167441c789
Move _visit_child_role to DelegatedRole
Make _visit_child_role a public method of
DelegatedRole class. Reduce debug logging.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-07-28 17:57:43 +03:00
Martin Vrachev
4f37de1b53 Metadata API: add Metadata.to_bytes()
Metadata.to_bytes() is missing from the API and that is now becoming
annoying when writing the tests.
I think it makes sense to add:
it'll complete the serializing counterparts to from_bytes()/from_file().

We can also reuse to_bytes() in to_file() and that way ensure we don't
import the JSONSerializer locally twice.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-07-15 15:38:14 +03:00
Jussi Kukkonen
23e4f5cdcc
Merge pull request #1481 from MVrachev/fix-role-keyids-order
Metadata API: preserve Role.keyids order
2021-07-15 11:52:35 +03:00
Joshua Lock
bd5912bcc7
Merge pull request #1436 from jku/verify-delegate
Metadata API: Implement threshold verification
2021-07-12 11:45:57 +01:00
Martin Vrachev
df9f3df75d Metadata API: preserve Role.keyids order
We made Role.keyids a set because the keyids are supposed
to be unique and this still makes sense.

However, the data should also preserve order
(when deserialized and serialized) and currently, it does not.
This is fairly serious since writing signed data potentially modifies
the data (making the signature invalid).

The simplest solution (as proposed by Teodora) is to sort the
set during serialization and that would ensure the order of the items.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-07-09 16:51:25 +03:00
Jussi Kukkonen
271d5b7810 Metadata API: verify_delegate: refactor
* Rename arguments so connection between the role name and the
  metadata is stronger.
* Also add a comment on the list comprehension + next() trick.
* Add return value annotation
* Raise early if delegations is None to make the flow more obvious
  (and modify test case so we have coverage for the new case)

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-07-08 20:16:42 +03:00
Martin Vrachev
452aad8d00 Metadata API: fix Key validation exception
In pr https://github.com/theupdateframework/tuf/pull/1449 we introduced
Key validation and there decided to raise "ValueError" if one of
keyid, scheme, keyval or keytype is not a string.
That seems like a mistake given that in the python docs it's written:
"Passing arguments of the wrong type
(e.g. passing a list when an int is expected) should result
in a TypeError, but passing arguments with the wrong value
(e.g. a number outside expected boundaries) should result
in a ValueError."

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-07-07 13:59:28 +03:00
Martin Vrachev
88422de1fc Metadata API: Fix keyval "public" requirement
Currently, we require that the keyval attribute in the Key class
is a dictionary and has "public" as a key, otherwise, we throw
KeyError or ValueError.

This requirement is too strict given that in the spec for KEYVAL it's
only said that KEYVAL is:
"A dictionary containing the public portion of the key."
See: https://theupdateframework.github.io/specification/latest/index.html#keyval

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-07-05 19:07:28 +03:00
Jussi Kukkonen
48b58d9be9 Metadata API: Don't peek into Key internals
There was an attempt at ensuring key content uniqueness in
verify_delegate() by making sure the values corresponding to "public"
keys in Key.keyval dictionaries are unique. This had two issues:
 * it wasn't a security measure: it's not difficult to produce two
   different "public" values of the same key content
 * Spec does not actually guarantee the existence of "public" key in
   the keyval dictionary (the three keys included in the spec just all
   happen to have it)

Luckily the spec does require KEYIDs to be unique so we do not need to
do all this: Just count keyids of keys with verified signatures. Keep
building a Set of keyids as a belt-and-suspenders strategy: Role keyids
are currently guaranteed to be unique but we'd notice here if they
weren't.

Add a logger call for failed verifys: this might useful to figure out
which keys exactly are the issue when a delegate can not be verified.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-07-05 15:13:00 +03:00
Jussi Kukkonen
d00af4c101 tests: Improve verify_delegate() tests
Make sure verify_delegate() succeeds when threshold is reached even if
some signatures fail to verify.

Make sure higher threshold (2/2) works.

Change error type for "Call is valid only on delegator metadata" error.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-07-05 15:13:00 +03:00
Jussi Kukkonen
37a4d41aad Metadata API: Implement threshold verification
The delegating Metadata (root or targets) verifies that the delegated
metadata is signed by required threshold of keys for the delegated
role.

Calling the function on non-delegator-metadata or giving a rolename
that is not actually delegated by the delegator is considered a
programming error and ValueError is raised.

If the threshold is not reached, UnsignedMetadataError is raised.

Tweak type annotation of Delegations.keys to match the one for
Root.keys (so they can be assigned to same local variable).

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-07-05 15:13:00 +03:00
Joshua Lock
7815f055e7
Merge pull request #1455 from jku/mypy-tweaks
Harden mypy configuration
2021-07-02 10:27:20 +01:00
Jussi Kukkonen
505b82a82c Metadata API: Fix Metadata.sign() return value
We've been returning Signature objects since 49aa0fc167.

Also add a test case that does something with the returned signature.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-06-23 14:35:36 +03:00
Jussi Kukkonen
f59fee59b0 Metadata API: Remove unreachable code
unrecognized_fields is always initialized

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-06-23 10:51:33 +03:00
Jussi Kukkonen
81def2dfcc Metadata API: Add missing type annotations
Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-06-23 10:51:33 +03:00
Jussi Kukkonen
f458e9230a
Merge pull request #1456 from jku/tighten-comments
Metadata API: Rewrite comments
2021-06-23 10:20:12 +03:00
Jussi Kukkonen
7108ea2e0e
Merge pull request #1454 from sechkova/hashes-handle-sslib-errors
BaseFile._verify_hashes: handle sslib errors
2021-06-23 10:19:14 +03:00
Jussi Kukkonen
b860ad813f
Merge pull request #1453 from avelichka/develop
Add 'ecdsa' to the list of supported key types
2021-06-23 10:17:19 +03:00
Jussi Kukkonen
79f4f41979 Metadata: Improve DelegatedRole docstring
Explain the ways a delegation can happen: Do not try to cover the
complete process (specification should do that) but offer enough
details that the complexity is not completely hidden from the viewer.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-06-22 15:33:26 +03:00
Jussi Kukkonen
38b6d440c0 Metadata API: Rewrite comments
Try to keep dostrings and comments to the point, avoid mentioning
details if they are not necessary or are likely to become outdated
and try to minimize number of comment lines.

Co-authored-by: Joshua Lock <jlock@vmware.com>
Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-06-22 15:33:07 +03:00
Teodora Sechkova
752a741d3a
Handle sslib exceptions in BaseFile._verify_hashes
Securesystemslib digest() and digest_fileobject()
calls raise sslib specific exceptions that need to be
handled and re-raised as TUF exceptions.

Updated tests in test_api.py accordingly.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-06-22 11:35:00 +03:00
Jussi Kukkonen
e6f743bbe3
Merge pull request #1435 from jku/handle-exceptions-in-verify
Handle exceptions in verify
2021-06-22 09:55:07 +03:00
Teodora Sechkova
e30faa89be
Remove empty hash dict check from MetaFile
The check for an empty hash dictionary is now part
of the hash validation function.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-06-17 14:38:02 +03:00
Teodora Sechkova
03f39b01e7
Add hash and length validation
- valid length: greater than zero
- valid hashes: a non-empty dictionary of type Dict[str, str]

Checking the validity of hash algorithms is not part
of the metadata input validation and is done by
securesystemslib during  hash verification.

Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
2021-06-17 14:38:01 +03:00
Velichka Atanasova
0fa6c6f2ca Add 'ecdsa' to the list of supported key types
Signed-off-by: Velichka Atanasova <avelichka@vmware.com>
2021-06-17 11:48:30 +03:00
Jussi Kukkonen
743c4408d4 Metadata API: Clean up verify_signature() exceptions
Aim to only raise UnsignedMetadataError from verify_signature().

Some of the situations could be things like UnsupportedAlgorithmError
-- where the underlying reason may be a missing dependency -- but it
seems impossible for a client to know whether it's that or whether it
is broken or malicious server side.

Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
2021-06-17 10:48:40 +03:00
Jussi Kukkonen
39ed706d72
Merge pull request #1437 from sechkova/hash-verification
Add hash and length verification to MetaFile and TargetFile
2021-06-16 22:18:03 +03:00
Jussi Kukkonen
6c4e2be196
Merge pull request #1450 from MVrachev/threshold-validation
Metadata API: Add simple threshold validation
2021-06-16 19:56:26 +03:00
Jussi Kukkonen
fa2268df5a
Merge pull request #1449 from MVrachev/key-validation
Metadata API: Add Key attributes types validation
2021-06-16 19:55:34 +03:00
Jussi Kukkonen
25e5f304ee
Merge pull request #1430 from MVrachev/spec_version-validation
New metadata API: spec_version attribute validation
2021-06-16 19:54:36 +03:00
Martin Vrachev
a9dc24adea Metadata API: Add a comment for Key validation
Clarify that we don't semantically validate "Key" instances during
initialization and that this is a responsibility of securesystemslib.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-06-16 16:37:48 +03:00
Martin Vrachev
6c5d970799 Metadata API: Add simple threshold validation
Probably there could be future API calls that modify "threshold"
to a new value, but the problem is we don't have a clear idea
if they would exist and what exactly they will do.
That's why it makes sense to validate against the potential problems
we can imagine - in this case, is passing a threshold below 1.

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-06-15 18:06:31 +03:00
Martin Vrachev
f20664d2fc Metadata API: Add Key attributes types validation
In our discussion with Jussi we come to the conclusion that we want
to verify that all Key attributes contain values in the expected types,
but at the same time, we don't want to focus on validating the semantics
behind them.
The reason is that having a Key instance with invalid attributes is
possible and supported by the spec.
That's why we have a "threshold" for the roles meaning we can have up to
a certain number of invalid Keys until we satisfy
the required threshold.

Also, for deeper semantic validation it's better to be done in
securesystemslib which does the actual work with keys.

For context see: https://github.com/theupdateframework/tuf/issues/1438

Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
2021-06-15 17:35:02 +03:00