SecurityEngineering/Certificate Verification: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
(beginning of updates to certificate verification documentation)
Line 1: Line 1:
== Certificate Verification in Firefox Before Version 31 ==
== Background ==


Firefox relies on NSS to implement various cryptographic functions. NSS consists of a collection of loosely-coupled libraries. libssl, for example, is the TLS implementation. NSS is a Mozilla project, but its development differs significantly from the rest of the tree. In fact, it has its own tree that is periodically imported wholesale into mozilla-central. The component that uses the NSS libraries in Firefox is a layer called PSM ("Personal Security Manager" or "Privacy and Security Module").
Gecko (and therefore Firefox) relies on NSS to implement various cryptographic functions. NSS consists of a collection of loosely-coupled libraries. libssl, for example, is the TLS implementation. NSS is a Mozilla project, but its development differs significantly from the rest of the tree. In fact, it has its own tree that is periodically imported wholesale into mozilla-central. The component that uses the NSS libraries in Firefox is a layer called PSM ("Personal Security Manager" or "Privacy and Security Module").


To enable secure TLS connections to the best of our ability, PSM implements a certificate verification callback. It performs a number of checks, but ultimately it must determine if it trusts a certificate presented by a peer. In versions before 31, it does this by calling one of two certificate verification libraries in NSS: "classic" or libpkix. The classic library handles DV ("domain validation") certificates while libpkix handles EV ("extended validation") certificates.
To enable secure TLS connections to the best of our ability, PSM implements a certificate verification callback. It performs a number of checks, but ultimately it must determine if it trusts a certificate presented by a peer. The approach PSM takes is to repeatedly build a potential path to a trust anchor, validate the potential path, and either return that path if it validates correctly or find another potential path. mozilla::pkix is a C++ library that provides a framework to implement this approach.


=== "classic" verification ===
== mozilla::pkix ==


The classic certificate verification algorithm performs issuer-independent checks on the given certificate, finds a potential issuer, verifies that the signature matches, and recurses. If multiple issuers are found, it attempts to use the "best" one. However, this is a heuristic, and as it does not perform backtracking, it can fail to verify a valid certificate. This is spectacularly apparent in the case of key-pinning if the algorithm chooses to not traverse a certificate path that contains a necessary key.
mozilla::pkix was originally implemented as part of mozilla-central (i.e. gecko) but has since been moved into NSS. However, it is not part of NSS' stable C API. As a library, mozilla::pkix uses the notion of a "trust domain" provided by the application to build a trusted chain from an end-entity certificate to a root. The trust domain is responsible for saying what trust level a certificate has, finding potential issuers of a certificate, and checking the revocation for a certificate. A certificate can be a trust anchor, it can inherit its trust, or it can be actively distrusted. Given an end-entity certificate and a trust domain, the library will perform issuer-independent checks on that certificate (e.g. expiration, appropriate key usages), get a list of potential issuers, and perform a depth-first traversal. If it encounters a distrusted certificate, it abandons searching that path. If it finds a trust anchor, it queries the trust domain again to see if that path is acceptable (this is where gecko implements checks that are specific to the platform at not the abstract problem of building a trusted certificate chain). If so, the end-entity certificate has successfully been verified.


Because this library is written in C and because NSS makes strong guarantees about API backwards-compatibility, it would require significant work to fix. This would be on par with writing a new verification library.
Unlike the other NSS libraries, mozilla::pkix is written in C++ and can take advantage of more modern language features.


The code is here: https://mxr.mozilla.org/mozilla-central/source/security/nss/lib/certhigh/
=== Trust Anchors ===


=== libpkix ===
The platform looks for trust anchors in a few locations. First, Mozilla ships a list of trust anchors with the platform corresponding to the root Certificate Authorities (CAs) in the Mozilla Root CA Program. Additionally, the user may import their own trust anchors. These are stored in the profile's cert9.db file. The user may also import third-party PKCS#11 modules that provide trust anchors. The enterprise roots feature, if enabled, may collect trust anchors provided by the operating system.
libpkix was auto-translated from Java to C. It attempts to implement Java's exception semantics in C. It makes liberal use of unclear macros (e.g. https://mxr.mozilla.org/mozilla-central/source/security/nss/lib/libpkix/pkix/util/pkix_tools.h#67 ). A source-line-counting tool clocks it in at 45,000 lines of code (the code is here: https://mxr.mozilla.org/mozilla-central/source/security/nss/lib/libpkix ). There are known bugs in the implementation. No one who works on it wants to continue working on it.


These libraries were not serving our needs and were impeding progress, which resulted in the decision to write the new verification library, mozilla::pkix.
=== Extended Validation ===


== Certificate Verification in Firefox As of Version 31 ==
As part of Mozilla's Root CA Program, there is a list of root certificates that are trusted to issue Extended Validation (EV) certificates. This list is available in code form at https://hg.mozilla.org/mozilla-central/annotate/80374044414da9f5b3634c91345d07612754fcda/security/certverifier/ExtendedValidation.cpp#l90
At the time of the decision to work on mozilla::pkix, we had a number of options:
# We could have fixed the classic verification implementation. As already stated, this would have required considerable work.
# We could have fixed and maintained libpkix ourselves. This was undesirable for the aforementioned reasons. Furthermore, as Google moved away from NSS, we would have had less and less help working on this library.
# We could have used whatever verification implementation Google develops. We would have had to wait a year or more for this, and we would be depending on Google to share their work.
# We could have used OpenSSL's certificate verification routine. Apparently it is buggy as well.
# We could have started over from scratch and written another entirely new verification library. This would have set us back a year.
# Finally, we could have used the new verification library known as "mozilla::pkix" (formerly known as "insanity::pkix"). This is what we decided to do.


=== mozilla::pkix Design ===
== Other Verification Routines in NSS ==
As a library, mozilla::pkix uses the notion of a "trust domain" provided by the application to build a trusted chain from an end-entity certificate to a root. The trust domain is responsible for saying what trust level a certificate has, finding potential issuers of a certificate, and checking the revocation for a certificate. A certificate can be a trust anchor, it can inherit its trust, or it can be actively distrusted. Given an end-entity certificate and a trust domain, the library will perform issuer-independent checks on that certificate (e.g. expiration, appropriate key usages), get a list of potential issuers, and perform a depth-first traversal. If it encounters a distrusted certificate, it abandons searching that path. If it finds a trust anchor, it queries the trust domain again to see if that path is acceptable (here is where we check key pinning). If so, the end-entity certificate has successfully been verified.


Unlike the NSS libraries, mozilla::pkix is written in C++. As a result, we can use scoped data types that automatically clean up after themselves rather than having to manually manage memory. This reduces memory-safety bugs as well as error-handling bugs.
NSS exposes other certificate verification functions that are not yet implemented using mozilla::pkix.


=== Progress ===
=== "classic" verification ===
We have been working on this project for a long time. Last year, progress was slower than everyone would have liked. However, starting in late January, development picked up considerably to the point where we had landed a working implementation (albeit with no OCSP checking) within a month. Since then, we have landed OCSP checking and test improvements, as well as an OCSP cache. In fact, we managed to have a sufficiently complete and interoperable implementation for it to be released in Firefox 31 on July 22nd, 2014.
The library code is here: https://mxr.mozilla.org/mozilla-central/source/security/pkix/ and the trust domain is here: https://mxr.mozilla.org/mozilla-central/source/security/certverifier/


=== Tests ===
The so-called "classic" certificate verification algorithm performs issuer-independent checks on the given certificate, finds a potential issuer, verifies that the signature matches, and recurses. If multiple issuers are found, it attempts to use the "best" one. However, this is a heuristic, and as it does not perform backtracking, it can fail to verify a valid certificate. This is spectacularly apparent in the case of key-pinning if the algorithm chooses to not traverse a certificate path that contains a necessary key.
Due to the sensitive nature of this code, we want to ensure proper testing. To that end, we first made sure the new implementation passed the same tests as the current implementation. We then added more tests, finding some bugs in both implementations in the process. At this point, while we will add still more tests, we believe it would be beneficial for the community at large to inspect the design and implementation of the code. Note that this stems not from a lack of confidence in code quality but rather the understanding that the privacy of our users depends on the correctness of this code.


Matt Wobensmith just completed compatibility-testing of 200k HTTPS sites and found 16 with issues. These were investigated and resolved to our satisfaction.
The code is here: https://hg.mozilla.org/projects/nss/file/tip/lib/certhigh


=== Implementation status ===
=== libpkix ===
 
libpkix was auto-translated from Java to C. It attempts to implement Java's exception semantics in C. It makes liberal use of unclear macros (e.g. https://hg.mozilla.org/projects/nss/annotate/5d9f8b809e6f7020529ba1345b64e36f61994c8d/lib/libpkix/pkix/util/pkix_tools.h#l67 ). A source-line-counting tool clocks it in at 45,000 lines of code (the code is here: https://hg.mozilla.org/projects/nss/file/tip/lib/libpkix ). There are known bugs in the implementation. No one who works on it wants to continue working on it.
Released in FF 31.
 
Remaining bugs:
# Enforce consistent handling of isCA bit and certSign/crlSign key usages: {{bug|970196}} (:briansmith)
# Enable all PSM xpcshell tests on Android/B2G: {{bug|676972}} (:keeler)
# Add SHA-2 support to the OCSP implementation: {{bug|966856}} (:keeler)
# Document functions exported from the library: {{bug|968451}} (:briansmith)
 
For more details, see the dependency trees for {{bug|915930}} and {{bug|976961}}, respectively.
 
== Choosing your Verification Library ==
 
mozilla::pkix has been enabled by default in Firefox 31. The boolean pref "security.use_mozillapkix_verification" controls this. There is an additional hidden boolean pref "security.use_libpkix_verification" that can be used to enable libpkix instead. If both of these prefs are false, classic verification is used. mozilla::pkix takes precedence over libpkix. See the following table:
 
{|border="1" cellpadding="5" cellspacing="0" align="center"
! colspan="2" rowspan=2|
! colspan="2"|security.use_libpkix_verification
|----
| false (default)
| true
|----
|rowspan=2 | '''security.use_mozillapkix_verification'''
| false
| Classic
| libpkix
|----
|true (default)
| mozilla::pkix
| mozilla::pkix
|----
|}
 
As of Firefox 33, mozilla::pkix is the only verification library available.

Revision as of 00:07, 8 October 2019

Background

Gecko (and therefore Firefox) relies on NSS to implement various cryptographic functions. NSS consists of a collection of loosely-coupled libraries. libssl, for example, is the TLS implementation. NSS is a Mozilla project, but its development differs significantly from the rest of the tree. In fact, it has its own tree that is periodically imported wholesale into mozilla-central. The component that uses the NSS libraries in Firefox is a layer called PSM ("Personal Security Manager" or "Privacy and Security Module").

To enable secure TLS connections to the best of our ability, PSM implements a certificate verification callback. It performs a number of checks, but ultimately it must determine if it trusts a certificate presented by a peer. The approach PSM takes is to repeatedly build a potential path to a trust anchor, validate the potential path, and either return that path if it validates correctly or find another potential path. mozilla::pkix is a C++ library that provides a framework to implement this approach.

mozilla::pkix

mozilla::pkix was originally implemented as part of mozilla-central (i.e. gecko) but has since been moved into NSS. However, it is not part of NSS' stable C API. As a library, mozilla::pkix uses the notion of a "trust domain" provided by the application to build a trusted chain from an end-entity certificate to a root. The trust domain is responsible for saying what trust level a certificate has, finding potential issuers of a certificate, and checking the revocation for a certificate. A certificate can be a trust anchor, it can inherit its trust, or it can be actively distrusted. Given an end-entity certificate and a trust domain, the library will perform issuer-independent checks on that certificate (e.g. expiration, appropriate key usages), get a list of potential issuers, and perform a depth-first traversal. If it encounters a distrusted certificate, it abandons searching that path. If it finds a trust anchor, it queries the trust domain again to see if that path is acceptable (this is where gecko implements checks that are specific to the platform at not the abstract problem of building a trusted certificate chain). If so, the end-entity certificate has successfully been verified.

Unlike the other NSS libraries, mozilla::pkix is written in C++ and can take advantage of more modern language features.

Trust Anchors

The platform looks for trust anchors in a few locations. First, Mozilla ships a list of trust anchors with the platform corresponding to the root Certificate Authorities (CAs) in the Mozilla Root CA Program. Additionally, the user may import their own trust anchors. These are stored in the profile's cert9.db file. The user may also import third-party PKCS#11 modules that provide trust anchors. The enterprise roots feature, if enabled, may collect trust anchors provided by the operating system.

Extended Validation

As part of Mozilla's Root CA Program, there is a list of root certificates that are trusted to issue Extended Validation (EV) certificates. This list is available in code form at https://hg.mozilla.org/mozilla-central/annotate/80374044414da9f5b3634c91345d07612754fcda/security/certverifier/ExtendedValidation.cpp#l90

Other Verification Routines in NSS

NSS exposes other certificate verification functions that are not yet implemented using mozilla::pkix.

"classic" verification

The so-called "classic" certificate verification algorithm performs issuer-independent checks on the given certificate, finds a potential issuer, verifies that the signature matches, and recurses. If multiple issuers are found, it attempts to use the "best" one. However, this is a heuristic, and as it does not perform backtracking, it can fail to verify a valid certificate. This is spectacularly apparent in the case of key-pinning if the algorithm chooses to not traverse a certificate path that contains a necessary key.

The code is here: https://hg.mozilla.org/projects/nss/file/tip/lib/certhigh

libpkix

libpkix was auto-translated from Java to C. It attempts to implement Java's exception semantics in C. It makes liberal use of unclear macros (e.g. https://hg.mozilla.org/projects/nss/annotate/5d9f8b809e6f7020529ba1345b64e36f61994c8d/lib/libpkix/pkix/util/pkix_tools.h#l67 ). A source-line-counting tool clocks it in at 45,000 lines of code (the code is here: https://hg.mozilla.org/projects/nss/file/tip/lib/libpkix ). There are known bugs in the implementation. No one who works on it wants to continue working on it.