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

Start adding more verification with VerificationOpts struct #153

Merged
merged 77 commits into from
Dec 8, 2022

Conversation

malancas
Copy link
Contributor

@malancas malancas commented Nov 17, 2022

Summary

Part of #121. This PR starts adding additional verification using a VerificationOpts struct. More verification will be added in follow ups after some upstream changes to the timestamp package.

This PR adds the following verifications:

  • Verify the TSR's certificate identifier matches a provided TSA certificate
  • Verify the leaf certificate's subject name matches a provided subject
  • Verify that the TSR's embedded leaf certificate matches a provided TSA certificate
  • Verify the TSA certificate and the intermediates have the extended key usage set to only timestamping
  • Verify the OID of the TSR matches an expected OID
  • Verify the status of the TSR does not contain an error
  • Verify the TSR's nonce matches a provided nonce

Release Note

Documentation

Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
cmd.Flags().String("nonce", "", "optional nonce passed with the request")
cmd.Flags().Var(NewFlagValue(oidFlag, ""), "oid", "optional oid passed with the request")
cmd.Flags().String("subject", "", "expected leaf certificate subject")
cmd.Flags().Var(NewFlagValue(fileFlag, ""), "tsa-cert", "path to TSA cert")
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: we could omit adding --tsa- as prefix to the flag.

Q: Is this an intermediate or root cert ?

Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
@@ -85,6 +97,62 @@ func runVerify() (interface{}, error) {
return nil, err
}

opts, err := verification.NewVerificationOpts(ts, artifact, pemBytes)
Copy link
Contributor Author

@malancas malancas Nov 17, 2022

Choose a reason for hiding this comment

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

@haydentherapper currently all verification is handled in a single function VerifyTimestampResponse exported from the verification package. Here I add new verification functions separately. Do you have thoughts on whether all new verification functions should be called from VerifyTimestampResponse or if they should be called directly from here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it'd be best to have a single exported verification function that performs all other verifications. If you think there's value in exporting the sub-verification functions, maybe we put them under an internal package?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Happy to use a single exported function, I'll make that change.

Signed-off-by: Meredith Lancaster <[email protected]>
func NewVerifyOpts() (VerifyOpts, error) {
opts := VerifyOpts{}

oidFlagVal := viper.GetString("oid")
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I might have misunderstood, but we should keep flags out of the core library and just in the client. If you want to have this function that handles things like parsing OIDs, it should take in values that are already read in from the flags. Also, personally I prefer to not have files read from disk as part of the library, so I think this should also be handled in the client and the chain passed into the function.

So I'd say either:

  • Move NewVerifyOpts to the client application with all flag parsing there
  • Move NewVerifyOpts to the client app, but provide some helper utilities for constructing OIDs and splitting PEM chains into cert pools
  • Keep NewVerifyOpts here, but have it take in all values (OID as a string, PEM chain as a string), and read in flags in the client app

Does that seem reasonable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree about keeping the flag parsing in the app CLI package. Just moved that over.

Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
@malancas malancas marked this pull request as ready for review November 21, 2022 20:11
@malancas malancas requested a review from a team as a code owner November 21, 2022 20:11
Signed-off-by: Meredith Lancaster <[email protected]>
@@ -43,6 +47,10 @@ func addVerifyFlags(cmd *cobra.Command) {
cmd.MarkFlagRequired("timestamp") //nolint:errcheck
cmd.Flags().Var(NewFlagValue(fileFlag, ""), "cert-chain", "path to certificate chain PEM file")
cmd.MarkFlagRequired("cert-chain") //nolint:errcheck
cmd.Flags().String("nonce", "", "optional nonce passed with the request")
cmd.Flags().Var(NewFlagValue(oidFlag, ""), "oid", "optional oid passed with the request")
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this refer to the hash algorithm OID ? If so, we might want to explain it in the description.

Copy link
Contributor

Choose a reason for hiding this comment

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

If so, we could rename this flag to hash-algorithm-oid

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, this refers to the TSA policy OID. I'll update the description to "optional TSA policy OID passed with the request".

if err != nil {
return verification.VerifyOpts{}, fmt.Errorf("failed to parse root and intermediate certs from cert-chain flag: %w", err)
}
if roots != nil && intermediates != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: The code looks good. I just share a suggestion here, feel free to adjust it.
To avoid all these != nil if statements for all the VerifyOpts fields, you could create functions for verifyOpts as such:

(v *VerifyOpts) OID(...) where you set the OID if passed.
(v *VerifyOpts) Nonce(...) where you set the Nonce if passed.
...
So you move all that logic inside these functions and only check for errors here.

Copy link
Contributor Author

@malancas malancas Dec 1, 2022

Choose a reason for hiding this comment

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

Since the VerifyOpts fields will be initialized to their respective zero values when the struct instance is created, we could just set the fields to the values returned by the getter functions like getCerts and remove the if statements altogether, since the getters will return zero values if no valid flag value is found. But I'm happy to add methods if you prefer.

Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
@malancas
Copy link
Contributor Author

malancas commented Dec 6, 2022

@haydentherapper there are changes to the PKCS7 methods in haydentherapper/pkcs7#2 that can be used in this pull request. I think it would be better to merge that one first and then pull the updated dependency into this pull request. The dependency update should be straight forward.

if err != nil {
return verification.VerifyOpts{}, fmt.Errorf("failed to parse root and intermediate certs from certificate-chain flag: %w", err)
}
if roots != nil && intermediates != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

It should be fine to unconditionally set the options. Also if there's only a root provided and no intermediates, will this conditional be skipped?

Copy link
Contributor Author

@malancas malancas Dec 6, 2022

Choose a reason for hiding this comment

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

Yes it will be skipped, I'll remove that.

Copy link
Contributor

@haydentherapper haydentherapper left a comment

Choose a reason for hiding this comment

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

LGTM, just a few nits

malancas and others added 7 commits December 6, 2022 16:07
Co-authored-by: Hayden B <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Co-authored-by: Hayden B <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Co-authored-by: Hayden B <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
Signed-off-by: Meredith Lancaster <[email protected]>
@haydentherapper
Copy link
Contributor

@malancas what do you think if we first merged this PR, rather than wait for the other? Then we'll get the pkcs7 changes merged, update the timestamp repo fork too, and then update verification to pass the intermediates/roots? We won't cut a new release til we get the CA cert verification in

Signed-off-by: Meredith Lancaster <[email protected]>
@malancas
Copy link
Contributor Author

malancas commented Dec 7, 2022

@haydentherapper that sounds good. I just want to call out the skipped test TestVerifyPassLeafCertificate. This test is currently failing because verifySignature expects that the TSR contain certificates. This function gets called by the PKCS7#VerifyWithChain method in the VerifyTSRWithChain function.

We can change the verifySignature function upstream along with the other changes discussed or I can change the code here and make sure the test passes. Let me know what you think.

@haydentherapper
Copy link
Contributor

@malancas Hm, yea, that's unfortunate. What do you think about updating the verifier to reject timestamps that do not include a certificate? We can leave all the other code in place, just add a TODO to remove the rejection once we update the pkcs7 library.

@haydentherapper
Copy link
Contributor

Another idea, can you populate p7.Certificates without affecting the signature verification? you might be able to, if nothing is recomputed that would mess up signature verification

@haydentherapper
Copy link
Contributor

Looking over the code, the signature is verified against p7.Content. I think signer.IssuerAndSerialNumber is unconditionally set, so then if you just populate p7.Certificates = []*x509.Certificate{leafCert}, I think it might work?

@malancas
Copy link
Contributor Author

malancas commented Dec 8, 2022

@haydentherapper populating p7.Certificates = []*x509.Certificate{leafCert} seems to work. I'll push a change with that.

Copy link
Contributor

@haydentherapper haydentherapper left a comment

Choose a reason for hiding this comment

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

Awesome!!

@haydentherapper haydentherapper merged commit c9b8a1a into sigstore:main Dec 8, 2022
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.

4 participants