summaryrefslogtreecommitdiff
path: root/lib/tls13/certificate_verify.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tls13/certificate_verify.c')
-rw-r--r--lib/tls13/certificate_verify.c91
1 files changed, 89 insertions, 2 deletions
diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c
index e702236797..8f92d4074b 100644
--- a/lib/tls13/certificate_verify.c
+++ b/lib/tls13/certificate_verify.c
@@ -27,6 +27,7 @@
#include "ext/signature.h"
#include "algorithms.h"
#include "tls13-sig.h"
+#include "mbuffers.h"
#include "tls13/certificate_verify.h"
#define SRV_CTX "TLS 1.3, server CertificateVerify"
@@ -47,6 +48,11 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
memset(&peer_cert, 0, sizeof(peer_cert));
+ /* this message is only expected if we have received
+ * a certificate message */
+ if (!(session->internals.hsk_flags & HSK_CRT_VRFY_EXPECTED))
+ return 0;
+
cred = (gnutls_certificate_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
if (unlikely(cred == NULL))
@@ -120,7 +126,88 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
return ret;
}
-int _gnutls13_send_certificate_verify(gnutls_session_t session)
+int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again)
{
- return 0;
+ int ret;
+ gnutls_pcert_st *apr_cert_list;
+ gnutls_privkey_t apr_pkey;
+ int apr_cert_list_length;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ gnutls_datum_t sig = {NULL, 0};
+ gnutls_sign_algorithm_t algo;
+ const gnutls_sign_entry_st *se;
+
+ if (again == 0) {
+ _gnutls_buffer_init(&buf);
+
+ ret = _gnutls_get_selected_cert(session, &apr_cert_list,
+ &apr_cert_list_length, &apr_pkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (apr_cert_list_length == 0) {
+ if (session->security_parameters.entity == GNUTLS_SERVER) {
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ } else {
+ /* if we didn't get a cert request there will not be any */
+ if (!(session->internals.hsk_flags & HSK_CRT_SENT))
+ return 0;
+ else
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ }
+
+ algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0);
+ if (algo == GNUTLS_SIGN_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY);
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ gnutls_sign_algorithm_set_server(session, algo);
+ else
+ gnutls_sign_algorithm_set_client(session, algo);
+
+ se = _gnutls_sign_to_entry(algo);
+
+ ret = _gnutls13_handshake_sign_data(session, &apr_cert_list[0], apr_pkey, &srv_ctx, &sig, se);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_buffer_append_data(&buf, se->aid.id, 2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data_prefix(&buf, 16, sig.data, sig.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bufel = _gnutls_handshake_alloc(session, buf.length);
+ if (bufel == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ _mbuffer_set_udata_size(bufel, 0);
+ ret = _mbuffer_append_data(bufel, buf.data, buf.length);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_buffer_clear(&buf);
+ gnutls_free(sig.data);
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY);
+
+ cleanup:
+ gnutls_free(sig.data);
+ _gnutls_buffer_clear(&buf);
+ _mbuffer_xfree(&bufel);
+ return ret;
}