diff options
Diffstat (limited to 'lib/ext')
-rw-r--r-- | lib/ext/Makefile.am | 2 | ||||
-rw-r--r-- | lib/ext/ecc.c | 302 | ||||
-rw-r--r-- | lib/ext/ecc.h | 37 |
3 files changed, 340 insertions, 1 deletions
diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am index a949912125..90dcb072ab 100644 --- a/lib/ext/Makefile.am +++ b/lib/ext/Makefile.am @@ -39,4 +39,4 @@ libgnutls_ext_la_SOURCES = max_record.c cert_type.c \ server_name.c signature.c safe_renegotiation.c \ max_record.h cert_type.h server_name.h srp.h \ session_ticket.h signature.h safe_renegotiation.h \ - session_ticket.c srp.c + session_ticket.c srp.c ecc.c diff --git a/lib/ext/ecc.c b/lib/ext/ecc.c new file mode 100644 index 0000000000..11c00716e4 --- /dev/null +++ b/lib/ext/ecc.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2010 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the code the Certificate Type TLS extension. + * This extension is currently gnutls specific. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" +#include <ext/ecc.h> +#include <gnutls_state.h> +#include <gnutls_num.h> + +/* Maps record size to numbers according to the + * extensions draft. + */ + +static int _gnutls_supported_ecc_recv_params (gnutls_session_t session, + const opaque * data, + size_t data_size); +static int _gnutls_supported_ecc_send_params (gnutls_session_t session, + gnutls_buffer_st * extdata); + +static int _gnutls_supported_ecc_pf_recv_params (gnutls_session_t session, + const opaque * data, + size_t data_size); +static int _gnutls_supported_ecc_pf_send_params (gnutls_session_t session, + gnutls_buffer_st * extdata); + +extension_entry_st ext_mod_supported_ecc = { + .name = "SUPPORTED ECC", + .type = GNUTLS_EXTENSION_SUPPORTED_ECC, + .parse_type = GNUTLS_EXT_TLS, + + .recv_func = _gnutls_supported_ecc_recv_params, + .send_func = _gnutls_supported_ecc_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL +}; + +extension_entry_st ext_mod_supported_ecc_pf = { + .name = "SUPPORTED ECC POINT FORMATS", + .type = GNUTLS_EXTENSION_SUPPORTED_ECC_PF, + .parse_type = GNUTLS_EXT_TLS, + + .recv_func = _gnutls_supported_ecc_pf_recv_params, + .send_func = _gnutls_supported_ecc_pf_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL +}; + +/* + * In case of a server: if a SUPPORTED_ECC extension type is received then it stores + * into the session security parameters the new value. The server may use gnutls_session_certificate_type_get(), + * to access it. + * + * In case of a client: If a supported_eccs have been specified then we send the extension. + * + */ +static int +_gnutls_supported_ecc_recv_params (gnutls_session_t session, + const opaque * data, size_t _data_size) +{ + int new_type = -1, ret, i; + ssize_t data_size = _data_size; + uint16_t len; + const opaque* p = data; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + /* A client shouldn't receive this extension */ + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + } + else + { /* SERVER SIDE - we must check if the sent supported ecc type is the right one + */ + if (data_size < 2) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + + DECR_LEN (data_size, 2); + len = _gnutls_read_uint16(p); + p += 2; + + DECR_LEN (data_size, len); + + for (i = 0; i < len; i+=2) + { + new_type = _gnutls_num_to_ecc (_gnutls_read_uint16(&p[i])); + if (new_type < 0) + continue; + + /* Check if we support this supported_ecc */ + if ((ret = + _gnutls_session_supports_ecc_curve (session, new_type)) < 0) + { + gnutls_assert (); + continue; + } + else + break; + /* new_type is ok */ + } + + if (new_type < 0) + { + gnutls_assert (); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + if ((ret = + _gnutls_session_supports_ecc_curve (session, new_type)) < 0) + { + /* The peer has requested unsupported ecc + * types. Instead of failing, procceed normally. + * (the ciphersuite selection would fail, or a + * non certificate ciphersuite will be selected). + */ + return gnutls_assert_val(0); + } + + _gnutls_session_ecc_curve_set (session, new_type); + } + + return 0; +} + + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_supported_ecc_send_params (gnutls_session_t session, gnutls_buffer_st* extdata) +{ + unsigned len, i; + int ret; + uint16_t p; + + /* this extension is only being sent on client side */ + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + + if (session->internals.priorities.supported_ecc.algorithms > 0) + { + + len = session->internals.priorities.supported_ecc.algorithms; + + /* this is a vector! + */ + ret = _gnutls_buffer_append_prefix(extdata, 16, len*2); + if (ret < 0) + return gnutls_assert_val(ret); + + for (i = 0; i < len; i++) + { + p = + _gnutls_ecc_to_num (session->internals.priorities. + supported_ecc.priority[i]); + ret = _gnutls_buffer_append_prefix(extdata, 16, p); + if (ret < 0) + return gnutls_assert_val(ret); + } + return (len + 1)*2; + } + + } + + return 0; +} + +/* + * In case of a server: if a SUPPORTED_ECC extension type is received then it stores + * into the session security parameters the new value. The server may use gnutls_session_certificate_type_get(), + * to access it. + * + * In case of a client: If a supported_eccs have been specified then we send the extension. + * + */ +static int +_gnutls_supported_ecc_pf_recv_params (gnutls_session_t session, + const opaque * data, size_t _data_size) +{ +int len, i; +int uncompressed = 0; +int data_size = _data_size; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + if (data_size < 1) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + + len = data[0]; + DECR_LEN (data_size, len+1); + + for (i=0;i<len;i++) + if (data[1+i] == 0) /* uncompressed */ + uncompressed = 1; + + if (uncompressed == 0) + return gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM); + } + else + { + /* only sanity check here. We only support uncompressed points + * and a client must support it thus nothing to check. + */ + if (_data_size < 1) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_supported_ecc_pf_send_params (gnutls_session_t session, gnutls_buffer_st* extdata) +{ + const opaque p[2] = {0x01, 0x00}; /* only support uncompressed point format */ + + /* this extension is only being sent on client and server side */ + _gnutls_buffer_append_data(extdata, p, 2); + return 2; +} + +/* Maps numbers to record sizes according to the + * extensions draft. + */ +int +_gnutls_num_to_ecc (int num) +{ + switch (num) + { + case 23: + /* sec256r1 */ + return GNUTLS_ECC_CURVE_SECP256R1; + case 24: + return GNUTLS_ECC_CURVE_SECP384R1; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } +} + +/* Maps record size to numbers according to the + * extensions draft. + */ +int +_gnutls_ecc_to_num (ecc_curve_t supported_ecc) +{ + switch (supported_ecc) + { + case GNUTLS_ECC_CURVE_SECP256R1: + return 23; + case GNUTLS_ECC_CURVE_SECP384R1: + return 24; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } +} + +/* Returns 0 if the given ECC curve is allowed in the current + * session. A negative error value is returned otherwise. + */ +int +_gnutls_session_supports_ecc_curve (gnutls_session_t session, int ecc_type) +{ + unsigned i; + + if (session->internals.priorities.supported_ecc.algorithms > 0) + { + for (i = 0; i < session->internals.priorities.supported_ecc.algorithms; i++) + { + if (session->internals.priorities.supported_ecc.priority[i] == ecc_type) + return 0; + } + } + + return GNUTLS_E_ECC_NO_SUPPORTED_CURVES; +} diff --git a/lib/ext/ecc.h b/lib/ext/ecc.h new file mode 100644 index 0000000000..745a000815 --- /dev/null +++ b/lib/ext/ecc.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ +#ifndef EXT_ECC_H +#define EXT_ECC_H + +#include <gnutls_extensions.h> + +extern extension_entry_st ext_mod_supported_ecc; +extern extension_entry_st ext_mod_supported_ecc_pf; + +int _gnutls_num_to_ecc (int num); +int _gnutls_ecc_to_num (ecc_curve_t); +int +_gnutls_session_supports_ecc_curve (gnutls_session_t session, int ecc_type); + +#endif |