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

Discussion: Consider how proposed CA/B Forum SCWG profiles will be linted #583

Open
sleevi opened this issue Mar 30, 2021 · 6 comments
Open

Comments

@sleevi
Copy link
Contributor

sleevi commented Mar 30, 2021

I recently shared with the CA/B Forum the latest draft that attempts to overhaul how the Server Certificate Working Group expresses requirements on certificates, by moving from its three section approach ("root", "subordinate", "leaf") into category-specific profiles that fully express the intersections and permitted variations.

I wanted to draw attention to it for ZLint contributors, since while it's a draft, there's opportunity to flag any approaches that may make things easier or harder to lint. That is, I'd like to avoid a situation like ETSI (mentioned in #581 (comment) ) where things are too context-dependent and subjective to be effectively linted.

To submit feedback to the CA/B Forum, questions@cabforum.org can be used (for those that aren't interested parties or employed by members), but I will also do my best to relay problems, although relaying solutions may be a bit IPR-problematic. It's open as a PR at sleevi/cabforum-docs#36 to provide easier diffing.

As a concrete example, the semantics of extensions like extendedKeyUsage or certificatePolicies can vary based on the type of CA certificate being issued, and whether the organization is an Affiliate or not of the issuing CA. The Affiliation status is not something we can know purely from the certificate alone. One option would be to signal some sort of "intended profile" (e.g. as a parameter to be passed through to lints as a context hint). Another option could just be as a warning/notice to require manual review.

I don't have strong opinions here, but did want to draw extra eyes to the work, in case folks read the profiles work and needed to say "Hold on, this will make linting much harder than it needs to be" :)

@christopher-henderson
Copy link
Member

First of all, this tabular format is very helpful from a legibility perspective. It seems like so much time is spent converting prosaic English into lints that a format that is darned near machine readable is appreciated. So a huge thanks for that 😄

This is certainly not the first time that we have faced the Context Demon. For example, we had #522 (What to do with ambiguous results?) which stemmed from certain lints only being deterministic if we were working within the context of the full certificate chain. And, of course, we have this week's #581 regarding ETSI and its policies that are out-of-band of the physical certificate in question.

That is, three GitHub issues make a line, so perhaps it's time to the pull the trigger on a context/config addition to ZLint.

type CheckAppliesWithCtx interface {
	CheckAppliesWithCtx(cert *x509.Certificate, ctx Context)
}

func (l *MyLint) CheckAppliesWithCtx(cert *x509.Certificate, ctx Context) bool {
	return cert.IsCA && ctx.cabfbr.ca_affiliated_with_issuer
}


ctxFreeApplies := l.CheckApplies(cert)
contextualApplies := true
// Make the presence of this interface purely optional
// so that A). we don't have to go back and add it to
// everything and B). so that future lints don't have
// to build a blind "return true" over-and-over again
// (I reckon that contextual lints should, hopefully,
// be much rarer than context free lints).
if lint, ok := l.(*CheckAppliesWithCtx); ok {
	contextual = lint.CheckAppliesWithCtx(cert, ctx)
}
return ctxFreeApplies && contextualApplies
$ zlint myCert.pem
# Now with a config file
$ cat >> myConfig.toml << EOF
[cabfbr]
ca_is_affiliated_with_issuer=true
EOF
$ zlint myCert.pem --config=myConfig.toml
# Which could naturally extend into CLI declarations
$ zlint myCert.pem --configs="cabfbr.ca_affiliated_with_issuer=true, cabfbr.beer_was_served_at_signing_ceremony=false"
# And maybe even environment variables
$ export ZLINT__CABFBR__CA_AFFILIATED_WITH_ISSUER=true
$ zlint myCert.pem

Of course, this then spirals into conversations on datastructures, file formats, and so on so I will leave that to another thread.

@sleevi would something like this ease your mind and empower you to write richer requirements? @cpu @zakird of course the specifics are negotiable, but is something like the above palatable to the project?

@sleevi
Copy link
Contributor Author

sleevi commented Apr 4, 2021

@christopher-henderson Kind of! One of the things I was worried about is that in the approach taken by the certificate profiles work, one of the goals I had was trying to align of "Every cert issued must meet at least ONE of the following profiles". I think ETSI shares some similarity with that (admittedly, it's a must larger set of profiles, some CA-defined), although I think #522 was a bit different (since that was about chain context).

Without wanting to rathole too much on datastructures/configurations, I think when combined with tools like EJBCA and how it expresses lints, we may want to frontload some of that logic into ZLint.

In particular, what I'm wondering is whether it makes sense to have a ZLint concept of "profile", which is a collection of lints to be run together on a cert. If any of those individual lints fail, the profile fails; likewise, any lints that warn, the profile warns. I'm not sure if there's a CheckApplies for the profile, or whether that's something supplied as an input. For a TLS CA, they might want to config all certs to be linted against (SubscriberCert OR OCSPResponder)? This is a TODO in the current draft profiles work: to express what each profile a given CA may issue. I think we could accomplish the same via your proposed format (e.g. a context variable that indicated which profile to use, for example), but what I wasn't sure is the impact of "invoke zlint for all certs issued from this CA".

The situation when a CA is allowed to issue X or Y - I'm not sure how our CheckApplies should work. Today, we largely use EKU and certificatePolicies, but the new tabular approach has the side-effect of making that much less obvious, and that's what prompted me to file this. In order to work through CheckApplies for something like, say, nameConstraints, you have to compare all the profiles to figure out when you can apply the check (and see, for example, a difference between TLS and non-TLS CAs).

For issuing intermediates, from a root at least there's a manual ceremony, so I would expect they run the lint as --profile=specific_profile_to_evaluate. Intermediates from another intermediate, which may be automated after the keygen ceremony, might have something like --profile="(A or B")", or might use the --config you mentioned specific to the cert (e.g. if EJBCA were to configure context variables per EJBCA-profile, and then pass those to the linter).

For ETSI, if it continues to be supported, I would imagine that there end up profiles like eIDAS's legally recognized types: e.g. qualified signature cert on a sscd, qualified signature cert not on a sscd, non-qualified timestamping certificate. But I didn't want to lean too heavily on ETSI when trying to figure out how to tackle this, precisely because of #581

@christopher-henderson
Copy link
Member

In particular, what I'm wondering is whether it makes sense to have a ZLint concept of "profile", which is a collection of lints to be run together on a cert.

So we do have a way to accomplish this, but from a CLI approach that would be painful for an individual operator to type out if these profiles are even modestly rich.

From the README

echo "Lint mycert.pem with just the two named lints"
zlint -includeNames=e_mp_exponent_cannot_be_one,e_mp_modulus_must_be_divisible_by_8 mycert.pem

Which, of course, there would be nothing stopping people from writing bash scripts with the appropriate -includeNames here to encapsulate a particular profile. Alternatively, we could maintain a list of known profiles such that

$ zlint --profile=specific_profile_to_evaluate

Would internally expand to

$ zlint -includeNames=one_lint,two_lint,three_lint,ah_ah_ah_lint

Now as for --profile="(A or B")", would this mean Run profile A and if it succeeds then exit with code 0. Otherwise, run profile B and report its outcome?

@sleevi
Copy link
Contributor Author

sleevi commented Apr 4, 2021

Right, my concern (with the existing approach and the context) is that it would significantly make it more difficult for CAs to get things right, as they have to re-derive from the BRs all the lints to enable for a given profile, and it means introducing new lints for those profiles wouldn’t get enabled.

I see it as a spectrum of flexibility vs complexity, for both this project and the CA: how much work for CAs do we de-duplicate, accepting that complexity for ourselves, versus expose CAs to.

For example, let’s say we said profiles = a bundle of lints, and we manage that in ZLint, but a CA that wanted (A or B) syntax was on their own. They COULD wrap that via a helper script (as the EJBCA example shows them doing for ZLint already), to run with A, and if that fails, run with B. The Boolean logic could totally be scripted, whether via Bash or via wrapping ZLint itself as a library with a CA-specific driver program.

Ultimately, I think all the maintainers share a common goal of making it easy enough to use that the majority/all CAs can run this as part of their infrastructure, without making this too complex or unwieldy to maintain. I’m honestly not sure where the best point along that spectrum is, but my worry with the profiles work is that it would inadvertently make it too complex for CAs to run or for us to maintain. That’s why I wanted to get ahead of that with this issue 😅

@christopher-henderson
Copy link
Member

@sleevi so it seems that we have two enhancements here and I'm fishing for whether or not they need to be tackled together in order to make sense at all or if they can be landed one at a time (and perhaps timelines so that we try to head the industry off at the pass).

  1. Context declarations for information that either spans multiple certificates or is not encoded within certificates at all, and
  2. Profiles, which are preset collections of lints to run together

I believe that #1 would go a great deal towards resolving other discussions as well. Not the least of which are the complicated ETSI requirements. #2 sounds easy enough.

Although, what is the timeline for the finalization for these profiles? I would both loathe to suddenly be behind published requirements, but at the same time I believe that the context objects would be more valuable towards chunking down the blacklog.

@sleevi
Copy link
Contributor Author

sleevi commented Apr 25, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants