[openssl] master update

nic.tuv at gmail.com nic.tuv at gmail.com
Wed Oct 14 16:17:08 UTC 2020


The branch master has been updated
       via  5b70206cb316024c6dc30ce54f585ce5cf001a56 (commit)
       via  8b17fbaf46ae8a1319be5975ad8da3e0f4932e77 (commit)
       via  a011b5861b545b40df3c6f111df4fbde74cd7c82 (commit)
       via  c1a74f59ac799087c511d641cb086722817b805b (commit)
       via  ecff43e0ca48b25ddb001b6b63f3b7f8431f6962 (commit)
       via  c8e3a4c61346915a30b0c3594a7710753b8d1126 (commit)
       via  32fea070dc679f656a9e3d152aa570f6b658bc8e (commit)
      from  47690cd4ceb3a1cfdf097d575cb0d3cf9c6dac13 (commit)


- Log -----------------------------------------------------------------
commit 5b70206cb316024c6dc30ce54f585ce5cf001a56
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Mon Sep 28 08:37:13 2020 +0300

    [test][tls-provider] Implement KEM algorithm
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

commit 8b17fbaf46ae8a1319be5975ad8da3e0f4932e77
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Mon Sep 28 04:32:03 2020 +0300

    [ssl] Support ssl_encapsulate on server side
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

commit a011b5861b545b40df3c6f111df4fbde74cd7c82
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Mon Sep 28 03:45:30 2020 +0300

    [ssl] Support ssl_decapsulate on client side
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

commit c1a74f59ac799087c511d641cb086722817b805b
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Mon Sep 28 02:16:29 2020 +0300

    Define OSSL_CAPABILITY_TLS_GROUP_IS_KEM
    
    Note that with this commit the optional parameter is introduced, but
    libssl still ignores it.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

commit ecff43e0ca48b25ddb001b6b63f3b7f8431f6962
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Mon Sep 28 01:58:24 2020 +0300

    [test][tls-provider] Add 2nd pluggable tls group for KEM
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

commit c8e3a4c61346915a30b0c3594a7710753b8d1126
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Mon Sep 28 01:26:41 2020 +0300

    [test][sslapitest] Add test for pluggable KEM group
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

commit 32fea070dc679f656a9e3d152aa570f6b658bc8e
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Mon Sep 28 01:05:27 2020 +0300

    [test][tls-provider] Group xor_group properties in a struct
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

-----------------------------------------------------------------------

Summary of changes:
 crypto/err/openssl.txt       |   2 +
 doc/man7/provider-base.pod   |  41 +++++-
 include/openssl/core_names.h |   1 +
 include/openssl/sslerr.h     |   2 +
 ssl/s3_lib.c                 | 162 ++++++++++++++++++---
 ssl/ssl_local.h              |   8 ++
 ssl/statem/extensions_clnt.c |  55 ++++---
 ssl/statem/extensions_srvr.c | 106 ++++++++++----
 ssl/t1_lib.c                 |   8 ++
 test/sslapitest.c            |  13 +-
 test/tls-provider.c          | 334 ++++++++++++++++++++++++++++++++++++-------
 11 files changed, 610 insertions(+), 122 deletions(-)

diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 1724982709..2aca84f838 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -1401,11 +1401,13 @@ SSL_F_SSL_CTX_USE_SERVERINFO_EX:543:SSL_CTX_use_serverinfo_ex
 SSL_F_SSL_CTX_USE_SERVERINFO_FILE:337:SSL_CTX_use_serverinfo_file
 SSL_F_SSL_DANE_DUP:403:ssl_dane_dup
 SSL_F_SSL_DANE_ENABLE:395:SSL_dane_enable
+SSL_F_SSL_DECAPSULATE:643:
 SSL_F_SSL_DERIVE:590:ssl_derive
 SSL_F_SSL_DO_CONFIG:391:ssl_do_config
 SSL_F_SSL_DO_HANDSHAKE:180:SSL_do_handshake
 SSL_F_SSL_DUP_CA_LIST:408:SSL_dup_CA_list
 SSL_F_SSL_ENABLE_CT:402:SSL_enable_ct
+SSL_F_SSL_ENCAPSULATE:644:
 SSL_F_SSL_GENERATE_PKEY_GROUP:559:ssl_generate_pkey_group
 SSL_F_SSL_GENERATE_SESSION_ID:547:ssl_generate_session_id
 SSL_F_SSL_GET_NEW_SESSION:181:ssl_get_new_session
diff --git a/doc/man7/provider-base.pod b/doc/man7/provider-base.pod
index efec869e25..b92f117d86 100644
--- a/doc/man7/provider-base.pod
+++ b/doc/man7/provider-base.pod
@@ -364,15 +364,17 @@ Applications can query the capabilities to discover those services.
 
 The "TLS-GROUP" capability can be queried by libssl to discover the list of
 TLS groups that a provider can support. Each group supported can be used for
-key exchange during a TLS handshake. TLS clients can advertise the list of
-TLS groups they support in the supported_groups extension, and TLS servers can
-select a group from the offered list that they also support. In this way a
-provider can add to the list of groups that libssl already supports with
-additional ones.
+I<key exchange> (KEX) or I<key encapsulation method> (KEM) during a TLS
+handshake.
+TLS clients can advertise the list of TLS groups they support in the
+supported_groups extension, and TLS servers can select a group from the offered
+list that they also support. In this way a provider can add to the list of
+groups that libssl already supports with additional ones.
 
 Each TLS group that a provider supports should be described via the callback
 passed in through the provider_get_capabilities function. Each group should have
-the following details supplied (all are mandatory):
+the following details supplied (all are mandatory, except
+B<OSSL_CAPABILITY_TLS_GROUP_IS_KEM>):
 
 =over 4
 
@@ -393,7 +395,9 @@ The TLS group id value as given in the IANA TLS Supported Groups registry.
 =item "tls-group-alg" (B<OSSL_CAPABILITY_TLS_GROUP_ALG>) <utf8 string>
 
 The name of a Key Management algorithm that the provider offers and that should
-be used with this group. Keys created should be able to support key exchange.
+be used with this group. Keys created should be able to support I<key exchange>
+or I<key encapsulation method> (KEM), as implied by the optional
+B<OSSL_CAPABILITY_TLS_GROUP_IS_KEM> flag.
 The algorithm must support key and parameter generation as well as the
 key/parameter generation parameter, B<OSSL_PKEY_PARAM_GROUP_NAME>. The group
 name given via "tls-group-name-internal" above will be passed via
@@ -405,6 +409,29 @@ The number of bits of security offered by keys in this group. The number of bits
 should be comparable with the ones given in table 2 and 3 of the NIST SP800-57
 document.
 
+=item "tls-group-is-kem" (B<OSSL_CAPABILITY_TLS_GROUP_IS_KEM>) <unsigned integer>
+
+Boolean flag to describe if the group should be used in I<key exchange> (KEX)
+mode (0, default) or in I<key encapsulation method> (KEM) mode (1).
+
+This parameter is optional: if not specified, KEX mode is assumed as the default
+mode for the group.
+
+In KEX mode, in a typical Diffie-Hellman fashion, both sides execute I<keygen>
+then I<derive> against the peer public key. To operate in KEX mode, the group
+implementation must support the provider functions as described in
+L<provider-keyexch(7)>.
+
+In KEM mode, the client executes I<keygen> and sends its public key, the server
+executes I<encapsulate> using the client's public key and sends back the
+resulting I<ciphertext>, finally the client executes I<decapsulate> to retrieve
+the same I<shared secret> generated by the server's I<encapsulate>. To operate
+in KEM mode, the group implementation must support the provider functions as
+described in L<provider-kem(7)>.
+
+Both in KEX and KEM mode, the resulting I<shared secret> is then used according
+to the protocol specification.
+
 =item "tls-min-tls" (B<OSSL_CAPABILITY_TLS_GROUP_MIN_TLS>) <integer>
 
 =item "tls-max-tls" (B<OSSL_CAPABILITY_TLS_GROUP_MAX_TLS>) <integer>
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index c9f2bfab5e..4a4bd36cbe 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -492,6 +492,7 @@ extern "C" {
 #define OSSL_CAPABILITY_TLS_GROUP_ID                "tls-group-id"
 #define OSSL_CAPABILITY_TLS_GROUP_ALG               "tls-group-alg"
 #define OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS     "tls-group-sec-bits"
+#define OSSL_CAPABILITY_TLS_GROUP_IS_KEM            "tls-group-is-kem"
 #define OSSL_CAPABILITY_TLS_GROUP_MIN_TLS           "tls-min-tls"
 #define OSSL_CAPABILITY_TLS_GROUP_MAX_TLS           "tls-max-tls"
 #define OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS          "tls-min-dtls"
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index d4ee837a1e..56ece0d175 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -183,11 +183,13 @@ int ERR_load_SSL_strings(void);
 #  define SSL_F_SSL_CTX_USE_SERVERINFO_FILE                0
 #  define SSL_F_SSL_DANE_DUP                               0
 #  define SSL_F_SSL_DANE_ENABLE                            0
+#  define SSL_F_SSL_DECAPSULATE                            0
 #  define SSL_F_SSL_DERIVE                                 0
 #  define SSL_F_SSL_DO_CONFIG                              0
 #  define SSL_F_SSL_DO_HANDSHAKE                           0
 #  define SSL_F_SSL_DUP_CA_LIST                            0
 #  define SSL_F_SSL_ENABLE_CT                              0
+#  define SSL_F_SSL_ENCAPSULATE                            0
 #  define SSL_F_SSL_GENERATE_PKEY_GROUP                    0
 #  define SSL_F_SSL_GENERATE_SESSION_ID                    0
 #  define SSL_F_SSL_GET_NEW_SESSION                        0
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 94c2d8c2ce..1fd424a52e 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -4832,6 +4832,32 @@ EVP_PKEY *ssl_generate_param_group(SSL *s, uint16_t id)
     return pkey;
 }
 
+/* Generate secrets from pms */
+int ssl_gensecret(SSL *s, unsigned char *pms, size_t pmslen)
+{
+    int rv = 0;
+
+    /* SSLfatal() called as appropriate in the below functions */
+    if (SSL_IS_TLS13(s)) {
+        /*
+         * If we are resuming then we already generated the early secret
+         * when we created the ClientHello, so don't recreate it.
+         */
+        if (!s->hit)
+            rv = tls13_generate_secret(s, ssl_handshake_md(s), NULL, NULL,
+                    0,
+                    (unsigned char *)&s->early_secret);
+        else
+            rv = 1;
+
+        rv = rv && tls13_generate_handshake_secret(s, pms, pmslen);
+    } else {
+        rv = ssl_generate_master_secret(s, pms, pmslen, 0);
+    }
+
+    return rv;
+}
+
 /* Derive secrets for ECDH/DH */
 int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int gensecret)
 {
@@ -4876,22 +4902,118 @@ int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int gensecret)
 
     if (gensecret) {
         /* SSLfatal() called as appropriate in the below functions */
-        if (SSL_IS_TLS13(s)) {
-            /*
-             * If we are resuming then we already generated the early secret
-             * when we created the ClientHello, so don't recreate it.
-             */
-            if (!s->hit)
-                rv = tls13_generate_secret(s, ssl_handshake_md(s), NULL, NULL,
-                                           0,
-                                           (unsigned char *)&s->early_secret);
-            else
-                rv = 1;
-
-            rv = rv && tls13_generate_handshake_secret(s, pms, pmslen);
-        } else {
-            rv = ssl_generate_master_secret(s, pms, pmslen, 0);
-        }
+        rv = ssl_gensecret(s, pms, pmslen);
+    } else {
+        /* Save premaster secret */
+        s->s3.tmp.pms = pms;
+        s->s3.tmp.pmslen = pmslen;
+        pms = NULL;
+        rv = 1;
+    }
+
+ err:
+    OPENSSL_clear_free(pms, pmslen);
+    EVP_PKEY_CTX_free(pctx);
+    return rv;
+}
+
+/* Decapsulate secrets for KEM */
+int ssl_decapsulate(SSL *s, EVP_PKEY *privkey,
+                    const unsigned char *ct, size_t ctlen,
+                    int gensecret)
+{
+    int rv = 0;
+    unsigned char *pms = NULL;
+    size_t pmslen = 0;
+    EVP_PKEY_CTX *pctx;
+
+    if (privkey == NULL) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_DECAPSULATE,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    pctx = EVP_PKEY_CTX_new_from_pkey(s->ctx->libctx, privkey, s->ctx->propq);
+
+    if (EVP_PKEY_decapsulate_init(pctx) <= 0
+            || EVP_PKEY_decapsulate(pctx, NULL, &pmslen, ct, ctlen) <= 0) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_DECAPSULATE,
+                 ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    pms = OPENSSL_malloc(pmslen);
+    if (pms == NULL) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_DECAPSULATE,
+                 ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (EVP_PKEY_decapsulate(pctx, pms, &pmslen, ct, ctlen) <= 0) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_DECAPSULATE,
+                 ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if (gensecret) {
+        /* SSLfatal() called as appropriate in the below functions */
+        rv = ssl_gensecret(s, pms, pmslen);
+    } else {
+        /* Save premaster secret */
+        s->s3.tmp.pms = pms;
+        s->s3.tmp.pmslen = pmslen;
+        pms = NULL;
+        rv = 1;
+    }
+
+ err:
+    OPENSSL_clear_free(pms, pmslen);
+    EVP_PKEY_CTX_free(pctx);
+    return rv;
+}
+
+int ssl_encapsulate(SSL *s, EVP_PKEY *pubkey,
+                    unsigned char **ctp, size_t *ctlenp,
+                    int gensecret)
+{
+    int rv = 0;
+    unsigned char *pms = NULL, *ct = NULL;
+    size_t pmslen = 0, ctlen = 0;
+    EVP_PKEY_CTX *pctx;
+
+    if (pubkey == NULL) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_ENCAPSULATE,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    pctx = EVP_PKEY_CTX_new_from_pkey(s->ctx->libctx, pubkey, s->ctx->propq);
+
+    if (EVP_PKEY_encapsulate_init(pctx) <= 0
+            || EVP_PKEY_encapsulate(pctx, NULL, &ctlen, NULL, &pmslen) <= 0
+            || pmslen == 0 || ctlen == 0) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_ENCAPSULATE,
+                 ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    pms = OPENSSL_malloc(pmslen);
+    ct = OPENSSL_malloc(ctlen);
+    if (pms == NULL || ct == NULL) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_ENCAPSULATE,
+                 ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (EVP_PKEY_encapsulate(pctx, ct, &ctlen, pms, &pmslen) <= 0) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_ENCAPSULATE,
+                 ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if (gensecret) {
+        /* SSLfatal() called as appropriate in the below functions */
+        rv = ssl_gensecret(s, pms, pmslen);
     } else {
         /* Save premaster secret */
         s->s3.tmp.pms = pms;
@@ -4900,8 +5022,16 @@ int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int gensecret)
         rv = 1;
     }
 
+    if (rv > 0) {
+        /* Pass ownership of ct to caller */
+        *ctp = ct;
+        *ctlenp = ctlen;
+        ct = NULL;
+    }
+
  err:
     OPENSSL_clear_free(pms, pmslen);
+    OPENSSL_free(ct);
     EVP_PKEY_CTX_free(pctx);
     return rv;
 }
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index fd4eacdc38..66a84cf54e 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -818,6 +818,7 @@ typedef struct tls_group_info_st {
     int maxtls;              /* Maximum TLS version (or 0 for undefined) */
     int mindtls;             /* Minimum DTLS version, -1 unsupported */
     int maxdtls;             /* Maximum DTLS version (or 0 for undefined) */
+    char is_kem;             /* Mode for this Group: 0 is KEX, 1 is KEM */
 } TLS_GROUP_INFO;
 
 /* flags values */
@@ -2453,8 +2454,15 @@ __owur int ssl_fill_hello_random(SSL *s, int server, unsigned char *field,
 __owur int ssl_generate_master_secret(SSL *s, unsigned char *pms, size_t pmslen,
                                       int free_pms);
 __owur EVP_PKEY *ssl_generate_pkey(SSL *s, EVP_PKEY *pm);
+__owur int ssl_gensecret(SSL *s, unsigned char *pms, size_t pmslen);
 __owur int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey,
                       int genmaster);
+__owur int ssl_decapsulate(SSL *s, EVP_PKEY *privkey,
+                           const unsigned char *ct, size_t ctlen,
+                           int gensecret);
+__owur int ssl_encapsulate(SSL *s, EVP_PKEY *pubkey,
+                           unsigned char **ctp, size_t *ctlenp,
+                           int gensecret);
 __owur EVP_PKEY *ssl_dh_to_pkey(DH *dh);
 __owur unsigned int ssl_get_max_send_fragment(const SSL *ssl);
 __owur unsigned int ssl_get_split_send_fragment(const SSL *ssl);
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index 189e2c9e5e..15cd622ed5 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -1830,6 +1830,7 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
     unsigned int group_id;
     PACKET encoded_pt;
     EVP_PKEY *ckey = s->s3.tmp.pkey, *skey = NULL;
+    const TLS_GROUP_INFO *ginf = NULL;
 
     /* Sanity check */
     if (ckey == NULL || s->s3.peer_tmp != NULL) {
@@ -1893,6 +1894,12 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
         return 0;
     }
 
+    if ((ginf = tls1_group_id_lookup(s->ctx, group_id)) == NULL) {
+        SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_KEY_SHARE,
+                 SSL_R_BAD_KEY_SHARE);
+        return 0;
+    }
+
     if (!PACKET_as_length_prefixed_2(pkt, &encoded_pt)
             || PACKET_remaining(&encoded_pt) == 0) {
         SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_STOC_KEY_SHARE,
@@ -1900,27 +1907,39 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
         return 0;
     }
 
-    skey = EVP_PKEY_new();
-    if (skey == NULL || EVP_PKEY_copy_parameters(skey, ckey) <= 0) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_STOC_KEY_SHARE,
-                 SSL_R_COPY_PARAMETERS_FAILED);
-        return 0;
-    }
+    if (!ginf->is_kem) {
+        /* Regular KEX */
+        skey = EVP_PKEY_new();
+        if (skey == NULL || EVP_PKEY_copy_parameters(skey, ckey) <= 0) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_STOC_KEY_SHARE,
+                    SSL_R_COPY_PARAMETERS_FAILED);
+            return 0;
+        }
 
-    if (!EVP_PKEY_set1_tls_encodedpoint(skey, PACKET_data(&encoded_pt),
-                                        PACKET_remaining(&encoded_pt))) {
-        SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_KEY_SHARE,
-                 SSL_R_BAD_ECPOINT);
-        EVP_PKEY_free(skey);
-        return 0;
-    }
+        if (!EVP_PKEY_set1_tls_encodedpoint(skey, PACKET_data(&encoded_pt),
+                    PACKET_remaining(&encoded_pt))) {
+            SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_KEY_SHARE,
+                    SSL_R_BAD_ECPOINT);
+            EVP_PKEY_free(skey);
+            return 0;
+        }
 
-    if (ssl_derive(s, ckey, skey, 1) == 0) {
-        /* SSLfatal() already called */
-        EVP_PKEY_free(skey);
-        return 0;
+        if (ssl_derive(s, ckey, skey, 1) == 0) {
+            /* SSLfatal() already called */
+            EVP_PKEY_free(skey);
+            return 0;
+        }
+        s->s3.peer_tmp = skey;
+    } else {
+        /* KEM Mode */
+        const unsigned char *ct = PACKET_data(&encoded_pt);
+        size_t ctlen = PACKET_remaining(&encoded_pt);
+
+        if (ssl_decapsulate(s, ckey, ct, ctlen, 1) == 0) {
+            /* SSLfatal() already called */
+            return 0;
+        }
     }
-    s->s3.peer_tmp = skey;
 #endif
 
     return 1;
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index 9ec48ef56a..eb24d0a19e 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1696,6 +1696,7 @@ EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt,
     unsigned char *encodedPoint;
     size_t encoded_pt_len = 0;
     EVP_PKEY *ckey = s->s3.peer_tmp, *skey = NULL;
+    const TLS_GROUP_INFO *ginf = NULL;
 
     if (s->hello_retry_request == SSL_HRR_PENDING) {
         if (ckey != NULL) {
@@ -1733,37 +1734,92 @@ EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt,
         return EXT_RETURN_FAIL;
     }
 
-    skey = ssl_generate_pkey(s, ckey);
-    if (skey == NULL) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE,
-                 ERR_R_MALLOC_FAILURE);
+    if ((ginf = tls1_group_id_lookup(s->ctx, s->s3.group_id)) == NULL) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                 SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, ERR_R_INTERNAL_ERROR);
         return EXT_RETURN_FAIL;
     }
 
-    /* Generate encoding of server key */
-    encoded_pt_len = EVP_PKEY_get1_tls_encodedpoint(skey, &encodedPoint);
-    if (encoded_pt_len == 0) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE,
-                 ERR_R_EC_LIB);
-        EVP_PKEY_free(skey);
-        return EXT_RETURN_FAIL;
-    }
+    if (!ginf->is_kem) {
+        /* Regular KEX */
+        skey = ssl_generate_pkey(s, ckey);
+        if (skey == NULL) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE,
+                     ERR_R_MALLOC_FAILURE);
+            return EXT_RETURN_FAIL;
+        }
 
-    if (!WPACKET_sub_memcpy_u16(pkt, encodedPoint, encoded_pt_len)
-            || !WPACKET_close(pkt)) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE,
-                 ERR_R_INTERNAL_ERROR);
-        EVP_PKEY_free(skey);
+        /* Generate encoding of server key */
+        encoded_pt_len = EVP_PKEY_get1_tls_encodedpoint(skey, &encodedPoint);
+        if (encoded_pt_len == 0) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE,
+                     ERR_R_EC_LIB);
+            EVP_PKEY_free(skey);
+            return EXT_RETURN_FAIL;
+        }
+
+        if (!WPACKET_sub_memcpy_u16(pkt, encodedPoint, encoded_pt_len)
+                || !WPACKET_close(pkt)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE,
+                     ERR_R_INTERNAL_ERROR);
+            EVP_PKEY_free(skey);
+            OPENSSL_free(encodedPoint);
+            return EXT_RETURN_FAIL;
+        }
         OPENSSL_free(encodedPoint);
-        return EXT_RETURN_FAIL;
-    }
-    OPENSSL_free(encodedPoint);
 
-    /* This causes the crypto state to be updated based on the derived keys */
-    s->s3.tmp.pkey = skey;
-    if (ssl_derive(s, skey, ckey, 1) == 0) {
-        /* SSLfatal() already called */
-        return EXT_RETURN_FAIL;
+        /*
+         * This causes the crypto state to be updated based on the derived keys
+         */
+        s->s3.tmp.pkey = skey;
+        if (ssl_derive(s, skey, ckey, 1) == 0) {
+            /* SSLfatal() already called */
+            return EXT_RETURN_FAIL;
+        }
+    } else {
+        /* KEM mode */
+        unsigned char *ct = NULL;
+        size_t ctlen = 0;
+
+        /*
+         * This does not update the crypto state.
+         *
+         * The generated pms is stored in `s->s3.tmp.pms` to be later used via
+         * ssl_gensecret().
+         */
+        if (ssl_encapsulate(s, ckey, &ct, &ctlen, 0) == 0) {
+            /* SSLfatal() already called */
+            return EXT_RETURN_FAIL;
+        }
+
+        if (ctlen == 0) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE,
+                     ERR_R_INTERNAL_ERROR);
+            OPENSSL_free(ct);
+            return EXT_RETURN_FAIL;
+        }
+
+        if (!WPACKET_sub_memcpy_u16(pkt, ct, ctlen)
+                || !WPACKET_close(pkt)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE,
+                     ERR_R_INTERNAL_ERROR);
+            OPENSSL_free(ct);
+            return EXT_RETURN_FAIL;
+        }
+        OPENSSL_free(ct);
+
+        /*
+         * This causes the crypto state to be updated based on the generated pms
+         */
+        if (ssl_gensecret(s, s->s3.tmp.pms, s->s3.tmp.pmslen) == 0) {
+            /* SSLfatal() already called */
+            return EXT_RETURN_FAIL;
+        }
     }
     return EXT_RETURN_SENT;
 #else
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 927154fd98..8005f4ee32 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -249,6 +249,7 @@ static int add_provider_groups(const OSSL_PARAM params[], void *data)
     TLS_GROUP_INFO *ginf = NULL;
     EVP_KEYMGMT *keymgmt;
     unsigned int gid;
+    unsigned int is_kem = 0;
     int ret = 0;
 
     if (ctx->group_list_max_len == ctx->group_list_len) {
@@ -321,6 +322,13 @@ static int add_provider_groups(const OSSL_PARAM params[], void *data)
         goto err;
     }
 
+    p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_IS_KEM);
+    if (p != NULL && (!OSSL_PARAM_get_uint(p, &is_kem) || is_kem > 1)) {
+        SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+    ginf->is_kem = 1 & is_kem;
+
     p = OSSL_PARAM_locate_const(params, OSSL_CAPABILITY_TLS_GROUP_MIN_TLS);
     if (p == NULL || !OSSL_PARAM_get_int(p, &ginf->mintls)) {
         SSLerr(0, ERR_R_PASSED_INVALID_ARGUMENT);
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 4331f41549..6dc3be92cb 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -7935,7 +7935,7 @@ static int test_sigalgs_available(int idx)
 #endif /* OPENSSL_NO_EC */
 
 #ifndef OPENSSL_NO_TLS1_3
-static int test_pluggable_group(void)
+static int test_pluggable_group(int idx)
 {
     SSL_CTX *cctx = NULL, *sctx = NULL;
     SSL *clientssl = NULL, *serverssl = NULL;
@@ -7943,6 +7943,7 @@ static int test_pluggable_group(void)
     OSSL_PROVIDER *tlsprov = OSSL_PROVIDER_load(libctx, "tls-provider");
     /* Check that we are not impacted by a provider without any groups */
     OSSL_PROVIDER *legacyprov = OSSL_PROVIDER_load(libctx, "legacy");
+    const char *group_name = idx == 0 ? "xorgroup" : "xorkemgroup";
 
     if (!TEST_ptr(tlsprov) || !TEST_ptr(legacyprov))
         goto end;
@@ -7956,8 +7957,8 @@ static int test_pluggable_group(void)
                                              NULL, NULL)))
         goto end;
 
-    if (!TEST_true(SSL_set1_groups_list(serverssl, "xorgroup"))
-            || !TEST_true(SSL_set1_groups_list(clientssl, "xorgroup")))
+    if (!TEST_true(SSL_set1_groups_list(serverssl, group_name))
+            || !TEST_true(SSL_set1_groups_list(clientssl, group_name)))
         goto end;
 
     if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
@@ -8136,10 +8137,10 @@ int setup_tests(void)
         goto err;
 
 #if !defined(OPENSSL_NO_KTLS) && !defined(OPENSSL_NO_SOCK)
-#if !defined(OPENSSL_NO_TLS1_2) || !defined(OPENSSL_NO_TLS1_3)
+# if !defined(OPENSSL_NO_TLS1_2) || !defined(OPENSSL_NO_TLS1_3)
     ADD_ALL_TESTS(test_ktls, 32);
     ADD_ALL_TESTS(test_ktls_sendfile_anytls, 6);
-#endif
+# endif
 #endif
     ADD_TEST(test_large_message_tls);
     ADD_TEST(test_large_message_tls_read_ahead);
@@ -8247,7 +8248,7 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_sigalgs_available, 6);
 #endif
 #ifndef OPENSSL_NO_TLS1_3
-    ADD_TEST(test_pluggable_group);
+    ADD_ALL_TESTS(test_pluggable_group, 2);
 #endif
 #ifndef OPENSSL_NO_TLS1_2
     ADD_TEST(test_ssl_dup);
diff --git a/test/tls-provider.c b/test/tls-provider.c
index 924ede501b..bcbcd710ce 100644
--- a/test/tls-provider.c
+++ b/test/tls-provider.c
@@ -41,41 +41,134 @@ typedef struct xorkey_st {
     int haspubkey;
 } XORKEY;
 
-/* We define a dummy TLS group called "xorgroup" for test purposes */
 
-static unsigned int group_id = 0; /* IANA reserved for private use */
-static unsigned int secbits = 128;
-static unsigned int mintls = TLS1_3_VERSION;
-static unsigned int maxtls = 0;
-static unsigned int mindtls = -1;
-static unsigned int maxdtls = -1;
+/* Key Management for the dummy XOR KEX and KEM algorithms */
+
+static OSSL_FUNC_keymgmt_new_fn xor_newdata;
+static OSSL_FUNC_keymgmt_free_fn xor_freedata;
+static OSSL_FUNC_keymgmt_has_fn xor_has;
+static OSSL_FUNC_keymgmt_copy_fn xor_copy;
+static OSSL_FUNC_keymgmt_gen_init_fn xor_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_params_fn xor_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn xor_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_fn xor_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn xor_gen_cleanup;
+static OSSL_FUNC_keymgmt_get_params_fn xor_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn xor_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn xor_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn xor_settable_params;
+
+/*
+ * Dummy "XOR" Key Exchange algorithm. We just xor the private and public keys
+ * together. Don't use this!
+ */
+
+static OSSL_FUNC_keyexch_newctx_fn xor_newctx;
+static OSSL_FUNC_keyexch_init_fn xor_init;
+static OSSL_FUNC_keyexch_set_peer_fn xor_set_peer;
+static OSSL_FUNC_keyexch_derive_fn xor_derive;
+static OSSL_FUNC_keyexch_freectx_fn xor_freectx;
+static OSSL_FUNC_keyexch_dupctx_fn xor_dupctx;
+
+/*
+ * Dummy "XOR" Key Encapsulation Method. We just build a KEM over the xor KEX.
+ * Don't use this!
+ */
+
+static OSSL_FUNC_kem_newctx_fn xor_newctx;
+static OSSL_FUNC_kem_freectx_fn xor_freectx;
+static OSSL_FUNC_kem_dupctx_fn xor_dupctx;
+static OSSL_FUNC_kem_encapsulate_init_fn xor_init;
+static OSSL_FUNC_kem_encapsulate_fn xor_encapsulate;
+static OSSL_FUNC_kem_decapsulate_init_fn xor_init;
+static OSSL_FUNC_kem_decapsulate_fn xor_decapsulate;
+
+
+/*
+ * We define 2 dummy TLS groups called "xorgroup" and "xorkemgroup" for test
+ * purposes
+ */
+struct tls_group_st {
+    unsigned int group_id; /* for "tls-group-id", see provider-base(7) */
+    unsigned int secbits;
+    unsigned int mintls;
+    unsigned int maxtls;
+    unsigned int mindtls;
+    unsigned int maxdtls;
+    unsigned int is_kem; /* boolean */
+};
+
+#define XORGROUP_NAME "xorgroup"
+#define XORGROUP_NAME_INTERNAL "xorgroup-int"
+static struct tls_group_st xor_group = {
+    0,                  /* group_id, set by randomize_tls_group_id() */
+    128,                /* secbits */
+    TLS1_3_VERSION,     /* mintls */
+    0,                  /* maxtls */
+    -1,                 /* mindtls */
+    -1,                 /* maxdtls */
+    0                   /* is_kem */
+};
+
+#define XORKEMGROUP_NAME "xorkemgroup"
+#define XORKEMGROUP_NAME_INTERNAL "xorkemgroup-int"
+static struct tls_group_st xor_kemgroup = {
+    0,                  /* group_id, set by randomize_tls_group_id() */
+    128,                /* secbits */
+    TLS1_3_VERSION,     /* mintls */
+    0,                  /* maxtls */
+    -1,                 /* mindtls */
+    -1,                 /* maxdtls */
+    1                   /* is_kem */
+};
 
-#define GROUP_NAME "xorgroup"
-#define GROUP_NAME_INTERNAL "xorgroup-int"
 #define ALGORITHM "XOR"
 
 static const OSSL_PARAM xor_group_params[] = {
     OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME,
-                           GROUP_NAME, sizeof(GROUP_NAME)),
+                           XORGROUP_NAME, sizeof(XORGROUP_NAME)),
     OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL,
-                           GROUP_NAME_INTERNAL, sizeof(GROUP_NAME_INTERNAL)),
+                           XORGROUP_NAME_INTERNAL,
+                           sizeof(XORGROUP_NAME_INTERNAL)),
     OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, ALGORITHM,
                            sizeof(ALGORITHM)),
-    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, &group_id),
-    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, &secbits),
-    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, &mintls),
-    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, &maxtls),
-    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, &mindtls),
-    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, &maxdtls),
+    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, &xor_group.group_id),
+    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS,
+                    &xor_group.secbits),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, &xor_group.mintls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, &xor_group.maxtls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, &xor_group.mindtls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, &xor_group.maxdtls),
+    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_IS_KEM, &xor_group.is_kem),
     OSSL_PARAM_END
 };
 
+static const OSSL_PARAM xor_kemgroup_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME,
+                           XORKEMGROUP_NAME, sizeof(XORKEMGROUP_NAME)),
+    OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL,
+                           XORKEMGROUP_NAME_INTERNAL,
+                           sizeof(XORKEMGROUP_NAME_INTERNAL)),
+    OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, ALGORITHM,
+                           sizeof(ALGORITHM)),
+    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, &xor_kemgroup.group_id),
+    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS,
+                    &xor_kemgroup.secbits),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, &xor_kemgroup.mintls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, &xor_kemgroup.maxtls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, &xor_kemgroup.mindtls),
+    OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, &xor_kemgroup.maxdtls),
+    OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_IS_KEM, &xor_kemgroup.is_kem),
+    OSSL_PARAM_END
+};
+
+
 static int tls_prov_get_capabilities(void *provctx, const char *capability,
                                      OSSL_CALLBACK *cb, void *arg)
 {
-    /* We're only adding one group so we only call the callback once */
     if (strcmp(capability, "TLS-GROUP") == 0)
-        return cb(xor_group_params, arg);
+        return cb(xor_group_params, arg)
+            && cb(xor_kemgroup_params, arg);
 
     /* We don't support this capability */
     return 0;
@@ -86,16 +179,10 @@ static int tls_prov_get_capabilities(void *provctx, const char *capability,
  * together. Don't use this!
  */
 
-static OSSL_FUNC_keyexch_newctx_fn xor_newctx;
-static OSSL_FUNC_keyexch_init_fn xor_init;
-static OSSL_FUNC_keyexch_set_peer_fn xor_set_peer;
-static OSSL_FUNC_keyexch_derive_fn xor_derive;
-static OSSL_FUNC_keyexch_freectx_fn xor_freectx;
-static OSSL_FUNC_keyexch_dupctx_fn xor_dupctx;
-
 typedef struct {
     XORKEY *key;
     XORKEY *peerkey;
+    void *provctx;
 } PROV_XOR_CTX;
 
 static void *xor_newctx(void *provctx)
@@ -105,6 +192,8 @@ static void *xor_newctx(void *provctx)
     if (pxorctx == NULL)
         return NULL;
 
+    pxorctx->provctx = provctx;
+
     return pxorctx;
 }
 
@@ -188,21 +277,136 @@ static const OSSL_ALGORITHM tls_prov_keyexch[] = {
     { NULL, NULL, NULL }
 };
 
-/* Key Management for the dummy XOR key exchange algorithm */
+/*
+ * Dummy "XOR" Key Encapsulation Method. We just build a KEM over the xor KEX.
+ * Don't use this!
+ */
 
-static OSSL_FUNC_keymgmt_new_fn xor_newdata;
-static OSSL_FUNC_keymgmt_free_fn xor_freedata;
-static OSSL_FUNC_keymgmt_has_fn xor_has;
-static OSSL_FUNC_keymgmt_copy_fn xor_copy;
-static OSSL_FUNC_keymgmt_gen_init_fn xor_gen_init;
-static OSSL_FUNC_keymgmt_gen_set_params_fn xor_gen_set_params;
-static OSSL_FUNC_keymgmt_gen_settable_params_fn xor_gen_settable_params;
-static OSSL_FUNC_keymgmt_gen_fn xor_gen;
-static OSSL_FUNC_keymgmt_gen_cleanup_fn xor_gen_cleanup;
-static OSSL_FUNC_keymgmt_get_params_fn xor_get_params;
-static OSSL_FUNC_keymgmt_gettable_params_fn xor_gettable_params;
-static OSSL_FUNC_keymgmt_set_params_fn xor_set_params;
-static OSSL_FUNC_keymgmt_settable_params_fn xor_settable_params;
+static int xor_encapsulate(void *vpxorctx,
+                           unsigned char *ct, size_t *ctlen,
+                           unsigned char *ss, size_t *sslen)
+{
+    /*
+     * We are building this around a KEX:
+     *
+     * 1. we generate ephemeral keypair
+     * 2. we encode our ephemeral pubkey as the outgoing ct
+     * 3. we derive using our ephemeral privkey in combination with the peer
+     *    pubkey from the ctx; the result is our ss.
+     */
+    int rv = 0;
+    void *genctx = NULL, *derivectx = NULL;
+    XORKEY *ourkey = NULL;
+    PROV_XOR_CTX *pxorctx = vpxorctx;
+
+    if (ct == NULL || ss == NULL) {
+        /* Just return sizes */
+
+        if (ctlen == NULL && sslen == NULL)
+            return 0;
+        if (ctlen != NULL)
+            *ctlen = XOR_KEY_SIZE;
+        if (sslen != NULL)
+            *sslen = XOR_KEY_SIZE;
+        return 1;
+    }
+
+    /* 1. Generate keypair */
+    genctx = xor_gen_init(pxorctx->provctx, OSSL_KEYMGMT_SELECT_KEYPAIR);
+    if (genctx == NULL)
+        goto end;
+    ourkey = xor_gen(genctx, NULL, NULL);
+    if (ourkey == NULL)
+        goto end;
+
+    /* 2. Encode ephemeral pubkey as ct */
+    memcpy(ct, ourkey->pubkey, XOR_KEY_SIZE);
+    *ctlen = XOR_KEY_SIZE;
+
+    /* 3. Derive ss via KEX */
+    derivectx = xor_newctx(pxorctx->provctx);
+    if (derivectx == NULL
+            || !xor_init(derivectx, ourkey)
+            || !xor_set_peer(derivectx, pxorctx->key)
+            || !xor_derive(derivectx, ss, sslen, XOR_KEY_SIZE))
+        goto end;
+
+    rv = 1;
+
+ end:
+    xor_gen_cleanup(genctx);
+    xor_freedata(ourkey);
+    xor_freectx(derivectx);
+    return rv;
+}
+
+static int xor_decapsulate(void *vpxorctx,
+                           unsigned char *ss, size_t *sslen,
+                           const unsigned char *ct, size_t ctlen)
+{
+    /*
+     * We are building this around a KEX:
+     *
+     * - ct is our peer's pubkey
+     * - decapsulate is just derive.
+     */
+    int rv = 0;
+    void *derivectx = NULL;
+    XORKEY *peerkey = NULL;
+    PROV_XOR_CTX *pxorctx = vpxorctx;
+
+    if (ss == NULL) {
+        /* Just return size */
+        if (sslen == NULL)
+            return 0;
+        *sslen = XOR_KEY_SIZE;
+        return 1;
+    }
+
+    if (ctlen != XOR_KEY_SIZE)
+        return 0;
+    peerkey = xor_newdata(pxorctx->provctx);
+    if (peerkey == NULL)
+        goto end;
+    memcpy(peerkey->pubkey, ct, XOR_KEY_SIZE);
+
+    /* Derive ss via KEX */
+    derivectx = xor_newctx(pxorctx->provctx);
+    if (derivectx == NULL
+            || !xor_init(derivectx, pxorctx->key)
+            || !xor_set_peer(derivectx, peerkey)
+            || !xor_derive(derivectx, ss, sslen, XOR_KEY_SIZE))
+        goto end;
+
+    rv = 1;
+
+ end:
+    xor_freedata(peerkey);
+    xor_freectx(derivectx);
+    return rv;
+}
+
+static const OSSL_DISPATCH xor_kem_functions[] = {
+    { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))xor_newctx },
+    { OSSL_FUNC_KEM_FREECTX, (void (*)(void))xor_freectx },
+    { OSSL_FUNC_KEM_DUPCTX, (void (*)(void))xor_dupctx },
+    { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (void (*)(void))xor_init },
+    { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))xor_encapsulate },
+    { OSSL_FUNC_KEM_DECAPSULATE_INIT, (void (*)(void))xor_init },
+    { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))xor_decapsulate },
+    { 0, NULL }
+};
+
+static const OSSL_ALGORITHM tls_prov_kem[] = {
+    /*
+     * Obviously this is not FIPS approved, but in order to test in conjuction
+     * with the FIPS provider we pretend that it is.
+     */
+    { "XOR", "provider=tls-provider,fips=yes", xor_kem_functions },
+    { NULL, NULL, NULL }
+};
+
+/* Key Management for the dummy XOR key exchange algorithm */
 
 static void *xor_newdata(void *provctx)
 {
@@ -269,7 +473,7 @@ static ossl_inline int xor_get_params(void *vkey, OSSL_PARAM params[])
         return 0;
 
     if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
-        && !OSSL_PARAM_set_int(p, secbits))
+        && !OSSL_PARAM_set_int(p, xor_group.secbits))
         return 0;
 
     if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_TLS_ENCODED_PT)) != NULL) {
@@ -355,7 +559,8 @@ static int xor_gen_set_params(void *genctx, const OSSL_PARAM params[])
     p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
     if (p != NULL) {
         if (p->data_type != OSSL_PARAM_UTF8_STRING
-                || strcmp(p->data, GROUP_NAME_INTERNAL) != 0)
+                || (strcmp(p->data, XORGROUP_NAME_INTERNAL) != 0
+                    &&  strcmp(p->data, XORKEMGROUP_NAME_INTERNAL) != 0))
             return 0;
     }
 
@@ -435,6 +640,8 @@ static const OSSL_ALGORITHM *tls_prov_query(void *provctx, int operation_id,
         return tls_prov_keymgmt;
     case OSSL_OP_KEYEXCH:
         return tls_prov_keyexch;
+    case OSSL_OP_KEM:
+        return tls_prov_kem;
     }
     return NULL;
 }
@@ -447,19 +654,19 @@ static const OSSL_DISPATCH tls_prov_dispatch_table[] = {
     { 0, NULL }
 };
 
-int tls_provider_init(const OSSL_CORE_HANDLE *handle,
-                      const OSSL_DISPATCH *in,
-                      const OSSL_DISPATCH **out,
-                      void **provctx)
+static
+unsigned int randomize_tls_group_id(OPENSSL_CTX *libctx)
 {
-    OPENSSL_CTX *libctx = OPENSSL_CTX_new();
-
-    *provctx = libctx;
-
     /*
      * Randomise the group_id we're going to use to ensure we don't interoperate
      * with anything but ourselves.
      */
+    unsigned int group_id;
+    static unsigned int mem[10] = { 0 };
+    static int in_mem = 0;
+    int i;
+
+ retry:
     if (!RAND_bytes_ex(libctx, (unsigned char *)&group_id, sizeof(group_id)))
         return 0;
     /*
@@ -469,6 +676,33 @@ int tls_provider_init(const OSSL_CORE_HANDLE *handle,
     group_id %= 65279 - 65024;
     group_id += 65024;
 
+    /* Ensure we did not already issue this group_id */
+    for (i = 0; i < in_mem; i++)
+        if (mem[i] == group_id)
+            goto retry;
+
+    /* Add this group_id to the list of ids issued by this function */
+    mem[in_mem++] = group_id;
+
+    return group_id;
+}
+
+int tls_provider_init(const OSSL_CORE_HANDLE *handle,
+                      const OSSL_DISPATCH *in,
+                      const OSSL_DISPATCH **out,
+                      void **provctx)
+{
+    OPENSSL_CTX *libctx = OPENSSL_CTX_new();
+
+    *provctx = libctx;
+
+    /*
+     * Randomise the group_id we're going to use to ensure we don't interoperate
+     * with anything but ourselves.
+     */
+    xor_group.group_id = randomize_tls_group_id(libctx);
+    xor_kemgroup.group_id = randomize_tls_group_id(libctx);
+
     *out = tls_prov_dispatch_table;
     return 1;
 }


More information about the openssl-commits mailing list