Implemented RFC7250 certificate type negotiation extensions.
authorTom Vrancken <dev@tomvrancken.nl>
Wed, 15 Aug 2018 16:29:32 +0000 (18:29 +0200)
committerTom Vrancken <dev@tomvrancken.nl>
Mon, 20 Aug 2018 15:08:01 +0000 (17:08 +0200)
Signed-off-by: Tom Vrancken <dev@tomvrancken.nl>
34 files changed:
NEWS
doc/Makefile.am
doc/cha-gtls-app.texi
doc/manpages/Makefile.am
lib/algorithms/cert_types.c
lib/auth/cert.c
lib/auth/rsa.c
lib/auth/srp_rsa.c
lib/cert-session.c
lib/constate.c
lib/ext/Makefile.am
lib/ext/cert_types.h [new file with mode: 0644]
lib/ext/client_cert_type.c [new file with mode: 0644]
lib/ext/client_cert_type.h [new file with mode: 0644]
lib/ext/server_cert_type.c [new file with mode: 0644]
lib/ext/server_cert_type.h [new file with mode: 0644]
lib/gnutls_int.h
lib/handshake.c
lib/hello_ext.c
lib/hello_ext_lib.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/priority.c
lib/session.c
lib/session_pack.c
lib/state.c
lib/state.h
lib/tls13/certificate_verify.c
src/common.c
src/tests.c
symbols.last
tests/Makefile.am
tests/crt_type-neg-common.c [new file with mode: 0644]
tests/tls-crt_type-neg.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 7b9a53d00ab0f1b2f2aae4b85209b6c25876f7f1..57d27039038a57333b4b67f54b2780d2d1edf2aa 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -18,9 +18,18 @@ See the end for copying conditions.
 ** libgnutls: The 'record size limit' extension is added and preferred to the
    'max record size' extension when possible.
 
+** Added support for seperately negotiating client and server certificate types as
+   defined in RFC7250. This mechanism must be explicitly enabled via the
+   GNUTLS_ENABLE_CERT_TYPE_NEG flag in gnutls_init().
+
+
 ** API and ABI modifications:
 GNUTLS_ENABLE_EARLY_START: Added
 gnutls_record_set_max_early_data_size: Added
+gnutls_certificate_type_get2: Added
+gnutls_priority_certificate_type_list2: Added
+GNUTLS_ENABLE_CERT_TYPE_NEG: Added
+gnutls_ctype_target_t: New enumeration
 
 
 * Version 3.6.3 (released 2018-07-16)
index 5d6cd0c1bc1a5beb4faa396e7ff020d41e3d8435..782b3cf55ff9b5c225897baf800122aebf023596 100644 (file)
@@ -547,6 +547,7 @@ ENUMS += enums/gnutls_cipher_algorithm_t
 ENUMS += enums/gnutls_close_request_t
 ENUMS += enums/gnutls_compression_method_t
 ENUMS += enums/gnutls_credentials_type_t
+ENUMS += enums/gnutls_ctype_target_t
 ENUMS += enums/gnutls_digest_algorithm_t
 ENUMS += enums/gnutls_ecc_curve_t
 ENUMS += enums/gnutls_ext_flags_t
@@ -796,6 +797,8 @@ FUNCS += functions/gnutls_certificate_set_x509_trust_mem
 FUNCS += functions/gnutls_certificate_set_x509_trust_mem.short
 FUNCS += functions/gnutls_certificate_type_get
 FUNCS += functions/gnutls_certificate_type_get.short
+FUNCS += functions/gnutls_certificate_type_get2
+FUNCS += functions/gnutls_certificate_type_get2.short
 FUNCS += functions/gnutls_certificate_type_get_id
 FUNCS += functions/gnutls_certificate_type_get_id.short
 FUNCS += functions/gnutls_certificate_type_get_name
@@ -1528,6 +1531,8 @@ FUNCS += functions/gnutls_prf_rfc5705
 FUNCS += functions/gnutls_prf_rfc5705.short
 FUNCS += functions/gnutls_priority_certificate_type_list
 FUNCS += functions/gnutls_priority_certificate_type_list.short
+FUNCS += functions/gnutls_priority_certificate_type_list2
+FUNCS += functions/gnutls_priority_certificate_type_list2.short
 FUNCS += functions/gnutls_priority_cipher_list
 FUNCS += functions/gnutls_priority_cipher_list.short
 FUNCS += functions/gnutls_priority_compression_list
index 8fd31b2add904ef2ba7831331254e5a9da2bf859..9a4cf299337b799bfb8b6d276195b2e0aeeab321 100644 (file)
@@ -1292,6 +1292,18 @@ Catch all which enables all curves from NORMAL priority is CURVE-ALL. Note
 that the CURVE keyword is kept for backwards compatibility only, for new
 applications see the GROUP keyword above.
 
+@item Certificate types @tab
+Certificate type negotitation must be explicitly enabled via the
+GNUTLS_ENABLE_CERT_TYPE_NEG flag in gnutls_init().
+Certificate types can be given in a symmetric fashion (i.e. the same for
+both client and server) or, as of GnuTLS 3.6.4, in an asymmetric fashion
+(i.e. different for the client than for the server).
+
+Currently supported types are:
+CTYPE-X509 or CTYPE-X.509. Catch all is CTYPE-ALL.
+CTYPE-CLI-X509 or CTYPE-CLI-X.509, CTYPE-SRV-X509 or CTYPE-SRV-X.509.
+Catch all is CTYPE-CLI-ALL and CTYPE-SRV-ALL.
+
 @end multitable
 @caption{The supported algorithm keywords in priority strings.}
 @end float
index b4dc4ae8c932aabf1c704bd26c8346c2f6937b08..241d9870af25e1c55e5e699430bed021d949feb8 100644 (file)
@@ -193,6 +193,7 @@ APIMANS += gnutls_certificate_set_x509_trust_dir.3
 APIMANS += gnutls_certificate_set_x509_trust_file.3
 APIMANS += gnutls_certificate_set_x509_trust_mem.3
 APIMANS += gnutls_certificate_type_get.3
+APIMANS += gnutls_certificate_type_get2.3
 APIMANS += gnutls_certificate_type_get_id.3
 APIMANS += gnutls_certificate_type_get_name.3
 APIMANS += gnutls_certificate_type_list.3
@@ -559,6 +560,7 @@ APIMANS += gnutls_prf.3
 APIMANS += gnutls_prf_raw.3
 APIMANS += gnutls_prf_rfc5705.3
 APIMANS += gnutls_priority_certificate_type_list.3
+APIMANS += gnutls_priority_certificate_type_list2.3
 APIMANS += gnutls_priority_cipher_list.3
 APIMANS += gnutls_priority_compression_list.3
 APIMANS += gnutls_priority_deinit.3
index a7c27e0fe3808f55f79985840a01dca3394d88ad..dc9fc9a38823e45418ceb073fb02ab39d64e2000 100644 (file)
@@ -41,6 +41,8 @@ const char *gnutls_certificate_type_get_name(gnutls_certificate_type_t
 
        if (type == GNUTLS_CRT_X509)
                ret = "X.509";
+       if (type == GNUTLS_CRT_RAWPK)
+               ret = "Raw Public Key";
 
        return ret;
 }
@@ -61,6 +63,9 @@ gnutls_certificate_type_t gnutls_certificate_type_get_id(const char *name)
        if (strcasecmp(name, "X.509") == 0
            || strcasecmp(name, "X509") == 0)
                return GNUTLS_CRT_X509;
+       if (strcasecmp(name, "RAWPK") == 0
+                       || strcasecmp(name, "RAWPUBKEY") == 0)
+               return GNUTLS_CRT_RAWPK;
 
        return ret;
 }
index 4e2e484a2e727ebc3285d08eb6c44cb6149cf03d..069968c5d38d290fd73a09ec36064cc15be893d4 100644 (file)
@@ -308,7 +308,7 @@ get_issuers(gnutls_session_t session,
        int i;
        unsigned size;
 
-       if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
+       if (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT) != GNUTLS_CRT_X509)
                return 0;
 
        /* put the requested DNs to req_dn, only in case
@@ -339,7 +339,7 @@ get_issuers(gnutls_session_t session,
        return 0;
 }
 
-/* Calls the client or server get callback.
+/* Calls the client or server certificate get callback.
  */
 static int
 call_get_cert_callback(gnutls_session_t session,
@@ -349,7 +349,7 @@ call_get_cert_callback(gnutls_session_t session,
 {
        gnutls_privkey_t local_key = NULL;
        int ret = GNUTLS_E_INTERNAL_ERROR;
-       gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
+       gnutls_certificate_type_t type;
        gnutls_certificate_credentials_t cred;
        gnutls_pcert_st *pcert = NULL;
        gnutls_ocsp_data_st *ocsp = NULL;
@@ -363,6 +363,19 @@ call_get_cert_callback(gnutls_session_t session,
                return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
        }
 
+       /* Correctly set the certificate type depending on whether we
+        * have explicitly negotiated certificate types (RFC7250).
+        */
+       if (_gnutls_has_negotiate_ctypes(session)) {
+               if (IS_SERVER(session)) {
+                       type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+               } else { // Client mode
+                       type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT);
+               }
+       } else {
+               type = DEFAULT_CERT_TYPE;
+       }
+
        if (cred->get_cert_callback3) {
                struct gnutls_cert_retr_st info;
                unsigned int flags = 0;
@@ -432,9 +445,9 @@ _gnutls_select_client_cert(gnutls_session_t session,
 
        if (cred->get_cert_callback3 != NULL) {
 
-               /* use a callback to get certificate 
+               /* use a callback to get certificate
                 */
-               if (session->security_parameters.cert_type == GNUTLS_CRT_X509) {
+               if (session->security_parameters.client_ctype == GNUTLS_CRT_X509) {
                        issuers_dn_length =
                            get_issuers_num(session, data, data_size);
                        if (issuers_dn_length < 0) {
@@ -473,7 +486,7 @@ _gnutls_select_client_cert(gnutls_session_t session,
        } else {
                /* If we have no callbacks, try to guess.
                 */
-               if (session->security_parameters.cert_type == GNUTLS_CRT_X509) {
+               if (session->security_parameters.client_ctype == GNUTLS_CRT_X509) {
                        result =
                            find_x509_client_cert(session, cred, _data, _data_size,
                                           pk_algos, pk_algos_length, &indx);
@@ -565,7 +578,7 @@ static int gen_x509_crt(gnutls_session_t session, gnutls_buffer_st * data)
 int
 _gnutls_gen_cert_client_crt(gnutls_session_t session, gnutls_buffer_st * data)
 {
-       switch (session->security_parameters.cert_type) {
+       switch (session->security_parameters.client_ctype) {
        case GNUTLS_CRT_X509:
                return gen_x509_crt(session, data);
        default:
@@ -577,7 +590,7 @@ _gnutls_gen_cert_client_crt(gnutls_session_t session, gnutls_buffer_st * data)
 int
 _gnutls_gen_cert_server_crt(gnutls_session_t session, gnutls_buffer_st * data)
 {
-       switch (session->security_parameters.cert_type) {
+       switch (session->security_parameters.server_ctype) {
        case GNUTLS_CRT_X509:
                return gen_x509_crt(session, data);
        default:
@@ -756,6 +769,7 @@ int _gnutls_proc_crt(gnutls_session_t session, uint8_t * data, size_t data_size)
 {
        int ret;
        gnutls_certificate_credentials_t cred;
+       gnutls_certificate_type_t cert_type;
 
        cred =
            (gnutls_certificate_credentials_t) _gnutls_get_cred(session,
@@ -765,19 +779,28 @@ int _gnutls_proc_crt(gnutls_session_t session, uint8_t * data, size_t data_size)
                return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
        }
 
-       switch (session->security_parameters.cert_type) {
-       case GNUTLS_CRT_X509:
-               ret = _gnutls_proc_x509_server_crt(session, data, data_size);
-               break;
-       default:
-               gnutls_assert();
-               return GNUTLS_E_INTERNAL_ERROR;
+       /* Determine what certificate type we need to process */
+       if (IS_SERVER(session)) {
+               // We are the server therefore we process the client certificate
+               cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT);
+       } else {
+               // We are the client therefore we process the server certificate
+               cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+       }
+
+       switch (cert_type) {
+               case GNUTLS_CRT_X509:
+                       ret = _gnutls_proc_x509_server_crt(session, data, data_size);
+                       break;
+               default:
+                       gnutls_assert();
+                       return GNUTLS_E_INTERNAL_ERROR;
        }
 
        return ret;
 }
 
-/* Checks if we support the given signature algorithm 
+/* Checks if we support the given signature algorithm
  * (RSA or DSA). Returns the corresponding gnutls_pk_algorithm_t
  * if true;
  */
@@ -1016,7 +1039,7 @@ _gnutls_proc_cert_client_crt_vrfy(gnutls_session_t session,
 
        ret = _gnutls_get_auth_info_pcert(&peer_cert,
                                          session->security_parameters.
-                                         cert_type, info);
+                                         client_ctype, info);
 
        if (ret < 0) {
                gnutls_assert();
@@ -1078,7 +1101,7 @@ _gnutls_gen_cert_server_cert_req(gnutls_session_t session,
                }
        }
 
-       if (session->security_parameters.cert_type == GNUTLS_CRT_X509 &&
+       if (session->security_parameters.client_ctype == GNUTLS_CRT_X509 &&
            session->internals.ignore_rdn_sequence == 0) {
 
                ret =
@@ -1215,9 +1238,17 @@ static void get_server_name(gnutls_session_t session, uint8_t * name,
        return;
 }
 
-/* Selects a signature algorithm (if required by the ciphersuite and TLS
- * version), appropriate for the certificate. If none can be selected
- * returns an error.
+/* Checks the compatibility of the pubkey in the certificate with the
+ * ciphersuite and selects a signature algorithm (if required by the
+ * ciphersuite and TLS version) appropriate for the certificate. If none
+ * can be selected returns an error.
+ *
+ * IMPORTANT
+ * Currently this function is only called from _gnutls_server_select_cert,
+ * i.e. it is only called at the server. We therefore retrieve the
+ * negotiated server certificate type within this function.
+ * If, in the future, this routine is called at the client then we
+ * need to adapt the implementation accordingly.
  */
 static
 int cert_select_sign_algorithm(gnutls_session_t session,
@@ -1231,8 +1262,14 @@ int cert_select_sign_algorithm(gnutls_session_t session,
        unsigned key_usage;
        gnutls_sign_algorithm_t algo;
        const version_entry_st *ver = get_version(session);
+       gnutls_certificate_type_t ctype;
 
-       if (session->security_parameters.cert_type != cert_type) {
+       assert(IS_SERVER(session));
+
+       /* Retrieve the server certificate type */
+       ctype = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+
+       if (ctype != cert_type) {
                return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
        }
 
@@ -1297,7 +1334,7 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e
         * the ciphersuites.
         */
 
-       /* If the callback which retrieves certificate has been set,
+       /* If the callback which retrieves the certificate has been set,
         * use it and leave. We make sure that this is called once.
         */
        if (cred->get_cert_callback3) {
@@ -1358,8 +1395,6 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e
                                        /* found */
                                        goto finished;
                                }
-
-
                        }
                }
        }
@@ -1524,6 +1559,7 @@ _gnutls_proc_dhe_signature(gnutls_session_t session, uint8_t * data,
        const version_entry_st *ver = get_version(session);
        gnutls_certificate_credentials_t cred;
        unsigned vflags;
+       gnutls_certificate_type_t cert_type;
 
        if (unlikely(info == NULL || info->ncerts == 0 || ver == NULL)) {
                gnutls_assert();
@@ -1565,10 +1601,11 @@ _gnutls_proc_dhe_signature(gnutls_session_t session, uint8_t * data,
        signature.data = data;
        signature.size = sigsize;
 
+       // Retrieve the negotiated certificate type
+       cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+
        if ((ret =
-            _gnutls_get_auth_info_pcert(&peer_cert,
-                                        session->security_parameters.cert_type,
-                                        info)) < 0) {
+            _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info)) < 0) {
                gnutls_assert();
                return ret;
        }
index f2e36bbe22bacbbe5c08608bc77f3b9068395057..6afc91ae67228e53ccf9c09699065fba70f2af37 100644 (file)
@@ -82,6 +82,18 @@ int check_key_usage_for_enc(gnutls_session_t session, unsigned key_usage)
 }
 
 /* This function reads the RSA parameters from peer's certificate;
+ *
+ * IMPORTANT:
+ * Currently this function gets only called on the client side
+ * during generation of the client kx msg. This function
+ * retrieves the RSA params from the peer's certificate. That is in
+ * this case the server's certificate. As of GNUTLS version 3.6.4 it is
+ * possible to negotiate different certificate types for client and
+ * server. Therefore the correct cert type needs to be retrieved to be
+ * used for the _gnutls_get_auth_info_pcert call. If this
+ * function is to be called on the server side in the future, extra
+ * checks need to be build in order to retrieve te correct
+ * certificate type.
  */
 int
 _gnutls_get_public_rsa_params(gnutls_session_t session,
@@ -91,6 +103,9 @@ _gnutls_get_public_rsa_params(gnutls_session_t session,
        cert_auth_info_t info;
        unsigned key_usage;
        gnutls_pcert_st peer_cert;
+       gnutls_certificate_type_t cert_type;
+
+       assert(!IS_SERVER(session));
 
        /* normal non export case */
 
@@ -101,10 +116,10 @@ _gnutls_get_public_rsa_params(gnutls_session_t session,
                return GNUTLS_E_INTERNAL_ERROR;
        }
 
-       ret =
-           _gnutls_get_auth_info_pcert(&peer_cert,
-                                       session->security_parameters.
-                                       cert_type, info);
+       // Get the negotiated server certificate type
+       cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+
+       ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);
 
        if (ret < 0) {
                gnutls_assert();
index 2101f70a0fb48a1581c19547377084d8d3d5f713..06c6971d8054d7fc34efcc592e8754bec7d436ed 100644 (file)
@@ -241,8 +241,7 @@ proc_srp_cert_server_kx(gnutls_session_t session, uint8_t * data,
 
        ret =
            _gnutls_get_auth_info_pcert(&peer_cert,
-                                       session->security_parameters.
-                                       cert_type, info);
+                                       session->security_parameters.server_ctype, info);
 
        if (ret < 0) {
                gnutls_assert();
index 772d1417d99caecc256e7140b96f5a77ad9cdce6..580a871964d01419e45e00b9d5134fc8fc597dbc 100644 (file)
@@ -719,12 +719,12 @@ gnutls_certificate_verify_peers(gnutls_session_t session,
                return GNUTLS_E_NO_CERTIFICATE_FOUND;
 
 
-       switch (gnutls_certificate_type_get(session)) {
-       case GNUTLS_CRT_X509:
-               return _gnutls_x509_cert_verify_peers(session, data, elements,
-                                                     status);
-       default:
-               return GNUTLS_E_INVALID_REQUEST;
+       switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+               case GNUTLS_CRT_X509:
+                       return _gnutls_x509_cert_verify_peers(session, data, elements,
+                                                                               status);
+               default:
+                       return GNUTLS_E_INVALID_REQUEST;
        }
 }
 
@@ -820,14 +820,13 @@ time_t gnutls_certificate_expiration_time_peers(gnutls_session_t session)
                return (time_t) - 1;
        }
 
-       switch (gnutls_certificate_type_get(session)) {
-       case GNUTLS_CRT_X509:
-               return
-                   _gnutls_x509_get_raw_crt_expiration_time(&info->
-                                                            raw_certificate_list
-                                                            [0]);
-       default:
-               return (time_t) - 1;
+       switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+               case GNUTLS_CRT_X509:
+                       return
+                                       _gnutls_x509_get_raw_crt_expiration_time(&info->
+                                                                                raw_certificate_list[0]);
+               default:
+                       return (time_t) - 1;
        }
 }
 
@@ -857,13 +856,12 @@ time_t gnutls_certificate_activation_time_peers(gnutls_session_t session)
                return (time_t) - 1;
        }
 
-       switch (gnutls_certificate_type_get(session)) {
-       case GNUTLS_CRT_X509:
-               return
-                   _gnutls_x509_get_raw_crt_activation_time(&info->
-                                                            raw_certificate_list
-                                                            [0]);
-       default:
-               return (time_t) - 1;
+       switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+               case GNUTLS_CRT_X509:
+                       return
+                                       _gnutls_x509_get_raw_crt_activation_time(&info->
+                                                                                raw_certificate_list[0]);
+               default:
+                       return (time_t) - 1;
        }
 }
index 62623c10dd21f04d73e2e0b3b4d9bc6e97bddfad..456316258b60ce233a5694464d40e9eae658122a 100644 (file)
@@ -668,9 +668,9 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t
 #define CPY_COMMON(tls13_sem) \
        if (!tls13_sem) { \
                dst->cs = src->cs; \
-               memcpy( dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
-               memcpy( dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
-               memcpy( dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
+               memcpy(dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
+               memcpy(dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
+               memcpy(dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
                dst->ext_master_secret = src->ext_master_secret; \
                dst->etm = src->etm; \
                dst->max_record_recv_size = src->max_record_recv_size; \
@@ -679,10 +679,11 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t
                dst->grp = src->grp; \
                dst->pversion = src->pversion; \
        } \
-       memcpy( dst->session_id, src->session_id, GNUTLS_MAX_SESSION_ID_SIZE); \
+       memcpy(dst->session_id, src->session_id, GNUTLS_MAX_SESSION_ID_SIZE); \
        dst->session_id_size = src->session_id_size; \
        dst->timestamp = src->timestamp; \
-       dst->cert_type = src->cert_type; \
+       dst->client_ctype = src->client_ctype; \
+       dst->server_ctype = src->server_ctype; \
        dst->client_auth_type = src->client_auth_type; \
        dst->server_auth_type = src->server_auth_type
 
index b7f204f1d96cf1121a4dd4dea43bea6bd0416886..c8ef79101cc960ac94de773391474804612392ae 100644 (file)
@@ -48,7 +48,10 @@ libgnutls_ext_la_SOURCES = max_record.c \
        supported_groups.c supported_groups.h \
        ec_point_formats.c ec_point_formats.h \
        early_data.c early_data.h \
-       record_size_limit.c record_size_limit.h
+       record_size_limit.c record_size_limit.h \
+       client_cert_type.c client_cert_type.h \
+       server_cert_type.c server_cert_type.h \
+       cert_types.h
 
 if ENABLE_ALPN
 libgnutls_ext_la_SOURCES += alpn.c alpn.h
diff --git a/lib/ext/cert_types.h b/lib/ext/cert_types.h
new file mode 100644 (file)
index 0000000..c54e0f2
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 ARPA2 project
+ *
+ * Author: Tom Vrancken (dev@tomvrancken.nl)
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This file provides common functionality for certificate type
+ * handling during TLS hello extensions.
+ *
+ */
+
+/* Maps IANA TLS Certificate Types identifiers to internal
+ * certificate type representation.
+ */
+static inline gnutls_certificate_type_t _gnutls_IANA2cert_type(int num)
+{
+       switch (num) {
+               case 0:
+                       return GNUTLS_CRT_X509;
+               default:
+                       return GNUTLS_CRT_UNKNOWN;
+       }
+}
+
+/* Maps internal certificate type representation to
+ * IANA TLS Certificate Types identifiers.
+ */
+static inline int _gnutls_cert_type2IANA(gnutls_certificate_type_t cert_type)
+{
+       switch (cert_type) {
+               case GNUTLS_CRT_X509:
+                       return 0;
+               default:
+                       return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+       }
+}
diff --git a/lib/ext/client_cert_type.c b/lib/ext/client_cert_type.c
new file mode 100644 (file)
index 0000000..8bce721
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2016 - 2018 ARPA2 project
+ *
+ * Author: Tom Vrancken (dev@tomvrancken.nl)
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This file is part of the client_certificate_type extension as
+ * defined in RFC7250 (https://tools.ietf.org/html/rfc7250).
+ *
+ * The client_certificate_type extension in the client hello indicates
+ * the certificate types the client is able to provide to the server,
+ * when requested using a certificate_request message.
+ */
+
+#include "gnutls_int.h"
+#include <gnutls/gnutls.h>
+#include "ext/cert_types.h"
+#include "ext/client_cert_type.h"
+#include "hello_ext.h"
+#include "hello_ext_lib.h"
+#include "errors.h"
+#include <state.h>
+#include <datum.h>
+
+
+static int _gnutls_client_cert_type_recv_params(gnutls_session_t session,
+                                               const uint8_t* data,
+                                               size_t data_size);
+static int _gnutls_client_cert_type_send_params(gnutls_session_t session,
+                                               gnutls_buffer_st* data);
+
+
+const hello_ext_entry_st ext_mod_client_cert_type = {
+       .name = "Client Certificate Type",
+       .tls_id = 19,
+       .gid = GNUTLS_EXTENSION_CLIENT_CERT_TYPE,
+       .parse_type = GNUTLS_EXT_TLS,
+       .validity = GNUTLS_EXT_FLAG_TLS |
+               GNUTLS_EXT_FLAG_DTLS |
+               GNUTLS_EXT_FLAG_CLIENT_HELLO |
+               GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO |
+               GNUTLS_EXT_FLAG_EE,
+       .recv_func = _gnutls_client_cert_type_recv_params,
+       .send_func = _gnutls_client_cert_type_send_params,
+       .pack_func = _gnutls_hello_ext_default_pack,
+       .unpack_func = _gnutls_hello_ext_default_unpack,
+       .deinit_func = _gnutls_hello_ext_default_deinit,
+       .cannot_be_overriden = 1
+};
+
+
+static int _gnutls_client_cert_type_recv_params(gnutls_session_t session,
+                                               const uint8_t* data,
+                                               size_t data_size)
+{
+       int ret;
+       gnutls_datum_t cert_types; // Holds the received cert types
+       gnutls_datum_t sent_cert_types; // Holds the previously sent cert types
+       gnutls_certificate_type_t cert_type;
+
+       uint8_t i, found = 0;
+       ssize_t len = data_size;
+       const uint8_t* pdata = data;
+
+       /* Only activate this extension if cert type negotiation is enabled
+        * and we have cert credentials set */
+       if (!_gnutls_has_negotiate_ctypes(session) ||
+                       _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)
+               return 0;
+
+       if (!IS_SERVER(session)) {      // client mode
+
+               /* Compare packet length with expected packet length. For the
+                * client this is a single byte. */
+               if (len != 1) {
+                       return
+                                       gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+               }
+
+               /* The server picked one of the offered cert types iff he supports
+                * at least one of them and decided to do a client certificate
+                * request. If both parties play by the rules then we may only
+                * receive a cert type that we offered, i.e. one that we support.
+                * Because the world isn't as beautiful as it may seem, we're going
+                * to check it nevertheless. */
+               cert_type = _gnutls_IANA2cert_type(pdata[0]);
+
+               // Check validity of cert type
+               if (cert_type == GNUTLS_CRT_UNKNOWN) {
+                       return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+               }
+
+               /* Get the cert types that we sent to the server (they were stored
+                * in IANA representation.
+                */
+               ret = _gnutls_hello_ext_get_datum(session,
+                                                        GNUTLS_EXTENSION_CLIENT_CERT_TYPE,
+                                                        &sent_cert_types);
+               if (ret < 0) {
+                       /* This should not happen and indicate a memory corruption!
+                        * Assertion are always on in production code so execution
+                        * will halt here. */
+                       assert(false);
+               }
+
+               // Check whether what we got back is actually offered by us
+               for (i = 0; i < sent_cert_types.size; i++) {
+                       if (_gnutls_IANA2cert_type(sent_cert_types.data[i]) == cert_type)
+                               found = 1;
+               }
+
+               if (found) {
+                       // Everything OK, now set the client certificate type
+                       _gnutls_session_client_cert_type_set(session, cert_type);
+                       ret = GNUTLS_E_SUCCESS;
+               } else {
+                       // No valid cert type found
+                       ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+               }
+
+               return ret;
+
+       } else {        // server mode
+               // Compare packet length with expected packet length.
+               DECR_LEN(len, 1);
+               if (data[0] != len) {
+                       return
+                                       gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+               }
+               pdata += 1;
+
+               // Assign the contents of our data buffer to a gnutls_datum_t
+               cert_types.data = (uint8_t*)pdata; // Need casting to get rid of 'discards const qualifier' warning
+               cert_types.size = len;
+
+               // Store the client certificate types in our session
+               _gnutls_hello_ext_set_datum(session,
+                                                        GNUTLS_EXTENSION_CLIENT_CERT_TYPE,
+                                                        &cert_types);
+
+               /* We receive a list of supported certificate types that the client
+                * is able to provide when requested via a client certificate
+                * request. This list is sorted by order of preference. We now check
+                * in this order of preference whether we support any of these
+                * certificate types.
+                */
+               for (i = 0; i < cert_types.size; i++) {
+                       // Convert to internal representation
+                       cert_type = _gnutls_IANA2cert_type(cert_types.data[i]);
+
+                       // If we have an invalid cert id then continue to the next
+                       if (cert_type == GNUTLS_CRT_UNKNOWN)
+                               continue;
+
+                       // Check for support of this cert type
+                       if (_gnutls_session_cert_type_supported
+                                       (session, cert_type, false, GNUTLS_CTYPE_CLIENT) == 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               // We found a matching ctype, we pick this one
+               if (found) {
+                       _gnutls_session_client_cert_type_set(session, cert_type);
+                       ret = GNUTLS_E_SUCCESS;
+               } else {
+                       /* If no supported certificate type can be found we terminate
+                        * with a fatal alert of type "unsupported_certificate"
+                        * (according to specification rfc7250).
+                        */
+                       _gnutls_handshake_log
+                                       ("EXT[%p]: No supported client certificate type was found. "
+                                        "Aborting connection.\n", session);
+                       ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+               }
+
+               return ret;
+       }
+}
+
+static int _gnutls_client_cert_type_send_params(gnutls_session_t session,
+                                               gnutls_buffer_st* data)
+{
+       int ret;
+       uint8_t cert_type; // Holds an IANA cert type ID
+       uint8_t i = 0, num_cert_types = 0;
+       priority_st* cert_priors;
+       gnutls_datum_t tmp_cert_types; // For type conversion
+       uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types
+       const version_entry_st* vers = get_version(session);
+
+       /* Only activate this extension if cert type negotiation is enabled
+        * and we have cert credentials set */
+       if (!_gnutls_has_negotiate_ctypes(session) ||
+                       _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)
+               return 0;
+
+       if (!IS_SERVER(session)) {      // Client mode
+               // For brevity
+               cert_priors =
+                               &session->internals.priorities->client_ctype;
+
+               /* Retrieve client certificate type priorities if any. If no
+                * priorities are set then the default client certificate type
+                * initialization values apply. This default is currently set to
+                * x.509 in which case we don't enable this extension.
+                */
+               if (cert_priors->algorithms > 0) {      // Priorities are explicitly set
+                       /* If the certificate priority is explicitly set to only
+                        * X.509 (default) then, according to spec we don't send
+                        * this extension. We check this here to avoid further work in
+                        * this routine. We also check it below after pruning supported
+                        * types.
+                        */
+                       if (cert_priors->algorithms == 1 &&
+                                       cert_priors->priority[0] == DEFAULT_CERT_TYPE) {
+                               _gnutls_handshake_log
+                                               ("EXT[%p]: Client certificate type was set to default cert type (%s). "
+                                                "We therefore do not send this extension.\n",
+                                                session,
+                                                gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE));
+
+                               // Explicitly set but default ctype, so don't send anything
+                               return 0;
+                       }
+
+                       /* We are only allowed to send certificate types that we support,
+                        * i.e. have credentials for. Therefore we check this here and
+                        * prune our original list.
+                        */
+                       for (i = 0; i < cert_priors->algorithms; i++) {
+                               if (_gnutls_session_cert_type_supported
+                                               (session, cert_priors->priority[i],
+                                                true, GNUTLS_CTYPE_CLIENT) == 0) {
+                                       /* Check whether we are allowed to store another cert type
+                                        * in our buffer. In other words, prevent a possible buffer
+                                        * overflow. This situation can occur when a user sets
+                                        * duplicate cert types in the priority strings. */
+                                       if (num_cert_types >= GNUTLS_CRT_MAX)
+                                               return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+                                       // Convert to IANA representation
+                                       cert_type = _gnutls_cert_type2IANA(cert_priors->priority[i]);
+                                       // Add this cert type to our list with supported types
+                                       cert_types[num_cert_types] = cert_type;
+                                       num_cert_types++;
+
+                                       _gnutls_handshake_log
+                                                       ("EXT[%p]: Client certificate type %s (%d) was queued.\n",
+                                                        session,
+                                                        gnutls_certificate_type_get_name(cert_priors->priority[i]),
+                                                        cert_type);
+                               }
+                       }
+
+                       /* Check whether there are any supported certificate types left
+                        * after the previous pruning step. If not, we do not send this
+                        * extension. Also, if the only supported type is the default type
+                        * we do not send this extension (according to RFC7250).
+                        */
+                       if (num_cert_types == 0) {
+                               _gnutls_handshake_log
+                                               ("EXT[%p]: Client certificate types were set but none of them is supported. "
+                                                "You might want to check your credentials or your priorities. "
+                                                "We do not send this extension.\n",
+                                                session);
+
+                               return 0;
+                       } else if (num_cert_types == 1 &&
+                                        _gnutls_IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) {
+                               _gnutls_handshake_log
+                                               ("EXT[%p]: The only supported client certificate type is (%s) which is the default. "
+                                                "We therefore do not send this extension.\n",
+                                                session,
+                                                gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE));
+
+                               return 0;
+                       }
+
+                       /* We have data to send and store a copy internally. We convert
+                        * our list with supported cert types to a datum_t in order to
+                        * be able to make the ..._set_datum call.
+                        */
+                       tmp_cert_types.data = cert_types;
+                       tmp_cert_types.size = num_cert_types;
+
+                       _gnutls_hello_ext_set_datum(session,
+                                                                GNUTLS_EXTENSION_CLIENT_CERT_TYPE,
+                                                                &tmp_cert_types);
+
+                       /* Serialize the certificate types into a sequence of octets
+                        * uint8: length of sequence of cert types (1 octet)
+                        * uint8: cert types (0 <= #octets <= 255)
+                        */
+                       ret = _gnutls_buffer_append_data_prefix(data, 8,
+                                                                                                                                                                                       cert_types,
+                                                                                                                                                                                       num_cert_types);
+
+                       // Check for errors
+                       if (ret < 0) {
+                               return gnutls_assert_val(ret);
+                       } else {
+                               // Number of bytes we are sending
+                               return num_cert_types + 1;
+                       }
+               }
+       } else {        // Server mode
+               /* TLS 1.2:
+                * Check whether we are going to send a certificate request,
+                * otherwise omit the response. This is conform spec.
+                * (RFC7250, 4.2 case 3.).
+                *
+                * TLS 1.3:
+                * TLS 1.3 supports post-handshake authentication for the client.
+                * It means that a server can ask for a client certificate anytime
+                * after the handshake. In order for this to work we must always
+                * complete the certificate type negotiation and therefore respond
+                * with a cert type message.
+                */
+               if (session->internals.send_cert_req != 0 ||
+                               vers->tls13_sem) {
+                       /* Retrieve negotiated client certificate type and send it to
+                        * the client.
+                        * The scenario where we want to send a certificate request but
+                        * do not have a matching certificate does not occur because we
+                        * already terminate the connection at reception of this extension
+                        * when we cannot find a matching client certificate. This is conform
+                        * spec (RFC7250, 4.2 case 2.).
+                        */
+                       cert_type =
+                                       _gnutls_cert_type2IANA(session->
+                                                       security_parameters.client_ctype);
+
+                       ret = gnutls_buffer_append_data(data, &cert_type, 1);
+
+                       if (ret < 0)
+                               return gnutls_assert_val(ret);
+
+                       return 1; // sent one byte
+               }
+       }
+
+       // In all other cases don't enable this extension
+       return 0;
+}
+
+
+/** Extension interface **/
+
+/* The interface is defined in state.c:
+ * Public:
+ * - gnutls_certificate_type_get2
+ *
+ * Private:
+ * - _gnutls_session_client_cert_type_set
+ */
diff --git a/lib/ext/client_cert_type.h b/lib/ext/client_cert_type.h
new file mode 100644 (file)
index 0000000..454e9bf
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 - 2018 ARPA2 project
+ *
+ * Author: Tom Vrancken (dev@tomvrancken.nl)
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This file is part of the client_certificate_type extension as
+ * defined in RFC7250 (https://tools.ietf.org/html/rfc7250).
+ *
+ * The client_certificate_type extension in the client hello indicates
+ * the certificate types the client is able to provide to the server,
+ * when requested using a certificate_request message.
+ */
+
+#ifndef EXT_CLIENT_CERT_TYPE_H
+#define EXT_CLIENT_CERT_TYPE_H
+
+#include <hello_ext.h>
+
+extern const hello_ext_entry_st ext_mod_client_cert_type;
+
+#endif
diff --git a/lib/ext/server_cert_type.c b/lib/ext/server_cert_type.c
new file mode 100644 (file)
index 0000000..b1086c7
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2016 - 2018 ARPA2 project
+ *
+ * Author: Tom Vrancken (dev@tomvrancken.nl)
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This file is part of the server_certificate_type extension as
+ * defined in RFC7250 (https://tools.ietf.org/html/rfc7250).
+ *
+ * The server_certificate_type extension in the client hello indicates
+ * the types of certificates the client is able to process when provided
+ * by the server in a subsequent certificate payload.
+ */
+
+#include <gnutls_int.h>
+#include <gnutls/gnutls.h>
+#include "ext/cert_types.h"
+#include "ext/server_cert_type.h"
+#include "hello_ext.h"
+#include "hello_ext_lib.h"
+#include "errors.h"
+#include <state.h>
+#include <datum.h>
+
+
+static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
+                                               const uint8_t* data,
+                                               size_t data_size);
+static int _gnutls_server_cert_type_send_params(gnutls_session_t session,
+                                               gnutls_buffer_st* data);
+
+
+const hello_ext_entry_st ext_mod_server_cert_type = {
+       .name = "Server Certificate Type",
+       .tls_id = 20,
+       .gid = GNUTLS_EXTENSION_SERVER_CERT_TYPE,
+       .parse_type = GNUTLS_EXT_TLS,
+       .validity = GNUTLS_EXT_FLAG_TLS |
+               GNUTLS_EXT_FLAG_DTLS |
+               GNUTLS_EXT_FLAG_CLIENT_HELLO |
+               GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO |
+               GNUTLS_EXT_FLAG_EE,
+       .recv_func = _gnutls_server_cert_type_recv_params,
+       .send_func = _gnutls_server_cert_type_send_params,
+       .pack_func = _gnutls_hello_ext_default_pack,
+       .unpack_func = _gnutls_hello_ext_default_unpack,
+       .deinit_func = _gnutls_hello_ext_default_deinit,
+       .cannot_be_overriden = 1
+};
+
+
+static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
+                                               const uint8_t* data,
+                                               size_t data_size)
+{
+       int ret;
+       gnutls_datum_t cert_types; // Holds the received cert types
+       gnutls_datum_t sent_cert_types; // Holds the previously sent cert types
+       gnutls_certificate_type_t cert_type;
+
+       uint8_t i, found = 0;
+       ssize_t len = data_size;
+       const uint8_t* pdata = data;
+
+       /* Only activate this extension if cert type negotiation is enabled
+        * and we have cert credentials set */
+       if (!_gnutls_has_negotiate_ctypes(session) ||
+                       _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)
+               return 0;
+
+       if (!IS_SERVER(session)) {      // client mode
+
+               /* Compare packet length with expected packet length. For the
+                * client this is a single byte. */
+               if (len != 1) {
+                       return
+                                       gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+               }
+
+               /* The server picked one of the offered cert types iff he supports
+                * at least one of them. If both parties play by the rules then we
+                * may only receive a cert type that we offered, i.e. one that we
+                * support. Because the world isn't as beautiful as it may seem,
+                * we're going to check it nevertheless. */
+               cert_type = _gnutls_IANA2cert_type(pdata[0]);
+
+               // Check validity of cert type
+               if (cert_type == GNUTLS_CRT_UNKNOWN) {
+                       return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+               }
+
+               /* Get the cert types that we sent to the server (they were stored
+                * in IANA representation.
+                */
+               ret = _gnutls_hello_ext_get_datum(session,
+                                                        GNUTLS_EXTENSION_SERVER_CERT_TYPE,
+                                                        &sent_cert_types);
+               if (ret < 0) {
+                       /* This should not happen and indicate a memory corruption!
+                        * Assertion are always on in production code so execution
+                        * will halt here. */
+                       assert(false);
+               }
+
+               // Check whether what we got back is actually offered by us
+               for (i = 0; i < sent_cert_types.size; i++) {
+                       if (_gnutls_IANA2cert_type(sent_cert_types.data[i]) == cert_type)
+                               found = 1;
+               }
+
+               if (found) {
+                       // Everything OK, now set the server certificate type
+                       _gnutls_session_server_cert_type_set(session, cert_type);
+                       ret = GNUTLS_E_SUCCESS;
+               } else {
+                       // No valid cert type found
+                       ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+               }
+
+               return ret;
+
+       } else {                // server mode
+               // Compare packet length with expected packet length.
+               DECR_LEN(len, 1);
+               if (data[0] != len) {
+                       return
+                                       gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+               }
+               pdata += 1;
+
+               // Assign the contents of our data buffer to a gnutls_datum_t
+               cert_types.data = (uint8_t*)pdata; // Need casting to get rid of 'discards const qualifier' warning
+               cert_types.size = len;
+
+               // Store the server certificate types in our session
+               _gnutls_hello_ext_set_datum(session,
+                                                        GNUTLS_EXTENSION_SERVER_CERT_TYPE,
+                                                        &cert_types);
+
+               /* We receive a list of supported certificate types that the client
+                * is able to process when offered by the server via a subsequent
+                * Certificate message. This list is sorted by order of preference.
+                * We now check in this order of preference whether we support any
+                * of these certificate types.
+                */
+               for (i = 0; i < cert_types.size; i++) {
+                       // Convert to internal representation
+                       cert_type = _gnutls_IANA2cert_type(cert_types.data[i]);
+
+                       // If we have an invalid cert id then continue to the next
+                       if (cert_type == GNUTLS_CRT_UNKNOWN)
+                               continue;
+
+                       // Check for support of this cert type
+                       if (_gnutls_session_cert_type_supported
+                                       (session, cert_type, true, GNUTLS_CTYPE_SERVER) == 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               // We found a matching ctype, we pick this one
+               if (found) {
+                       _gnutls_session_server_cert_type_set(session, cert_type);
+                       ret = GNUTLS_E_SUCCESS;
+               } else {
+                       /* If no supported certificate type can be found we terminate
+                        * with a fatal alert of type "unsupported_certificate"
+                        * (according to specification rfc7250).
+                        */
+                       ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+               }
+
+               return ret;
+       }
+}
+
+static int _gnutls_server_cert_type_send_params(gnutls_session_t session,
+                                               gnutls_buffer_st* data)
+{
+       int ret;
+       uint8_t cert_type; // Holds an IANA cert type ID
+       uint8_t i = 0, num_cert_types = 0;
+       priority_st* cert_priors;
+       gnutls_datum_t tmp_cert_types; // For type conversion
+       uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types
+
+       /* Only activate this extension if cert type negotiation is enabled
+        * and we have cert credentials set */
+       if (!_gnutls_has_negotiate_ctypes(session) ||
+                       _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)
+               return 0;
+
+       if (!IS_SERVER(session)) {      // Client mode
+               // For brevity
+               cert_priors =
+                               &session->internals.priorities->server_ctype;
+
+               /* Retrieve server certificate type priorities if any. If no
+                * priorities are set then the default server certificate type
+                * initialization values apply. This default is currently set to
+                * X.509 in which case we don't enable this extension.
+                */
+               if (cert_priors->algorithms > 0) {      // Priorities are explicitly set
+                       /* If the certificate priority is explicitly set to only
+                        * X.509 (default) then, according to spec we don't send
+                        * this extension. We check this here to avoid further work in
+                        * this routine. We also check it below after pruning supported
+                        * types.
+                        */
+                       if (cert_priors->algorithms == 1 &&
+                                       cert_priors->priority[0] == DEFAULT_CERT_TYPE) {
+                               _gnutls_handshake_log
+                                               ("EXT[%p]: Server certificate type was set to default cert type (%s). "
+                                                "We therefore do not send this extension.\n",
+                                                session,
+                                                gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE));
+
+                               // Explicitly set but default ctype, so don't send anything
+                               return 0;
+                       }
+
+                       /* We are only allowed to send certificate types that we support.
+                        * Therefore we check this here and prune our original list.
+                        * This check might seem redundant now because we don't check for
+                        * credentials (they are not needed for a client) and only check the
+                        * priorities over which we already iterate. In the future,
+                        * additional checks might be necessary and they can be easily
+                        * added in the ..type_supported() routine without modifying the
+                        * structure of the code here.
+                        */
+                       for (i = 0; i < cert_priors->algorithms; i++) {
+                               if (_gnutls_session_cert_type_supported
+                                               (session, cert_priors->priority[i],
+                                                false, GNUTLS_CTYPE_SERVER) == 0) {
+                                       /* Check whether we are allowed to store another cert type
+                                        * in our buffer. In other words, prevent a possible buffer
+                                        * overflow. This situation can occur when a user sets
+                                        * duplicate cert types in the priority strings. */
+                                       if (num_cert_types >= GNUTLS_CRT_MAX)
+                                               return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+                                       // Convert to IANA representation
+                                       cert_type = _gnutls_cert_type2IANA(cert_priors->priority[i]);
+                                       // Add this cert type to our list with supported types
+                                       cert_types[num_cert_types] = cert_type;
+                                       num_cert_types++;
+
+                                       _gnutls_handshake_log
+                                                       ("EXT[%p]: Server certificate type %s (%d) was queued.\n",
+                                                        session,
+                                                        gnutls_certificate_type_get_name(cert_priors->priority[i]),
+                                                        cert_type);
+                               }
+                       }
+
+                       /* Check whether there are any supported certificate types left
+                        * after the previous pruning step. If not, we do not send this
+                        * extension. Also, if the only supported type is the default type
+                        * we do not send this extension (according to RFC7250).
+                        */
+                       if (num_cert_types == 0) {      // For now, this should not occur since we only check priorities while pruning.
+                               _gnutls_handshake_log
+                                               ("EXT[%p]: Server certificate types were set but none of them is supported. "
+                                                "We do not send this extension.\n",
+                                                session);
+
+                               return 0;
+                       } else if (num_cert_types == 1 &&
+                                        _gnutls_IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) {
+                               _gnutls_handshake_log
+                                               ("EXT[%p]: The only supported server certificate type is (%s) which is the default. "
+                                                "We therefore do not send this extension.\n",
+                                                session,
+                                                gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE));
+
+                               return 0;
+                       }
+
+                       /* We have data to send and store a copy internally. We convert
+                        * our list with supported cert types to a datum_t in order to
+                        * be able to make the ..._set_datum call.
+                        */
+                       tmp_cert_types.data = cert_types;
+                       tmp_cert_types.size = num_cert_types;
+
+                       _gnutls_hello_ext_set_datum(session,
+                                                                GNUTLS_EXTENSION_SERVER_CERT_TYPE,
+                                                                &tmp_cert_types);
+
+                       /* Serialize the certificate types into a sequence of octets
+                        * uint8: length of sequence of cert types (1 octet)
+                        * uint8: cert types (0 <= #octets <= 255)
+                        */
+                       ret = _gnutls_buffer_append_data_prefix(data, 8,
+                                                               cert_types,
+                                                               num_cert_types);
+
+                       // Check for errors and cleanup in case of error
+                       if (ret < 0) {
+                               return gnutls_assert_val(ret);
+                       } else {
+                               // Number of bytes we are sending
+                               return num_cert_types + 1;
+                       }
+               }
+       } else {        // Server mode
+               // Retrieve negotiated server certificate type and send it
+               cert_type =
+                               _gnutls_cert_type2IANA(session->security_parameters.
+                                               server_ctype);
+
+               ret = gnutls_buffer_append_data(data, &cert_type, 1);
+
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+
+               return 1;       // sent one byte
+       }
+
+       // In all other cases don't enable this extension
+       return 0;
+}
+
+
+/** Extension interface **/
+
+/* The interface is defined in state.c:
+ * Public:
+ * - gnutls_certificate_type_get2
+ *
+ * Private:
+ * - _gnutls_session_server_cert_type_set
+ */
diff --git a/lib/ext/server_cert_type.h b/lib/ext/server_cert_type.h
new file mode 100644 (file)
index 0000000..b5eab5c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 - 2018 ARPA2 project
+ *
+ * Author: Tom Vrancken (dev@tomvrancken.nl)
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This file is part of the server_certificate_type extension as
+ * defined in RFC7250 (https://tools.ietf.org/html/rfc7250).
+ *
+ * The server_certificate_type extension in the client hello indicates
+ * the certificate types the client is able to process from the server
+ * in order to authenticate the server.
+ */
+
+#ifndef EXT_SERVER_CERT_TYPE_H
+#define EXT_SERVER_CERT_TYPE_H
+
+#include <hello_ext.h>
+
+extern const hello_ext_entry_st ext_mod_server_cert_type;
+
+#endif
index dfec39ec33a63d9f824fca6366497bc3967f3045..8ad6b1652d094a6e118f5eac8a4a713f1a58693e 100644 (file)
@@ -191,9 +191,17 @@ typedef enum record_send_state_t {
        RECORD_SEND_KEY_UPDATE_3
 } record_send_state_t;
 
-/* the maximum size of encrypted packets */
+/* The mode check occurs a lot throughout GnuTLS and can be replaced by
+ * the following shorter macro. Also easier to update one macro
+ * in the future when the internal structure changes than all the conditionals
+ * itself.
+ */
+#define IS_SERVER(session) (session->security_parameters.entity == GNUTLS_SERVER)
+
+/* To check whether we have a DTLS session */
 #define IS_DTLS(session) (session->internals.transport == GNUTLS_DGRAM)
 
+/* the maximum size of encrypted packets */
 #define DEFAULT_MAX_RECORD_SIZE 16384
 #define DEFAULT_MAX_EARLY_DATA_SIZE 16384
 #define TLS_RECORD_HEADER_SIZE 5
@@ -327,6 +335,8 @@ typedef enum extensions_t {
        GNUTLS_EXTENSION_MAX_RECORD_SIZE = 0,
        GNUTLS_EXTENSION_STATUS_REQUEST,
        GNUTLS_EXTENSION_CERT_TYPE,
+       GNUTLS_EXTENSION_CLIENT_CERT_TYPE,
+       GNUTLS_EXTENSION_SERVER_CERT_TYPE,
        GNUTLS_EXTENSION_SUPPORTED_GROUPS,
        GNUTLS_EXTENSION_SUPPORTED_EC_POINT_FORMATS,
        GNUTLS_EXTENSION_SRP,
@@ -760,8 +770,9 @@ typedef struct {
        /* The maximum amount of early data */
        uint32_t max_early_data_size;
 
-       /* holds the negotiated certificate type */
-       gnutls_certificate_type_t cert_type;
+       /* holds the negotiated certificate types */
+       gnutls_certificate_type_t client_ctype;
+       gnutls_certificate_type_t server_ctype;
 
        /* The selected (after server hello EC or DH group */
        const gnutls_group_entry_st *grp;
@@ -887,7 +898,8 @@ typedef struct sign_algo_list_st {
 /* For the external api */
 struct gnutls_priority_st {
        priority_st protocol;
-       priority_st cert_type;
+       priority_st client_ctype;
+       priority_st server_ctype;
 
        /* The following are not necessary to be stored in
         * the structure; however they are required by the
@@ -1045,6 +1057,7 @@ typedef struct {
                                                 * the client finished message */
        gnutls_buffer_st handshake_hash_buffer; /* used to keep the last received handshake
                                                 * message */
+
        bool resumable; /* TRUE or FALSE - if we can resume that session */
 
        send_ticket_state_t ticket_state; /* used by gnutls_session_ticket_send() */
@@ -1434,13 +1447,13 @@ void _gnutls_free_auth_info(gnutls_session_t session);
 /* These two macros return the advertised TLS version of
  * the peer.
  */
-#define _gnutls_get_adv_version_major( session) \
+#define _gnutls_get_adv_version_major(session) \
        session->internals.adv_version_major
 
-#define _gnutls_get_adv_version_minor( session) \
+#define _gnutls_get_adv_version_minor(session) \
        session->internals.adv_version_minor
 
-#define set_adv_version( session, major, minor) \
+#define set_adv_version(session, major, minor) \
        session->internals.adv_version_major = major; \
        session->internals.adv_version_minor = minor
 
@@ -1493,4 +1506,9 @@ inline static size_t max_user_send_size(gnutls_session_t session,
        return max;
 }
 
+inline static bool _gnutls_has_negotiate_ctypes(gnutls_session_t session)
+{
+       return session->internals.flags & GNUTLS_ENABLE_CERT_TYPE_NEG;
+}
+
 #endif                         /* GNUTLS_INT_H */
index ebea926aa5d2b27026be9fc314d6d5fd94308cbd..914f8ecacc0a6746d20d07cc7bbc6b1f1162486e 100644 (file)
@@ -198,8 +198,10 @@ static int tls12_resume_copy_required_vals(gnutls_session_t session, unsigned ti
                                    id) < 0)
                return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
 
-       session->security_parameters.cert_type =
-           session->internals.resumed_security_parameters.cert_type;
+       session->security_parameters.client_ctype =
+           session->internals.resumed_security_parameters.client_ctype;
+       session->security_parameters.server_ctype =
+           session->internals.resumed_security_parameters.server_ctype;
 
        if (!ticket) {
                memcpy(session->security_parameters.session_id,
index 9e30bd5413d2b53d20d5ff5b6f77808db16a170d..28e8e8c08e0c958d2bd6f0e22e64a13abfed2b67 100644 (file)
@@ -55,6 +55,8 @@
 #include <ext/record_size_limit.h>
 #include "extv.h"
 #include <num.h>
+#include <ext/client_cert_type.h>
+#include <ext/server_cert_type.h>
 
 static void
 unset_ext_data(gnutls_session_t session, const struct hello_ext_entry_st *, unsigned idx);
@@ -79,6 +81,8 @@ static hello_ext_entry_st const *extfunc[MAX_EXT_TYPES+1] = {
        [GNUTLS_EXTENSION_HEARTBEAT] = &ext_mod_heartbeat,
 #endif
        [GNUTLS_EXTENSION_SESSION_TICKET] = &ext_mod_session_ticket,
+       [GNUTLS_EXTENSION_CLIENT_CERT_TYPE] = &ext_mod_client_cert_type,
+       [GNUTLS_EXTENSION_SERVER_CERT_TYPE] = &ext_mod_server_cert_type,
        [GNUTLS_EXTENSION_SUPPORTED_GROUPS] = &ext_mod_supported_groups,
        [GNUTLS_EXTENSION_SUPPORTED_EC_POINT_FORMATS] = &ext_mod_supported_ec_point_formats,
        [GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS] = &ext_mod_sig,
index 5ee433b24e96ed293db0301975d62d2708f7f53a..663354caaf1cffdbe51ddc46fd2d82fc8994d25b 100644 (file)
@@ -40,7 +40,7 @@ void _gnutls_hello_ext_default_deinit(gnutls_ext_priv_data_t priv)
 int
 _gnutls_hello_ext_set_datum(gnutls_session_t session,
                            extensions_t id, const gnutls_datum_t *data)
-{
+{ //REMARK: we are limiting the data size to fit in a uint16 while a datum_t uses unsigned int!
        gnutls_ext_priv_data_t epriv;
 
        if (_gnutls_hello_ext_get_priv(session, id, &epriv) >= 0)
index 90ff1985d13161897d602e6ec50fba8830eb566e..4d138b84465987e74114b9aca6d5e734443b1302 100644 (file)
@@ -406,6 +406,7 @@ typedef enum {
  *   applications which hide the length of transferred data via the TLS1.3 padding mechanism and
  *   are already taking steps to hide the data processing time. This comes at a performance
  *   penalty.
+ * @GNUTLS_ENABLE_CERT_TYPE_NEG: Enable certificate type negotiation extensions (RFC7250).
  *
  * Enumeration of different flags for gnutls_init() function. All the flags
  * can be combined except @GNUTLS_SERVER and @GNUTLS_CLIENT which are mutually
@@ -433,7 +434,8 @@ typedef enum {
        GNUTLS_POST_HANDSHAKE_AUTH = (1<<14),
        GNUTLS_NO_AUTO_REKEY = (1<<15),
        GNUTLS_SAFE_PADDING_CHECK = (1<<16),
-       GNUTLS_ENABLE_EARLY_START = (1<<17)
+       GNUTLS_ENABLE_EARLY_START = (1<<17),
+       GNUTLS_ENABLE_CERT_TYPE_NEG = (1<<18)
 } gnutls_init_flags_t;
 
 /* compatibility defines (previous versions of gnutls
@@ -719,7 +721,7 @@ typedef enum {
  * @GNUTLS_CRT_UNKNOWN: Unknown certificate type.
  * @GNUTLS_CRT_X509: X.509 Certificate.
  * @GNUTLS_CRT_OPENPGP: OpenPGP certificate.
- * @GNUTLS_CRT_RAW: Raw public key (SubjectPublicKey)
+ * @GNUTLS_CRT_RAWPK: Raw public-key (SubjectPublicKeyInfo)
  *
  * Enumeration of different certificate types.
  */
@@ -727,7 +729,8 @@ typedef enum {
        GNUTLS_CRT_UNKNOWN = 0,
        GNUTLS_CRT_X509 = 1,
        GNUTLS_CRT_OPENPGP = 2,
-       GNUTLS_CRT_RAW = 3
+       GNUTLS_CRT_RAWPK = 3,
+       GNUTLS_CRT_MAX = GNUTLS_CRT_RAWPK
 } gnutls_certificate_type_t;
 
 /**
@@ -1060,6 +1063,24 @@ typedef enum {
        GNUTLS_GOST_PARAMSET_CP_D
 } gnutls_gost_paramset_t;
 
+/**
+ * gnutls_ctype_target_t:
+ * @GNUTLS_CTYPE_CLIENT: for requesting client certificate type values.
+ * @GNUTLS_CTYPE_SERVER: for requesting server certificate type values.
+ * @GNUTLS_CTYPE_OURS: for requesting our certificate type values.
+ * @GNUTLS_CTYPE_PEERS: for requesting the peers' certificate type values.
+ *
+ * Enumeration of certificate type targets with respect to asymmetric
+ * certificate types as specified in RFC7250 and P2P connection set up
+ * as specified in draft-vanrein-tls-symmetry-02.
+ */
+typedef enum {
+       GNUTLS_CTYPE_CLIENT,
+       GNUTLS_CTYPE_SERVER,
+       GNUTLS_CTYPE_OURS,
+       GNUTLS_CTYPE_PEERS
+} gnutls_ctype_target_t;
+
 /* If you want to change this, then also change the define in
  * gnutls_int.h, and recompile.
  */
@@ -1154,6 +1175,9 @@ gnutls_kx_algorithm_t gnutls_kx_get(gnutls_session_t session);
 gnutls_mac_algorithm_t gnutls_mac_get(gnutls_session_t session);
 gnutls_certificate_type_t
 gnutls_certificate_type_get(gnutls_session_t session);
+gnutls_certificate_type_t
+gnutls_certificate_type_get2(gnutls_session_t session,
+                                                               gnutls_ctype_target_t target);
 
 int gnutls_sign_algorithm_get(gnutls_session_t session);
 int gnutls_sign_algorithm_get_client(gnutls_session_t session);
@@ -1585,6 +1609,9 @@ int gnutls_priority_set_direct(gnutls_session_t session,
 
 int gnutls_priority_certificate_type_list(gnutls_priority_t pcache,
                                          const unsigned int **list);
+int gnutls_priority_certificate_type_list2(gnutls_priority_t pcache,
+                                         const unsigned int **list,
+                                         gnutls_ctype_target_t target);
 int gnutls_priority_sign_list(gnutls_priority_t pcache,
                              const unsigned int **list);
 int gnutls_priority_protocol_list(gnutls_priority_t pcache,
index d827790745b002c79286c832bc38f6d792303cdd..c20dd33cfefef73f7dec47231790d5ad0162732f 100644 (file)
@@ -1244,6 +1244,8 @@ GNUTLS_3_6_4
        gnutls_ffdhe_6144_group_prime;
        gnutls_ffdhe_6144_group_generator;
        gnutls_ffdhe_6144_key_bits;
+       gnutls_certificate_type_get2;
+       gnutls_priority_certificate_type_list2;
 } GNUTLS_3_6_3;
 
 GNUTLS_FIPS140_3_4 {
index 1d495d0e674db1469bcffa57dcfc13dfaafdc1bc..00681c53e81c5e7839e4ce1d501328dc099ccd42 100644 (file)
@@ -517,6 +517,12 @@ static const int cert_type_priority_default[] = {
        0
 };
 
+static const int cert_type_priority_all[] = {
+       GNUTLS_CRT_X509,
+       GNUTLS_CRT_RAWPK,
+       0
+};
+
 typedef void (rmadd_func) (priority_st * priority_list, unsigned int alg);
 
 static void prio_remove(priority_st * priority_list, unsigned int algo)
@@ -1621,7 +1627,9 @@ gnutls_priority_init(gnutls_priority_t * priority_cache,
        if (strcasecmp(broken_list[0], LEVEL_NONE) != 0) {
                _set_priority(&(*priority_cache)->protocol,
                              protocol_priority);
-               _set_priority(&(*priority_cache)->cert_type,
+               _set_priority(&(*priority_cache)->client_ctype,
+                             cert_type_priority_default);
+               _set_priority(&(*priority_cache)->server_ctype,
                              cert_type_priority_default);
                _set_priority(&(*priority_cache)->_sign_algo,
                              sign_priority_default);
@@ -1755,8 +1763,39 @@ gnutls_priority_init(gnutls_priority_t * priority_cache,
                                                goto error;
                                }
                        } else if (strncasecmp
-                                (&broken_list[i][1], "CTYPE-", 6) == 0) {
-                               continue;
+                               (&broken_list[i][1], "CTYPE-", 6) == 0) { // Certificate types
+                               if (strncasecmp(&broken_list[i][1], "CTYPE-ALL", 9) == 0)
+                               { // Symmetric cert types, all types allowed
+                                       bulk_fn(&(*priority_cache)->client_ctype, cert_type_priority_all);
+                                       bulk_fn(&(*priority_cache)->server_ctype, cert_type_priority_all);
+                               } else if (strncasecmp(&broken_list[i][1], "CTYPE-CLI-", 10) == 0)
+                               { // Client certificate types
+                                       if (strncasecmp(&broken_list[i][1], "CTYPE-CLI-ALL", 13) == 0)
+                                       { // All client cert types allowed
+                                               bulk_fn(&(*priority_cache)->client_ctype,       cert_type_priority_all);
+                                       } else if ((algo = gnutls_certificate_type_get_id
+                                                       (&broken_list[i][11])) != GNUTLS_CRT_UNKNOWN)
+                                       { // Specific client cert type allowed
+                                               fn(&(*priority_cache)->client_ctype, algo);
+                                       } else goto error;
+                               } else if (strncasecmp(&broken_list[i][1], "CTYPE-SRV-", 10) == 0)
+                               { // Server certificate types
+                                       if (strncasecmp(&broken_list[i][1], "CTYPE-SRV-ALL", 13) == 0)
+                                       { // All server cert types allowed
+                                               bulk_fn(&(*priority_cache)->server_ctype,       cert_type_priority_all);
+                                       } else if ((algo = gnutls_certificate_type_get_id
+                                                       (&broken_list[i][11])) != GNUTLS_CRT_UNKNOWN)
+                                       { // Specific server cert type allowed
+                                               fn(&(*priority_cache)->server_ctype, algo);
+                                       } else goto error;
+                               } else { // Symmetric certificate type
+                                       if ((algo = gnutls_certificate_type_get_id
+                                            (&broken_list[i][7])) != GNUTLS_CRT_UNKNOWN)
+                                       {
+                                               fn(&(*priority_cache)->client_ctype, algo);
+                                               fn(&(*priority_cache)->server_ctype, algo);
+                                       } else goto error;
+                               }
                        } else if (strncasecmp
                                 (&broken_list[i][1], "SIGN-", 5) == 0) {
                                if (strncasecmp
@@ -2207,7 +2246,13 @@ gnutls_priority_sign_list(gnutls_priority_t pcache,
  * @list: will point to an integer list
  *
  * Get a list of available certificate types in the priority
- * structure. 
+ * structure.
+ *
+ * As of version 3.6.4 this function is an alias for
+ * gnutls_priority_certificate_type_list2 with the target parameter
+ * set to:
+ * - GNUTLS_CTYPE_SERVER, if the %SERVER_PRECEDENCE option is set
+ * - GNUTLS_CTYPE_CLIENT, otherwise.
  *
  * Returns: the number of certificate types, or an error code.
  * Since: 3.0
@@ -2216,11 +2261,50 @@ int
 gnutls_priority_certificate_type_list(gnutls_priority_t pcache,
                                      const unsigned int **list)
 {
-       if (pcache->cert_type.algorithms == 0)
-               return 0;
+       gnutls_ctype_target_t target =
+               pcache->server_precedence ? GNUTLS_CTYPE_SERVER : GNUTLS_CTYPE_CLIENT;
 
-       *list = pcache->cert_type.priority;
-       return pcache->cert_type.algorithms;
+       return gnutls_priority_certificate_type_list2(pcache, list, target);
+}
+
+/**
+ * gnutls_priority_certificate_type_list2:
+ * @pcache: is a #gnutls_prioritity_t type.
+ * @list: will point to an integer list.
+ * @target: is a #gnutls_ctype_target_t type. Valid arguments are
+ *   GNUTLS_CTYPE_CLIENT and GNUTLS_CTYPE_SERVER
+ *
+ * Get a list of available certificate types for the given target
+ * in the priority structure.
+ *
+ * Returns: the number of certificate types, or an error code.
+ *
+ * Since: 3.6.4
+ **/
+int
+gnutls_priority_certificate_type_list2(gnutls_priority_t pcache,
+                                     const unsigned int **list, gnutls_ctype_target_t target)
+{
+       switch (target) {
+               case GNUTLS_CTYPE_CLIENT:
+                       if(pcache->client_ctype.algorithms > 0) {
+                               *list = pcache->client_ctype.priority;
+                               return pcache->client_ctype.algorithms;
+                       }
+                       break;
+               case GNUTLS_CTYPE_SERVER:
+                       if(pcache->server_ctype.algorithms > 0) {
+                               *list = pcache->server_ctype.priority;
+                               return pcache->server_ctype.algorithms;
+                       }
+                       break;
+               default:
+                       // Invalid target given
+                       gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       // Found a matching target but non of them had any ctypes set
+       return 0;
 }
 
 /**
index 1622b29764de270045344c09e332a7e41fc11310..5d862198b5f8e2da38d92229ac75dde02fd76617 100644 (file)
@@ -337,7 +337,7 @@ char *gnutls_session_get_desc(gnutls_session_t session)
 {
        gnutls_kx_algorithm_t kx;
        const char *kx_str, *sign_str;
-       unsigned type;
+       gnutls_certificate_type_t ctype_client, ctype_server;
        char kx_name[64] = "";
        char proto_name[32];
        char _group_name[24];
@@ -423,17 +423,29 @@ char *gnutls_session_get_desc(gnutls_session_t session)
                }
        }
 
-
-       type = gnutls_certificate_type_get(session);
-       if (type == GNUTLS_CRT_X509 || type == GNUTLS_CRT_UNKNOWN)
-               snprintf(proto_name, sizeof(proto_name), "%s",
-                        gnutls_protocol_get_name(get_num_version
-                                                 (session)));
-       else
-               snprintf(proto_name, sizeof(proto_name), "%s-%s",
-                        gnutls_protocol_get_name(get_num_version
-                                                 (session)),
-                        gnutls_certificate_type_get_name(type));
+       // Check whether we have negotiated certificate types
+       if (_gnutls_has_negotiate_ctypes(session)) {
+               // Get certificate types
+               ctype_client = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT);
+               ctype_server = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+
+               if (ctype_client == ctype_server) {
+                       // print proto version, client/server cert type
+                       snprintf(proto_name, sizeof(proto_name), "%s-%s",
+                                gnutls_protocol_get_name(get_num_version(session)),
+                                gnutls_certificate_type_get_name(ctype_client));
+               } else {
+                       // print proto version, client cert type, server cert type
+                       snprintf(proto_name, sizeof(proto_name), "%s-%s-%s",
+                                gnutls_protocol_get_name(get_num_version(session)),
+                                gnutls_certificate_type_get_name(ctype_client),
+                                gnutls_certificate_type_get_name(ctype_server));
+               }
+       } else { // Assumed default certificate type (X.509)
+                       snprintf(proto_name, sizeof(proto_name), "%s",
+                                gnutls_protocol_get_name(get_num_version
+                                                               (session)));
+       }
 
        desc = gnutls_malloc(DESC_SIZE);
        if (desc == NULL)
index 9fbd5b3ae85d60ae7d2cb4b5dd2d72896a9fcfb9..f8b1830568ca83f2721c13f7d10d8d634aa228ba 100644 (file)
@@ -898,7 +898,8 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
        if (!session->security_parameters.pversion->tls13_sem) {
                BUFFER_APPEND(ps, session->security_parameters.cs->id, 2);
 
-               BUFFER_APPEND_NUM(ps, session->security_parameters.cert_type);
+               BUFFER_APPEND_NUM(ps, session->security_parameters.client_ctype);
+               BUFFER_APPEND_NUM(ps, session->security_parameters.server_ctype);
 
                BUFFER_APPEND_PFX1(ps, session->security_parameters.master_secret,
                              GNUTLS_MASTER_SIZE);
@@ -1001,8 +1002,11 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
                        return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
                BUFFER_POP_NUM(ps,
-                              session->internals.resumed_security_parameters.
-                              cert_type);
+                               session->internals.resumed_security_parameters.
+                               client_ctype);
+               BUFFER_POP_NUM(ps,
+                               session->internals.resumed_security_parameters.
+                               server_ctype);
 
                /* master secret */
                ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
@@ -1129,8 +1133,10 @@ gnutls_session_set_premaster(gnutls_session_t session, unsigned int entity,
        if (session->internals.resumed_security_parameters.cs == NULL)
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
-       session->internals.resumed_security_parameters.cert_type =
-           DEFAULT_CERT_TYPE;
+       session->internals.resumed_security_parameters.client_ctype =
+                       DEFAULT_CERT_TYPE;
+       session->internals.resumed_security_parameters.server_ctype =
+                       DEFAULT_CERT_TYPE;
        session->internals.resumed_security_parameters.pversion =
            version_to_entry(version);
        if (session->internals.resumed_security_parameters.pversion ==
index ab0021a64ed925a986f72b0299f5bc822c488a1a..58db8f9a32871c3ddaf6755b7319f02a95203282 100644 (file)
@@ -92,13 +92,71 @@ gnutls_cipher_algorithm_t gnutls_cipher_get(gnutls_session_t session)
  * The certificate type is by default X.509, unless it is negotiated
  * as a TLS extension.
  *
+ * As of version 3.6.4 it is recommended to use
+ * gnutls_certificate_type_get2().
+ *
  * Returns: the currently used #gnutls_certificate_type_t certificate
- *   type.
+ *   type as negotiated for 'our' side of the connection.
  **/
 gnutls_certificate_type_t
 gnutls_certificate_type_get(gnutls_session_t session)
 {
-       return session->security_parameters.cert_type;
+       return gnutls_certificate_type_get2(session, GNUTLS_CTYPE_OURS);
+}
+
+/**
+ * gnutls_certificate_type_get2:
+ * @session: is a #gnutls_session_t type.
+ * @target: is a #gnutls_ctype_target_t type.
+ *
+ * The raw public-key extension (RFC7250) introduces a mechanism
+ * to specifcy different certificate types for the client and server. We
+ * therefore distinguish between negotiated certificate types for the
+ * client and server. The @target parameter specifies whether you want
+ * the negotiated certificate type for the client (GNUTLS_CTYPE_CLIENT)
+ * or for the server (GNUTLS_CTYPE_SERVER). Additionally, in P2P mode
+ * connection set up where you don't know in advance who will be client
+ * and who will be server you can use the flag (GNUTLS_CTYPE_OURS) and
+ * (GNUTLS_CTYPE_PEERS) to retrieve the corresponding certificate types.
+ *
+ * In case no certificate types were explicitly set via the priority
+ * strings to be negotiated during the handshake, then this function
+ * will return the default certificate type (X.509) for both the
+ * client and the server.
+ *
+ * Returns: the currently used #gnutls_certificate_type_t certificate
+ *   type for the client or the server.
+ *
+ * Since: 3.6.4
+ **/
+gnutls_certificate_type_t
+gnutls_certificate_type_get2(gnutls_session_t session,
+                                                               gnutls_ctype_target_t target)
+{
+       switch (target) {
+               case GNUTLS_CTYPE_CLIENT:
+                       return session->security_parameters.client_ctype;
+                       break;
+               case GNUTLS_CTYPE_SERVER:
+                       return session->security_parameters.server_ctype;
+                       break;
+               case GNUTLS_CTYPE_OURS:
+                       if (IS_SERVER(session)) {
+                               return session->security_parameters.server_ctype;
+                       } else {
+                               return session->security_parameters.client_ctype;
+                       }
+                       break;
+               case GNUTLS_CTYPE_PEERS:
+                       if (IS_SERVER(session)) {
+                               return session->security_parameters.client_ctype;
+                       } else {
+                               return session->security_parameters.server_ctype;
+                       }
+                       break;
+               default:                // Illegal parameter passed
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
 }
 
 /**
@@ -190,6 +248,100 @@ void reset_binders(gnutls_session_t session)
        memset(session->key.binders, 0, sizeof(session->key.binders));
 }
 
+/* Check whether certificate credentials of type @cert_type are set
+ * for the current session.
+ */
+static bool _gnutls_has_cert_credentials(gnutls_session_t session,
+                                               gnutls_certificate_type_t cert_type)
+{
+       unsigned i;
+       unsigned cert_found = 0;
+       gnutls_certificate_credentials_t cred;
+
+       /* First, check for certificate credentials. If we have no certificate
+        * credentials set then we don't support certificates at all.
+        */
+       cred = (gnutls_certificate_credentials_t)
+                       _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+
+       if (cred == NULL)
+               return false;
+
+       /* There are credentials initialized. Now check whether we can find
+        * pre-set certificates of the required type, but only if we don't
+        * use the callback functions.
+        */
+       if (cred->get_cert_callback3 == NULL) {
+               for (i = 0; i < cred->ncerts; i++) {
+                       if (cred->certs[i].cert_list[0].type == cert_type) {
+                               cert_found = 1;
+                               break;
+                       }
+               }
+
+               if (cert_found == 0) {
+                       /* No matching certificate found. */
+                       return false;
+               }
+       }
+
+       return true; // OK
+}
+
+/* Check if the given certificate type is supported.
+ * This means that it is enabled by the priority functions,
+ * and in some cases a matching certificate exists. A check for
+ * the latter can be toggled via the parameter @check_credentials.
+ */
+int
+_gnutls_session_cert_type_supported(gnutls_session_t session,
+                                   gnutls_certificate_type_t cert_type,
+                                   bool check_credentials,
+                                   gnutls_ctype_target_t target)
+{
+       unsigned i;
+       priority_st* ctype_priorities;
+
+       // Perform a credentials check if requested
+       if (check_credentials)  {
+               if (!_gnutls_has_cert_credentials(session, cert_type))
+                       return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+       }
+
+       /* So far so good. We have the required credentials (if needed).
+        * Now check whether we are allowed to use them according to our
+        * priorities.
+        */
+       // Which certificate type should we query?
+       switch (target) {
+               case GNUTLS_CTYPE_CLIENT:
+                       ctype_priorities =
+                                       &(session->internals.priorities->client_ctype);
+                       break;
+               case GNUTLS_CTYPE_SERVER:
+                       ctype_priorities =
+                                       &(session->internals.priorities->server_ctype);
+                       break;
+               default:
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       // No explicit priorities set, and default ctype is asked
+       if (ctype_priorities->algorithms == 0
+           && cert_type == DEFAULT_CERT_TYPE)
+               return 0; // ok
+
+       /* Now lets find out whether our cert type is in our priority
+        * list, i.e. set of allowed cert types.
+        */
+       for (i = 0; i < ctype_priorities->algorithms; i++) {
+               if (ctype_priorities->priority[i] == cert_type)
+                       return 0;       /* ok */
+       }
+
+       return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+}
+
 static void deinit_keys(gnutls_session_t session)
 {
        const version_entry_st *vers = get_version(session);
@@ -318,7 +470,8 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
            (flags & GNUTLS_SERVER ? GNUTLS_SERVER : GNUTLS_CLIENT);
 
        /* the default certificate type for TLS */
-       (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE;
+       (*session)->security_parameters.client_ctype = DEFAULT_CERT_TYPE;
+       (*session)->security_parameters.server_ctype = DEFAULT_CERT_TYPE;
 
        /* Initialize buffers */
        _gnutls_buffer_init(&(*session)->internals.handshake_hash_buffer);
@@ -926,6 +1079,24 @@ _gnutls_rsa_pms_set_version(gnutls_session_t session,
        session->internals.rsa_pms_version[1] = minor;
 }
 
+void _gnutls_session_client_cert_type_set(gnutls_session_t session,
+                             gnutls_certificate_type_t ct)
+{
+       _gnutls_handshake_log
+           ("HSK[%p]: Selected client certificate type %s (%d)\n", session,
+            gnutls_certificate_type_get_name(ct), ct);
+       session->security_parameters.client_ctype = ct;
+}
+
+void _gnutls_session_server_cert_type_set(gnutls_session_t session,
+                             gnutls_certificate_type_t ct)
+{
+       _gnutls_handshake_log
+           ("HSK[%p]: Selected server certificate type %s (%d)\n", session,
+            gnutls_certificate_type_get_name(ct), ct);
+       session->security_parameters.server_ctype = ct;
+}
+
 /**
  * gnutls_handshake_set_post_client_hello_function:
  * @session: is a #gnutls_session_t type.
index 75d0f35fc18d11e16e711c6117a5253382acaaa1..a93e5d49ce18d73ab00d2e53f92321fa0ff66daf 100644 (file)
 
 #include "gnutls_int.h"
 
+void _gnutls_session_client_cert_type_set(gnutls_session_t session,
+                                        gnutls_certificate_type_t);
+void _gnutls_session_server_cert_type_set(gnutls_session_t session,
+                                        gnutls_certificate_type_t);
+
 inline static const gnutls_group_entry_st *
 get_group(gnutls_session_t session)
 {
@@ -73,6 +78,11 @@ _gnutls_hello_set_default_version(gnutls_session_t session,
 
 #endif
 
+
+int _gnutls_session_cert_type_supported(gnutls_session_t session,
+                                   gnutls_certificate_type_t cert_type,
+                                   bool check_credentials,
+                                   gnutls_ctype_target_t target);
 int _gnutls_dh_set_secret_bits(gnutls_session_t session, unsigned bits);
 
 int _gnutls_dh_set_peer_public(gnutls_session_t session, bigint_t public);
index 96076e4e4691b99738b21cc0089b2e573482c269..01966b14d19216039fb45d21148f1463c8adaa71 100644 (file)
@@ -51,6 +51,7 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
        gnutls_pcert_st peer_cert;
        cert_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
        bool server = 0;
+       gnutls_certificate_type_t cert_type;
 
        memset(&peer_cert, 0, sizeof(peer_cert));
 
@@ -73,7 +74,7 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
        if (ret < 0)
                return gnutls_assert_val(ret);
 
-       _gnutls_handshake_log("HSK[%p]: parsing certificate verify\n", session);
+       _gnutls_handshake_log("HSK[%p]: Parsing certificate verify\n", session);
 
        if (buf.length < 2) {
                gnutls_assert();
@@ -83,7 +84,7 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
 
        se = _gnutls_tls_aid_to_sign_entry(buf.data[0], buf.data[1], get_version(session));
        if (se == NULL) {
-               _gnutls_handshake_log("found unsupported signature (%d.%d)\n", (int)buf.data[0], (int)buf.data[1]);
+               _gnutls_handshake_log("Found unsupported signature (%d.%d)\n", (int)buf.data[0], (int)buf.data[1]);
                ret = gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
                goto cleanup;
        }
@@ -110,8 +111,12 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
                goto cleanup;
        }
 
+       /* We verify the certificate of the peer. Therefore we need to
+        * retrieve the negotiated certificate type for the peer. */
+       cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS);
+
        /* Verify the signature */
-       ret = _gnutls_get_auth_info_pcert(&peer_cert, session->security_parameters.cert_type, info);
+       ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);
        if (ret < 0) {
                gnutls_assert();
                goto cleanup;
index a376fdacd8542701535c8bbf0829a5ef1724aab8..d94253311c7abc920a41bd1cccbffe70a7d2e985 100644 (file)
@@ -656,8 +656,9 @@ void print_list(const char *priorities, int verbose)
 #if 0
                {
                        ret =
-                           gnutls_priority_certificate_type_list(pcache,
-                                                                 &list);
+                           gnutls_priority_certificate_type_list2(pcache,
+                                                                 &list,
+                                                                 GNUTLS_CTYPE_CLIENT);
 
                        printf("Certificate types: ");
                        if (ret == 0)
index ffa772553a6507455424f854528740bf5768c4b2..ff4d7b07f460ac3305781171436c9d8dd42d4587 100644 (file)
@@ -117,6 +117,7 @@ char prio_str[512] = "";
 #define BLOCK_CIPHERS "+3DES-CBC:+AES-128-CBC:+CAMELLIA-128-CBC:+AES-256-CBC:+CAMELLIA-256-CBC"
 #define ALL_COMP "+COMP-NULL"
 #define ALL_MACS "+MAC-ALL:+MD5:+SHA1"
+#define ALL_CERTTYPES "+CTYPE-X509"
 #define ALL_KX "+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+ECDHE-RSA:+ECDHE-ECDSA:+ANON-ECDH"
 #define INIT_STR "NONE:"
 char rest[128] = "%UNSAFE_RENEGOTIATION:+SIGN-ALL:+GROUP-ALL";
index e75cdfb09c5209de70d602e5140adf0b4bdbfbc8..f55663209f407061417d291fdcc4bd5e0bd6961b 100644 (file)
@@ -95,6 +95,7 @@ gnutls_certificate_set_x509_trust@GNUTLS_3_4
 gnutls_certificate_set_x509_trust_dir@GNUTLS_3_4
 gnutls_certificate_set_x509_trust_file@GNUTLS_3_4
 gnutls_certificate_set_x509_trust_mem@GNUTLS_3_4
+gnutls_certificate_type_get2@GNUTLS_3_6_4
 gnutls_certificate_type_get@GNUTLS_3_4
 gnutls_certificate_type_get_id@GNUTLS_3_4
 gnutls_certificate_type_get_name@GNUTLS_3_4
@@ -541,6 +542,7 @@ gnutls_pkcs_schema_get_oid@GNUTLS_3_4
 gnutls_prf@GNUTLS_3_4
 gnutls_prf_raw@GNUTLS_3_4
 gnutls_prf_rfc5705@GNUTLS_3_4
+gnutls_priority_certificate_type_list2@GNUTLS_3_6_4
 gnutls_priority_certificate_type_list@GNUTLS_3_4
 gnutls_priority_cipher_list@GNUTLS_3_4
 gnutls_priority_compression_list@GNUTLS_3_4
index 3a42b90bf9a7c733d21755c25746c3768370cdc8..c0ecfed3f2e4f1c0e9af2f7b0c60e4987a3a2096 100644 (file)
@@ -67,7 +67,8 @@ EXTRA_DIST = suppressions.valgrind eagain-common.h cert-common.h test-chains.h \
        data/pkcs7-cat-ca.pem data/long.crl data/long.pem data/large-cert.pem \
        testpkcs11.pkcs15 testpkcs11.softhsm testpkcs11.sc-hsm testpkcs11-certs/ca.crt testpkcs11-certs/ca-tmpl \
        testpkcs11-certs/client.key testpkcs11-certs/server.crt testpkcs11-certs/server-tmpl \
-       testpkcs11-certs/ca.key testpkcs11-certs/client.crt testpkcs11-certs/client-tmpl testpkcs11-certs/server.key
+       testpkcs11-certs/ca.key testpkcs11-certs/client.crt testpkcs11-certs/client-tmpl testpkcs11-certs/server.key \
+       crt_type-neg-common.c
 
 AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
 AM_CPPFLAGS = \
@@ -202,7 +203,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei
         ip-check mini-x509-ipaddr trust-store base64-raw random-art dhex509self \
         dss-sig-val sign-pk-api tls-session-ext-override record-pad \
         tls13-server-kx-neg gnutls_ext_raw_parse_dtls key-export-pkcs8 \
-        null_retrieve_function tls-record-size-limit
+        null_retrieve_function tls-record-size-limit tls-crt_type-neg
 
 if HAVE_SECCOMP_TESTS
 ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp
diff --git a/tests/crt_type-neg-common.c b/tests/crt_type-neg-common.c
new file mode 100644 (file)
index 0000000..ac99e20
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2017 - 2018 ARPA2 project
+ *
+ * Author: Tom Vrancken
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+#include <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/abstract.h>
+#include <stdbool.h>
+
+// Credential type flags
+#define CRED_EMPTY 1<<0
+#define CRED_X509 1<<1
+#define CRED_RAWPK 1<<2
+
+// Test case definition
+typedef struct test_case_st {
+       const char *name;
+       const char *client_prio;
+       const char *server_prio;
+       const char set_cli_creds;
+       const char set_srv_creds;
+       gnutls_certificate_type_t expected_cli_ctype;
+       gnutls_certificate_type_t expected_srv_ctype;
+       int client_err;
+       int server_err;
+       bool enable_cert_type_neg_cli;
+       bool enable_cert_type_neg_srv;
+} test_case_st;
+
+
+static void try(test_case_st * test)
+{
+       int sret, cret;         // Needed for HANDSHAKE macro
+       /* To hold negotiated certificate types */
+       gnutls_certificate_type_t srv_srv_ctype, srv_cli_ctype;
+       gnutls_certificate_type_t cli_srv_ctype, cli_cli_ctype;
+       /* To hold certificate credentials */
+       gnutls_certificate_credentials_t client_creds = NULL;
+       gnutls_certificate_credentials_t server_creds = NULL;
+
+       gnutls_session_t server, client;
+       gnutls_pubkey_t rawpk = NULL; // For RawPubKey tmp
+       gnutls_privkey_t privkey = NULL; // For RawPubKey tmp
+
+       sret = cret = GNUTLS_E_AGAIN;
+
+       // Initialize creds
+       assert(gnutls_certificate_allocate_credentials(&client_creds) >= 0);
+       assert(gnutls_certificate_allocate_credentials(&server_creds) >= 0);
+
+       // Print test
+       success("Running %s...\n", test->name);
+
+       // Init client/server
+       if(test->enable_cert_type_neg_cli) {
+               assert(gnutls_init(&client, GNUTLS_CLIENT | GNUTLS_ENABLE_CERT_TYPE_NEG) >= 0);
+       } else {
+               assert(gnutls_init(&client, GNUTLS_CLIENT) >= 0);
+       }
+
+       if (test->enable_cert_type_neg_srv) {
+               assert(gnutls_init(&server, GNUTLS_SERVER | GNUTLS_ENABLE_CERT_TYPE_NEG) >= 0);
+       } else {
+               assert(gnutls_init(&server, GNUTLS_SERVER) >= 0);
+       }
+
+       /* Set up our credentials for this handshake */
+       // Test for using empty cli credentials
+       if (test->set_cli_creds == CRED_EMPTY) {
+               gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, client_creds);
+       } else {
+               // Test for using X509 cli credentials
+               if (test->set_cli_creds & CRED_X509) {
+                       assert(gnutls_certificate_set_x509_key_mem
+                                                (client_creds, &cli_ca3_cert,  &cli_ca3_key, GNUTLS_X509_FMT_PEM) >= 0);
+               }
+
+               // Test for using RawPubKey cli credentials
+               if (test->set_cli_creds & CRED_RAWPK) {
+                       // TODO set client RawPubKey when support is ready
+               }
+
+               // -- Add extra ctype creds here in the future --
+
+               // Finally set the credentials
+               gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, client_creds);
+       }
+
+       // Test for using empty srv credentials
+       if (test->set_srv_creds == CRED_EMPTY) {
+               gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, server_creds);
+       } else {
+               // Test for using X509 srv credentials
+               if (test->set_srv_creds & CRED_X509) {
+                       assert(gnutls_certificate_set_x509_key_mem
+                                                (server_creds, &server_ca3_localhost_rsa_decrypt_cert,
+                               &server_ca3_key, GNUTLS_X509_FMT_PEM) >= 0);
+                       assert(gnutls_certificate_set_x509_key_mem
+                                                (server_creds, &server_ca3_localhost_ecc_cert,
+                               &server_ca3_ecc_key, GNUTLS_X509_FMT_PEM) >= 0);
+                       assert(gnutls_certificate_set_x509_key_mem
+                                                (server_creds, &server_ca3_localhost_rsa_sign_cert,
+                               &server_ca3_key, GNUTLS_X509_FMT_PEM) >= 0);
+                       gnutls_certificate_set_known_dh_params(server_creds,
+                                                                                GNUTLS_SEC_PARAM_MEDIUM);
+               }
+
+               // Test for using RawPubKey srv credentials
+               if( test->set_srv_creds & CRED_RAWPK ) {
+                       //TODO when RawPK support is finished
+               }
+
+               // -- Add extra ctype creds here in the future --
+
+               // Finally set the credentials
+               gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, server_creds);
+       }
+
+       // Server settings
+       gnutls_transport_set_push_function(server, server_push);
+       gnutls_transport_set_pull_function(server, server_pull);
+       gnutls_transport_set_ptr(server, server);
+       assert(gnutls_priority_set_direct(server, test->server_prio, 0) >= 0);
+
+       // Client settings
+       gnutls_transport_set_push_function(client, client_push);
+       gnutls_transport_set_pull_function(client, client_pull);
+       gnutls_transport_set_ptr(client, client);
+       assert(gnutls_priority_set_direct(client, test->client_prio, 0) >= 0);
+
+       // Try handshake
+       if (test->client_err && test->server_err) {
+               /* We expect errors during the handshake and don't check
+                * any negotiated certificate types */
+               HANDSHAKE_EXPECT(client, server, test->client_err, test->server_err);
+       } else {
+               /* We expect a handshake without errors and check the negotiated
+                * certificate types */
+               HANDSHAKE(client, server);
+
+               /* Get the negotiated certificate types */
+               srv_srv_ctype =
+                               gnutls_certificate_type_get2(server, GNUTLS_CTYPE_SERVER);
+               srv_cli_ctype =
+                               gnutls_certificate_type_get2(server, GNUTLS_CTYPE_CLIENT);
+               cli_srv_ctype =
+                               gnutls_certificate_type_get2(client, GNUTLS_CTYPE_SERVER);
+               cli_cli_ctype =
+                               gnutls_certificate_type_get2(client, GNUTLS_CTYPE_CLIENT);
+
+               /* Check whether the API functions return the correct cert types for OURS and PEERS */
+               assert(srv_srv_ctype == gnutls_certificate_type_get2(server, GNUTLS_CTYPE_OURS));
+               assert(srv_srv_ctype == gnutls_certificate_type_get2(client, GNUTLS_CTYPE_PEERS));
+               assert(cli_cli_ctype == gnutls_certificate_type_get2(server, GNUTLS_CTYPE_PEERS));
+               assert(cli_cli_ctype == gnutls_certificate_type_get2(client, GNUTLS_CTYPE_OURS));
+
+               // For debugging
+               if (debug) {
+                               success("Srv srv ctype: %s\n", gnutls_certificate_type_get_name(srv_srv_ctype));
+                               success("Srv cli ctype: %s\n", gnutls_certificate_type_get_name(srv_cli_ctype));
+                               success("Cli srv ctype: %s\n", gnutls_certificate_type_get_name(cli_srv_ctype));
+                               success("Cli srv ctype: %s\n", gnutls_certificate_type_get_name(cli_cli_ctype));
+               }
+
+               /* Check whether the negotiated certificate types match the expected results */
+               // Matching server ctype
+               if (srv_srv_ctype != cli_srv_ctype) {
+                       fail("%s: client negotiated different server ctype than server (%s, %s)!\n", test->name, gnutls_certificate_type_get_name(cli_srv_ctype), gnutls_certificate_type_get_name(srv_srv_ctype));
+               }
+               // Matching client ctype
+               if (srv_cli_ctype != cli_cli_ctype) {
+                       fail("%s: client negotiated different client ctype than server (%s, %s)!\n", test->name, gnutls_certificate_type_get_name(cli_cli_ctype), gnutls_certificate_type_get_name(srv_cli_ctype));
+               }
+               // Matching expected server ctype
+               if (srv_srv_ctype != test->expected_srv_ctype) {
+                       fail("%s: negotiated server ctype diffs the expected (%s, %s)!\n", test->name, gnutls_certificate_type_get_name(srv_srv_ctype), gnutls_certificate_type_get_name(test->expected_srv_ctype));
+               }
+               // Matching expected client ctype
+               if (srv_cli_ctype != test->expected_cli_ctype) {
+                       fail("%s: negotiated server ctype diffs the expected (%s, %s)!\n", test->name, gnutls_certificate_type_get_name(srv_cli_ctype), gnutls_certificate_type_get_name(test->expected_cli_ctype));
+               }
+       }
+
+       // Cleanup
+       gnutls_deinit(server);
+       gnutls_deinit(client);
+       gnutls_certificate_free_credentials(client_creds);
+       gnutls_certificate_free_credentials(server_creds);
+       gnutls_pubkey_deinit(rawpk);
+       gnutls_privkey_deinit(privkey);
+
+       reset_buffers();
+}
diff --git a/tests/tls-crt_type-neg.c b/tests/tls-crt_type-neg.c
new file mode 100644 (file)
index 0000000..ff5aa08
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2017 - 2018 ARPA2 project
+ *
+ * Author: Tom Vrancken
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This program tests the certificate type negotiation mechnism for
+ * the handshake as specified in RFC7250 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include "utils.h"
+#include "cert-common.h"
+#include "eagain-common.h"
+#include "crt_type-neg-common.c"
+
+test_case_st tests[] = {
+       /* Tests with only a single credential set for client/server.
+        * Tests for X.509 cases.
+        */
+       {
+               /* Default case A
+                *
+                * Priority cli: NORMAL
+                * Priority srv: NORMAL
+                * Certificate negotiation mechanism: disabled
+                * Cli creds: None
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: cert types should default to X.509
+                */
+        .name = "Default case A. Neg off (default). Creds set (CLI/SRV): None/X509.",
+        .client_prio = "NORMAL",
+        .server_prio = "NORMAL",
+        .set_cli_creds = CRED_EMPTY,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = false,
+        .enable_cert_type_neg_srv = false},
+       {
+               /* Default case B
+                *
+                * Priority: NORMAL
+                * Certificate negotiation mechanism: disabled
+                * Cli creds: X.509
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: cert types should default to X.509
+                */
+        .name = "Default case B. Neg off (default). Creds set (CLI/SRV): X509/X509.",
+        .client_prio = "NORMAL",
+        .server_prio = "NORMAL",
+        .set_cli_creds = CRED_X509,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = false,
+        .enable_cert_type_neg_srv = false},
+       {
+               /* No server credentials
+                *
+                * Priority: NORMAL
+                * Certificate negotiation mechanism: disabled
+                * Cli creds: None
+                * Srv creds: None
+                * Handshake: results in errors
+                * Negotiation: cert types are not evaluated
+                */
+        .name = "No server creds. Creds set (CLI/SRV): None/None.",
+        .client_prio = "NORMAL",
+        .server_prio = "NORMAL",
+        .set_cli_creds = CRED_EMPTY,
+        .set_srv_creds = CRED_EMPTY,
+        .client_err = GNUTLS_E_AGAIN,
+        .server_err = GNUTLS_E_NO_CIPHER_SUITES,
+        .enable_cert_type_neg_cli = false,
+        .enable_cert_type_neg_srv = false},
+       {
+               /* Client can negotiate, server not
+                *
+                * Priority: NORMAL
+                * Certificate negotiation mechanism (cli/srv): enabled/disabled
+                * Cli creds: None
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: cert types should default to X.509
+                */
+        .name = "Client can negotiate, server not",
+        .client_prio = "NORMAL",
+        .server_prio = "NORMAL",
+        .set_cli_creds = CRED_EMPTY,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = true,
+        .enable_cert_type_neg_srv = false},
+       {
+               /* Server can negotiate, client not
+                *
+                * Priority: NORMAL
+                * Certificate negotiation mechanism (cli/srv): disabled/enabled
+                * Cli creds: None
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: cert types should default to X.509
+                */
+        .name = "Server can negotiate, client not",
+        .client_prio = "NORMAL",
+        .server_prio = "NORMAL",
+        .set_cli_creds = CRED_EMPTY,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = false,
+        .enable_cert_type_neg_srv = true},
+       {
+               /* Client and server can negotiate
+                *
+                * Priority: NORMAL
+                * Certificate negotiation mechanism (cli/srv): enabled/enabled
+                * Cli creds: None
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: cert types should default to X.509
+                */
+        .name = "Client and server can negotiate",
+        .client_prio = "NORMAL",
+        .server_prio = "NORMAL",
+        .set_cli_creds = CRED_EMPTY,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = true,
+        .enable_cert_type_neg_srv = true},
+       {
+               /* Negotiate both, cli creds x509, srv creds x509
+                *
+                * Priority: NORMAL + request x509 for cli and srv
+                * Certificate negotiation mechanism (cli/srv): enabled/enabled
+                * Cli creds: X.509
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: Fallback to default cli X.509, srv X.509 because
+                *   we advertise with only the cert type defaults.
+                */
+        .name = "Negotiate CLI X.509 + SRV X.509, cli/srv X.509 creds set",
+        .client_prio = "NORMAL:+CTYPE-CLI-X509:+CTYPE-SRV-X509",
+        .server_prio = "NORMAL:+CTYPE-CLI-X509:+CTYPE-SRV-X509",
+        .set_cli_creds = CRED_X509,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = true,
+        .enable_cert_type_neg_srv = true},
+       {
+               /* Negotiate cli x509, cli creds x509, srv creds x509
+                *
+                * Priority: NORMAL + request x509 for cli
+                * Certificate negotiation mechanism (cli/srv): enabled/enabled
+                * Cli creds: X.509
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: Fallback to default cli X.509, srv X.509 because
+                *   we advertise with only the cert type defaults.
+                */
+        .name = "Negotiate CLI X.509, cli/srv X.509 creds set",
+        .client_prio = "NORMAL:+CTYPE-CLI-X509",
+        .server_prio = "NORMAL:+CTYPE-CLI-X509",
+        .set_cli_creds = CRED_X509,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = true,
+        .enable_cert_type_neg_srv = true},
+       {
+               /* Negotiate srv x509, cli creds x509, srv creds x509
+                *
+                * Priority: NORMAL + request x509 for srv
+                * Certificate negotiation mechanism (cli/srv): enabled/enabled
+                * Cli creds: X.509
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: Fallback to default cli X.509, srv X.509 because
+                *   we advertise with only the cert type defaults.
+                */
+        .name = "Negotiate SRV X.509, cli/srv X.509 creds set",
+        .client_prio = "NORMAL:+CTYPE-SRV-X509",
+        .server_prio = "NORMAL:+CTYPE-SRV-X509",
+        .set_cli_creds = CRED_X509,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = true,
+        .enable_cert_type_neg_srv = true},
+       {
+               /* All types allowed for CLI, cli creds x509, srv creds x509
+                *
+                * Priority: NORMAL + allow all client cert types
+                * Certificate negotiation mechanism (cli/srv): enabled/enabled
+                * Cli creds: X.509
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: Fallback to default cli X.509, srv X.509 because
+                *   we advertise with only the cert type defaults.
+                */
+        .name = "Negotiate CLI all, cli/srv X.509 creds set",
+        .client_prio = "NORMAL:+CTYPE-CLI-ALL",
+        .server_prio = "NORMAL:+CTYPE-CLI-ALL",
+        .set_cli_creds = CRED_X509,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = true,
+        .enable_cert_type_neg_srv = true},
+       {
+               /* All types allowed for SRV, cli creds x509, srv creds x509
+                *
+                * Priority: NORMAL + allow all server cert types
+                * Certificate negotiation mechanism (cli/srv): enabled/enabled
+                * Cli creds: X.509
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: Fallback to default cli X.509, srv X.509 because
+                *   we advertise with only the cert type defaults.
+                */
+        .name = "Negotiate SRV all, cli/srv X.509 creds set",
+        .client_prio = "NORMAL:+CTYPE-SRV-ALL",
+        .server_prio = "NORMAL:+CTYPE-SRV-ALL",
+        .set_cli_creds = CRED_X509,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = true,
+        .enable_cert_type_neg_srv = true},
+       {
+               /* All types allowed for CLI/SRV, cli creds x509, srv creds x509
+                *
+                * Priority: NORMAL + allow all client and server cert types
+                * Certificate negotiation mechanism (cli/srv): enabled/enabled
+                * Cli creds: X.509
+                * Srv creds: X.509
+                * Handshake: should complete without errors
+                * Negotiation: Fallback to default cli X.509, srv X.509 because
+                *   we advertise with only the cert type defaults.
+                */
+        .name = "Negotiate CLI/SRV all, cli/srv X.509 creds set",
+        .client_prio = "NORMAL:+CTYPE-CLI-ALL:+CTYPE-SRV-ALL",
+        .server_prio = "NORMAL:+CTYPE-CLI-ALL:+CTYPE-SRV-ALL",
+        .set_cli_creds = CRED_X509,
+        .set_srv_creds = CRED_X509,
+        .expected_cli_ctype = GNUTLS_CRT_X509,
+        .expected_srv_ctype = GNUTLS_CRT_X509,
+        .enable_cert_type_neg_cli = true,
+        .enable_cert_type_neg_srv = true}
+
+       /* Tests with only a single credential set for client/server.
+        * Tests for Raw public-key cases.
+        */
+       //TODO implement when Raw public key support is finished
+
+       /* Tests with only a single credential set for client/server.
+        * Tests for KDH cases.
+        */
+       //TODO implement when KDH support is finished
+
+       /* Tests with multiple credentials set for client/server. */
+       //TODO implement when support for more cert types is ready
+};
+
+void doit(void)
+{
+       unsigned i;
+       global_init();
+
+       for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+               try(&tests[i]);
+       }
+
+       gnutls_global_deinit();
+}