Skip to content

Commit

Permalink
Support for certificate mode (ARMmbed#72)
Browse files Browse the repository at this point in the history
* Support for certificate mode

Added API for setting certificates for client mode.

* coap-service unit test updates

New unit tests for coap service API functions
  • Loading branch information
Tero Heinonen authored Sep 5, 2017
1 parent d65b6b0 commit 2d622e0
Show file tree
Hide file tree
Showing 14 changed files with 367 additions and 187 deletions.
19 changes: 19 additions & 0 deletions coap-service/coap_service_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,25 @@ extern int8_t coap_service_set_handshake_timeout(int8_t service_id, uint32_t min
*- 0 For success
*/
extern int8_t coap_service_set_duplicate_message_buffer(int8_t service_id, uint8_t size);

/**
* \brief Set DTLS certificates
*
* Set DTLS certificates.
*
* \param service_id Id number of the current service.
* \param root_cert Pointer to CA certificate
* \param root_cert_len CA certificate length
* \param own_cert pointer to own certificate
* \param own_cert_len length of own certificate
* \param priv_key pointer to private key
* \param priv_key_len length of private key
*
* \return -1 For failure
*- 0 For success
*/

extern int8_t coap_service_certificate_set(int8_t service_id, const unsigned char *root_cert, uint16_t root_cert_len, const unsigned char *own_cert, uint16_t own_cert_len, const unsigned char *priv_key, uint8_t priv_key_len);
#ifdef __cplusplus
}
#endif
Expand Down
127 changes: 61 additions & 66 deletions source/coap_connection_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static int8_t virtual_socket_id_allocate()
return new_virtual_socket_id;
}

static secure_session_t *secure_session_create(internal_socket_t *parent, const uint8_t *address_ptr, uint16_t port)
static secure_session_t *secure_session_create(internal_socket_t *parent, const uint8_t *address_ptr, uint16_t port, SecureConnectionMode secure_mode)
{
if(!address_ptr){
return NULL;
Expand Down Expand Up @@ -184,7 +184,7 @@ static secure_session_t *secure_session_create(internal_socket_t *parent, const
memcpy(this->remote_host.address, address_ptr, 16);
this->remote_host.identifier = port;

this->sec_handler = coap_security_create(parent->socket, this->timer.id, this, ECJPAKE,
this->sec_handler = coap_security_create(parent->socket, this->timer.id, this, secure_mode,
&secure_session_sendto, &secure_session_recvfrom, &start_timer, &timer_status);
if( !this->sec_handler ){
ns_dyn_mem_free(this);
Expand Down Expand Up @@ -595,31 +595,34 @@ static void secure_recv_sckt_msg(void *cb_res)

// Create session
if (!session) {
session = secure_session_create(sock, src_address.address, src_address.identifier);
}
if (!session) {
tr_err("secure_recv_sckt_msg session creation failed - OOM");
return;
}
// Record the destination. We are not strict on local address - all
// session_find calls match only on remote address and port. But we
// record the last-used destination address to use it as the source of
// outgoing packets.
memcpy(session->local_address, dst_address, 16);
session->last_contact_time = coap_service_get_internal_timer_ticks();
// Start handshake
if (!coap_security_handler_is_started(session->sec_handler) ){
uint8_t *pw = ns_dyn_mem_alloc(64);
uint8_t pw_len;
if( sock->parent->_get_password_cb && 0 == sock->parent->_get_password_cb(sock->socket, src_address.address, src_address.identifier, pw, &pw_len)){
//TODO: get_password_cb should support certs and PSK also
coap_security_keys_t keys;
keys._priv = pw;
keys._priv_len = pw_len;
coap_security_handler_connect_non_blocking(session->sec_handler, true, DTLS, keys, sock->timeout_min, sock->timeout_max);
coap_security_keys_t keys;
memset(&keys, 0, sizeof(coap_security_keys_t));

if (sock->parent->_get_password_cb && 0 == sock->parent->_get_password_cb(sock->socket, src_address.address, src_address.identifier, &keys)) {
session = secure_session_create(sock, src_address.address, src_address.identifier, keys.mode);
if (!session) {
tr_err("secure_recv_sckt_msg session creation failed - OOM");
ns_dyn_mem_free(keys._key);
return;
}
//TODO: error handling
} else {
return;
}

// Record the destination. We are not strict on local address - all
// session_find calls match only on remote address and port. But we
// record the last-used destination address to use it as the source of
// outgoing packets.
memcpy(session->local_address, dst_address, 16);

session->last_contact_time = coap_service_get_internal_timer_ticks();
// Start handshake
if (!coap_security_handler_is_started(session->sec_handler)) {
coap_security_handler_connect_non_blocking(session->sec_handler, true, DTLS, keys, sock->timeout_min, sock->timeout_max);
ns_dyn_mem_free(keys._key);

}
ns_dyn_mem_free(pw);
} else {
//Continue handshake
if (session->session_state == SECURE_SESSION_HANDSHAKE_ONGOING) {
Expand Down Expand Up @@ -703,34 +706,29 @@ int coap_connection_handler_virtual_recv(coap_conn_handler_t *handler, uint8_t a
}

if (handler->socket->is_secure) {
coap_security_keys_t keys;
memset(&keys, 0, sizeof(coap_security_keys_t));

secure_session_t *session = secure_session_find(sock, address, port);
if (!session) {
session = secure_session_create(sock, address, port);
}
if (!session) {
tr_err("coap_connection_handler_virtual_recv session creation failed - OOM");
return -1;
}

session->last_contact_time = coap_service_get_internal_timer_ticks();

if (!coap_security_handler_is_started(session->sec_handler)) {
uint8_t *pw = ns_dyn_mem_alloc(64);
uint8_t pw_len;
if (sock->parent->_get_password_cb && 0 == sock->parent->_get_password_cb(sock->socket, address, port, pw, &pw_len)) {
//TODO: get_password_cb should support certs and PSK also
coap_security_keys_t keys;
keys._priv = pw;
keys._priv_len = pw_len;
if (sock->parent->_get_password_cb && 0 == sock->parent->_get_password_cb(sock->socket, address, port, &keys)) {
session = secure_session_create(sock, address, port, keys.mode);
if (!session) {
tr_err("coap_connection_handler_virtual_recv session creation failed - OOM");
ns_dyn_mem_free(keys._key);
return -1;
}
coap_security_handler_connect_non_blocking(session->sec_handler, true, DTLS, keys, handler->socket->timeout_min, handler->socket->timeout_max);
//TODO: error handling
ns_dyn_mem_free(pw);
ns_dyn_mem_free(keys._key);
return 0;
} else {
ns_dyn_mem_free(pw);
return -1;
}
} else {
}

session->last_contact_time = coap_service_get_internal_timer_ticks();

if (coap_security_handler_is_started(session->sec_handler)) {
if (session->session_state == SECURE_SESSION_HANDSHAKE_ONGOING) {
int ret = coap_security_handler_continue_connecting(session->sec_handler);
if(ret == 0){
Expand Down Expand Up @@ -813,6 +811,9 @@ void connection_handler_destroy(coap_conn_handler_t *handler, bool multicast_gro
if (multicast_group_leave) {
coap_multicast_group_join_or_leave(handler->socket->socket, SOCKET_IPV6_LEAVE_GROUP, handler->socket_interface_selection);
}
if (handler->security_keys) {
ns_dyn_mem_free(handler->security_keys);
}
int_socket_delete(handler->socket);
ns_dyn_mem_free(handler);
}
Expand Down Expand Up @@ -873,30 +874,24 @@ int coap_connection_handler_send_data(coap_conn_handler_t *handler, const ns_add
handler->socket->bypass_link_sec = bypass_link_sec;
secure_session_t *session = secure_session_find(handler->socket, dest_addr->address, dest_addr->identifier);
if (!session) {
session = secure_session_create(handler->socket, dest_addr->address, dest_addr->identifier);
if (!session) {
return -1;
}
session->last_contact_time = coap_service_get_internal_timer_ticks();
uint8_t *pw = ns_dyn_mem_alloc(64);
if (!pw) {
//todo: free secure session?
coap_security_keys_t security_material;
memset(&security_material, 0, sizeof(coap_security_keys_t));

if (!handler->_get_password_cb || 0 != handler->_get_password_cb(handler->socket->socket, (uint8_t*)dest_addr->address, dest_addr->identifier, &security_material)) {
return -1;
}
uint8_t pw_len;
if (handler->_get_password_cb && 0 == handler->_get_password_cb(handler->socket->socket, (uint8_t*)dest_addr->address, dest_addr->identifier, pw, &pw_len)) {
//TODO: get_password_cb should support certs and PSK also
coap_security_keys_t keys;
keys._priv = pw;
keys._priv_len = pw_len;
coap_security_handler_connect_non_blocking(session->sec_handler, false, DTLS, keys, handler->socket->timeout_min, handler->socket->timeout_max);
ns_dyn_mem_free(pw);
return -2;
} else {
//free secure session?
ns_dyn_mem_free(pw);

session = secure_session_create(handler->socket, dest_addr->address, dest_addr->identifier, security_material.mode);
if (!session) {
ns_dyn_mem_free(security_material._key);
return -1;
}
session->last_contact_time = coap_service_get_internal_timer_ticks();

coap_security_handler_connect_non_blocking(session->sec_handler, false, DTLS, security_material, handler->socket->timeout_min, handler->socket->timeout_max);
ns_dyn_mem_free(security_material._key);
return -2;

} else if (session->session_state == SECURE_SESSION_OK) {
if (coap_security_handler_send_message(session->sec_handler, data_ptr, data_len ) > 0 ) {
session->last_contact_time = coap_service_get_internal_timer_ticks();
Expand Down
112 changes: 21 additions & 91 deletions source/coap_security_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ static const int PSK_SUITES[] = {
0
};

#define TRACE_GROUP "CsSh"

static void set_timer( void *sec_obj, uint32_t int_ms, uint32_t fin_ms );
static int get_timer( void *sec_obj );
static int coap_security_handler_configure_keys( coap_security_t *sec, coap_security_keys_t keys );

int entropy_poll( void *data, unsigned char *output, size_t len, size_t *olen );

Expand Down Expand Up @@ -276,26 +276,30 @@ static int export_key_block(void *ctx,
}
#endif

int coap_security_handler_configure_keys( coap_security_t *sec, coap_security_keys_t keys )
static int coap_security_handler_configure_keys (coap_security_t *sec, coap_security_keys_t keys, bool is_server)
{
int ret = -1;
switch( sec->_conn_mode ){
case Certificate:{
case CERTIFICATE:{
#if defined(MBEDTLS_X509_CRT_PARSE_C)
if( mbedtls_x509_crt_parse( &sec->_cacert, keys._server_cert,
keys._server_cert_len ) < 0 ){
if( mbedtls_x509_crt_parse( &sec->_cacert, keys._ca_cert,
keys._ca_cert_len ) < 0 ){
break;
}
if( mbedtls_x509_crt_parse( &sec->_owncert, keys._pub_cert_or_identifier,
keys._pub_len ) < 0 ){
if( mbedtls_x509_crt_parse( &sec->_owncert, keys._own_cert,
keys._own_cert_len ) < 0 ){
break;
}
if( mbedtls_pk_parse_key(&sec->_pkey, keys._priv, keys._priv_len, NULL, 0) < 0){
if( mbedtls_pk_parse_key(&sec->_pkey, keys._priv_key, keys._priv_key_len, NULL, 0) < 0){
break;
}
//TODO: If needed in server mode, this won't work
if( 0 != mbedtls_ssl_conf_own_cert(&sec->_conf, &sec->_owncert, &sec->_pkey) ){
break;

if (!is_server) {
if (0 != mbedtls_ssl_conf_own_cert(&sec->_conf, &sec->_owncert, &sec->_pkey)) {
break;
}
} else {
//TODO: add server certi
}
//TODO: use MBEDTLS_SSL_VERIFY_REQUIRED instead of optional
mbedtls_ssl_conf_authmode( &sec->_conf, MBEDTLS_SSL_VERIFY_OPTIONAL );
Expand All @@ -306,7 +310,7 @@ int coap_security_handler_configure_keys( coap_security_t *sec, coap_security_ke
}
case PSK: {
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
if( 0 != mbedtls_ssl_conf_psk(&sec->_conf, keys._priv, keys._priv_len, keys._pub_cert_or_identifier, keys._pub_len) ){
if( 0 != mbedtls_ssl_conf_psk(&sec->_conf, keys._priv_key, keys._priv_key_len, keys._own_cert, keys._own_cert_len) ){
break;
}
mbedtls_ssl_conf_ciphersuites(&sec->_conf, PSK_SUITES);
Expand All @@ -316,7 +320,7 @@ int coap_security_handler_configure_keys( coap_security_t *sec, coap_security_ke
}
case ECJPAKE: {
#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
if( mbedtls_ssl_set_hs_ecjpake_password(&sec->_ssl, keys._priv, keys._priv_len) != 0 ){
if( mbedtls_ssl_set_hs_ecjpake_password(&sec->_ssl, keys._key, keys._key_len) != 0 ){
return -1;
}
mbedtls_ssl_conf_ciphersuites(&sec->_conf, ECJPAKE_SUITES);
Expand All @@ -336,79 +340,6 @@ int coap_security_handler_configure_keys( coap_security_t *sec, coap_security_ke
return ret;
}

int coap_security_handler_connect(coap_security_t *sec, bool is_server, SecureSocketMode sock_mode, coap_security_keys_t keys){
int ret = -1;

if( !sec ){
return ret;
}
sec->_is_blocking = true;

int endpoint = MBEDTLS_SSL_IS_CLIENT;
if( is_server ){
endpoint = MBEDTLS_SSL_IS_SERVER;
}

int mode = MBEDTLS_SSL_TRANSPORT_DATAGRAM;
if( sock_mode == TLS ){
mode = MBEDTLS_SSL_TRANSPORT_STREAM;
}

if( ( mbedtls_ssl_config_defaults( &sec->_conf,
endpoint,
mode, 0 ) ) != 0 )
{
return -1;
}

mbedtls_ssl_set_bio( &sec->_ssl, sec,
f_send, f_recv, NULL );

mbedtls_ssl_set_timer_cb( &sec->_ssl, sec, set_timer,
get_timer );

if( coap_security_handler_configure_keys( sec, keys ) != 0 ){
return -1;
}

#ifdef MBEDTLS_SSL_SRV_C
mbedtls_ssl_conf_dtls_cookies(&sec->_conf, simple_cookie_write,
simple_cookie_check,
&sec->_cookie);
#endif

sec->_is_started = true;

do {
ret = mbedtls_ssl_handshake_step( &sec->_ssl );
if( ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED ){ //cookie check failed
if( is_server ){
mbedtls_ssl_session_reset(&sec->_ssl);
#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
if( mbedtls_ssl_set_hs_ecjpake_password(&sec->_ssl, keys._priv, keys._priv_len) != 0 ){
return -1;
}
#endif
ret = MBEDTLS_ERR_SSL_WANT_READ; //needed to keep doing
}else{
ret = -1;
}
}
}while( ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE );

if( ret != 0){
ret = -1;
}else{
if( mbedtls_ssl_get_verify_result( &sec->_ssl ) != 0 )
{
ret = -1;
}
}

return ret;
}

int coap_security_handler_connect_non_blocking(coap_security_t *sec, bool is_server, SecureSocketMode sock_mode, coap_security_keys_t keys, uint32_t timeout_min, uint32_t timeout_max)
{

Expand Down Expand Up @@ -457,13 +388,13 @@ int coap_security_handler_connect_non_blocking(coap_security_t *sec, bool is_ser
#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
//TODO: Figure out better way!!!
//Password should never be stored in multiple places!!!
if( is_server && keys._priv_len > 0){
memcpy(sec->_pw, keys._priv, keys._priv_len);
sec->_pw_len = keys._priv_len;
if ((sec->_conn_mode == ECJPAKE) && is_server && keys._key_len > 0){
memcpy(sec->_pw, keys._key, keys._key_len);
sec->_pw_len = keys._key_len;
}
#endif

if( coap_security_handler_configure_keys( sec, keys ) != 0 ){
if (coap_security_handler_configure_keys(sec, keys, is_server) != 0) {
return -1;
}

Expand Down Expand Up @@ -499,7 +430,6 @@ int coap_security_handler_continue_connecting(coap_security_t *sec){

while( ret != MBEDTLS_ERR_SSL_WANT_READ ){
ret = mbedtls_ssl_handshake_step( &sec->_ssl );

if( MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED == ret){
mbedtls_ssl_session_reset(&sec->_ssl);
#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
Expand Down
Loading

0 comments on commit 2d622e0

Please sign in to comment.