Skip to content

Improve verification results #2551

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 5, 2024

Conversation

jku
Copy link
Member

@jku jku commented Feb 1, 2024

This is an attempt at fixing the verification result handling that has been found to not work well. This API is useful mostly to repository implementations: clients should likely keep using verify_delegate()

Modify VerificationResult

  • Remove union(): this is now handled by the new RootVerificationResult
  • include threshold: This is useful and can be added now that union() does not exist
  • Use dict[str, Key] instead of set[str] for signed and unsigned
  • Note that the (broken metadata) case of role keyid that does not have a matching key, is now not counted as "unsigned" as it was before: it's just not counted at all

Add RootVerificationResult

  • Externally looks like VerificationResult (without threshold)
  • It is actually two VerificationResults in a trenchcoat: Root.get_root_verification_result() is provided as a straightforward way to get one

Note that RootVerificationResult.signed and RootVerificationResult.unsigned assume that there are no keyid collisions between the two verifying roles (same keyid is not used for two different keys): This does not affect verified status in any way (only the signed and unsigned lists) so I think this is totally acceptable.

So far the usage looks quite nice but I think we want to try to use this in an app or two to avoid redoing the mistake of the first VerificationResult...

Fixes #2544

  • The code follows the Code Style Guidelines
  • Tests have been added for the bug fix or new feature
  • Docs have been added for the bug fix or new feature

jku added 4 commits February 1, 2024 18:26
This is an API break as VerificationResult changes:
 * Now contains threshold
 * Now contains Keys and not just keyids

Note that there is a small edge case functionality change:
 * if the role does not have a key for the keyid, then we no longer
   include that key in "unsigned"

I think that is an acceptable change.

Signed-off-by: Jussi Kukkonen <[email protected]>
This is a thin wrapper over two VerificationResults:
useful when verifying root signatures.

Now the API for getting verification results for root and
the API for getting the results for other metadata is different.

Client use cases can continue using verify_delegate() so should not
be affected.

Signed-off-by: Jussi Kukkonen <[email protected]>
Changes are
* expected result changes (like the handling of keyids without keys)
* test refactoring to have access to the Key
* Removal of union test
* use the fact that VerificationResult is Truthy in asserts
  (to get 1 more line of coverage)

Signed-off-by: Jussi Kukkonen <[email protected]>
This does much the same tests as test_signed_get_verification_result()
above it does, just using two root roles.

Signed-off-by: Jussi Kukkonen <[email protected]>
@jku jku force-pushed the improve-verification-result branch from 505e6d1 to cd0fd5c Compare February 1, 2024 19:13
@jku
Copy link
Member Author

jku commented Feb 1, 2024

Still wondering if it would be useful for the two result types to have a common base type, instead of being just duck typed to feel similar.

The way I see a signing tool or repository UI using these is this:

  • getting verification results happens separately for root and other roles (because it has to)
  • but printing the results should be possible with the same code (with maybe a small tweak for root case)

So maybe it is fine for the app to have something like

def describe_result(result: VerificationResult | RootVerificationResult) -> str:
    ...

I would maybe rather not create some complicated inheritance (and inevitably large classes) when the dataclasses look like they might just work like that...

jku added 2 commits February 2, 2024 11:02
dict unions are only supported in 3.9.

Signed-off-by: Jussi Kukkonen <[email protected]>
Now that VerificationResult has threshold, this can be simpler.

Signed-off-by: Jussi Kukkonen <[email protected]>
@coveralls
Copy link

coveralls commented Feb 2, 2024

Pull Request Test Coverage Report for Build 7785217570

  • 0 of 37 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.04%) to 97.847%

Totals Coverage Status
Change from base Build 7747121727: 0.04%
Covered Lines: 1382
Relevant Lines: 1405

💛 - Coveralls

Copy link
Member

@lukpueh lukpueh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very helpful. Let me try it in RSTUF and see if it really is.

lukpueh added a commit to lukpueh/repository-service-tuf-cli that referenced this pull request Feb 2, 2024
WIP in theupdateframework/python-tuf#2551

There is still a bit of annoying work to do to get only those keys that will
decrease the missing signatures count (see `keys_to_use`), or to
deduplicate individual results (see `results_to_show`) but I am not sure
if python-tuf can provide generally useful API for this.

Signed-off-by: Lukas Puehringer <[email protected]>
lukpueh added a commit to lukpueh/repository-service-tuf-worker that referenced this pull request Feb 2, 2024
WIP in theupdateframework/python-tuf#2551

Replaces clunky _validate_signature and _validate_threshold methods.

This works nicely!

Signed-off-by: Lukas Puehringer <[email protected]>
@jku jku force-pushed the improve-verification-result branch from bb011ef to 16f91eb Compare February 3, 2024 09:48
This is an example of using the verification resutls in a repository.

The only remaining tricky part is in _get_verification_result():
* has to figure out the delegating metadata (something we currently
  cannot provide in repository.Repository for the general case)
* Needs a special case for first root

Signed-off-by: Jussi Kukkonen <[email protected]>
@jku jku force-pushed the improve-verification-result branch from 16f91eb to b8dbe30 Compare February 3, 2024 15:10
@lukpueh
Copy link
Member

lukpueh commented Feb 5, 2024

I tried this in RSTUF and found it very helpful.

I think adding a common base class would be okay. The inheritance model could look something like this.

class VerificationResult:
  verified: bool
  signed: dict[str, Key]
  unsigned: dict[str, Key]

class Single(VerificationResult):
  threshold: int

class Combined(VerificationResult):
  first: Single
  second: Single

But this does not add much value. The more interesting question is, if we want to provide other common behavior. Two things that I would have found useful:

  • a list of missing signature infos:
    unique tuples of threshold - len(signed) and unsigned if verified is False per Single result

  • keys that effectively decrease the missing signature count
    union of unsigned if verified is False per Single result

jku added 2 commits February 5, 2024 13:51
Change the "other" argument to optional "previous" and
handle the None case in code.

Signed-off-by: Jussi Kukkonen <[email protected]>
@jku
Copy link
Member Author

jku commented Feb 5, 2024

The common base class probably looks more complicated: because Combined wants to override verified, signed and unsigned they probably would have to be properties in the parent class already.

@jku
Copy link
Member Author

jku commented Feb 5, 2024

  • a list of missing signature infos:
    unique tuples of threshold - len(signed) and unsigned if verified is False per Single result
  • keys that effectively decrease the missing signature count
    union of unsigned if verified is False per Single result

I'm not completely sold on these because :

  • this is only relevant for root and only when root signers change
  • I think most repositories in practice would want to encourage everyone to sign (to make the signing events Proof-of-Ownership events at the same time)
  • Handling multliple tuples does not seem any easier than handling result.first and result.second (or if you think it is we can just make those a tuple instead of two fields) so you could just handle the underlying VerificationResults yourself

We could add this to VerificationResult if it makes things easier:

@property
def missing(self) -> int:
    return max(0, self.threshold - len(self.signed))

jku added 4 commits February 5, 2024 14:36
This is helper to tell how many signatures are still required.
Also change the order of Roots given to RootVerificationResult
(this way first is version N, second is version N+1).

Signed-off-by: Jussi Kukkonen <[email protected]>
Change tests so the previous root version is what the code expects.

Signed-off-by: Jussi Kukkonen <[email protected]>
@jku jku marked this pull request as ready for review February 5, 2024 13:34

for keyid in role.keyids:
try:
key = self.get_key(keyid)
except ValueError:
unsigned.add(keyid)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. This means that in the new VerificationResult there might be role keyids that are neither in signed nor unsigned. That's probably okay. But should we mention it somewhere. Maybe in the PR description?

Copy link
Member Author

@jku jku Feb 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it is a change -- I think I mentioned it in the first commit message , can add to PR decription.

I was also considering just raising at this point (because clearly the role is in a bad state)... but then that sounds like punishing the delegated role signing for delegator metadata error -- an error that can still leave the delegated role capable of signing.

@jku jku merged commit be55b87 into theupdateframework:develop Feb 5, 2024
lukpueh added a commit to lukpueh/repository-service-tuf-cli that referenced this pull request Feb 7, 2024
WIP in theupdateframework/python-tuf#2551

There is still a bit of annoying work to do to get only those keys that will
decrease the missing signatures count (see `keys_to_use`), or to
deduplicate individual results (see `results_to_show`) but I am not sure
if python-tuf can provide generally useful API for this.

Signed-off-by: Lukas Puehringer <[email protected]>
@jku jku deleted the improve-verification-result branch December 30, 2024 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

VerificationResult should include keys for keyids
3 participants