summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Vrancken <dev@tomvrancken.nl>2018-08-15 18:29:32 +0200
committerTom Vrancken <dev@tomvrancken.nl>2018-08-20 17:08:01 +0200
commit07180a416731749883234f931ac18831ff38abbb (patch)
tree0b9d13b9ad394477d566f01ba8e279c33711cc7c
parenta42db538c3f01aa76e2c1a2affc39237840c2522 (diff)
downloadgnutls-07180a416731749883234f931ac18831ff38abbb.tar.gz
Implemented RFC7250 certificate type negotiation extensions.
Signed-off-by: Tom Vrancken <dev@tomvrancken.nl>
-rw-r--r--NEWS9
-rw-r--r--doc/Makefile.am5
-rw-r--r--doc/cha-gtls-app.texi12
-rw-r--r--doc/manpages/Makefile.am2
-rw-r--r--lib/algorithms/cert_types.c5
-rw-r--r--lib/auth/cert.c93
-rw-r--r--lib/auth/rsa.c23
-rw-r--r--lib/auth/srp_rsa.c3
-rw-r--r--lib/cert-session.c42
-rw-r--r--lib/constate.c11
-rw-r--r--lib/ext/Makefile.am5
-rw-r--r--lib/ext/cert_types.h50
-rw-r--r--lib/ext/client_cert_type.c371
-rw-r--r--lib/ext/client_cert_type.h36
-rw-r--r--lib/ext/server_cert_type.c348
-rw-r--r--lib/ext/server_cert_type.h36
-rw-r--r--lib/gnutls_int.h32
-rw-r--r--lib/handshake.c6
-rw-r--r--lib/hello_ext.c4
-rw-r--r--lib/hello_ext_lib.c2
-rw-r--r--lib/includes/gnutls/gnutls.h.in33
-rw-r--r--lib/libgnutls.map2
-rw-r--r--lib/priority.c100
-rw-r--r--lib/session.c36
-rw-r--r--lib/session_pack.c16
-rw-r--r--lib/state.c177
-rw-r--r--lib/state.h10
-rw-r--r--lib/tls13/certificate_verify.c11
-rw-r--r--src/common.c5
-rw-r--r--src/tests.c1
-rw-r--r--symbols.last2
-rw-r--r--tests/Makefile.am5
-rw-r--r--tests/crt_type-neg-common.c209
-rw-r--r--tests/tls-crt_type-neg.c303
34 files changed, 1895 insertions, 110 deletions
diff --git a/NEWS b/NEWS
index 7b9a53d00a..57d2703903 100644
--- 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)
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 5d6cd0c1bc..782b3cf55f 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -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
diff --git a/doc/cha-gtls-app.texi b/doc/cha-gtls-app.texi
index 8fd31b2add..9a4cf29933 100644
--- a/doc/cha-gtls-app.texi
+++ b/doc/cha-gtls-app.texi
@@ -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
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
index b4dc4ae8c9..241d9870af 100644
--- a/doc/manpages/Makefile.am
+++ b/doc/manpages/Makefile.am
@@ -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
diff --git a/lib/algorithms/cert_types.c b/lib/algorithms/cert_types.c
index a7c27e0fe3..dc9fc9a388 100644
--- a/lib/algorithms/cert_types.c
+++ b/lib/algorithms/cert_types.c
@@ -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;
}
diff --git a/lib/auth/cert.c b/lib/auth/cert.c
index 4e2e484a2e..069968c5d3 100644
--- a/lib/auth/cert.c
+++ b/lib/auth/cert.c
@@ -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;
}
diff --git a/lib/auth/rsa.c b/lib/auth/rsa.c
index f2e36bbe22..6afc91ae67 100644
--- a/lib/auth/rsa.c
+++ b/lib/auth/rsa.c
@@ -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();
diff --git a/lib/auth/srp_rsa.c b/lib/auth/srp_rsa.c
index 2101f70a0f..06c6971d80 100644
--- a/lib/auth/srp_rsa.c
+++ b/lib/auth/srp_rsa.c
@@ -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();
diff --git a/lib/cert-session.c b/lib/cert-session.c
index 772d1417d9..580a871964 100644
--- a/lib/cert-session.c
+++ b/lib/cert-session.c
@@ -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;
}
}
diff --git a/lib/constate.c b/lib/constate.c
index 62623c10dd..456316258b 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -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
diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am
index b7f204f1d9..c8ef79101c 100644
--- a/lib/ext/Makefile.am
+++ b/lib/ext/Makefile.am
@@ -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
index 0000000000..c54e0f2bfe
--- /dev/null
+++ b/lib/ext/cert_types.h
@@ -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
index 0000000000..8bce721ace
--- /dev/null
+++ b/lib/ext/client_cert_type.c
@@ -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
index 0000000000..454e9bff56
--- /dev/null
+++ b/lib/ext/client_cert_type.h
@@ -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
index 0000000000..b1086c7f10
--- /dev/null
+++ b/lib/ext/server_cert_type.c
@@ -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
index 0000000000..b5eab5c9e6
--- /dev/null
+++ b/lib/ext/server_cert_type.h
@@ -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
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index dfec39ec33..8ad6b1652d 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -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 */
diff --git a/lib/handshake.c b/lib/handshake.c
index ebea926aa5..914f8ecacc 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -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,
diff --git a/lib/hello_ext.c b/lib/hello_ext.c
index 9e30bd5413..28e8e8c08e 100644
--- a/lib/hello_ext.c
+++ b/lib/hello_ext.c
@@ -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,
diff --git a/lib/hello_ext_lib.c b/lib/hello_ext_lib.c
index 5ee433b24e..663354caaf 100644
--- a/lib/hello_ext_lib.c
+++ b/lib/hello_ext_lib.c
@@ -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)
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 90ff1985d1..4d138b8446 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -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,
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index d827790745..c20dd33cfe 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -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 {
diff --git a/lib/priority.c b/lib/priority.c
index 1d495d0e67..00681c53e8 100644
--- a/lib/priority.c
+++ b/lib/priority.c
@@ -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;
}
/**
diff --git a/lib/session.c b/lib/session.c
index 1622b29764..5d862198b5 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -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)
diff --git a/lib/session_pack.c b/lib/session_pack.c
index 9fbd5b3ae8..f8b1830568 100644
--- a/lib/session_pack.c
+++ b/lib/session_pack.c
@@ -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 ==
diff --git a/lib/state.c b/lib/state.c
index ab0021a64e..58db8f9a32 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -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.
diff --git a/lib/state.h b/lib/state.h
index 75d0f35fc1..a93e5d49ce 100644
--- a/lib/state.h
+++ b/lib/state.h
@@ -25,6 +25,11 @@
#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);
diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c
index 96076e4e46..01966b14d1 100644
--- a/lib/tls13/certificate_verify.c
+++ b/lib/tls13/certificate_verify.c
@@ -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;
diff --git a/src/common.c b/src/common.c
index a376fdacd8..d94253311c 100644
--- a/src/common.c
+++ b/src/common.c
@@ -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)
diff --git a/src/tests.c b/src/tests.c
index ffa772553a..ff4d7b07f4 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -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";
diff --git a/symbols.last b/symbols.last
index e75cdfb09c..f55663209f 100644
--- a/symbols.last
+++ b/symbols.last
@@ -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
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 3a42b90bf9..c0ecfed3f2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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
index 0000000000..ac99e20984
--- /dev/null
+++ b/tests/crt_type-neg-common.c
@@ -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
index 0000000000..ff5aa08885
--- /dev/null
+++ b/tests/tls-crt_type-neg.c
@@ -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();
+}