Giter VIP home page Giter VIP logo

webpki-roots's People

Contributors

briansmith avatar cpu avatar ctz avatar dependabot-preview[bot] avatar dependabot[bot] avatar djc avatar fauxfaux avatar fenhl avatar japaric avatar jbg avatar jsha avatar mspiegel avatar paolobarbolini avatar zh-jq-b avatar zkonge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webpki-roots's Issues

Switch to Rust build scripts

Depending on Python and OpenSSL for a Rust project that aims to replace the OpenSSL dependency looks kind of embarrassing. By moving to a Rust build script, not only would it be faster, but you'd be able to use existing libraries directly rather than delegating to OpenSSL.

There are two ways to do this:

  • Use a regular build.rs. This requires that each consumer download the list of certificates and generate the list of roots themselves, which means any certificate could poof out from under people without any crate version change. That is probably undesirable.
  • Use external Rust code to update the lib.rs, as the Python script currently does, before publish. The code would have to be executed via another crate or via cargo-script.

In either case, you'd be eliminating the dependency on competing crypto libraries (which OpenSSL is), and eliminating the dependency on a completely unrelated, external programming language (Python). Modern Linux systems do not even have Python installed by default, so it's one more step for users looking to contribute to this project. Additionally, as a small bonus, the build script would work on Windows. It is a small bonus given Windows' unsuitability to low-level applications, as any serious developer will be using WSL anyway, but even if only for drive-by contributions, it is a small victory.

Using CCADB as the source of truth

I think it makes sense to transition the webpki-roots generation process to use ccadb as its source of truth. There are a few advantages in my mind:

  1. it removes a middle-man, the mkcert.org service called out as potentially problematic as a dependency in #25.
  2. it will generalize beyond Mozilla's specific requirements. Other root programs are participating in CCADB.
  3. it offers richer metadata, e.g. about distrust after dates, applied name constraints, etc.

With this goal in mind I've been (slowly, on the side) creating a set of CCADB related tools over in cpu/ccadb-utils:

  • the ccadb-csv crate is a very thin abstraction around raw CSV reports available on CCADB. It does no parsing above destructuring CSV records and presents the content "as-is", in string form.
  • the ccadb-csv-fetch crate is a small binary for fetching the CSV reports to disk. It bakes in exactly one trust anchor, trying to minimize the trust surface required for acquiring metadata from CCADB.
  • the ccadb-webpki-roots crate converts the CCADB Mozilla included roots report into a .rs for use inside webpki-roots.
  • and lastly, not of use to this repo but there's a ccadb-crl-fetch crate for downloading disclosed CRLs, mostly as a test corpus.

Some open questions I'd like to discuss:

  1. Do folks agree we should switch to CCADB as the data source for this crate?
  2. What's the best way to integrate my work if folks agree to the above?
    1. We could use ccadb-utils as part of a build process in this repo.
    2. We could move the ccadb-utils repo into the Rustls organization and use it as part of the build process in this repo.
    3. We could lift something smaller out of the ccadb-utils repo into this repo.
    4. some other option.

I think I lightly prefer keeping the tooling in a separate repository (and have no strong feelings about whether it lives in the rustls org or not) and using it as a dev-dependency in this repo. If we go this route I would love reviews on the code in ccadb-utils - it was written solo and could probably use polish/love from more experienced rustaceans.

My thinking is that there's likely to be continued value in having a richer way to work with the CCADB and we can consolidate that work in a separate repo as opposed to having to maintain the webpki-roots specific parts here, and the rest elsewhere. As one example of a future use-case there's in-progress work in the TLS working group to use CCADB data to execute certificate compression (draft-jackson-tls-cert-abridge).

Question: what assurance do you offer the Root CA store is 'trustworthy'

So I've started using Rust more, in 2020 I found and utilised Rustls (thank you)

I listen to Deirdre Connolly who is a massive fan of Rustls (and I am a fan of her so by extension..) and it eventually occurred to me that I should at least verify 'my trust' in 'my' usage.

Inherently a Root CA store must be trusted when used, and to be trustworthy the expectation is the trust we place can be validated.

Being open source is a good start, but it doesn't answer the specific question; The certificates in this Root CA store are verifiable trustworthy

And this repo does not actually address assurance directly either

I can verify what the code does, bare with me as I'm the maintainer of tlstrust on pypi which aims to verify trustworthiness of all Root CA stores, there is no bias, you have not been targeted


Assertion 1. The crate contains curated root certificates for use with the webpki and anywhere rustls itself is used. You apply filtering on the subject and produce the resultant Root CA Trust Store.

Assertion 2. Before you process the bundle of certs you fetch over HTTP from mkcert.org, mistakenly referred to as the "Mozilla tooling", which it is not

Assertion 3. Mozilla's original source of these root certificates is the Common CA Database (CCADB) (also the source for Google, Microsoft, Apple, and many more) but the not-Mozilla mkcert tooling consumes the Network Security Services (NSS) bundle instead because it's already curated for their own products


Conclusion 1. The assurance of NSS is not yours to provide, it is gained from Mozilla

Conclusion 2. You cannot verify anything about the environment mkcert.org - what is served over HTTP could have been MitM anywhere along the line. It would have been verifiable had you run the mkcert tooling locally in an environment you control and/or validated the resultant fingerprints from CCADB/NSS

Conclusion 3. The subject is not secure so filtering on it is not a trustworthy mechanism. If additional filtering by you is necessary at all, the most appropriate filtering mechanism would be the Subject Key Identifier (SKI), or the serial number, or a SHA256 fingerprint.


At this stage, using well defined and accepted security characteristics, I can't see how the Rustls Root CA store is verifiable.
Without verification it really is not trustworthy.
And the single most important inherent characteristic of a Root CA store is that is must be trusted.

Why so many semver-incompatible releases?

I was wondering why this crate so frequently has new semver-incompatible releases. Currently a project I work on depends on both 0.19.0 and 0.20.0, then I saw 0.21.0 is out now as well; I generally try to avoid duplicate versions of dependencies. It would be nice if new versions that only update TLS_SERVER_ROOTS entries but don't change the webpki version or otherwise have API-breaking changes could be patch releases (so 0.21.0 would have been 0.20.1).

TUBITAK1_NAME_CONSTRAINTS misencoded?

Summary

In #36 the TUBITAK1_NAME_CONSTRAINTS constant was updated:

const TUBITAK1_NAME_CONSTRAINTS: &[&[u8]] =
&[b"\xa0\x0b\x30\x09\xa0\x07", b"\x30\x05\x82\x03", b".tr"];

There was some back and forth in #36 about the encoded contents, but looking closer today I think we might have arrived at the wrong encoding.

Right now the name_constraint for this root ends up loaded into the TrustAnchor's name_constraints field as:
0xA0 0x0B 0x30 0x09 0xA0 0x07 0x30 0x05 0x82 0x03 0x2E 0x74 0x72

I think the correct encoding is:
0xA0 0x07 0x30 0x05 0x82 0x03 0x2E 0x74 0x72

Notably this drops the first four bytes (0xA0 0x0B 0x30 0x09) of the existing constraint bytes.

(Side note; I really dislike the encoding format that the lib.rs has where it uses a byte string with ascii range characters represented as-is and the rest hex escaped. I think it would be better to express this as Some(&[<hex literals>]))

Discussion

I arrived at this conclusion in two ways:

1. The new test case isn't using the permitted subtrees.

The test case added in ff68fd9 doesn't seem to be using the name constraint when making a trust decision.

If you step through execution of the test in a debugger, letting execution get down into the webpki/subject_name/verify.rs's check_presented_id_conforms_to_constraints_in_subtree fn that gets called three times (once per cert in the chain) to perform subtree name constraint validation, you'll find that the base GeneralName read from match general_subtree(&mut constraints) is not a GeneralName::DnsName(".tr".as_bytes()) as you'd expect, but GeneralName::Unsupported.

This happens because when GeneralName::from_der is called to parse the base general name it is being given the DER encoded input 0xA0 0x07 0x30 0x05 0x82 0x03 0x2e 0x74 0x72. This breaks down to a tag of 0xA0 (CONTEXT_SPECIFIC | CONSTRUCTED | 0, aka OTHER_NAME_TAG), a length of 0x07, and the value 0x30 0x05 0x82 0x03 0x2E 0x74 0x72 (aka the nonsense string: "0\x05\x82\x03.tr"). The GeneralName::from_der processing of this content matches the OTHER_NAME_TAG into the GeneralName::Unsupported arm, returning an unsupported general name as the base of the permitted subtree constraint.

Back in check_presented_id_conforms_to_constraints_in_subtree execution continues through match (name, base) where we hit the _ arm because we are never comparing GeneralName::Unsupported names from a certificate in the chain against the GeneralName::Unsupported base from the permitted subtrees.

Ultimately check_presented_id_conforms_to_constraints_in_subtree finds has_permitted_subtrees_mismatch == false and has_permitted_subtrees_match == false and returns NameIteration::KeepGoing. The net result is that validation succeeds, but no permitted subtree base was ever truly examined against a certificate subject name.

It seems obvious that the GeneralName base for the permitted subtrees should be a DnsName, but in case there was any doubt that otherName isn't correct, RFC 5280 4.2.1.10 says:

The syntax and semantics for name constraints for otherName, ediPartyName, and registeredID are not defined by this specification, however, syntax and semantics for name constraints for other name forms may be specified in other documents.

2. PyCA Cryptography generates a different encoding for the same constraint

To help double check my reasoning I wrote a quick and dirty Python script to spit out a CA certificate with a name constraint that I think matches the intent for KamuSM:

ca_cert_builder = ca_cert_builder.add_extension(
    x509.NameConstraints(permitted_subtrees=[x509.DNSName(".tr")], excluded_subtrees=None),
    critical=True,
)

Loading the encoded cert as a TrustAnchor we end up with a name_constraints field of Some(&[0xA0, 0x07, 0x30, 0x05, 0x82, 0x03, 0x2E, 0x74, 0x72]). This matches the last 9 bytes of the current name constraint, but is missing the first four bytes (0xA0 0x0B 0x30 0x09).

Running through the same debugging session as described above for the existing name constraint we can see that when it comes time to read the base of the permitted subtree GeneralName::from_der gets the DER input: 0x82, 0x03, 0x2E, 0x74, 0x72. This breaks down as tag 0x82 (CONTEXT_SPECIFIC | CONSTRUCTED | 2, aka DNS_NAME_TAG), a length of 0x03, and the value 0x2E 0x74 0x72, aka ".tr".

Unlike the previous session, the DNS_NAME_TAG is matched into a GeneralName::DnsName(...), not a GeneralName::Unsupported.

Back in check_presented_id_conforms_to_constraints_in_subtree when we come to match (name, base) we can compare a DnsName from the end entity certificate to the DnsName from the base of the permitted subtree. Unlike the previous session, we actually do a comparison against the base of the permitted subtree using dns_name::presented_id_matches_constraint.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.