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....@gmail.com>
Date:   Mon Sep 28 08:37:13 2020 +0300

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

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

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

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

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

commit c1a74f59ac799087c511d641cb086722817b805b
Author: Nicola Tuveri <nic....@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 <m...@openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

commit ecff43e0ca48b25ddb001b6b63f3b7f8431f6962
Author: Nicola Tuveri <nic....@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 <m...@openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13018)

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

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

commit 32fea070dc679f656a9e3d152aa570f6b658bc8e
Author: Nicola Tuveri <nic....@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 <m...@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;
 }

Reply via email to