Re: [dtn] PKIX Extended Key Purpose recommendations

Benjamin Kaduk <> Thu, 03 December 2020 07:29 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 1B6023A08D6 for <>; Wed, 2 Dec 2020 23:29:52 -0800 (PST)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -1.92
X-Spam-Status: No, score=-1.92 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, RCVD_IN_MSPIKE_H4=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id Jdh5309o9vwb for <>; Wed, 2 Dec 2020 23:29:50 -0800 (PST)
Received: from ( []) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by (Postfix) with ESMTPS id E92513A08C5 for <>; Wed, 2 Dec 2020 23:29:49 -0800 (PST)
Received: from ([]) (authenticated bits=56) (User authenticated as kaduk@ATHENA.MIT.EDU) by (8.14.7/8.12.4) with ESMTP id 0B37Tgwh017165 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 3 Dec 2020 02:29:47 -0500
Date: Wed, 2 Dec 2020 23:29:42 -0800
From: Benjamin Kaduk <>
To: Brian Sipos <>
Cc: Russ Housley <>, "" <>
Message-ID: <>
References: <>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
In-Reply-To: <>
Archived-At: <>
Subject: Re: [dtn] PKIX Extended Key Purpose recommendations
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: "Delay Tolerant Networking \(DTN\) discussion list at the IETF." <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Thu, 03 Dec 2020 07:29:52 -0000

Hi Brian,

Replying just on the OpenSSL front for now, since I am not sure how much
longer I will still be awake...

On Fri, Nov 20, 2020 at 05:34:50PM +0000, Brian Sipos wrote:
> The good news for rationale and understanding of these uses are that they are very similar to SMTP/TLS [1] and S/MIME [2] respectively. Although it appears that the id-kp-emailProtection OID was only ever specified for S/MIME and not for email transport between MTAs. I don't know what kinds of policies SMTP MTAs have with regard to PKIX certificates, even recent MTA-STS spec [3] doesn't define or reference a more detailed PKIX profile and doesn't mention any X.509 extensions at all.
> The bad news is that in some very simple testing of adding a new purpose OID I see that OpenSSL, which is used by both of my reference libraries Qt5 and python "ssl" package, appears to assume [4] that when I want "TLS on a socket" what I mean is "TLS for Web client/server purposes" and requires either id-kp-serverAuth or id-kp-clientAuth. There also appear to be quite a number of other implicit uses of the Web purposes in OpenSSL and no real access via higher-level APIs to affect this. The other key purposes seem to be usable only through different APIs unrelated to TLS.

It ends up not actually being too hard to get OpenSSL to behave differently
in this regard, though it is very much one of those "the answer is simple
only once you already know it" situations, and is further complicated by
the cornucopia of similarly named API functions.  (Neither of these is
unusual for OpenSSL, sadly, nor will they get fixed anytime soon, due to
the API stability promise.)

It is perhaps easiest to explain by reference to the libssl code that
surrounds the behavior in question; :

     * We need to inherit the verify parameters. These can be determined by
     * the context: if its a server it will verify SSL client certificates
     * or
     * vice versa.

    X509_STORE_CTX_set_default(ctx, s->server ? "ssl_client" : "ssl_server");
     * Anything non-default in "s->param" should overwrite anything in the
     * ctx.
    X509_VERIFY_PARAM_set1(param, s->param);

    if (s->verify_callback)
        X509_STORE_CTX_set_verify_cb(ctx, s->verify_callback);

    if (s->ctx->app_verify_callback != NULL)
        i = s->ctx->app_verify_callback(ctx, s->ctx->app_verify_arg);
        i = X509_verify_cert(ctx);

The X509_STORE_CTX_set_default() call is what ends up causing the X.509
implementation to look for the id-kp-serverAuth/id-kp-clientAuth EKU value,
when X509_verify_cert() is called.  However, it is also clear from the last
quoted part that the "app_verify_callback" can take the form of a thin
wrapper around X509_verify_cert() that applies the needed configuration
changes to the X509_STORE_CTX configuration.  (Note that there is also the
verify_callback that can be used to override verification decisions at a
very fine-grained level but is also much more sensitive to implement
safely.  The man page at least does have a prominent disclaimer to not
confuse the two...) However, the the table of standard purpose values that
openssl natively supports (static X509_PURPOSE xstandard[] in v3_purp.c) is
pretty limited, and so in practice when using other EKUs we end up using
the "Any Purpose" entry and making the application code do the rest.

The X509_PURPOSE_add() API does to let the application add one at runtime,
but that involves getting a "NID" (OpenSSL-internal integer shorthand for
an ASN.1 object identifier), which involves OBJ_create() at runtime, as
well as specifying a function to do the actual checking.  If it's only
going to be used at one place in the application, that's a lot of overhead
when it can just be coded inline in the app verify callback!  (The relevant
checks that are done by default for the ssl_client/ssl_cerver uses are in
check_purpose_ssl_client() and check_purpose_ssl_server(), also in
v3_purp.c.  The xku_reject() macro they use is not helpful, since openssl
doesn't have a defined flag for the new EKU type, so this would entail the
loop over the X509_get_ext_d2i(., NID_ext_key_usage, ...) results and
looking for the new OID.)

I would like to write some code and have a bit more of an example for you,
but am wary about promising too much...

In short, there are no higher-level APIs because the OpenSSL implementation
requires too much knowledge in the library to make it easy to provide such
a high-level API.  But there is a hook to let application code run in
exactly the right place to take over the bits of X509 verification that are
affected, so the application can do what it needs to with only a relatively
small amount of low-level code.