summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTom Vrancken <email@tomvrancken.nl>2017-08-26 14:22:44 +0200
committerTom Vrancken <dev@tomvrancken.nl>2018-12-15 19:05:03 +0100
commit565efaeac828e89d2c1bac7a88c27303d1b62547 (patch)
tree5cc0e7c43c4933b743d2ba5889bad13f86e98323 /lib
parent71276d301a602926e44df818259ba1d99264a179 (diff)
downloadgnutls-565efaeac828e89d2c1bac7a88c27303d1b62547.tar.gz
Implemented support for raw public-key functionality (RFC7250).
Signed-off-by: Tom Vrancken <dev@tomvrancken.nl>
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am6
-rw-r--r--lib/algorithms/cert_types.c4
-rw-r--r--lib/auth/cert.c312
-rw-r--r--lib/auth/cert.h13
-rw-r--r--lib/auth/rsa.c2
-rw-r--r--lib/cert-cred-rawpk.c360
-rw-r--r--lib/cert-cred-x509.c241
-rw-r--r--lib/cert-cred.c197
-rw-r--r--lib/cert-cred.h53
-rw-r--r--lib/cert-session.c18
-rw-r--r--lib/ext/cert_types.h37
-rw-r--r--lib/ext/client_cert_type.c48
-rw-r--r--lib/ext/server_cert_type.c48
-rw-r--r--lib/gnutls_int.h34
-rw-r--r--lib/includes/gnutls/abstract.h8
-rw-r--r--lib/includes/gnutls/gnutls.h.in50
-rw-r--r--lib/libgnutls.map9
-rw-r--r--lib/pcert.c152
-rw-r--r--lib/pkcs11x.c2
-rw-r--r--lib/session.c13
-rw-r--r--lib/state.c36
-rw-r--r--lib/str_array.h20
-rw-r--r--lib/tls13/certificate_verify.c2
-rw-r--r--lib/verify-tofu.c116
-rw-r--r--lib/x509.h3
-rw-r--r--lib/x509/common.c2
-rw-r--r--lib/x509/common.h2
27 files changed, 1347 insertions, 441 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 32a8511b33..e7c5c7f40e 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -80,7 +80,7 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls
system-keys.h urls.c urls.h prf.c auto-verify.c dh-session.c \
cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \
crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \
- hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c
+ hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c
if WINDOWS
COBJECTS += system/keys-win.c
@@ -124,7 +124,7 @@ HFILES = abstract_int.h debug.h cipher.h \
srp.h auth/srp_kx.h auth/srp_passwd.h \
file.h supplemental.h crypto.h random.h system.h\
locks.h mbuffers.h ecc.h pin.h fips.h \
- priority_options.h secrets.h stek.h
+ priority_options.h secrets.h stek.h cert-cred.h
if ENABLE_PKCS11
HFILES += pkcs11_int.h pkcs11x.h
@@ -216,7 +216,7 @@ libgnutlsxx_la_CPPFLAGS = -I$(top_srcdir)/includes -I$(top_builddir)/includes -I
AM_CXXFLAGS = \
-I$(srcdir)/includes \
- -I$(builddir)/includes
+ -I$(builddir)/includes
lib_LTLIBRARIES += libgnutlsxx.la
diff --git a/lib/algorithms/cert_types.c b/lib/algorithms/cert_types.c
index 6ae4b7e160..9671e508d9 100644
--- a/lib/algorithms/cert_types.c
+++ b/lib/algorithms/cert_types.c
@@ -64,8 +64,7 @@ gnutls_certificate_type_t gnutls_certificate_type_get_id(const char *name)
if (c_strcasecmp(name, "X.509") == 0
|| c_strcasecmp(name, "X509") == 0)
return GNUTLS_CRT_X509;
- if (c_strcasecmp(name, "RAWPK") == 0
- || c_strcasecmp(name, "RAWPUBKEY") == 0)
+ if (c_strcasecmp(name, "RAWPK") == 0)
return GNUTLS_CRT_RAWPK;
return ret;
@@ -73,6 +72,7 @@ gnutls_certificate_type_t gnutls_certificate_type_get_id(const char *name)
static const gnutls_certificate_type_t supported_certificate_types[] = {
GNUTLS_CRT_X509,
+ GNUTLS_CRT_RAWPK,
0
};
diff --git a/lib/auth/cert.c b/lib/auth/cert.c
index 574514649c..b6bd3bf91e 100644
--- a/lib/auth/cert.c
+++ b/lib/auth/cert.c
@@ -60,7 +60,7 @@ selected_certs_set(gnutls_session_t session,
typedef enum CertificateSigType { RSA_SIGN = 1, DSA_SIGN = 2, ECDSA_SIGN = 64
} CertificateSigType;
-/* Moves data from a internal certificate struct (gnutls_pcert_st) to
+/* Moves data from an internal certificate struct (gnutls_pcert_st) to
* another internal certificate struct (cert_auth_info_t), and deinitializes
* the former.
*/
@@ -188,9 +188,14 @@ find_x509_client_cert(gnutls_session_t session,
* then send that one.
*/
if (cred->ncerts == 1 &&
- (data_size == 0 || (session->internals.flags & GNUTLS_FORCE_CLIENT_CERT))) {
+ (data_size == 0
+ || (session->internals.flags & GNUTLS_FORCE_CLIENT_CERT))) {
+ if (cred->certs[0].cert_list[0].type == GNUTLS_CRT_X509) {
+ /* This check is necessary to prevent sending other certificate
+ * credentials that are set (e.g. raw public-key). */
*indx = 0;
return 0;
+ }
}
do {
@@ -250,6 +255,47 @@ find_x509_client_cert(gnutls_session_t session,
}
+
+/* Locates the first raw public-key.
+ * Currently it only makes sense to associate one raw pubkey per session.
+ * Associating more raw pubkeys with a session has no use because we
+ * don't know how to select the correct one.
+ */
+static int
+find_rawpk_client_cert(gnutls_session_t session,
+ const gnutls_certificate_credentials_t cred,
+ const gnutls_pk_algorithm_t* pk_algos,
+ int pk_algos_length, int* indx)
+{
+ unsigned i;
+ gnutls_pk_algorithm_t pk;
+
+ *indx = -1;
+
+ for (i = 0; i < cred->ncerts; i++) {
+ /* We know that our list length will be 1, therefore we can
+ * ignore the rest.
+ */
+ if (cred->certs[i].cert_list_length == 1) {
+ pk = gnutls_pubkey_get_pk_algorithm(cred->certs[i].
+ cert_list[0].pubkey, NULL);
+
+ /* Check whether the public-key algorithm of our credential is in
+ * the list with supported public-key algorithms and whether the
+ * cert type matches. */
+ if ((check_pk_algo_in_list(pk_algos, pk_algos_length, pk) == 0)
+ && (cred->certs[i].cert_list[0].type == GNUTLS_CRT_RAWPK)) {
+ // We found a compatible credential
+ *indx = i;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
/* Returns the number of issuers in the server's
* certificate request packet.
*/
@@ -308,7 +354,7 @@ get_issuers(gnutls_session_t session,
int i;
unsigned size;
- if (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT) != GNUTLS_CRT_X509)
+ if (get_certificate_type(session, GNUTLS_CTYPE_CLIENT) != GNUTLS_CRT_X509)
return 0;
/* put the requested DNs to req_dn, only in case
@@ -363,19 +409,10 @@ 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;
- }
+ /* Correctly set the certificate type for ourselves */
+ type = get_certificate_type(session, GNUTLS_CTYPE_OURS);
+ /* Check whether a callback is set and call it */
if (cred->get_cert_callback3) {
struct gnutls_cert_retr_st info;
unsigned int flags = 0;
@@ -411,8 +448,7 @@ call_get_cert_callback(gnutls_session_t session,
return 0;
} else {
- gnutls_assert();
- return GNUTLS_E_INTERNAL_ERROR;
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
}
@@ -435,6 +471,7 @@ _gnutls_select_client_cert(gnutls_session_t session,
ssize_t data_size = _data_size;
int issuers_dn_length;
gnutls_datum_t *issuers_dn = NULL;
+ gnutls_certificate_type_t cert_type;
cred = (gnutls_certificate_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
@@ -443,11 +480,13 @@ _gnutls_select_client_cert(gnutls_session_t session,
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
+ cert_type = get_certificate_type(session, GNUTLS_CTYPE_CLIENT);
+
if (cred->get_cert_callback3 != NULL) {
/* use a callback to get certificate
*/
- if (session->security_parameters.client_ctype == GNUTLS_CRT_X509) {
+ if (cert_type == GNUTLS_CRT_X509) {
issuers_dn_length =
get_issuers_num(session, data, data_size);
if (issuers_dn_length < 0) {
@@ -486,16 +525,23 @@ _gnutls_select_client_cert(gnutls_session_t session,
} else {
/* If we have no callbacks, try to guess.
*/
- 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);
- } else {
- result = GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ switch (cert_type) {
+ case GNUTLS_CRT_X509:
+ result = find_x509_client_cert(session, cred, _data,
+ _data_size, pk_algos,
+ pk_algos_length, &indx);
+ break;
+ case GNUTLS_CRT_RAWPK:
+ result = find_rawpk_client_cert(session, cred,
+ pk_algos, pk_algos_length, &indx);
+ break;
+ default:
+ result = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+ break;
}
+
if (result < 0) {
- gnutls_assert();
- return result;
+ return gnutls_assert_val(result);
}
if (indx >= 0) {
@@ -575,27 +621,83 @@ static int gen_x509_crt(gnutls_session_t session, gnutls_buffer_st * data)
return data->length - init_pos;
}
+
+/* Generates a Raw Public Key certificate message that holds only the
+ * SubjectPublicKeyInfo part of a regular certificate message.
+ *
+ * Returns the number of bytes sent or a negative error code.
+ */
+int
+_gnutls_gen_rawpk_crt(gnutls_session_t session, gnutls_buffer_st* data)
+{
+ int ret;
+ gnutls_pcert_st *apr_cert_list;
+ gnutls_privkey_t apr_pkey;
+ int apr_cert_list_length;
+
+ if((ret = _gnutls_get_selected_cert(session, &apr_cert_list,
+ &apr_cert_list_length, &apr_pkey)) < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ /* Since we are transmitting a raw public key with no additional
+ * certificate credentials attached to it, it doesn't make sense to
+ * have more than one certificate set (i.e. to have a certificate chain).
+ * This is enforced by the API so having a value other than 1 should
+ * be an impossible situation.
+ */
+ assert(apr_cert_list_length == 1);
+
+ /* Write our certificate containing only the SubjectPublicKeyInfo to
+ * the output buffer. We always have exactly one certificate that
+ * contains our raw public key. Our message looks like:
+ * <length++certificate> where
+ * length = 3 bytes and
+ * certificate = length bytes.
+ */
+ ret = _gnutls_buffer_append_data_prefix(data, 24,
+ apr_cert_list[0].cert.data,
+ apr_cert_list[0].cert.size);
+
+ if (ret < 0) return gnutls_assert_val(ret);
+
+ return data->length;
+}
+
+
int
_gnutls_gen_cert_client_crt(gnutls_session_t session, gnutls_buffer_st * data)
{
- switch (session->security_parameters.client_ctype) {
- case GNUTLS_CRT_X509:
- return gen_x509_crt(session, data);
- default:
- gnutls_assert();
- return GNUTLS_E_INTERNAL_ERROR;
+ gnutls_certificate_type_t cert_type;
+
+ // Retrieve the (negotiated) certificate type for the client
+ cert_type = get_certificate_type(session, GNUTLS_CTYPE_CLIENT);
+
+ switch (cert_type) {
+ case GNUTLS_CRT_X509:
+ return gen_x509_crt(session, data);
+ case GNUTLS_CRT_RAWPK:
+ return _gnutls_gen_rawpk_crt(session, data);
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
}
int
_gnutls_gen_cert_server_crt(gnutls_session_t session, gnutls_buffer_st * data)
{
- switch (session->security_parameters.server_ctype) {
- case GNUTLS_CRT_X509:
- return gen_x509_crt(session, data);
- default:
- gnutls_assert();
- return GNUTLS_E_INTERNAL_ERROR;
+ gnutls_certificate_type_t cert_type;
+
+ // Retrieve the (negotiated) certificate type for the server
+ cert_type = get_certificate_type(session, GNUTLS_CTYPE_SERVER);
+
+ switch (cert_type) {
+ case GNUTLS_CRT_X509:
+ return gen_x509_crt(session, data);
+ case GNUTLS_CRT_RAWPK:
+ return _gnutls_gen_rawpk_crt(session, data);
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
}
@@ -765,6 +867,99 @@ _gnutls_proc_x509_crt(gnutls_session_t session,
}
+
+int _gnutls_proc_rawpk_crt(gnutls_session_t session,
+ uint8_t * data, size_t data_size)
+{
+ int cert_size, ret;
+ cert_auth_info_t info;
+ gnutls_pcert_st* peer_certificate;
+ gnutls_datum_t tmp_cert;
+
+ uint8_t *p = data;
+ ssize_t dsize = data_size;
+
+ /* We assume data != null and data_size > 0 because
+ * the caller checks this for us. */
+
+ /* Read the length of our certificate. We always have exactly
+ * one certificate that contains our raw public key. Our message
+ * looks like:
+ * <length++certificate> where
+ * length = 3 bytes and
+ * certificate = length bytes.
+ */
+ DECR_LEN(dsize, 3);
+ cert_size = _gnutls_read_uint24(p);
+ p += 3;
+
+ /* Ensure no discrepancy in data */
+ if (cert_size != dsize)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+
+ if (cert_size == 0) {
+ // No certificate was sent. This is not OK.
+ return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
+ }
+
+ DECR_LEN_FINAL(dsize, cert_size);
+
+ /* We are now going to read our certificate and store it into
+ * the authentication info structure.
+ */
+ tmp_cert.size = cert_size;
+ tmp_cert.data = p;
+
+ peer_certificate = gnutls_calloc(1, sizeof(*peer_certificate));
+ if (peer_certificate == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ // Import our raw certificate holding only a raw public key into this pcert
+ ret = gnutls_pcert_import_rawpk_raw(peer_certificate, &tmp_cert, GNUTLS_X509_FMT_DER, 0, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ // Check whether the PK algo is compatible with the negotiated KX
+ ret = check_pk_compat(session, peer_certificate->pubkey);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_auth_info_init(session, GNUTLS_CRD_CERTIFICATE,
+ sizeof(cert_auth_info_st), 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+
+ /* Copy our imported certificate into the auth info structure
+ * and free our temporary cert storage peer_certificate.
+ */
+ ret = _gnutls_pcert_to_auth_info(info, peer_certificate, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return GNUTLS_E_SUCCESS;
+
+cleanup:
+ if (peer_certificate != NULL) {
+ gnutls_pcert_deinit(peer_certificate);
+ gnutls_free(peer_certificate);
+ }
+
+ return ret;
+}
+
+
int _gnutls_proc_crt(gnutls_session_t session, uint8_t * data, size_t data_size)
{
int ret;
@@ -779,22 +974,17 @@ int _gnutls_proc_crt(gnutls_session_t session, uint8_t * data, size_t data_size)
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
- /* 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);
- }
+ /* Determine what certificate type we need to process.
+ * We need to process the certificate of the peer. */
+ cert_type = get_certificate_type(session, GNUTLS_CTYPE_PEERS);
switch (cert_type) {
case GNUTLS_CRT_X509:
- ret = _gnutls_proc_x509_crt(session, data, data_size);
- break;
+ return _gnutls_proc_x509_crt(session, data, data_size);
+ case GNUTLS_CRT_RAWPK:
+ return _gnutls_proc_rawpk_crt(session, data, data_size);
default:
- gnutls_assert();
- return GNUTLS_E_INTERNAL_ERROR;
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
return ret;
@@ -1148,8 +1338,7 @@ _gnutls_get_selected_cert(gnutls_session_t session,
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
- } else { /* CLIENT SIDE
- */
+ } else { /* CLIENT SIDE */
/* _gnutls_select_client_cert() must have been called before.
*/
*apr_cert_list = session->internals.selected_cert_list;
@@ -1267,7 +1456,7 @@ int cert_select_sign_algorithm(gnutls_session_t session,
assert(IS_SERVER(session));
/* Retrieve the server certificate type */
- ctype = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+ ctype = get_certificate_type(session, GNUTLS_CTYPE_SERVER);
if (ctype != cert_type) {
return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
@@ -1333,10 +1522,6 @@ _gnutls_select_server_cert(gnutls_session_t session, const gnutls_cipher_suite_e
* certificate and then check its compatibility with
* the ciphersuites.
*/
-
- /* 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) {
if (session->internals.selected_cert_list_length == 0) {
ret = call_get_cert_callback(session, NULL, 0, NULL, 0);
@@ -1407,10 +1592,10 @@ _gnutls_select_server_cert(gnutls_session_t session, const gnutls_cipher_suite_e
_gnutls_handshake_log
("HSK[%p]: checking compat of %s with certificate[%d] (%s/%s)\n",
session, cs->name, i,
- gnutls_pk_get_name(cred->certs[i].cert_list[0].pubkey->params.algo),
- gnutls_certificate_type_get_name(cred->certs
- [i].cert_list
- [0].type));
+ gnutls_pk_get_name(cred->certs[i].cert_list[0].pubkey->
+ params.algo),
+ gnutls_certificate_type_get_name(cred->certs[i].
+ cert_list[0].type));
ret = cert_select_sign_algorithm(session,
&cred->certs[i].cert_list[0],
@@ -1457,9 +1642,8 @@ _gnutls_select_server_cert(gnutls_session_t session, const gnutls_cipher_suite_e
ocsp_func,
ocsp_ptr);
} else {
- gnutls_assert();
/* Certificate does not support REQUESTED_ALGO. */
- return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
}
return 0;
@@ -1603,7 +1787,7 @@ _gnutls_proc_dhe_signature(gnutls_session_t session, uint8_t * data,
signature.size = sigsize;
// Retrieve the negotiated certificate type
- cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+ cert_type = get_certificate_type(session, GNUTLS_CTYPE_SERVER);
if ((ret =
_gnutls_get_auth_info_pcert(&peer_cert, cert_type, info)) < 0) {
diff --git a/lib/auth/cert.h b/lib/auth/cert.h
index fe3210f922..3f57ec1c74 100644
--- a/lib/auth/cert.h
+++ b/lib/auth/cert.h
@@ -32,6 +32,12 @@
#define MAX_OCSP_RESPONSES 8
+/* We use the structure below to hold a certificate chain
+ * with corresponding public/private key pair. This structure will
+ * also be used when raw public keys are used. The cert_list will
+ * then not hold the cert chain but only a raw public-key. In that case
+ * the list length is always 1.
+ */
typedef struct {
gnutls_pcert_st *cert_list; /* a certificate chain */
unsigned int cert_list_length; /* its length */
@@ -73,7 +79,7 @@ typedef struct gnutls_certificate_credentials_st {
/* X509 specific stuff */
gnutls_x509_trust_list_t tlist;
unsigned flags; /* gnutls_certificate_flags */
- unsigned int verify_flags; /* flags to be used at
+ unsigned int verify_flags; /* flags to be used at
* certificate verification.
*/
unsigned int verify_depth;
@@ -161,4 +167,9 @@ int _gnutls_proc_dhe_signature(gnutls_session_t session, uint8_t * data,
size_t _data_size,
gnutls_datum_t * vparams);
+int _gnutls_gen_rawpk_crt(gnutls_session_t session, gnutls_buffer_st* data);
+int _gnutls_proc_rawpk_crt(gnutls_session_t session,
+ uint8_t * data, size_t data_size);
+
+
#endif
diff --git a/lib/auth/rsa.c b/lib/auth/rsa.c
index 488569d3b7..c2203c7ed3 100644
--- a/lib/auth/rsa.c
+++ b/lib/auth/rsa.c
@@ -117,7 +117,7 @@ _gnutls_get_public_rsa_params(gnutls_session_t session,
}
// Get the negotiated server certificate type
- cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+ cert_type = get_certificate_type(session, GNUTLS_CTYPE_SERVER);
ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);
diff --git a/lib/cert-cred-rawpk.c b/lib/cert-cred-rawpk.c
new file mode 100644
index 0000000000..76cd653204
--- /dev/null
+++ b/lib/cert-cred-rawpk.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017 - 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/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <gnutls/gnutls.h>
+#include "datum.h"
+#include "auth/cert.h"
+#include "x509.h"
+#include "cert-cred.h"
+#include "read-file.h"
+#include <stdint.h>
+
+
+/**
+ * gnutls_certificate_set_rawpk_key_mem:
+ * @cred: is a #gnutls_certificate_credentials_t type.
+ * @spki: contains a raw public key in
+ * PKIX.SubjectPublicKeyInfo format.
+ * @pkey: contains a raw private key.
+ * @format: encoding of the keys. DER or PEM.
+ * @pass: an optional password to unlock the private key pkey.
+ * @key_usage: An ORed sequence of %GNUTLS_KEY_* flags.
+ * @names: is an array of DNS names belonging to the public-key (NULL if none).
+ * @names_length: holds the length of the names list.
+ * @flags: an ORed sequence of #gnutls_pkcs_encrypt_flags_t.
+ * These apply to the private key pkey.
+ *
+ * This function sets a public/private keypair in the
+ * #gnutls_certificate_credentials_t type to be used for authentication
+ * and/or encryption. @spki and @privkey should match otherwise set
+ * signatures cannot be validated. In case of no match this function
+ * returns %GNUTLS_E_CERTIFICATE_KEY_MISMATCH. This function should
+ * be called once for the client because there is currently no mechanism
+ * to determine which raw public-key to select for the peer when there
+ * are multiple present. Multiple raw public keys for the server can be
+ * distinghuished by setting the @names.
+ *
+ * Note here that @spki is a raw public-key as defined
+ * in RFC7250. It means that there is no surrounding certificate that
+ * holds the public key and that there is therefore no direct mechanism
+ * to prove the authenticity of this key. The keypair can be used during
+ * a TLS handshake but its authenticity should be established via a
+ * different mechanism (e.g. TOFU or known fingerprint).
+ *
+ * The supported formats are basic unencrypted key, PKCS8, PKCS12,
+ * and the openssl format and will be autodetected.
+ *
+ * If the raw public-key and the private key are given in PEM encoding
+ * then the strings that hold their values must be null terminated.
+ *
+ * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly
+ * set because there is no certificate structure around the key to define
+ * this value. See for more info gnutls_x509_crt_get_key_usage().
+ *
+ * Note that, this function by default returns zero on success and a
+ * negative value on error. Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2
+ * is set using gnutls_certificate_set_flags() it returns an index
+ * (greater or equal to zero). That index can be used in other functions
+ * to refer to the added key-pair.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, in case the
+ * key pair does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH is returned,
+ * in other erroneous cases a different negative error code is returned.
+ *
+ * Since: 3.6.6
+ **/
+int gnutls_certificate_set_rawpk_key_mem(gnutls_certificate_credentials_t cred,
+ const gnutls_datum_t* spki,
+ const gnutls_datum_t* pkey,
+ gnutls_x509_crt_fmt_t format,
+ const char* pass,
+ unsigned int key_usage,
+ const char **names,
+ unsigned int names_length,
+ unsigned int flags)
+{
+ int ret;
+ gnutls_privkey_t privkey;
+ gnutls_pcert_st* pcert;
+ gnutls_str_array_t str_names;
+ unsigned int i;
+
+ if (pkey == NULL || spki == NULL) {
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ }
+
+ /* Import our private key. This function does all the necessary
+ * inits, checks and imports. */
+ ret = _gnutls_read_key_mem(cred, pkey->data, pkey->size,
+ format, pass, flags, &privkey);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ /* We now convert our raw public key to a parsed certificate (pcert) structure */
+ pcert = gnutls_calloc(1, sizeof(*pcert));
+ if (pcert == NULL) {
+ gnutls_privkey_deinit(privkey);
+
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ // Import our raw public key to the pcert structure
+ ret = gnutls_pcert_import_rawpk_raw(pcert, spki,
+ format, key_usage, 0);
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+
+ return gnutls_assert_val(ret);
+ }
+
+ /* Process the names, if any */
+ _gnutls_str_array_init(&str_names);
+
+ if (names != NULL && names_length > 0) {
+ for (i = 0; i < names_length; i++) {
+ ret =
+ _gnutls_str_array_append_idna(&str_names, names[i],
+ strlen(names[i]));
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+ _gnutls_str_array_clear(&str_names);
+
+ return gnutls_assert_val(ret);
+ }
+ }
+ }
+
+ /* Now that we have converted the key material to our internal structures
+ * we can now add them to the credentials structure */
+ ret = _gnutls_certificate_credential_append_keypair(cred, privkey, str_names, pcert, 1);
+ // Check for errors
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+ gnutls_pcert_deinit(pcert);
+ gnutls_free(pcert);
+
+ return gnutls_assert_val(ret);
+ }
+ // Successfully added a certificate
+ cred->ncerts++;
+
+ /* Check whether the key pair matches.
+ * After this point we do not deinitialize anything on failure to avoid
+ * double freeing. We intentionally keep everything as the credentials state
+ * is documented to be in undefined state. */
+ if ((ret = _gnutls_check_key_cert_match(cred)) < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ CRED_RET_SUCCESS(cred);
+}
+
+
+/**
+ * gnutls_certificate_set_rawpk_key_file:
+ * @cred: is a #gnutls_certificate_credentials_t type.
+ * @rawpkfile: contains a raw public key in
+ * PKIX.SubjectPublicKeyInfo format.
+ * @privkeyfile: contains a file path to a private key.
+ * @format: encoding of the keys. DER or PEM.
+ * @pass: an optional password to unlock the private key privkeyfile.
+ * @key_usage: an ORed sequence of %GNUTLS_KEY_* flags.
+ * @names: is an array of DNS names belonging to the public-key (NULL if none).
+ * @names_length: holds the length of the names list.
+ * @privkey_flags: an ORed sequence of #gnutls_pkcs_encrypt_flags_t.
+ * These apply to the private key pkey.
+ * @pkcs11_flags: one of gnutls_pkcs11_obj_flags. These apply to URLs.
+ *
+ * This function sets a public/private keypair read from file in the
+ * #gnutls_certificate_credentials_t type to be used for authentication
+ * and/or encryption. @spki and @privkey should match otherwise set
+ * signatures cannot be validated. In case of no match this function
+ * returns %GNUTLS_E_CERTIFICATE_KEY_MISMATCH. This function should
+ * be called once for the client because there is currently no mechanism
+ * to determine which raw public-key to select for the peer when there
+ * are multiple present. Multiple raw public keys for the server can be
+ * distinghuished by setting the @names.
+ *
+ * Note here that @spki is a raw public-key as defined
+ * in RFC7250. It means that there is no surrounding certificate that
+ * holds the public key and that there is therefore no direct mechanism
+ * to prove the authenticity of this key. The keypair can be used during
+ * a TLS handshake but its authenticity should be established via a
+ * different mechanism (e.g. TOFU or known fingerprint).
+ *
+ * The supported formats are basic unencrypted key, PKCS8, PKCS12,
+ * and the openssl format and will be autodetected.
+ *
+ * If the raw public-key and the private key are given in PEM encoding
+ * then the strings that hold their values must be null terminated.
+ *
+ * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly
+ * set because there is no certificate structure around the key to define
+ * this value. See for more info gnutls_x509_crt_get_key_usage().
+ *
+ * Note that, this function by default returns zero on success and a
+ * negative value on error. Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2
+ * is set using gnutls_certificate_set_flags() it returns an index
+ * (greater or equal to zero). That index can be used in other functions
+ * to refer to the added key-pair.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, in case the
+ * key pair does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH is returned,
+ * in other erroneous cases a different negative error code is returned.
+ *
+ * Since: 3.6.6
+ */
+int gnutls_certificate_set_rawpk_key_file(gnutls_certificate_credentials_t cred,
+ const char* rawpkfile,
+ const char* privkeyfile,
+ gnutls_x509_crt_fmt_t format,
+ const char *pass,
+ unsigned int key_usage,
+ const char **names,
+ unsigned int names_length,
+ unsigned int privkey_flags,
+ unsigned int pkcs11_flags)
+{
+ int ret;
+ gnutls_privkey_t privkey;
+ gnutls_pubkey_t pubkey;
+ gnutls_pcert_st* pcert;
+ gnutls_datum_t rawpubkey = { NULL, 0 }; // to hold rawpk data from file
+ size_t key_size;
+ gnutls_str_array_t str_names;
+ unsigned int i;
+
+ if (rawpkfile == NULL || privkeyfile == NULL) {
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ }
+
+ /* Import our private key. This function does all the necessary
+ * inits, checks and imports. */
+ ret = _gnutls_read_key_file(cred, privkeyfile, format, pass, privkey_flags, &privkey);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ pcert = gnutls_calloc(1, sizeof(*pcert));
+ if (pcert == NULL) {
+ gnutls_privkey_deinit(privkey);
+
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ /* Check whether we are importing our raw public-key from a URL
+ * or from a regular file.
+ */
+ if (gnutls_url_is_supported(rawpkfile)) {
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+
+ return gnutls_assert_val(ret);
+ }
+
+ ret = gnutls_pubkey_import_url(pubkey, rawpkfile, pkcs11_flags);
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+ gnutls_pubkey_deinit(pubkey);
+
+ return gnutls_assert_val(ret);
+ }
+
+ ret = gnutls_pcert_import_rawpk(pcert, pubkey, 0);
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+ gnutls_pubkey_deinit(pubkey);
+
+ return gnutls_assert_val(ret);
+ }
+
+ } else {
+ /* Read our raw public-key into memory from file */
+ rawpubkey.data = (void*) read_binary_file(rawpkfile, &key_size);
+ if (rawpubkey.data == NULL) {
+ gnutls_privkey_deinit(privkey);
+
+ return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+ }
+ rawpubkey.size = key_size; // Implicit type casting
+
+ /* We now convert our raw public key that we've loaded into memory to
+ * a parsed certificate (pcert) structure. Note that rawpubkey will
+ * be copied into pcert. Therefore we can directly cleanup rawpubkey.
+ */
+ ret = gnutls_pcert_import_rawpk_raw(pcert, &rawpubkey,
+ format, key_usage, 0);
+
+ _gnutls_free_datum(&rawpubkey);
+
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+
+ return gnutls_assert_val(ret);
+ }
+
+ }
+
+ /* Process the names, if any */
+ _gnutls_str_array_init(&str_names);
+
+ if (names != NULL && names_length > 0) {
+ for (i = 0; i < names_length; i++) {
+ ret =
+ _gnutls_str_array_append_idna(&str_names, names[i],
+ strlen(names[i]));
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+ _gnutls_str_array_clear(&str_names);
+
+ return gnutls_assert_val(ret);
+ }
+ }
+ }
+
+ /* Now that we have converted the key material to our internal structures
+ * we can now add them to the credentials structure */
+ ret = _gnutls_certificate_credential_append_keypair(cred, privkey, str_names, pcert, 1);
+ if (ret < 0) {
+ gnutls_privkey_deinit(privkey);
+ gnutls_pcert_deinit(pcert);
+ gnutls_free(pcert);
+
+ return gnutls_assert_val(ret);
+ }
+ // Successfully added a certificate
+ cred->ncerts++;
+
+ /* Check whether the key pair matches.
+ * After this point we do not deinitialize anything on failure to avoid
+ * double freeing. We intentionally keep everything as the credentials state
+ * is documented to be in undefined state. */
+ if ((ret = _gnutls_check_key_cert_match(cred)) < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ CRED_RET_SUCCESS(cred);
+}
+
diff --git a/lib/cert-cred-x509.c b/lib/cert-cred-x509.c
index 42a6bd5ba8..257f1b989a 100644
--- a/lib/cert-cred-x509.c
+++ b/lib/cert-cred-x509.c
@@ -46,6 +46,7 @@
#include "read-file.h"
#include "system-keys.h"
#include "urls.h"
+#include "cert-cred.h"
#ifdef _WIN32
#include <wincrypt.h>
#endif
@@ -55,42 +56,10 @@
* related objects in a certificate credentials structure.
*/
-static int
-certificate_credential_append_keypair(gnutls_certificate_credentials_t res,
- gnutls_privkey_t key,
- gnutls_str_array_t names,
- gnutls_pcert_st * crt, int nr);
-
-#define CRED_RET_SUCCESS(cred) \
- if (cred->flags & GNUTLS_CERTIFICATE_API_V2) { \
- return cred->ncerts-1; \
- } else { \
- return 0; \
- }
-
-
-static int str_array_append_idna(gnutls_str_array_t * head, const char *name, size_t size)
-{
- int ret;
- gnutls_datum_t ahost;
-
- /* convert the provided hostname to ACE-Labels domain. */
- ret = gnutls_idna_map(name, size, &ahost, 0);
- if (ret < 0) {
- _gnutls_debug_log("unable to convert hostname %s to IDNA format\n", name);
- /* insert the raw name */
- return _gnutls_str_array_append(head, name, size);
- }
-
- ret = _gnutls_str_array_append(head, (char*)ahost.data, ahost.size);
- gnutls_free(ahost.data);
-
- return ret;
-}
/* Returns the name of the certificate of a null name
*/
-static int get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names)
+int _gnutls_get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names)
{
size_t max_size;
int i, ret = 0, ret2;
@@ -107,7 +76,7 @@ static int get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names)
have_dns_name = 1;
ret2 =
- str_array_append_idna(names, name,
+ _gnutls_str_array_append_idna(names, name,
max_size);
if (ret2 < 0) {
_gnutls_str_array_clear(names);
@@ -122,7 +91,7 @@ static int get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names)
gnutls_x509_crt_get_dn_by_oid(crt, OID_X520_COMMON_NAME, 0, 0,
name, &max_size);
if (ret >= 0) {
- ret = str_array_append_idna(names, name, max_size);
+ ret = _gnutls_str_array_append_idna(names, name, max_size);
if (ret < 0) {
_gnutls_str_array_clear(names);
return gnutls_assert_val(ret);
@@ -171,7 +140,7 @@ parse_der_cert_mem(gnutls_certificate_credentials_t res,
goto cleanup;
}
- ret = get_x509_name(crt, &names);
+ ret = _gnutls_get_x509_name(crt, &names);
if (ret < 0) {
gnutls_assert();
gnutls_x509_crt_deinit(crt);
@@ -186,7 +155,7 @@ parse_der_cert_mem(gnutls_certificate_credentials_t res,
goto cleanup;
}
- ret = certificate_credential_append_keypair(res, key, names, ccert, 1);
+ ret = _gnutls_certificate_credential_append_keypair(res, key, names, ccert, 1);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -279,7 +248,7 @@ parse_pem_cert_mem(gnutls_certificate_credentials_t res,
while (ptr != NULL && count < DEFAULT_MAX_VERIFY_DEPTH);
ret =
- get_x509_name(unsorted[0], &names);
+ _gnutls_get_x509_name(unsorted[0], &names);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -302,8 +271,7 @@ parse_pem_cert_mem(gnutls_certificate_credentials_t res,
}
ret =
- certificate_credential_append_keypair(res, key, names, pcerts,
- ncerts);
+ _gnutls_certificate_credential_append_keypair(res, key, names, pcerts, ncerts);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -370,8 +338,8 @@ static int tmp_pin_cb(void *userdata, int attempt, const char *token_url,
*
* It returns the private key read in @rkey.
*/
-static int
-read_key_mem(gnutls_certificate_credentials_t res,
+int
+_gnutls_read_key_mem(gnutls_certificate_credentials_t res,
const void *key, int key_size, gnutls_x509_crt_fmt_t type,
const char *pass, unsigned int flags,
gnutls_privkey_t *rkey)
@@ -501,7 +469,7 @@ read_cert_url(gnutls_certificate_credentials_t res, gnutls_privkey_t key, const
goto cleanup;
}
- ret = get_x509_name(crt, &names);
+ ret = _gnutls_get_x509_name(crt, &names);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -543,7 +511,7 @@ read_cert_url(gnutls_certificate_credentials_t res, gnutls_privkey_t key, const
t.data = NULL;
}
- ret = certificate_credential_append_keypair(res, key, names, ccert, count);
+ ret = _gnutls_certificate_credential_append_keypair(res, key, names, ccert, count);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -596,8 +564,8 @@ read_cert_file(gnutls_certificate_credentials_t res,
/* Reads PKCS-1 RSA private key file or a DSA file (in the format openssl
* stores it).
*/
-static int
-read_key_file(gnutls_certificate_credentials_t res,
+int
+_gnutls_read_key_file(gnutls_certificate_credentials_t res,
const char *keyfile, gnutls_x509_crt_fmt_t type,
const char *pass, unsigned int flags,
gnutls_privkey_t *rkey)
@@ -629,7 +597,7 @@ read_key_file(gnutls_certificate_credentials_t res,
return GNUTLS_E_FILE_ERROR;
}
- ret = read_key_mem(res, data, size, type, pass, flags, rkey);
+ ret = _gnutls_read_key_mem(res, data, size, type, pass, flags, rkey);
free(data);
return ret;
@@ -716,7 +684,7 @@ gnutls_certificate_set_x509_key_mem2(gnutls_certificate_credentials_t res,
/* this should be first
*/
- if ((ret = read_key_mem(res, key ? key->data : NULL,
+ if ((ret = _gnutls_read_key_mem(res, key ? key->data : NULL,
key ? key->size : 0, type, pass,
flags, &rkey)) < 0)
return ret;
@@ -736,57 +704,6 @@ gnutls_certificate_set_x509_key_mem2(gnutls_certificate_credentials_t res,
CRED_RET_SUCCESS(res);
}
-static int
-certificate_credential_append_keypair(gnutls_certificate_credentials_t res,
- gnutls_privkey_t key,
- gnutls_str_array_t names,
- gnutls_pcert_st * crt, int nr)
-{
- res->sorted_cert_idx = gnutls_realloc_fast(res->sorted_cert_idx,
- (1 + res->ncerts) *
- sizeof(unsigned int));
- if (res->sorted_cert_idx == NULL)
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-
- res->certs = gnutls_realloc_fast(res->certs,
- (1 + res->ncerts) *
- sizeof(certs_st));
- if (res->certs == NULL)
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-
- memset(&res->certs[res->ncerts], 0, sizeof(res->certs[0]));
-
- res->certs[res->ncerts].cert_list = crt;
- res->certs[res->ncerts].cert_list_length = nr;
- res->certs[res->ncerts].names = names;
- res->certs[res->ncerts].pkey = key;
-
- /* move RSA-PSS certificates before any RSA key.
- * Note that we cannot assume that any previous pointers
- * to sorted list are ok, due to the realloc in res->certs. */
- if (crt->pubkey->params.algo == GNUTLS_PK_RSA_PSS) {
- unsigned i,ridx;
- unsigned tmp;
-
- for (i=0;i<res->ncerts;i++) {
- ridx = res->sorted_cert_idx[i];
-
- if (res->certs[ridx].cert_list->pubkey->params.algo == GNUTLS_PK_RSA) {
- tmp = ridx;
- res->sorted_cert_idx[i] = res->ncerts;
- res->sorted_cert_idx[res->ncerts] = tmp;
- goto finish;
- }
- }
- }
-
- /* otherwise append it normally on the end */
- res->sorted_cert_idx[res->ncerts] = res->ncerts;
-
- finish:
- return 0;
-
-}
/**
* gnutls_certificate_set_x509_key:
@@ -856,7 +773,7 @@ gnutls_certificate_set_x509_key(gnutls_certificate_credentials_t res,
return GNUTLS_E_MEMORY_ERROR;
}
- ret = get_x509_name(cert_list[0], &names);
+ ret = _gnutls_get_x509_name(cert_list[0], &names);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -871,7 +788,7 @@ gnutls_certificate_set_x509_key(gnutls_certificate_credentials_t res,
}
ret =
- certificate_credential_append_keypair(res, pkey, names, pcerts,
+ _gnutls_certificate_credential_append_keypair(res, pkey, names, pcerts,
cert_list_size);
if (ret < 0) {
gnutls_assert();
@@ -1001,126 +918,6 @@ gnutls_certificate_get_x509_crt(gnutls_certificate_credentials_t res,
}
/**
- * gnutls_certificate_set_key:
- * @res: is a #gnutls_certificate_credentials_t type.
- * @names: is an array of DNS name of the certificate (NULL if none)
- * @names_size: holds the size of the names list
- * @pcert_list: contains a certificate list (path) for the specified private key
- * @pcert_list_size: holds the size of the certificate list
- * @key: is a #gnutls_privkey_t key
- *
- * This function sets a certificate/private key pair in the
- * gnutls_certificate_credentials_t type. This function may be
- * called more than once, in case multiple keys/certificates exist for
- * the server. For clients that want to send more than their own end-
- * entity certificate (e.g., also an intermediate CA cert), the full
- * certificate chain must be provided in @pcert_list.
- *
- * Note that the @key and the elements of @pcert_list will become part of the credentials
- * structure and must not be deallocated. They will be automatically deallocated
- * when the @res structure is deinitialized.
- *
- * If that function fails to load the @res structure is at an undefined state, it must
- * not be reused to load other keys or certificates.
- *
- * Note that, this function by default returns zero on success and a negative value on error.
- * Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2 is set using gnutls_certificate_set_flags()
- * it returns an index (greater or equal to zero). That index can be used to other functions to refer to the added key-pair.
- *
- * Returns: On success this functions returns zero, and otherwise a negative value on error (see above for modifying that behavior).
- *
- * Since: 3.0
- **/
-int
-gnutls_certificate_set_key(gnutls_certificate_credentials_t res,
- const char **names,
- int names_size,
- gnutls_pcert_st * pcert_list,
- int pcert_list_size, gnutls_privkey_t key)
-{
- int ret, i;
- gnutls_str_array_t str_names;
- gnutls_pcert_st *new_pcert_list;
-
- _gnutls_str_array_init(&str_names);
-
- if (names != NULL && names_size > 0) {
- for (i = 0; i < names_size; i++) {
- ret =
- str_array_append_idna(&str_names, names[i],
- strlen(names[i]));
- if (ret < 0) {
- ret = gnutls_assert_val(ret);
- goto cleanup;
- }
- }
- } else if (names == NULL && pcert_list[0].type == GNUTLS_CRT_X509) {
- gnutls_x509_crt_t crt;
-
- ret = gnutls_x509_crt_init(&crt);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
-
- ret = gnutls_x509_crt_import(crt, &pcert_list[0].cert, GNUTLS_X509_FMT_DER);
- if (ret < 0) {
- gnutls_assert();
- gnutls_x509_crt_deinit(crt);
- goto cleanup;
- }
-
- ret = get_x509_name(crt, &str_names);
- gnutls_x509_crt_deinit(crt);
-
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
- }
-
- if (res->pin.cb)
- gnutls_privkey_set_pin_function(key, res->pin.cb,
- res->pin.data);
-
- new_pcert_list = gnutls_malloc(sizeof(gnutls_pcert_st) * pcert_list_size);
- if (new_pcert_list == NULL) {
- gnutls_assert();
- return GNUTLS_E_MEMORY_ERROR;
- }
- memcpy(new_pcert_list, pcert_list, sizeof(gnutls_pcert_st) * pcert_list_size);
-
- ret =
- certificate_credential_append_keypair(res, key, str_names,
- new_pcert_list,
- pcert_list_size);
- if (ret < 0) {
- gnutls_assert();
- gnutls_free(new_pcert_list);
- goto cleanup;
- }
-
- res->ncerts++;
-
- /* Unlike gnutls_certificate_set_x509_key, we deinitialize everything
- * local after a failure. That is because the caller is responsible for
- * freeing these values after a failure, and if we keep references we
- * lead to double freeing */
- if ((ret = _gnutls_check_key_cert_match(res)) < 0) {
- gnutls_assert();
- gnutls_free(new_pcert_list);
- res->ncerts--;
- goto cleanup;
- }
-
- CRED_RET_SUCCESS(res);
-
- cleanup:
- _gnutls_str_array_clear(&str_names);
- return ret;
-}
-
-/**
* gnutls_certificate_set_trust_list:
* @res: is a #gnutls_certificate_credentials_t type.
* @tlist: is a #gnutls_x509_trust_list_t type
@@ -1265,7 +1062,7 @@ gnutls_certificate_set_x509_key_file2(gnutls_certificate_credentials_t res,
/* this should be first
*/
- if ((ret = read_key_file(res, keyfile, type, pass, flags, &rkey)) < 0)
+ if ((ret = _gnutls_read_key_file(res, keyfile, type, pass, flags, &rkey)) < 0)
return ret;
if ((ret = read_cert_file(res, rkey, certfile, type)) < 0) {
diff --git a/lib/cert-cred.c b/lib/cert-cred.c
index 2d7009b2e5..f04ded4c04 100644
--- a/lib/cert-cred.c
+++ b/lib/cert-cred.c
@@ -40,7 +40,202 @@
#include <str_array.h>
#include <x509/verify-high.h>
#include "x509/x509_int.h"
+#include "x509/common.h"
#include "dh.h"
+#include "cert-cred.h"
+
+
+/*
+ * Adds a public/private key pair to a certificate credential
+ */
+int
+_gnutls_certificate_credential_append_keypair(gnutls_certificate_credentials_t res,
+ gnutls_privkey_t key,
+ gnutls_str_array_t names,
+ gnutls_pcert_st * crt,
+ int nr)
+{
+ res->sorted_cert_idx = gnutls_realloc_fast(res->sorted_cert_idx,
+ (1 + res->ncerts) *
+ sizeof(unsigned int));
+ if (res->sorted_cert_idx == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ res->certs = gnutls_realloc_fast(res->certs,
+ (1 + res->ncerts) *
+ sizeof(certs_st));
+ if (res->certs == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ memset(&res->certs[res->ncerts], 0, sizeof(res->certs[0]));
+
+ res->certs[res->ncerts].cert_list = crt;
+ res->certs[res->ncerts].cert_list_length = nr;
+ res->certs[res->ncerts].names = names;
+ res->certs[res->ncerts].pkey = key;
+
+ /* move RSA-PSS certificates before any RSA key.
+ * Note that we cannot assume that any previous pointers
+ * to sorted list are ok, due to the realloc in res->certs. */
+ if (crt->pubkey->params.algo == GNUTLS_PK_RSA_PSS) {
+ unsigned i,ridx;
+ unsigned tmp;
+
+ for (i=0;i<res->ncerts;i++) {
+ ridx = res->sorted_cert_idx[i];
+
+ if (res->certs[ridx].cert_list->pubkey->params.algo == GNUTLS_PK_RSA) {
+ tmp = ridx;
+ res->sorted_cert_idx[i] = res->ncerts;
+ res->sorted_cert_idx[res->ncerts] = tmp;
+ goto finish;
+ }
+ }
+ }
+
+ /* otherwise append it normally on the end */
+ res->sorted_cert_idx[res->ncerts] = res->ncerts;
+
+ finish:
+ return 0;
+
+}
+
+
+/**
+ * gnutls_certificate_set_key:
+ * @res: is a #gnutls_certificate_credentials_t type.
+ * @names: is an array of DNS names belonging to the public-key (NULL if none)
+ * @names_size: holds the size of the names list
+ * @pcert_list: contains a certificate list (chain) or raw public-key
+ * @pcert_list_size: holds the size of the certificate list
+ * @key: is a #gnutls_privkey_t key corresponding to the first public-key in pcert_list
+ *
+ * This function sets a public/private key pair in the
+ * gnutls_certificate_credentials_t type. The given public key may be encapsulated
+ * in a certificate or can be given as a raw key. This function may be
+ * called more than once, in case multiple key pairs exist for
+ * the server. For clients that want to send more than their own end-
+ * entity certificate (e.g., also an intermediate CA cert), the full
+ * certificate chain must be provided in @pcert_list.
+ *
+ * Note that the @key will become part of the credentials structure and must
+ * not be deallocated. It will be automatically deallocated when the @res structure
+ * is deinitialized.
+ *
+ * If this function fails, the @res structure is at an undefined state and it must
+ * not be reused to load other keys or certificates.
+ *
+ * Note that, this function by default returns zero on success and a negative value on error.
+ * Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2 is set using gnutls_certificate_set_flags()
+ * it returns an index (greater or equal to zero). That index can be used for other functions to refer to the added key-pair.
+ *
+ * Since GnuTLS 3.6.6 this function also handles raw public keys.
+ *
+ * Returns: On success this functions returns zero, and otherwise a negative value on error (see above for modifying that behavior).
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_certificate_set_key(gnutls_certificate_credentials_t res,
+ const char **names,
+ int names_size,
+ gnutls_pcert_st * pcert_list,
+ int pcert_list_size,
+ gnutls_privkey_t key)
+{
+ int ret, i;
+ gnutls_str_array_t str_names;
+ gnutls_pcert_st *new_pcert_list;
+
+ /* Sanity checks */
+ // Check for a valid credential struct
+ if (res == NULL) {
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+
+ // A complete key pair must be given
+ if (pcert_list == NULL || key == NULL) {
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ }
+
+ /* Process the names, if any */
+ _gnutls_str_array_init(&str_names);
+
+ if (names != NULL && names_size > 0) {
+ for (i = 0; i < names_size; i++) {
+ ret =
+ _gnutls_str_array_append_idna(&str_names, names[i],
+ strlen(names[i]));
+ if (ret < 0) {
+ ret = gnutls_assert_val(ret);
+ goto cleanup;
+ }
+ }
+ } else if (names == NULL && pcert_list[0].type == GNUTLS_CRT_X509) {
+ gnutls_x509_crt_t crt;
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_import(crt, &pcert_list[0].cert, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_x509_crt_deinit(crt);
+ goto cleanup;
+ }
+
+ ret = _gnutls_get_x509_name(crt, &str_names);
+ gnutls_x509_crt_deinit(crt);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ if (res->pin.cb)
+ gnutls_privkey_set_pin_function(key, res->pin.cb,
+ res->pin.data);
+
+ new_pcert_list = gnutls_malloc(sizeof(gnutls_pcert_st) * pcert_list_size);
+ if (new_pcert_list == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ memcpy(new_pcert_list, pcert_list, sizeof(gnutls_pcert_st) * pcert_list_size);
+
+ ret =
+ _gnutls_certificate_credential_append_keypair(res, key, str_names,
+ new_pcert_list,
+ pcert_list_size);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(new_pcert_list);
+ goto cleanup;
+ }
+
+ res->ncerts++;
+
+ /* Unlike gnutls_certificate_set_x509_key, we deinitialize everything
+ * local after a failure. That is because the caller is responsible for
+ * freeing these values after a failure, and if we keep references we
+ * lead to double freeing */
+ if ((ret = _gnutls_check_key_cert_match(res)) < 0) {
+ gnutls_assert();
+ gnutls_free(new_pcert_list);
+ res->ncerts--;
+ goto cleanup;
+ }
+
+ CRED_RET_SUCCESS(res);
+
+ cleanup:
+ _gnutls_str_array_clear(&str_names);
+ return ret;
+}
/**
* gnutls_certificate_free_keys:
@@ -199,7 +394,7 @@ gnutls_certificate_free_credentials(gnutls_certificate_credentials_t sc)
// Check for valid pointer and otherwise do nothing
if (sc == NULL)
return;
-
+
gnutls_x509_trust_list_deinit(sc->tlist, 1);
gnutls_certificate_free_keys(sc);
memset(sc->pin_tmp, 0, sizeof(sc->pin_tmp));
diff --git a/lib/cert-cred.h b/lib/cert-cred.h
new file mode 100644
index 0000000000..06cba4dd58
--- /dev/null
+++ b/lib/cert-cred.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * 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/>
+ *
+ */
+
+#include <gnutls/abstract.h>
+#include "str_array.h"
+
+
+int
+_gnutls_certificate_credential_append_keypair(gnutls_certificate_credentials_t res,
+ gnutls_privkey_t key,
+ gnutls_str_array_t names,
+ gnutls_pcert_st * crt, int nr);
+
+int
+_gnutls_read_key_mem(gnutls_certificate_credentials_t res,
+ const void *key, int key_size, gnutls_x509_crt_fmt_t type,
+ const char *pass, unsigned int flags,
+ gnutls_privkey_t *rkey);
+
+int
+_gnutls_read_key_file(gnutls_certificate_credentials_t res,
+ const char *keyfile, gnutls_x509_crt_fmt_t type,
+ const char *pass, unsigned int flags,
+ gnutls_privkey_t *rkey);
+
+int
+_gnutls_get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names);
+
+#define CRED_RET_SUCCESS(cred) \
+ if (cred->flags & GNUTLS_CERTIFICATE_API_V2) { \
+ return cred->ncerts-1; \
+ } else { \
+ return 0; \
+ }
diff --git a/lib/cert-session.c b/lib/cert-session.c
index 2726512f5d..9a25eb57a4 100644
--- a/lib/cert-session.c
+++ b/lib/cert-session.c
@@ -599,6 +599,10 @@ _gnutls_x509_cert_verify_peers(gnutls_session_t session,
* default upper limits regarding the certificate key size and chain
* size are set. To override them use gnutls_certificate_set_verify_limits().
*
+ * Note that when using raw public-keys verification will not work because there is
+ * no corresponding certificate body belonging to the raw key that can be verified. In that
+ * case this function will return %GNUTLS_E_INVALID_REQUEST.
+ *
* Returns: %GNUTLS_E_SUCCESS (0) when the validation is performed, or a negative error code otherwise.
* A successful error code means that the @status parameter must be checked to obtain the validation status.
**/
@@ -640,6 +644,10 @@ gnutls_certificate_verify_peers2(gnutls_session_t session,
* default upper limits regarding the certificate key size and chain
* size are set. To override them use gnutls_certificate_set_verify_limits().
*
+ * Note that when using raw public-keys verification will not work because there is
+ * no corresponding certificate body belonging to the raw key that can be verified. In that
+ * case this function will return %GNUTLS_E_INVALID_REQUEST.
+ *
* Returns: %GNUTLS_E_SUCCESS (0) when the validation is performed, or a negative error code otherwise.
* A successful error code means that the @status parameter must be checked to obtain the validation status.
*
@@ -695,6 +703,10 @@ gnutls_typed_vdata_st data;
* default upper limits regarding the certificate key size and chain
* size are set. To override them use gnutls_certificate_set_verify_limits().
*
+ * Note that when using raw public-keys verification will not work because there is
+ * no corresponding certificate body belonging to the raw key that can be verified. In that
+ * case this function will return %GNUTLS_E_INVALID_REQUEST.
+ *
* Returns: %GNUTLS_E_SUCCESS (0) when the validation is performed, or a negative error code otherwise.
* A successful error code means that the @status parameter must be checked to obtain the validation status.
*
@@ -719,7 +731,7 @@ gnutls_certificate_verify_peers(gnutls_session_t session,
return GNUTLS_E_NO_CERTIFICATE_FOUND;
- switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+ switch (get_certificate_type(session, GNUTLS_CTYPE_PEERS)) {
case GNUTLS_CRT_X509:
return _gnutls_x509_cert_verify_peers(session, data, elements,
status);
@@ -820,7 +832,7 @@ time_t gnutls_certificate_expiration_time_peers(gnutls_session_t session)
return (time_t) - 1;
}
- switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+ switch (get_certificate_type(session, GNUTLS_CTYPE_PEERS)) {
case GNUTLS_CRT_X509:
return
_gnutls_x509_get_raw_crt_expiration_time(&info->
@@ -856,7 +868,7 @@ time_t gnutls_certificate_activation_time_peers(gnutls_session_t session)
return (time_t) - 1;
}
- switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+ switch (get_certificate_type(session, GNUTLS_CTYPE_PEERS)) {
case GNUTLS_CRT_X509:
return
_gnutls_x509_get_raw_crt_activation_time(&info->
diff --git a/lib/ext/cert_types.h b/lib/ext/cert_types.h
index c54e0f2bfe..04e024d5db 100644
--- a/lib/ext/cert_types.h
+++ b/lib/ext/cert_types.h
@@ -26,11 +26,13 @@
/* Maps IANA TLS Certificate Types identifiers to internal
* certificate type representation.
*/
-static inline gnutls_certificate_type_t _gnutls_IANA2cert_type(int num)
+static inline gnutls_certificate_type_t IANA2cert_type(int num)
{
switch (num) {
case 0:
return GNUTLS_CRT_X509;
+ case 2:
+ return GNUTLS_CRT_RAWPK;
default:
return GNUTLS_CRT_UNKNOWN;
}
@@ -39,12 +41,43 @@ static inline gnutls_certificate_type_t _gnutls_IANA2cert_type(int num)
/* Maps internal certificate type representation to
* IANA TLS Certificate Types identifiers.
*/
-static inline int _gnutls_cert_type2IANA(gnutls_certificate_type_t cert_type)
+static inline int cert_type2IANA(gnutls_certificate_type_t cert_type)
{
switch (cert_type) {
case GNUTLS_CRT_X509:
return 0;
+ case GNUTLS_CRT_RAWPK:
+ return 2;
default:
return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
}
}
+
+/* Checks whether the given cert type is enabled in the application
+ */
+static inline bool is_cert_type_enabled(gnutls_session_t session, gnutls_certificate_type_t cert_type)
+{
+ switch(cert_type) {
+ case GNUTLS_CRT_X509:
+ // Default cert type, always enabled
+ return true;
+ case GNUTLS_CRT_RAWPK:
+ return session->internals.flags & GNUTLS_ENABLE_RAWPK;
+ default:
+ // When not explicitly supported here disable it
+ return false;
+ }
+}
+
+/* Checks whether alternative cert types (i.e. other than X.509)
+ * are enabled in the application
+ */
+static inline bool are_alternative_cert_types_allowed(gnutls_session_t session)
+{
+ // OR-ed list of defined cert type init flags
+ #define CERT_TYPES_FLAGS GNUTLS_ENABLE_RAWPK
+
+ return session->internals.flags & CERT_TYPES_FLAGS;
+
+ #undef CERT_TYPES_FLAGS
+}
diff --git a/lib/ext/client_cert_type.c b/lib/ext/client_cert_type.c
index 5449eae678..534c407b3a 100644
--- a/lib/ext/client_cert_type.c
+++ b/lib/ext/client_cert_type.c
@@ -33,8 +33,8 @@
#include "hello_ext.h"
#include "hello_ext_lib.h"
#include "errors.h"
-#include <state.h>
-#include <datum.h>
+#include "state.h"
+#include "datum.h"
static int _gnutls_client_cert_type_recv_params(gnutls_session_t session,
@@ -76,10 +76,10 @@ static int _gnutls_client_cert_type_recv_params(gnutls_session_t session,
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)
+ /* Only activate this extension if we have cert credentials set
+ * and alternative cert types are allowed */
+ if (!are_alternative_cert_types_allowed(session) ||
+ (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL))
return 0;
if (!IS_SERVER(session)) { // client mode
@@ -97,7 +97,7 @@ static int _gnutls_client_cert_type_recv_params(gnutls_session_t session,
* 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]);
+ cert_type = IANA2cert_type(pdata[0]);
// Check validity of cert type
if (cert_type == GNUTLS_CRT_UNKNOWN) {
@@ -119,7 +119,7 @@ static int _gnutls_client_cert_type_recv_params(gnutls_session_t session,
// 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)
+ if (IANA2cert_type(sent_cert_types.data[i]) == cert_type)
found = 1;
}
@@ -160,7 +160,7 @@ static int _gnutls_client_cert_type_recv_params(gnutls_session_t session,
*/
for (i = 0; i < cert_types.size; i++) {
// Convert to internal representation
- cert_type = _gnutls_IANA2cert_type(cert_types.data[i]);
+ cert_type = IANA2cert_type(cert_types.data[i]);
// If we have an invalid cert id then continue to the next
if (cert_type == GNUTLS_CRT_UNKNOWN)
@@ -201,13 +201,13 @@ static int _gnutls_client_cert_type_send_params(gnutls_session_t session,
uint8_t i = 0, num_cert_types = 0;
priority_st* cert_priorities;
gnutls_datum_t tmp_cert_types; // For type conversion
- uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types
+ uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types. Inv: 0 <= cert type Id < 256
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)
+ /* Only activate this extension if we have cert credentials set
+ * and alternative cert types are allowed */
+ if (!are_alternative_cert_types_allowed(session) ||
+ (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL))
return 0;
if (!IS_SERVER(session)) { // Client mode
@@ -255,7 +255,13 @@ static int _gnutls_client_cert_type_send_params(gnutls_session_t session,
return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
// Convert to IANA representation
- cert_type = _gnutls_cert_type2IANA(cert_priorities->priorities[i]);
+ ret = cert_type2IANA(cert_priorities->priorities[i]);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ cert_type = ret; // For readability
+
// Add this cert type to our list with supported types
cert_types[num_cert_types] = cert_type;
num_cert_types++;
@@ -282,7 +288,7 @@ static int _gnutls_client_cert_type_send_params(gnutls_session_t session,
return 0;
} else if (num_cert_types == 1 &&
- _gnutls_IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) {
+ 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",
@@ -342,9 +348,13 @@ static int _gnutls_client_cert_type_send_params(gnutls_session_t session,
* 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 = cert_type2IANA(get_certificate_type(
+ session, GNUTLS_CTYPE_CLIENT));
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ cert_type = ret; // For readability
ret = gnutls_buffer_append_data(data, &cert_type, 1);
diff --git a/lib/ext/server_cert_type.c b/lib/ext/server_cert_type.c
index a00a0376c9..35c6d751db 100644
--- a/lib/ext/server_cert_type.c
+++ b/lib/ext/server_cert_type.c
@@ -33,8 +33,8 @@
#include "hello_ext.h"
#include "hello_ext_lib.h"
#include "errors.h"
-#include <state.h>
-#include <datum.h>
+#include "state.h"
+#include "datum.h"
static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
@@ -76,10 +76,10 @@ static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
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)
+ /* Only activate this extension if we have cert credentials set
+ * and alternative cert types are allowed */
+ if (!are_alternative_cert_types_allowed(session) ||
+ (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL))
return 0;
if (!IS_SERVER(session)) { // client mode
@@ -96,7 +96,7 @@ static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
* 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]);
+ cert_type = IANA2cert_type(pdata[0]);
// Check validity of cert type
if (cert_type == GNUTLS_CRT_UNKNOWN) {
@@ -118,7 +118,7 @@ static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
// 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)
+ if (IANA2cert_type(sent_cert_types.data[i]) == cert_type)
found = 1;
}
@@ -159,7 +159,7 @@ static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
*/
for (i = 0; i < cert_types.size; i++) {
// Convert to internal representation
- cert_type = _gnutls_IANA2cert_type(cert_types.data[i]);
+ cert_type = IANA2cert_type(cert_types.data[i]);
// If we have an invalid cert id then continue to the next
if (cert_type == GNUTLS_CRT_UNKNOWN)
@@ -197,12 +197,12 @@ static int _gnutls_server_cert_type_send_params(gnutls_session_t session,
uint8_t i = 0, num_cert_types = 0;
priority_st* cert_priorities;
gnutls_datum_t tmp_cert_types; // For type conversion
- uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types
+ uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types. Inv: 0 <= cert type Id < 256
- /* 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)
+ /* Only activate this extension if we have cert credentials set
+ * and alternative cert types are allowed */
+ if (!are_alternative_cert_types_allowed(session) ||
+ (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL))
return 0;
if (!IS_SERVER(session)) { // Client mode
@@ -255,7 +255,13 @@ static int _gnutls_server_cert_type_send_params(gnutls_session_t session,
return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
// Convert to IANA representation
- cert_type = _gnutls_cert_type2IANA(cert_priorities->priorities[i]);
+ ret = cert_type2IANA(cert_priorities->priorities[i]);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ cert_type = ret; // For readability
+
// Add this cert type to our list with supported types
cert_types[num_cert_types] = cert_type;
num_cert_types++;
@@ -281,7 +287,7 @@ static int _gnutls_server_cert_type_send_params(gnutls_session_t session,
return 0;
} else if (num_cert_types == 1 &&
- _gnutls_IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) {
+ 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",
@@ -320,9 +326,13 @@ static int _gnutls_server_cert_type_send_params(gnutls_session_t session,
}
} else { // Server mode
// Retrieve negotiated server certificate type and send it
- cert_type =
- _gnutls_cert_type2IANA(session->security_parameters.
- server_ctype);
+ ret = cert_type2IANA(get_certificate_type(
+ session, GNUTLS_CTYPE_SERVER));
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ cert_type = ret; // For readability
ret = gnutls_buffer_append_data(data, &cert_type, 1);
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index cccc3d1a97..8baa8815e7 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -1561,9 +1561,39 @@ 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)
+/* Returns the during the handshake negotiated certificate type(s).
+ * See state.c for the full function documentation.
+ *
+ * This function is made static inline for optimization reasons.
+ */
+static inline gnutls_certificate_type_t
+get_certificate_type(gnutls_session_t session,
+ gnutls_ctype_target_t target)
{
- return session->internals.flags & GNUTLS_ENABLE_CERT_TYPE_NEG;
+ 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_CRT_UNKNOWN;
+ }
}
/* Macros to aide constant time/mem checks */
diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h
index fcff0a562f..223fb2ed1f 100644
--- a/lib/includes/gnutls/abstract.h
+++ b/lib/includes/gnutls/abstract.h
@@ -693,6 +693,14 @@ int gnutls_pcert_export_openpgp(gnutls_pcert_st * pcert,
void gnutls_pcert_deinit(gnutls_pcert_st * pcert);
+int gnutls_pcert_import_rawpk(gnutls_pcert_st* pcert,
+ gnutls_pubkey_t key, unsigned int flags);
+
+int gnutls_pcert_import_rawpk_raw(gnutls_pcert_st* pcert,
+ const gnutls_datum_t* rawpubkey,
+ gnutls_x509_crt_fmt_t format,
+ unsigned int key_usage, unsigned int flags);
+
/* For certificate credentials */
/* This is the same as gnutls_certificate_retrieve_function()
* but retrieves a gnutls_pcert_st which requires much less processing
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 73141a3a3b..1c3455c0cf 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -421,7 +421,6 @@ 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).
* @GNUTLS_AUTO_REAUTH: Enable transparent re-authentication in client side when the server
* requests to. That is, reauthentication is handled within gnutls_record_recv(), and
* the %GNUTLS_E_REHANDSHAKE or %GNUTLS_E_REAUTH_REQUEST are not returned. This must be
@@ -430,6 +429,10 @@ typedef enum {
* since gnutls_record_recv() could be interrupted when sending when this flag is enabled.
* Note this flag may not be used if you are using the same session for sending and receiving
* in different threads.
+ * @GNUTLS_ENABLE_EARLY_DATA: Under TLS1.3 allow the server to receive early data sent as part of the initial ClientHello (0-RTT).
+ * This is not enabled by default as early data has weaker security properties than other data. Since 3.6.5.
+ *
+ * @GNUTLS_ENABLE_RAWPK: Allows raw public-keys to be negotiated during the handshake. Since 3.6.6.
*
* Enumeration of different flags for gnutls_init() function. All the flags
* can be combined except @GNUTLS_SERVER and @GNUTLS_CLIENT which are mutually
@@ -458,7 +461,7 @@ typedef enum {
GNUTLS_NO_AUTO_REKEY = (1<<15),
GNUTLS_SAFE_PADDING_CHECK = (1<<16),
GNUTLS_ENABLE_EARLY_START = (1<<17),
- GNUTLS_ENABLE_CERT_TYPE_NEG = (1<<18),
+ GNUTLS_ENABLE_RAWPK = (1<<18),
GNUTLS_AUTO_REAUTH = (1<<19),
GNUTLS_ENABLE_EARLY_DATA = (1<<20)
} gnutls_init_flags_t;
@@ -476,6 +479,8 @@ typedef enum {
#define GNUTLS_ENABLE_FALSE_START (1<<8)
#define GNUTLS_FORCE_CLIENT_CERT (1<<9)
#define GNUTLS_NO_TICKETS (1<<10)
+#define GNUTLS_ENABLE_CERT_TYPE_NEG 0
+ // Here for compatibility reasons
/**
* gnutls_alert_level_t:
@@ -630,7 +635,7 @@ const char
* @GNUTLS_CERT_SIGNER_NOT_FOUND: The certificate's issuer is not known.
* This is the case if the issuer is not included in the trusted certificate list.
* @GNUTLS_CERT_SIGNER_NOT_CA: The certificate's signer was not a CA. This
- * may happen if this was a version 1 certificate, which is common with
+ * may happen if this was a version 1 certificate, which is common with
* some CAs, or a version 3 certificate without the basic constrains extension.
* @GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE: The certificate's signer constraints were
* violated.
@@ -1405,7 +1410,7 @@ ssize_t gnutls_record_recv(gnutls_session_t session, void *data,
typedef struct mbuffer_st *gnutls_packet_t;
ssize_t
-gnutls_record_recv_packet(gnutls_session_t session,
+gnutls_record_recv_packet(gnutls_session_t session,
gnutls_packet_t *packet);
void gnutls_packet_get(gnutls_packet_t packet, gnutls_datum_t *data, unsigned char *sequence);
@@ -1685,7 +1690,7 @@ const char *
gnutls_protocol_get_name(gnutls_protocol_t version) __GNUTLS_CONST__;
-/* get/set session
+/* get/set session
*/
int gnutls_session_set_data(gnutls_session_t session,
const void *session_data,
@@ -1776,7 +1781,7 @@ int gnutls_session_channel_binding(gnutls_session_t session,
gnutls_channel_binding_t cbtype,
gnutls_datum_t * cb);
-/* checks if this session is a resumed one
+/* checks if this session is a resumed one
*/
int gnutls_session_is_resumed(gnutls_session_t session);
int gnutls_session_resumption_requested(gnutls_session_t session);
@@ -2130,6 +2135,29 @@ gnutls_ocsp_status_request_get2(gnutls_session_t session,
unsigned idx,
gnutls_datum_t * response);
+/* RAW public key functions (RFC7250) */
+int gnutls_certificate_set_rawpk_key_mem(gnutls_certificate_credentials_t cred,
+ const gnutls_datum_t* spki,
+ const gnutls_datum_t* pkey,
+ gnutls_x509_crt_fmt_t format,
+ const char* pass,
+ unsigned int key_usage,
+ const char **names,
+ unsigned int names_length,
+ unsigned int flags);
+
+int gnutls_certificate_set_rawpk_key_file(gnutls_certificate_credentials_t cred,
+ const char* rawpkfile,
+ const char* privkeyfile,
+ gnutls_x509_crt_fmt_t format,
+ const char *pass,
+ unsigned int key_usage,
+ const char **names,
+ unsigned int names_length,
+ unsigned int privkey_flags,
+ unsigned int pkcs11_flags);
+
+
/* global state functions
*/
int gnutls_global_init(void);
@@ -2272,7 +2300,7 @@ void gnutls_transport_set_errno_function(gnutls_session_t session,
void gnutls_transport_set_errno(gnutls_session_t session, int err);
-/* session specific
+/* session specific
*/
void gnutls_session_set_ptr(gnutls_session_t session, void *ptr);
void *gnutls_session_get_ptr(gnutls_session_t session);
@@ -2305,7 +2333,7 @@ int gnutls_random_art(gnutls_random_art_t type,
int gnutls_idna_map(const char * input, unsigned ilen, gnutls_datum_t *out, unsigned flags);
int gnutls_idna_reverse_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags);
-/* SRP
+/* SRP
*/
typedef struct gnutls_srp_server_credentials_st
@@ -2974,13 +3002,13 @@ typedef int (*gnutls_supp_recv_func) (gnutls_session_t session,
typedef int (*gnutls_supp_send_func) (gnutls_session_t session,
gnutls_buffer_t buf);
-int gnutls_supplemental_register(const char *name,
- gnutls_supplemental_data_format_type_t type,
+int gnutls_supplemental_register(const char *name,
+ gnutls_supplemental_data_format_type_t type,
gnutls_supp_recv_func supp_recv_func,
gnutls_supp_send_func supp_send_func);
int gnutls_session_supplemental_register(gnutls_session_t session, const char *name,
- gnutls_supplemental_data_format_type_t type,
+ gnutls_supplemental_data_format_type_t type,
gnutls_supp_recv_func supp_recv_func,
gnutls_supp_send_func supp_send_func,
unsigned int flags);
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index bfb447ccfd..197644ea43 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1264,6 +1264,15 @@ GNUTLS_3_6_5
gnutls_privkey_decrypt_data2;
} GNUTLS_3_6_4;
+GNUTLS_3_6_6
+{
+ global:
+ gnutls_certificate_set_rawpk_key_mem;
+ gnutls_certificate_set_rawpk_key_file;
+ gnutls_pcert_import_rawpk;
+ gnutls_pcert_import_rawpk_raw;
+} GNUTLS_3_6_5;
+
GNUTLS_FIPS140_3_4 {
global:
gnutls_cipher_self_test;
diff --git a/lib/pcert.c b/lib/pcert.c
index b800e91fc4..816a748b05 100644
--- a/lib/pcert.c
+++ b/lib/pcert.c
@@ -27,6 +27,7 @@
#include <x509.h>
#include "x509/x509_int.h"
#include <gnutls/x509.h>
+#include "x509_b64.h"
/**
* gnutls_pcert_import_x509:
@@ -215,7 +216,7 @@ gnutls_pcert_list_import_x509_raw(gnutls_pcert_st *pcert_list,
cleanup:
for (i = 0; i < *pcert_list_size; i++)
gnutls_x509_crt_deinit(crt[i]);
-
+
cleanup_crt:
gnutls_free(crt);
return ret;
@@ -356,6 +357,133 @@ int gnutls_pcert_import_x509_raw(gnutls_pcert_st * pcert,
}
/**
+ * gnutls_pcert_import_rawpk:
+ * @pcert: The pcert structure to import the data into.
+ * @pubkey: The raw public-key in #gnutls_pubkey_t format to be imported
+ * @flags: zero for now
+ *
+ * This convenience function will import (i.e. convert) the given raw
+ * public key @pubkey into a #gnutls_pcert_st structure. The structure
+ * must be deinitialized afterwards using gnutls_pcert_deinit(). The
+ * given @pubkey must not be deinitialized because it will be associated
+ * with the given @pcert structure and will be deinitialized with it.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.6
+ **/
+int gnutls_pcert_import_rawpk(gnutls_pcert_st* pcert,
+ gnutls_pubkey_t pubkey, unsigned int flags)
+{
+ int ret;
+
+ if (pubkey == NULL) {
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ }
+
+ memset(pcert, 0, sizeof(*pcert));
+
+ /* A pcert struct holds a raw copy of the certificate data.
+ * Therefore we convert our gnutls_pubkey_t to its raw DER
+ * representation and copy it into our pcert. It is this raw data
+ * that will be transfered to the peer via a Certificate msg.
+ * According to the spec (RFC7250) a DER representation must be used.
+ */
+ ret = gnutls_pubkey_export2(pubkey, GNUTLS_X509_FMT_DER, &pcert->cert);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ pcert->pubkey = pubkey;
+
+ pcert->type = GNUTLS_CRT_RAWPK;
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_pcert_import_rawpk_raw:
+ * @pcert: The pcert structure to import the data into.
+ * @rawpubkey: The raw public-key in #gnutls_datum_t format to be imported.
+ * @format: The format of the raw public-key. DER or PEM.
+ * @key_usage: An ORed sequence of %GNUTLS_KEY_* flags.
+ * @flags: zero for now
+ *
+ * This convenience function will import (i.e. convert) the given raw
+ * public key @rawpubkey into a #gnutls_pcert_st structure. The structure
+ * must be deinitialized afterwards using gnutls_pcert_deinit().
+ * Note that the caller is responsible for freeing @rawpubkey. All necessary
+ * values will be copied into @pcert.
+ *
+ * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly
+ * set because there is no certificate structure around the key to define
+ * this value. See for more info gnutls_x509_crt_get_key_usage().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.6
+ **/
+int gnutls_pcert_import_rawpk_raw(gnutls_pcert_st* pcert,
+ const gnutls_datum_t* rawpubkey,
+ gnutls_x509_crt_fmt_t format,
+ unsigned int key_usage, unsigned int flags)
+{
+ int ret;
+
+ if (rawpubkey == NULL) {
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ }
+
+ memset(pcert, 0, sizeof(*pcert));
+
+ ret = gnutls_pubkey_init(&pcert->pubkey);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ // Convert our raw public-key to a gnutls_pubkey_t structure
+ ret = gnutls_pubkey_import(pcert->pubkey, rawpubkey, format);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ pcert->pubkey->key_usage = key_usage;
+
+ /* A pcert struct holds a raw copy of the certificate data.
+ * It is this raw data that will be transfered to the peer via a
+ * Certificate message. According to the spec (RFC7250) a DER
+ * representation must be used. Therefore we check the format and
+ * convert if necessary.
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ ret = _gnutls_fbase64_decode(PEM_PK,
+ rawpubkey->data, rawpubkey->size,
+ &pcert->cert);
+
+ if (ret < 0) {
+ gnutls_pubkey_deinit(pcert->pubkey);
+
+ return gnutls_assert_val(ret);
+ }
+ } else {
+ // Directly copy the raw DER data to our pcert
+ ret = _gnutls_set_datum(&pcert->cert, rawpubkey->data, rawpubkey->size);
+
+ if (ret < 0) {
+ gnutls_pubkey_deinit(pcert->pubkey);
+
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ pcert->type = GNUTLS_CRT_RAWPK;
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
* gnutls_pcert_export_x509:
* @pcert: The pcert structure.
* @crt: An initialized #gnutls_x509_crt_t.
@@ -420,15 +548,17 @@ _gnutls_get_auth_info_pcert(gnutls_pcert_st * pcert,
cert_auth_info_t info)
{
switch (type) {
- case GNUTLS_CRT_X509:
- return gnutls_pcert_import_x509_raw(pcert,
- &info->
- raw_certificate_list
- [0],
- GNUTLS_X509_FMT_DER,
- 0);
- default:
- gnutls_assert();
- return GNUTLS_E_INTERNAL_ERROR;
+ case GNUTLS_CRT_X509:
+ return gnutls_pcert_import_x509_raw(pcert,
+ &info->raw_certificate_list[0],
+ GNUTLS_X509_FMT_DER,
+ 0);
+ case GNUTLS_CRT_RAWPK:
+ return gnutls_pcert_import_rawpk_raw(pcert,
+ &info->raw_certificate_list[0],
+ GNUTLS_X509_FMT_DER,
+ 0, 0);
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
}
diff --git a/lib/pkcs11x.c b/lib/pkcs11x.c
index fc428e17a4..c11f12cb8d 100644
--- a/lib/pkcs11x.c
+++ b/lib/pkcs11x.c
@@ -277,7 +277,7 @@ gnutls_pkcs11_obj_get_exts(gnutls_pkcs11_obj_t obj,
spki.data = obj->raw.data;
spki.size = obj->raw.size;
} else {
- ret = x509_raw_crt_to_raw_pubkey(&obj->raw, &spki);
+ ret = _gnutls_x509_raw_crt_to_raw_pubkey(&obj->raw, &spki);
if (ret < 0)
return gnutls_assert_val(ret);
deinit_spki = 1;
diff --git a/lib/session.c b/lib/session.c
index a7ac943153..a5c9c93d53 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -27,6 +27,7 @@
#include <datum.h>
#include "buffers.h"
#include "state.h"
+#include "ext/cert_types.h"
/**
* gnutls_session_get_data:
@@ -423,11 +424,10 @@ char *gnutls_session_get_desc(gnutls_session_t session)
}
}
- // Check whether we have negotiated certificate types
- if (_gnutls_has_negotiate_ctypes(session)) {
+ if (are_alternative_cert_types_allowed(session)) {
// Get certificate types
- ctype_client = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT);
- ctype_server = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+ ctype_client = get_certificate_type(session, GNUTLS_CTYPE_CLIENT);
+ ctype_server = get_certificate_type(session, GNUTLS_CTYPE_SERVER);
if (ctype_client == ctype_server) {
// print proto version, client/server cert type
@@ -442,9 +442,8 @@ char *gnutls_session_get_desc(gnutls_session_t session)
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)));
+ snprintf(proto_name, sizeof(proto_name), "%s",
+ gnutls_protocol_get_name(get_num_version(session)));
}
desc = gnutls_malloc(DESC_SIZE);
diff --git a/lib/state.c b/lib/state.c
index 392183c95f..540a83c7b8 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -52,6 +52,7 @@
#include <gnutls/dtls.h>
#include "dtls.h"
#include "tls13/session_ticket.h"
+#include "ext/cert_types.h"
/* to be used by supplemental data support to disable TLS1.3
* when supplemental data have been globally registered */
@@ -140,30 +141,9 @@ 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);
- }
+ /* We want to inline this function so therefore
+ * we've defined it in gnutls_int.h */
+ return get_certificate_type(session, target);
}
/**
@@ -309,6 +289,10 @@ _gnutls_session_cert_type_supported(gnutls_session_t session,
unsigned i;
priority_st* ctype_priorities;
+ // Check whether this cert type is enabled by the application
+ if (!is_cert_type_enabled(session, cert_type))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+
// Perform a credentials check if requested
if (check_credentials) {
if (!_gnutls_has_cert_credentials(session, cert_type))
@@ -336,14 +320,14 @@ _gnutls_session_cert_type_supported(gnutls_session_t session,
// No explicit priorities set, and default ctype is asked
if (ctype_priorities->num_priorities == 0
&& cert_type == DEFAULT_CERT_TYPE)
- return 0; // ok
+ return 0;
/* 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->num_priorities; i++) {
if (ctype_priorities->priorities[i] == cert_type)
- return 0; /* ok */
+ return 0;
}
return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
diff --git a/lib/str_array.h b/lib/str_array.h
index ef68783905..abec702488 100644
--- a/lib/str_array.h
+++ b/lib/str_array.h
@@ -112,4 +112,24 @@ inline static int _gnutls_str_array_append(gnutls_str_array_t * head,
return 0;
}
+inline static int _gnutls_str_array_append_idna(gnutls_str_array_t * head,
+ const char *name, size_t size)
+{
+ int ret;
+ gnutls_datum_t ahost;
+
+ /* convert the provided hostname to ACE-Labels domain. */
+ ret = gnutls_idna_map(name, size, &ahost, 0);
+ if (ret < 0) {
+ _gnutls_debug_log("unable to convert hostname %s to IDNA format\n", name);
+ /* insert the raw name */
+ return _gnutls_str_array_append(head, name, size);
+ }
+
+ ret = _gnutls_str_array_append(head, (char*)ahost.data, ahost.size);
+ gnutls_free(ahost.data);
+
+ return ret;
+}
+
#endif
diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c
index 01966b14d1..26c93d2c1d 100644
--- a/lib/tls13/certificate_verify.c
+++ b/lib/tls13/certificate_verify.c
@@ -113,7 +113,7 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
/* 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);
+ cert_type = get_certificate_type(session, GNUTLS_CTYPE_PEERS);
/* Verify the signature */
ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);
diff --git a/lib/verify-tofu.c b/lib/verify-tofu.c
index 310778629b..66a583ded7 100644
--- a/lib/verify-tofu.c
+++ b/lib/verify-tofu.c
@@ -77,11 +77,11 @@ struct gnutls_tdb_int default_tdb = {
* @cert: The raw (der) data of the certificate
* @flags: should be 0.
*
- * This function will try to verify the provided (raw or DER-encoded) certificate
- * using a list of stored public keys. The @service field if non-NULL should
- * be a port number.
+ * This function will try to verify a raw public-key or a public-key provided via
+ * a raw (DER-encoded) certificate using a list of stored public keys.
+ * The @service field if non-NULL should be a port number.
*
- * The @retrieve variable if non-null specifies a custom backend for
+ * The @db_name variable if non-null specifies a custom backend for
* the retrieval of entries. If it is NULL then the
* default file backend will be used. In POSIX-like systems the
* file backend uses the $HOME/.gnutls/known_hosts file.
@@ -93,10 +93,12 @@ struct gnutls_tdb_int default_tdb = {
* the given key is found, and 0 if it was found. The storage
* function should return 0 on success.
*
+ * As of GnuTLS 3.6.6 this function also verifies raw public keys.
+ *
* Returns: If no associated public key is found
* then %GNUTLS_E_NO_CERTIFICATE_FOUND will be returned. If a key
* is found but does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
- * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned,
* or a negative error value on other errors.
*
* Since: 3.0.13
@@ -110,14 +112,11 @@ gnutls_verify_stored_pubkey(const char *db_name,
const gnutls_datum_t * cert,
unsigned int flags)
{
- gnutls_datum_t pubkey = { NULL, 0 };
+ gnutls_datum_t pubkey = { NULL, 0 }; // Holds the pubkey in subjectPublicKeyInfo format (DER encoded)
int ret;
char local_file[MAX_FILENAME];
+ bool need_free;
- if (cert_type != GNUTLS_CRT_X509)
- return
- gnutls_assert_val
- (GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
if (db_name == NULL && tdb == NULL) {
ret = find_config_file(local_file, sizeof(local_file));
@@ -129,18 +128,38 @@ gnutls_verify_stored_pubkey(const char *db_name,
if (tdb == NULL)
tdb = &default_tdb;
- ret = x509_raw_crt_to_raw_pubkey(cert, &pubkey);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
+ /* Import the public key depending on the provided certificate type */
+ switch (cert_type) {
+ case GNUTLS_CRT_X509:
+ /* Extract the pubkey from the cert. This function does a malloc
+ * deep down the call chain. We are responsible for freeing. */
+ ret = _gnutls_x509_raw_crt_to_raw_pubkey(cert, &pubkey);
+
+ if (ret < 0) {
+ _gnutls_free_datum(&pubkey);
+ return gnutls_assert_val(ret);
+ }
+
+ need_free = true;
+ break;
+ case GNUTLS_CRT_RAWPK:
+ pubkey.data = cert->data;
+ pubkey.size = cert->size;
+ need_free = false;
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
}
+ // Verify our pubkey against the database
ret = tdb->verify(db_name, host, service, &pubkey);
if (ret < 0 && ret != GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
ret = gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
- cleanup:
- gnutls_free(pubkey.data);
+ if (need_free) {
+ _gnutls_free_datum(&pubkey);
+ }
+
return ret;
}
@@ -203,7 +222,7 @@ static int parse_commitment_line(char *line,
/* hash and hex encode */
ret =
- _gnutls_hash_fast((gnutls_digest_algorithm_t)hash_algo->id,
+ _gnutls_hash_fast((gnutls_digest_algorithm_t)hash_algo->id,
skey->data, skey->size, phash);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -301,7 +320,7 @@ static int parse_line(char *line,
return 0;
}
-/* Returns the base64 key if found
+/* Returns the base64 key if found
*/
static int verify_pubkey(const char *file,
const char *host, const char *service,
@@ -460,11 +479,11 @@ int store_commitment(const char *db_name, const char *host,
* @expiration: The expiration time (use 0 to disable expiration)
* @flags: should be 0.
*
- * This function will store the provided (raw or DER-encoded) certificate to
- * the list of stored public keys. The key will be considered valid until
- * the provided expiration time.
+ * This function will store a raw public-key or a public-key provided via
+ * a raw (DER-encoded) certificate to the list of stored public keys. The key
+ * will be considered valid until the provided expiration time.
*
- * The @store variable if non-null specifies a custom backend for
+ * The @tdb variable if non-null specifies a custom backend for
* the storage of entries. If it is NULL then the
* default file backend will be used.
*
@@ -475,6 +494,8 @@ int store_commitment(const char *db_name, const char *host,
* time in seconds since the epoch (0 for no expiration), and a base64
* encoding of the raw (DER) public key information (SPKI) of the peer.
*
+ * As of GnuTLS 3.6.6 this function also accepts raw public keys.
+ *
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
@@ -489,14 +510,11 @@ gnutls_store_pubkey(const char *db_name,
const gnutls_datum_t * cert,
time_t expiration, unsigned int flags)
{
- gnutls_datum_t pubkey = { NULL, 0 };
+ gnutls_datum_t pubkey = { NULL, 0 }; // Holds the pubkey in subjectPublicKeyInfo format (DER encoded)
int ret;
char local_file[MAX_FILENAME];
+ bool need_free;
- if (cert_type != GNUTLS_CRT_X509)
- return
- gnutls_assert_val
- (GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
if (db_name == NULL && tdb == NULL) {
ret =
@@ -517,22 +535,38 @@ gnutls_store_pubkey(const char *db_name,
if (tdb == NULL)
tdb = &default_tdb;
- ret = x509_raw_crt_to_raw_pubkey(cert, &pubkey);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
+ /* Import the public key depending on the provided certificate type */
+ switch (cert_type) {
+ case GNUTLS_CRT_X509:
+ /* Extract the pubkey from the cert. This function does a malloc
+ * deep down the call chain. We are responsible for freeing. */
+ ret = _gnutls_x509_raw_crt_to_raw_pubkey(cert, &pubkey);
+
+ if (ret < 0) {
+ _gnutls_free_datum(&pubkey);
+ return gnutls_assert_val(ret);
+ }
+
+ need_free = true;
+ break;
+ case GNUTLS_CRT_RAWPK:
+ pubkey.data = cert->data;
+ pubkey.size = cert->size;
+ need_free = false;
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
}
_gnutls_debug_log("Configuration file: %s\n", db_name);
tdb->store(db_name, host, service, expiration, &pubkey);
- ret = 0;
-
- cleanup:
- gnutls_free(pubkey.data);
+ if (need_free) {
+ _gnutls_free_datum(&pubkey);
+ }
- return ret;
+ return GNUTLS_E_SUCCESS;
}
/**
@@ -546,11 +580,11 @@ gnutls_store_pubkey(const char *db_name,
* @expiration: The expiration time (use 0 to disable expiration)
* @flags: should be 0 or %GNUTLS_SCOMMIT_FLAG_ALLOW_BROKEN.
*
- * This function will store the provided hash commitment to
+ * This function will store the provided hash commitment to
* the list of stored public keys. The key with the given
* hash will be considered valid until the provided expiration time.
*
- * The @store variable if non-null specifies a custom backend for
+ * The @tdb variable if non-null specifies a custom backend for
* the storage of entries. If it is NULL then the
* default file backend will be used.
*
@@ -604,12 +638,10 @@ gnutls_store_commitment(const char *db_name,
_gnutls_debug_log("Configuration file: %s\n", db_name);
- tdb->cstore(db_name, host, service, expiration,
+ tdb->cstore(db_name, host, service, expiration,
(gnutls_digest_algorithm_t)me->id, hash);
- ret = 0;
-
- return ret;
+ return 0;
}
#define CONFIG_FILE "known_hosts"
diff --git a/lib/x509.h b/lib/x509.h
index 859824056a..67eb957d78 100644
--- a/lib/x509.h
+++ b/lib/x509.h
@@ -21,7 +21,7 @@
*/
#include <libtasn1.h>
-#include <gnutls/abstract.h>
+
int _gnutls_x509_cert_verify_peers(gnutls_session_t session,
gnutls_typed_vdata_st * data,
@@ -35,6 +35,7 @@ int _gnutls_x509_cert_verify_peers(gnutls_session_t session,
#define PEM_CRL_SEP "-----BEGIN X509 CRL"
+
int _gnutls_x509_raw_privkey_to_gkey(gnutls_privkey_t * privkey,
const gnutls_datum_t * raw_key,
gnutls_x509_crt_fmt_t type);
diff --git a/lib/x509/common.c b/lib/x509/common.c
index 060c2aede1..9ce4275229 100644
--- a/lib/x509/common.c
+++ b/lib/x509/common.c
@@ -1653,7 +1653,7 @@ int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt,
}
/* Converts an X.509 certificate to subjectPublicKeyInfo */
-int x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert,
+int _gnutls_x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert,
gnutls_datum_t * rpubkey)
{
gnutls_x509_crt_t crt = NULL;
diff --git a/lib/x509/common.h b/lib/x509/common.h
index 2ff979380f..878da42045 100644
--- a/lib/x509/common.h
+++ b/lib/x509/common.h
@@ -260,7 +260,7 @@ int _gnutls_copy_string(gnutls_datum_t* str, uint8_t *out, size_t *out_size);
int _gnutls_copy_data(gnutls_datum_t* str, uint8_t *out, size_t *out_size);
int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out);
-int x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert,
+int _gnutls_x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert,
gnutls_datum_t * rpubkey);
int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt,