mirror of https://github.com/ARMmbed/mbed-os.git
397 lines
12 KiB
C
397 lines
12 KiB
C
/*
|
|
* Copyright (c) 2015 ARM Limited. All rights reserved.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "mbedtls/sha256.h"
|
|
#include "mbedtls/error.h"
|
|
#include "mbedtls/ssl_cookie.h"
|
|
#include "mbedtls/entropy_poll.h"
|
|
#include "ns_trace.h"
|
|
#include "nsdynmemLIB.h"
|
|
#include "coap_connection_handler.h"
|
|
#include "coap_security_handler.h"
|
|
#include "randLIB.h"
|
|
#include "mbedtls/ssl_ciphersuites.h"
|
|
|
|
|
|
const static int PSK_SUITES[] = {
|
|
MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8,
|
|
0
|
|
};
|
|
|
|
|
|
static void set_timer( void *sec_obj, uint32_t int_ms, uint32_t fin_ms );
|
|
static int get_timer( void *sec_obj );
|
|
|
|
int entropy_poll( void *data, unsigned char *output, size_t len, size_t *olen );
|
|
//Point these back to M2MConnectionHandler!!!
|
|
int f_send( void *ctx, const unsigned char *buf, size_t len );
|
|
int f_recv(void *ctx, unsigned char *buf, size_t len);
|
|
|
|
static int coap_security_handler_init(thread_security_t *sec){
|
|
const char *pers = "dtls_client";
|
|
mbedtls_ssl_init( &sec->_ssl );
|
|
mbedtls_ssl_config_init( &sec->_conf );
|
|
mbedtls_ctr_drbg_init( &sec->_ctr_drbg );
|
|
mbedtls_entropy_init( &sec->_entropy );
|
|
|
|
memset(&sec->_cookie, 0, sizeof(simple_cookie_t));
|
|
memset(&sec->_keyblk, 0, sizeof(key_block_t));
|
|
|
|
sec->_is_started = false;
|
|
|
|
if( mbedtls_entropy_add_source( &sec->_entropy, entropy_poll, NULL,
|
|
128, 0 ) < 0 ){
|
|
return -1;
|
|
}
|
|
|
|
if( ( mbedtls_ctr_drbg_seed( &sec->_ctr_drbg, mbedtls_entropy_func, &sec->_entropy,
|
|
(const unsigned char *) pers,
|
|
strlen( pers ) ) ) != 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void coap_security_handler_reset(thread_security_t *sec){
|
|
mbedtls_entropy_free( &sec->_entropy );
|
|
mbedtls_ctr_drbg_free( &sec->_ctr_drbg );
|
|
mbedtls_ssl_config_free(&sec->_conf);
|
|
mbedtls_ssl_free(&sec->_ssl);
|
|
}
|
|
|
|
|
|
thread_security_t *thread_security_create(int8_t socket_id, int8_t timer_id, uint8_t *address_ptr, uint16_t port,
|
|
send_cb *send_cb,
|
|
receive_cb *receive_cb,
|
|
start_timer_cb *start_timer_cb,
|
|
timer_status_cb *timer_status_cb)
|
|
{
|
|
if( !address_ptr || send_cb == NULL || receive_cb == NULL || start_timer_cb == NULL || timer_status_cb == NULL){
|
|
return NULL;
|
|
}
|
|
thread_security_t *this = ns_dyn_mem_alloc(sizeof(thread_security_t));
|
|
if( !this ){
|
|
return NULL;
|
|
}
|
|
if( -1 == coap_security_handler_init(this) ){
|
|
ns_dyn_mem_free(this);
|
|
return NULL;
|
|
}
|
|
this->_remote_port = port;
|
|
memcpy(this->_remote_address, address_ptr, 16);
|
|
this->_socket_id = socket_id;
|
|
this->_timer_id = timer_id;
|
|
this->_send_cb = send_cb;
|
|
this->_receive_cb = receive_cb;
|
|
this->_start_timer_cb = start_timer_cb;
|
|
this->_timer_status_cb = timer_status_cb;
|
|
|
|
return this;
|
|
}
|
|
|
|
void thread_security_destroy(thread_security_t *sec){
|
|
if( sec ){
|
|
coap_security_handler_reset(sec);
|
|
ns_dyn_mem_free(sec);
|
|
sec = NULL;
|
|
}
|
|
}
|
|
|
|
/**** Random number functions ****/
|
|
|
|
/**
|
|
* Get a random array of bytes.
|
|
* Called back by mbedtls when it wants to fill a buffer with random data
|
|
* Must return 0 on success.
|
|
*/
|
|
static int get_random(void *ctx, unsigned char *buf, size_t len)
|
|
{
|
|
static int initialised = 0;
|
|
uint32_t i;
|
|
|
|
(void)ctx; /* No context */
|
|
|
|
if (!initialised) {
|
|
randLIB_seed_random();
|
|
initialised = 1;
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
buf[i] = (uint8_t)randLIB_get_8bit();
|
|
}
|
|
return 0; /* Success */
|
|
}
|
|
|
|
/**** Cookie functions ****/
|
|
static int simple_cookie_write(void *ctx,
|
|
unsigned char **p, unsigned char *end,
|
|
const unsigned char *info, size_t ilen)
|
|
{
|
|
//TODO: As per RFC 6347 cookie must be stateless. This is not the case in here!
|
|
//This should be fixed if we see that dos attack would be an issue.
|
|
//this is proposed solution in RFC: Cookie = HMAC(Secret, Client-IP, Client-Parameters)
|
|
//Secret is generated here and oftenly changed to prevent statistical attack
|
|
simple_cookie_t *p_cookie = (simple_cookie_t *)ctx;
|
|
|
|
/* Not using additional info */
|
|
(void)info;
|
|
(void)ilen;
|
|
|
|
if ((size_t)(end - *p) < COOKIE_SIMPLE_LEN) {
|
|
return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/* Use a simple random number of length COOKIE_SIMPLE_LEN bytes */
|
|
get_random(NULL, p_cookie->value, COOKIE_SIMPLE_LEN);
|
|
memcpy(*p, p_cookie->value, COOKIE_SIMPLE_LEN);
|
|
p_cookie->len = COOKIE_SIMPLE_LEN;
|
|
*p += COOKIE_SIMPLE_LEN;
|
|
return 0;
|
|
}
|
|
|
|
static int simple_cookie_check(void *ctx,
|
|
const unsigned char *cookie, size_t clen,
|
|
const unsigned char *info, size_t ilen)
|
|
{
|
|
simple_cookie_t *p_cookie = (simple_cookie_t *)ctx;
|
|
|
|
/* Not using additional info */
|
|
(void)info;
|
|
(void)ilen;
|
|
|
|
if ((p_cookie->len == 0) ||
|
|
(clen != p_cookie->len) ||
|
|
(memcmp(cookie, p_cookie->value, p_cookie->len) != 0)) {
|
|
return -1; /* This is what it is in mbedtls... */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**** Key export function ****/
|
|
|
|
static int export_key_block(void *ctx,
|
|
const unsigned char *kb, const unsigned char *mk,
|
|
size_t maclen, size_t keylen, size_t ivlen)
|
|
{
|
|
key_block_t *p_key_block = (key_block_t *)ctx;
|
|
|
|
/* Not using master key */
|
|
(void)mk;
|
|
|
|
/* Sanity check MAC and key lengths */
|
|
if ((maclen != 0) || (((2 * keylen) + (2 * ivlen)) != KEY_BLOCK_LEN)) {
|
|
return -1; /* Something seriously wrong! */
|
|
}
|
|
|
|
/* Copy the key block we are using */
|
|
/* No need to skip over MAC keys, MAC len must be 0 if we are here */
|
|
memcpy(p_key_block->value, kb /* + (2 * maclen)*/, (2 * keylen) + (2 * ivlen));
|
|
return 0;
|
|
}
|
|
|
|
/**** Timer functions ****/
|
|
|
|
/**
|
|
* Set timer function.
|
|
* Called back by mbedtls when it wants to set a timer.
|
|
* Accepts an intermediate and a final delay in milliseconds
|
|
* If the final delay is 0, cancels the running timer.
|
|
* TODO - might be better to use an event timer in conjunction with
|
|
* CoAP tasklet
|
|
*/
|
|
static void set_timer(void *sec_obj, uint32_t int_ms, uint32_t fin_ms)
|
|
{
|
|
thread_security_t *sec = (thread_security_t *)sec_obj;
|
|
if( sec->_start_timer_cb ){
|
|
sec->_start_timer_cb( sec->_timer_id, int_ms, fin_ms);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get timer function.
|
|
* Called back by mbedtls when it wants to set a timer.
|
|
* Returns the state of the current timer
|
|
* TODO - might be better to use an event timer in conjunction with
|
|
* CoAP tasklet
|
|
*/
|
|
static int get_timer(void *sec_obj)
|
|
{
|
|
thread_security_t *sec = (thread_security_t *)sec_obj;
|
|
if( sec->_timer_status_cb ){
|
|
return sec->_timer_status_cb(sec->_timer_id);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int coap_security_handler_connect(thread_security_t *sec, bool is_server, const unsigned char *pw, uint8_t len){
|
|
|
|
if( !sec ){
|
|
return -1;
|
|
}
|
|
int endpoint = MBEDTLS_SSL_IS_CLIENT;
|
|
if( is_server ){
|
|
endpoint = MBEDTLS_SSL_IS_SERVER;
|
|
}
|
|
|
|
if( ( mbedtls_ssl_config_defaults( &sec->_conf,
|
|
endpoint,
|
|
MBEDTLS_SSL_TRANSPORT_DATAGRAM, 0 ) ) != 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
mbedtls_ssl_conf_handshake_timeout( &sec->_conf, 60000, 61000 );
|
|
mbedtls_ssl_conf_rng( &sec->_conf, mbedtls_ctr_drbg_random, &sec->_ctr_drbg );
|
|
//mbedtls_ssl_conf_rng(&sec->_conf, get_random, NULL);
|
|
|
|
if( ( mbedtls_ssl_setup( &sec->_ssl, &sec->_conf ) ) != 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 );
|
|
|
|
//TODO: Figure out better way!!!
|
|
//Password should never be stored in multiple places!!!
|
|
if( is_server && len > 0){
|
|
memcpy(sec->_pw, pw, len);
|
|
sec->_pw_len = len;
|
|
}
|
|
if( mbedtls_ssl_set_hs_ecjpake_password(&sec->_ssl, pw, len) != 0 ){
|
|
return -1;
|
|
}
|
|
mbedtls_ssl_conf_ciphersuites(&sec->_conf, PSK_SUITES);
|
|
|
|
mbedtls_ssl_conf_dtls_cookies(&sec->_conf, simple_cookie_write,
|
|
simple_cookie_check,
|
|
&sec->_cookie);
|
|
|
|
mbedtls_ssl_conf_export_keys_cb(&sec->_conf,
|
|
export_key_block,
|
|
&sec->_keyblk);
|
|
|
|
sec->_is_started = true;
|
|
|
|
int ret = mbedtls_ssl_handshake_step( &sec->_ssl );
|
|
if( ret == 0 ){
|
|
ret = mbedtls_ssl_handshake_step( &sec->_ssl );
|
|
if( is_server && 0 == ret){
|
|
ret = coap_security_handler_continue_connecting( sec );
|
|
}
|
|
}
|
|
|
|
if( ret >= 0){
|
|
ret = 1;
|
|
}else
|
|
{
|
|
ret = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int coap_security_handler_continue_connecting(thread_security_t *sec){
|
|
int ret=-1;
|
|
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( mbedtls_ssl_set_hs_ecjpake_password(&sec->_ssl, sec->_pw, sec->_pw_len) != 0 ){
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
if(MBEDTLS_ERR_SSL_TIMEOUT == ret ||
|
|
MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO == ret ||
|
|
MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE == ret ||
|
|
MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST == ret ||
|
|
MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE == ret ||
|
|
MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE == ret ||
|
|
MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC == ret ||
|
|
MBEDTLS_ERR_SSL_BAD_HS_FINISHED == ret) {
|
|
return MBEDTLS_ERR_SSL_TIMEOUT;
|
|
}
|
|
if( sec->_ssl.state == MBEDTLS_SSL_HANDSHAKE_OVER ){
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int coap_security_handler_send_message(thread_security_t *sec, unsigned char *message, size_t len){
|
|
int ret=-1;
|
|
|
|
if( sec ){
|
|
do ret = mbedtls_ssl_write( &sec->_ssl, (unsigned char *) message, len );
|
|
while( ret == MBEDTLS_ERR_SSL_WANT_READ ||
|
|
ret == MBEDTLS_ERR_SSL_WANT_WRITE );
|
|
}
|
|
|
|
return ret; //bytes written
|
|
}
|
|
|
|
int thread_security_send_close_alert(thread_security_t *sec)
|
|
{
|
|
if( !sec ){
|
|
return -1;
|
|
}
|
|
return mbedtls_ssl_close_notify(&sec->_ssl);
|
|
|
|
coap_security_handler_reset(sec);
|
|
coap_security_handler_init(sec);
|
|
}
|
|
|
|
int coap_security_handler_read(thread_security_t *sec, unsigned char* buffer, size_t len){
|
|
int ret=-1;
|
|
|
|
if( sec && buffer ){
|
|
memset( buffer, 0, len );
|
|
do ret = mbedtls_ssl_read( &sec->_ssl, buffer, len );
|
|
while( ret == MBEDTLS_ERR_SSL_WANT_READ ||
|
|
ret == MBEDTLS_ERR_SSL_WANT_WRITE );
|
|
}
|
|
|
|
return ret; //bytes read
|
|
}
|
|
|
|
int f_send( void *ctx, const unsigned char *buf, size_t len){
|
|
thread_security_t *sec = (thread_security_t *)ctx;
|
|
return sec->_send_cb(sec->_socket_id, sec->_remote_address, sec->_remote_port, buf, len);
|
|
}
|
|
|
|
int f_recv(void *ctx, unsigned char *buf, size_t len){
|
|
thread_security_t *sec = (thread_security_t *)ctx;
|
|
return sec->_receive_cb(sec->_socket_id, buf, len);
|
|
}
|
|
|
|
int entropy_poll( void *ctx, unsigned char *output, size_t len,
|
|
size_t *olen )
|
|
{
|
|
//TODO: change to more secure random
|
|
srand(time(NULL));
|
|
char *c = (char*)ns_dyn_mem_temporary_alloc(len);
|
|
if( !c ){
|
|
return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
|
|
}
|
|
memset(c, 0, len);
|
|
for(uint16_t i=0; i < len; i++){
|
|
c[i] = rand() % 256;
|
|
}
|
|
memmove(output, c, len);
|
|
*olen = len;
|
|
|
|
ns_dyn_mem_free(c);
|
|
return( 0 );
|
|
}
|