Skip to content
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

bug: fail to verify COSE signature with extra attributes in the protected header #88

Closed
shizhMSFT opened this issue Oct 14, 2022 · 16 comments · Fixed by #89
Closed

bug: fail to verify COSE signature with extra attributes in the protected header #88

shizhMSFT opened this issue Oct 14, 2022 · 16 comments · Fixed by #89
Assignees
Labels
bug Something isn't working
Milestone

Comments

@shizhMSFT
Copy link
Contributor

When the COSE signature contains extra attribute keys, which are integers not strings, the code reports error:

extendedAttributes key requires string type

Those keys should be ignored if not marked as critical, or report error if marked as critical. If they are known attributes, we may convert keys into strings so that it can fill in the signature.Attribute structure.

Related code:

// fetch all the extended signed attributes
systemHeaders := []interface{}{cose.HeaderLabelAlgorithm, cose.HeaderLabelCritical, cose.HeaderLabelContentType,
headerLabelExpiry, headerLabelSigningScheme, headerLabelSigningTime, headerLabelAuthenticSigningTime}
var extendedAttributeKeys []string
for label := range protected {
if contains(systemHeaders, label) {
continue
}
label, ok := label.(string)
if !ok {
return nil, &signature.InvalidSignatureError{Msg: "extendedAttributes key requires string type"}
}
extendedAttributeKeys = append(extendedAttributeKeys, label)
}

@patrickzheng200
Copy link

I will take this one.

@priteshbandi
Copy link
Contributor

Those keys should be ignored if not marked as critical, or report error if marked as critical.

Can this cause issues if a plugin is expecting those attributes? Also, silently ignoring attributes doesn't look right. Do you have a specific usecase?

If they are known attributes, we may convert keys into strings so that it can fill in the signature.Attribute structure.

What will we do in case of a collision? IMO unless we have a specific usecase to support, we should continue to fail verification if we encounter non-string keys in signed attributes.

@patrickzheng200
Copy link

patrickzheng200 commented Oct 18, 2022

Those keys should be ignored if not marked as critical, or report error if marked as critical.

Can this cause issues if a plugin is expecting those attributes? Also, silently ignoring attributes doesn't look right. Do you have a specific usecase?

If they are known attributes, we may convert keys into strings so that it can fill in the signature.Attribute structure.

What will we do in case of a collision? IMO unless we have a specific usecase to support, we should continue to fail verification if we encounter non-string keys in signed attributes.

Please correct me if I'm wrong, according to our implementation in notation-go executePlugin, we are only passing critical extended attributes to plugin.VerifySignatureRequest. In other words, if a plugin expects a certain attribute, it has to be marked as critical anyway.

For COSE envelope, there could be non-string keys, for example, key of kid is 4. In this case, if it is non-critical, we can ignore it instead of failing the verification (one of our users is blocked due to this logic). If it's marked critical, we return an error requiring string type. If a plugin expects this extended attribute, then it needs to mark the attribute as critical for now.
Finally, there shouldn't be collision issue for a COSE envelope, since ProtectedHeader is a Map by design.
@shizhMSFT @priteshbandi

@yizha1 yizha1 moved this from Todo to PR Review in Notary Project Planning Board Oct 18, 2022
@yizha1 yizha1 moved this from PR Review to In Progress in Notary Project Planning Board Oct 18, 2022
@priteshbandi
Copy link
Contributor

Those keys should be ignored if not marked as critical, or report error if marked as critical.

Can this cause issues if a plugin is expecting those attributes? Also, silently ignoring attributes doesn't look right. Do you have a specific usecase?

If they are known attributes, we may convert keys into strings so that it can fill in the signature.Attribute structure.

What will we do in case of a collision? IMO unless we have a specific usecase to support, we should continue to fail verification if we encounter non-string keys in signed attributes.

Please correct me if I'm wrong, according to our implementation in notation-go executePlugin, we are only passing critical extended attributes to plugin.VerifySignatureRequest. In other words, if a plugin expects a certain attribute, it has to be marked as critical anyway.

For COSE envelope, there could be non-string keys, for example, key of kid is 4. In this case, if it is non-critical, we can ignore it instead of failing the verification (one of our users is blocked due to this logic). If it's marked critical, we return an error requiring string type. If a plugin expects this extended attribute, then it needs to mark the attribute as critical for now. Finally, there shouldn't be collision issue for a COSE envelope, since ProtectedHeader is a Map by design. @shizhMSFT @priteshbandi

Yes, currently we only pass critical attributes to plugin but we don't want to limit it for future usecase.

In cose both 4 as int and 4 as string can co-exist in ProtectedHeader map. If both are marked critical which one will we prefer and why?
Signature's generated using notation wont have signed integer keys in ProtectedHeader. Do you have a specific usecase that you want to support with ignoring integer values?

@patrickzheng200
Copy link

patrickzheng200 commented Oct 19, 2022

Those keys should be ignored if not marked as critical, or report error if marked as critical.

Can this cause issues if a plugin is expecting those attributes? Also, silently ignoring attributes doesn't look right. Do you have a specific usecase?

If they are known attributes, we may convert keys into strings so that it can fill in the signature.Attribute structure.

What will we do in case of a collision? IMO unless we have a specific usecase to support, we should continue to fail verification if we encounter non-string keys in signed attributes.

Please correct me if I'm wrong, according to our implementation in notation-go executePlugin, we are only passing critical extended attributes to plugin.VerifySignatureRequest. In other words, if a plugin expects a certain attribute, it has to be marked as critical anyway.
For COSE envelope, there could be non-string keys, for example, key of kid is 4. In this case, if it is non-critical, we can ignore it instead of failing the verification (one of our users is blocked due to this logic). If it's marked critical, we return an error requiring string type. If a plugin expects this extended attribute, then it needs to mark the attribute as critical for now. Finally, there shouldn't be collision issue for a COSE envelope, since ProtectedHeader is a Map by design. @shizhMSFT @priteshbandi

Yes, currently we only pass critical attributes to plugin but we don't want to limit it for future usecase.

In cose both 4 as int and 4 as string can co-exist in ProtectedHeader map. If both are marked critical which one will we prefer and why? Signature's generated using notation wont have signed integer keys in ProtectedHeader. Do you have a specific usecase that you want to support with ignoring integer values?

So we have 2 scenarios here:

  1. Critical extended attributes require keys of string type. -> In case of 4 as int and marked as critical mentioned in your comment, we would fail the verification. And therefore, this kind of collision will not happen.
  2. Non-critical extended attributes with non-string type keys will be ignored. -> This is solving one of our use cases, where it is failed because it has x5t (34 as int) in COSE's extended attribute. (This Signature is not generated using Notation.) And we don't want to fail the verification directly in this scenario.

For Notation, my understanding is that Sign and Verification are two separated operations. One can generate his own COSE Envelope somewhere else and use Notation to Verify it. Hence, I think we should accept a more general form of Signature Envelope like the one I mentioned above.

@patrickzheng200
Copy link

@rgnote @priteshbandi Any further comments/concerns on this one?

@yizha1 yizha1 moved this from In Progress to PR Review in Notary Project Planning Board Oct 25, 2022
@rgnote
Copy link
Contributor

rgnote commented Oct 26, 2022

Non-critical extended attributes with non-string type keys will be ignored. -> This is solving one of our use cases, where it is failed because it has x5t (34 as int) in COSE's extended attribute. (This Signature is not generated using Notation.) And we don't want to fail the verification directly in this scenario.

Ignoring keys silently doesn't seem the right approach here. It is a one-way door decision where notation will never be able to enforce string keys for non-critical extended attributes in the future. I agree that users should be able to verify signatures that are generated outside notation, but that doesn't mean notation allows verification of signatures that don't follow the notation guidelines. We should keep signatures restricted to what types notation is expecting today so that we can loosen these restrictions when we see new usecases (as opposed to the other way around)

That being said, can you explain your usecase where x5t (34 as int) can't be marked as string?

@yizha1 yizha1 modified the milestones: RC-1, beta-1 Oct 27, 2022
@patrickzheng200
Copy link

patrickzheng200 commented Oct 27, 2022

That being said, can you explain your usecase where x5t (34 as int) can't be marked as string?

It's because the COSE Header Parameters regulates the label type of a COSE header, from which it can be string or integer. In other words, if we claim to support COSE format, then we need to follow the COSE standard (in standard, x5t has label 34 as int).
After a discussion with @shizhMSFT, we propose another option here to avoid ignoring keys silently. We can change the key type of Attribute from string to interface{}. Note, JWS format will still enforce extended attribute keys as string type. This change will NOT touch JWS.
In case of the above-mentioned concern regarding collision when 4 as int and 4 as string both exist, we don't think it's a collision. Because we never do type conversion on keys, i.e. 4 as int and 4 as string are just two independent keys.
For more details, please take a look at PR #89. @rgnote @priteshbandi

@rgnote
Copy link
Contributor

rgnote commented Oct 27, 2022

LGTM

@rgnote rgnote closed this as completed Oct 27, 2022
Repository owner moved this from PR Review to Done in Notary Project Planning Board Oct 27, 2022
shizhMSFT pushed a commit that referenced this issue Oct 28, 2022
Fixed issue #88.

After this update:
1. JWS extended attribute keys still enforce string type.
2. COSE extended attribute keys will accept string and int type.

Signed-off-by: Patrick Zheng <[email protected]>
@rgnote
Copy link
Contributor

rgnote commented Nov 7, 2022

Reopening as this is causing an issue with verification plugins.

See https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#verify-signature-command

As part of criticalAttributes, the Verifier needs to send extended critical attributes to the verification plugin. That means, we will need to covert types of attributes to string (encoding/json doesn't support interface{} keys). That also means, attribute collisions (4 as int and 4 as string) are possible.

@shizhMSFT @patrickzheng200

@rgnote rgnote reopened this Nov 7, 2022
Repository owner moved this from Done to In Progress in Notary Project Planning Board Nov 7, 2022
@shizhMSFT
Copy link
Contributor Author

@rgnote In the spec you've pointed, unprocessedAttributes is an array of names of critical attributes that plugin. Since it is an array not a map, it can accept any type of keys although the spec is not well defined.

@rgnote
Copy link
Contributor

rgnote commented Nov 8, 2022

@shizhMSFT The issue is around extendedAttributes not unprocessedAttributes

@shizhMSFT
Copy link
Contributor Author

@rgnote I propose that extendedAttributes should be an array instead of a map. Again, as discussed in the meeting, non-critical attributes (i.e. optional attributes) are not passed to the plugin if unprocessedAttributes contains only names of the attributes.

@rgnote
Copy link
Contributor

rgnote commented Nov 8, 2022

I propose that extendedAttributes should be an array instead of a map.

Can you clarify how we will pass the attribute values if it is a array?

Again, as discussed in the meeting, non-critical attributes (i.e. optional attributes) are not passed to the plugin if unprocessedAttributes contains only names of the attributes.

non-critical attributes are not passed at all to the plugins. Please take a look at this code. extendedAttributes and unprocessedAttributes are tightly related. extendedAttributes contains the key-value pairs and the same keys will be present in unprocessedAttributes

@rgnote
Copy link
Contributor

rgnote commented Nov 8, 2022

I raised this PR to unblock JWS signatures. Please raise a PR for spec updates once you have an idea on how verification plugins needs to deal with COSE extended attributes

@shizhMSFT
Copy link
Contributor Author

Closed by notaryproject/notation-go#188 and will follow up notaryproject/specifications#201

Repository owner moved this from In Progress to Done in Notary Project Planning Board Nov 9, 2022
patrickzheng200 pushed a commit to patrickzheng200/notation-go that referenced this issue Nov 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

8 participants