-
Notifications
You must be signed in to change notification settings - Fork 278
Metadata API: Implement threshold verification #1436
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I've become biased after looking at the code in the next-gen client for a while because I couldn't point out any issues :)
I was searching for a way to avoid adding role_name
as an argument of verify_delegate
but I saw you already opened #1425 about it so +1 from me on that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't found any issues either.
LGTM!
tuf/api/metadata.py
Outdated
try: | ||
key.verify_signature(delegate, signed_serializer) | ||
# keyids are unique. Try to make sure the public keys are too | ||
signing_keys.add(key.keyval["public"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe making sure that we have keyval["public"]
uniqueness can be done instead in Root
and Targets
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah possible but I'd say it's a separate issue: #1429
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only very recently realised the TUF spec does not require the existence of "public" at all -- the three keys defined in the spec all happen to have it but it isn't required.
So this line needs to change. The spec does require that KEYIDs are unique so verify_delegate()
will just respect that and will not try to poke into the internals.
3e6def1
to
3185a30
Compare
tuf/api/metadata.py
Outdated
keys = self.signed.delegations.keys | ||
# Assume role names are unique in delegations.roles: #1426 | ||
roles = self.signed.delegations.roles | ||
role = next((r for r in roles if r.name == role_name), None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also from trishank: "What does this line do? Looks like magic"
My response was:
it returns the first role in delegations with name role_name (or None if no match).
I agree it's not pretty and a bit too clever.
I could do something like this instead:role = None for delegated_role in roles: if delegated_role.name == role_name: role = delegated_role break
but that's 5 lines of initialize-then-modify antipattern: not the end of the world but not perfect either.
the real fix is probably #1426 though (make roles a OrderedDict). Then this code becomes readable and efficient:
role = roles.get(role_name)
I think I'd leave it as is ... but if others think that's too clever I'm happy to change to the more verbose one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a one-liner comment for less familiar readers?
Find the first role in delegations with name role_name (or None if no match)
I think there are two things that we should definitely test:
Soo, yes I guess I plan to add one now... |
rebased on develop, added a commit to add a tests for
This also changes one error type as suggested by teodora |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding the tests!
Seems good to me!
Added a commit that removes the attempt to enforce key contents uniqueness:
Luckily the spec requires that KEYIDs are unique so verify_delegate() will just respect that and will not try to poke into the internals of the keys. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. I wonder if the parameter names in verify_delegate
could be renamed to more clearly indicate that role_name and delegate are related (an identifier and an instance)?
tuf/api/metadata.py
Outdated
keys = self.signed.delegations.keys | ||
# Assume role names are unique in delegations.roles: #1426 | ||
roles = self.signed.delegations.roles | ||
role = next((r for r in roles if r.name == role_name), None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a one-liner comment for less familiar readers?
Find the first role in delegations with name role_name (or None if no match)
I chose the more verbose names: it's easy to lose track of where the keys and thresholds really come from and what they are applied on so more verbose seems reasonable. |
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Someone made mypy rules stricter in the mean time 😬 so had to rebase and fix the annotation (no changes in existing commits, only in two new commit). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like how easy this is to follow, nice work.
We don't neeed those tests given that verify_with_threshold is a placeholder until theupdateframework#1436 is merged. Signed-off-by: Martin Vrachev <[email protected]>
* 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 <[email protected]>
Yes, agree with all suggestions. I've combined all the improvements from suggestions into one commit. The changes since last push are
It's minor enough that I don't think this absolutely needs another approval (so I will merge with the one I have) but ... there's not a lot of code that's more critical for TUF so feel free to have another look |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at the new changes and it seems good to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
#1417 is merged so this is good to go
The delegating Metadata (root or targets) verifies that the delegated metadata is signed by required threshold of keys for the delegated role.
This implementation raises UnsignedMetadataError if threshold is not met. Other possible exceptions are programming errors.
Fixes #1306