Explicitly Parameterized ECDSA X.509 Certificates

Greg McLearn Common Criteria

Update 2020-Aug-20: There appears to be a discussion within the OpenSSL project on the semantics of checking the OpenSSL flag we describe below. We are working to clarify this.

NIAP (the US Common Criteria Scheme) recently published a series of technical decisions (TDs) about the use of ECDSA X.509 certificates crossing numerous Protection Profiles. The concern with this new mandatory test is that cryptographic libraries, such as OpenSSL, do not (or may not) reject the requested certificates out of the box, which is now required. This means vendors will have to implement another customized check for X.509 certificates in order to be conformant to a number of Protection Profiles.

NIAP TD 0527 is one of the seven recently published TDs. It has the following two-part test (we’ve added the (a) and (b) designators to help showcase the two distinct parts and to aid in the discussion).

Test 8: (Conditional on support for EC certificates as indicated in FCS_COP.1/SigGen). 
(a) The evaluator shall establish a valid, trusted certificate chain consisting of an EC leaf certificate, an EC Intermediate CA certificate not designated as a trust anchor, and an EC certificate designated as a trusted anchor, where the elliptic curve parameters are specified as a named curve. The evaluator shall confirm that the TOE validates the certificate chain. 
(b) The evaluator shall replace the intermediate certificate in the certificate chain for Test 8 with a modified certificate, where the modified intermediate CA has a public key information field where the EC parameters uses an explicit format version of the Elliptic Curve parameters in the public key information field of the intermediate CA certificate from Test 8, and the modified Intermediate CA certificate is signed by the trusted EC root CA, but having no other changes. The evaluator shall confirm the TOE treats the certificate as invalid.

https://www.niap-ccevs.org/Documents_and_Guidance/view_td.cfm?TD=0527

The NIAP TDs (rightly) claim that the motivation for these is because ECDSA parsing is tricky and point at CVE-2020-0601 as an example of this.  The referenced CVE, summarized, is that Microsoft’s certificate validation code failed to recognize that you can have different private keys mapping to the same public key across different elliptic curves. They implemented a shortcut by deciding that if a legitimately signed certificate had a public key matching that of an existing trusted CA in the cert store, then no other checks were performed. The issue is that explicitly defined EC parameters could replicate known public keys — using legitimate, but different, private keys — if the curve parameters were explicitly defined.

According to RFC 5480, section 2.1.1, using explicitly defined EC parameters is NOT permitted for X.509 certificates. Therefore any system which permits validation of them could be setting themselves up for future problems.  This is what the NIAP TDs are really doing: forcing vendors to reject explicitly defined EC parameterized X.509 certificates to avoid CVE-2020-0601-like assumptions and problems from occurring.

Does this mean libraries such as OpenSSL is susceptible to CVE-2020-0601? The short answer is: not at all. That CVE took advantage of the fact that a specific validation implementation in Windows was performing a short-circuit validation based on assumptions about public key uniqueness. This is not a weakness of X.509 or ECDSA keys, but it does highlight the inherent complexities of dealing with both of those topics.

Let’s come back to the TD wording. The TD appears to suggest that the evaluator is meant to construct an explicitly parameterized form of an otherwise valid key generated from a named curve. This can easily be done using OpenSSL’s command line tool to convert a pre-existing EC key generated from a named curve to an explicit curve:

openssl ec -in good.key -param_enc explicit -out explicit.key

The good.key and the explicit.key are equivalent with the only difference being that the crucial parameters for the curve (field, a, b, generator, order, seed, cofactor, etc.) are explicitly defined. (You can compare the output with the officially published parameters for the named curves and verify for yourself that they are the same.) These explicit keys can be used to sign additional CSRs, which will all validate as correct using OpenSSL (or even Windows’ certutil for that matter). The resultant X.509 certificate will present the explicit parameters in their public key section.

The NIAP TD effectively says that if you are presented with an intermediate CA that has explicitly defined curve parameters, you must reject the certificate chain. While this is much broader than the original CVE-2020-0601, it is generally in line with RFC 5480. It’s unfortunate, however, that almost all OpenSSL consumers (and likely other crypto library users) will have to adapt another custom check in their code to meet CC certification requirements.

Let’s showcase such a check in a working example, using the OpenSSL API:

In OpenSSL 1.0.2 and 1.1.x there is a default validation routine callback (which you almost certainly don’t want to touch) and a custom handler installed using SSL_CTX_set_verify where additional user-defined policy checks can be defined. Adding to this user-specific callback check, we can determine if a cert violates the new requirement by returning 0 (bad) or 1 (okay). (For brevity, we do not perform any of the necessary error checking, certificate depth analysis, etc.)

int verify_callback(int ok, X509_STORE_CTX *ctx)  {
    X509 *err_cert = X509_STORE_CTX_get_current_cert(ctx);
    EVP_PKEY* pkey = X509_get_pubkey(err_cert);
    if (EVP_PKEY_EC == EVP_PKEY_type(pkey->type)) {
        EC_KEY* key = EVP_PKEY_get1_EC_KEY(pkey);
        const EC_GROUP* group = EC_KEY_get0_group(key);
        int flags = EC_GROUP_get_asn1_flag(group);
        if ( !(flags & OPENSSL_EC_NAMED_CURVE) ) {
            /* Is an ECDSA cert, and curve is 
               not named. Reject and audit the
               reason along with the cert ID. */
            ok = 0;
        }
        EC_KEY_free(key);
    }
    EVP_PKEY_free(pkey);
    return ok;
}

The above is somewhat more liberal than the test requires in that it will reject any certificate which uses explicitly defined parameters. However, that is in line with RFC 5480, section 2.1.1 which unequivocally states that such curves MUST NOT be used. If there are legitimate needs for explicitly defined curve parameters in leaf certs or self-signed roots, then you should carefully consider the requirement and adjust accordingly. At the moment, the new NIAP test case requires the evaluator to force an intermediate CA in a chain of 2-4 certs (depending on the PP and the context) to contain the explicit parameters. It is possible that the test may be expanded in the future to reject any X.509 certificate containing explicit parameters.

 

Let Lightship Security help you stay on top of your certification challenges. Talk to us today!