A super trimmed down TLS stack, GPL licensed
Dependents: MiniTLS-HTTPS-Example
MiniTLS - A super trimmed down TLS/SSL Library for embedded devices Author: Donatien Garnier Copyright (C) 2013-2014 AppNearMe Ltd
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Diff: tls/tls_handshake.c
- Revision:
- 2:527a66d0a1a9
- Child:
- 3:eb324ffffd2b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tls/tls_handshake.c Mon Jun 09 14:57:54 2014 +0000 @@ -0,0 +1,1671 @@ +/* +MiniTLS - A super trimmed down TLS/SSL Library for embedded devices +Author: Donatien Garnier +Copyright (C) 2013-2014 AppNearMe Ltd + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*//** + * \file tls_handshake.c + * \copyright Copyright (c) AppNearMe Ltd 2013 + * \author Donatien Garnier + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "tls_handshake.c" +#endif + +#include "core/fwk.h" + +#include "tls_handshake.h" +#include "tls_record.h" +#include "tls_socket.h" +#include "tls_alert.h" +#include "crypto/crypto_ecc.h" +#include "crypto/crypto_sha1.h" +#include "crypto/crypto_sha256.h" +#include "crypto/crypto_hmac_sha1.h" +#include "crypto/crypto_hmac_sha256.h" + +#define HANDSHAKE_MAX_PREMASTER_KEY_SIZE 64 +#define HANDSHAKE_RSA_PREMASTER_KEY_SIZE 48 + +typedef enum __handshake_type +{ + hello_request = (0), client_hello = (1), server_hello = (2), + certificate = (11), server_key_exchange = (12), + certificate_request = (13), server_hello_done = (14), + certificate_verify = (15), client_key_exchange = (16), + finished = (20), unknown = (255) +} handshake_type_t; + +static minitls_err_t send_hello_client(tls_handshake_t* handshake, buffer_t* buffer); +static minitls_err_t parse_hello_server(tls_handshake_t* handshake, buffer_t* buffer, bool* resumed); +static minitls_err_t parse_server_certificate(tls_handshake_t* handshake, buffer_t* buffer); +static minitls_err_t parse_server_key_exchange(tls_handshake_t* handshake, buffer_t* buffer); +static minitls_err_t parse_client_certificate_request(tls_handshake_t* handshake, buffer_t* buffer); +static minitls_err_t parse_hello_done_server(tls_handshake_t* handshake, buffer_t* buffer); +static minitls_err_t send_client_certificate(tls_handshake_t* handshake, buffer_t* buffer); +static minitls_err_t send_client_key_exchange(tls_handshake_t* handshake, buffer_t* buffer); +static minitls_err_t send_finished(tls_handshake_t* handshake, buffer_t* buffer); +static minitls_err_t parse_finished(tls_handshake_t* handshake, buffer_t* buffer); + +static minitls_err_t generate_record_keys(tls_handshake_t* handshake); + +//Receive stuff +static minitls_err_t parse_message(tls_handshake_t* handshake, buffer_t* buffer, handshake_type_t* message_type); + +//Send stuff +static minitls_err_t prepare_message(tls_handshake_t* handshake, handshake_type_t message_type, buffer_t* buffer, size_t size); +static minitls_err_t prepare_message_adjust_size(tls_handshake_t* handshake, buffer_t* buffer, size_t size); +static minitls_err_t send_message(tls_handshake_t* handshake, buffer_t* buffer); + +//Error handlers +static void handle_ret(tls_handshake_t* handshake, minitls_err_t ret); + +/* + P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + + HMAC_hash(secret, A(2) + seed) + + HMAC_hash(secret, A(3) + seed) + ... + + PRF(secret, label, seed) = P_hash(secret, label + seed) + */ + +//PRF computation +static minitls_err_t prf_compute(const uint8_t* secret, size_t secret_size, + const char* label, + const uint8_t* seed1, size_t seed1_size, + const uint8_t* seed2, size_t seed2_size, + uint8_t* out, size_t out_size); + +//Record-level change cipher spec request +static minitls_err_t change_cipher_spec_request(tls_handshake_t* handshake, buffer_t* buffer); + +minitls_err_t tls_handshake_init(tls_handshake_t* handshake, tls_socket_t* socket) +{ + handshake->tls_socket = socket; + + tls_handshake_clean(handshake); + //memset(handshake->master_key, 0, sizeof(HANDSHAKE_MASTER_KEY_SIZE)); + + //Reset state machine + handshake->state = TLS_HANDSHAKE_INIT; + + return MINITLS_OK; +} + + +minitls_err_t tls_handshake_start(tls_handshake_t* handshake) +{ + minitls_err_t ret = send_hello_client(handshake, &handshake->tls_socket->record.buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + handshake->state = TLS_HANDSHAKE_HELLO_SENT; + return MINITLS_OK; +} + +minitls_err_t tls_handshake_process(tls_handshake_t* handshake, buffer_t* buffer) +{ + //Parse buffer + DBG("Parsing message"); + handshake_type_t type; + minitls_err_t ret = parse_message(handshake, buffer, &type); + if(ret) + { + ERR("Parsing error"); + handle_ret(handshake, ret); + return ret; + } + + bool resumed = false; + + DBG("Message type %d", type); + + switch(type) + { + case hello_request: + if( (handshake->state == TLS_HANDSHAKE_INIT) || (handshake->state == TLS_HANDSHAKE_HELLO_SENT) ) + { + DBG("Hello request"); + } + else + { + ret = MINITLS_ERR_NOT_IMPLEMENTED; + handle_ret(handshake, ret); + return ret; + } + break; + case server_hello: + if(handshake->state == TLS_HANDSHAKE_HELLO_SENT) + { + DBG("Server hello"); + //Parse hello + handshake->state = TLS_HANDSHAKE_HELLO_RECEIVED; + ret = parse_hello_server(handshake, buffer, &resumed); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + if(resumed) + { + handshake->state = TLS_HANDSHAKE_HELLO_RECEIVED_SESSION_RESUMPTION; + + //Complete handshake + DBG("Expanding master key"); + + ret = generate_record_keys(handshake); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + DBG("Change cipher spec request"); + + ret = change_cipher_spec_request(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + DBG("Changing cipher spec (TX)"); + //Now we can change cipher spec -- TX way + ret = tls_record_change_cipher_spec(&handshake->tls_socket->record, true); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + DBG("Send finished"); + //Now send finished message + ret = send_finished(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + handshake->state = TLS_HANDSHAKE_FINISHED_SENT; + } + } + else + { + ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + handle_ret(handshake, ret); + return ret; + } + break; + case certificate: + if(handshake->state == TLS_HANDSHAKE_HELLO_RECEIVED) + { + DBG("Server certificate"); + //Parse certificate + handshake->state = TLS_HANDSHAKE_CERTIFICATE_RECEIVED; + ret = parse_server_certificate(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + } + else + { + ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + handle_ret(handshake, ret); + return ret; + } + break; + case server_key_exchange: + //With other types of authentication methods (PSK, anon) we could receive this message after the server's hello; however we don't support these cipher suites + if(CRYPTO_ECC && (handshake->state == TLS_HANDSHAKE_CERTIFICATE_RECEIVED)) + { + DBG("Server key exchange"); + //Parse server key + handshake->state = TLS_HANDSHAKE_SERVER_KEY_EXCHANGE_RECEIVED; + ret = parse_server_key_exchange(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + } + else + { + ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + handle_ret(handshake, ret); + return ret; + } + break; + case certificate_request: + //Obvi, we could receive this right after certificate - but we don't support this + if(handshake->state == TLS_HANDSHAKE_CERTIFICATE_RECEIVED) + { + WARN("Not supported"); + ret = MINITLS_ERR_NOT_IMPLEMENTED; + handle_ret(handshake, ret); + return ret; + } + else if( (CRYPTO_ECC && (handshake->state == TLS_HANDSHAKE_SERVER_KEY_EXCHANGE_RECEIVED)) || (CRYPTO_RSA && (handshake->state == TLS_HANDSHAKE_CERTIFICATE_RECEIVED)) ) + { + DBG("Client certificate request"); + //Parse certificate request + handshake->state = TLS_HANDSHAKE_CERTIFICATE_REQUEST_RECEIVED; + handshake->certificate_requested = true; + ret = parse_client_certificate_request(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + } + else + { + ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + handle_ret(handshake, ret); + return ret; + } + break; + case server_hello_done: + if( (CRYPTO_ECC && (handshake->state == TLS_HANDSHAKE_SERVER_KEY_EXCHANGE_RECEIVED)) || (CRYPTO_RSA && (handshake->state == TLS_HANDSHAKE_CERTIFICATE_RECEIVED)) + || (handshake->state == TLS_HANDSHAKE_CERTIFICATE_REQUEST_RECEIVED) ) + { + DBG("Hello done"); + //Parse hello done + handshake->state = TLS_HANDSHAKE_HELLO_DONE_RECEIVED; + ret = parse_hello_done_server(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + //Now reply + if(handshake->certificate_requested) + { + DBG("Send client certificate"); + //Send client certificate if requested + ret = send_client_certificate(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + handshake->state = TLS_HANDSHAKE_CERTIFICATE_SENT; + } + + DBG("Send client key exchange"); + //Send client key exchange + ret = send_client_key_exchange(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + handshake->state = TLS_HANDSHAKE_CLIENT_KEY_EXCHANGE_SENT; + + //Send certificate verify -- not for now + /* + ret = send_client_certificate_verify(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + handshake->state = TLS_HANDSHAKE_CERTIFICATE_VERIFY_SENT; + */ + + DBG("Expanding master key"); + + ret = generate_record_keys(handshake); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + DBG("Change cipher spec request"); + + ret = change_cipher_spec_request(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + DBG("Changing cipher spec (TX)"); + //Now we can change cipher spec -- TX way + ret = tls_record_change_cipher_spec(&handshake->tls_socket->record, true); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + DBG("Send finished"); + //Now send finished message + ret = send_finished(handshake, buffer); + if(ret) + { + handle_ret(handshake, ret); + return ret; + } + + handshake->state = TLS_HANDSHAKE_FINISHED_SENT; + } + else + { + ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + handle_ret(handshake, ret); + return ret; + } + break; + case finished: + if(handshake->state == TLS_HANDSHAKE_FINISHED_SENT) + { + //First check that the ChangeCipherSpec message has been sent by the other party (and that we are therefore secure in both ways) + DBG("Receive finished"); + if(!tls_record_is_secure(&handshake->tls_socket->record)) + { + ERR("Record is insecure"); + ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + handle_ret(handshake, ret); + return ret; + } + + //Parse finished message and check integrity of exchange + handshake->state = TLS_HANDSHAKE_FINISHED_RECEIVED; + ret = parse_finished(handshake, buffer); + if(ret) + { + ERR("Integrity check failed"); + handle_ret(handshake, ret); + return ret; + } + + DBG("Handshake done!"); + handshake->state = TLS_HANDSHAKE_DONE; + } + else + { + ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + handle_ret(handshake, ret); + return ret; + } + break; + default: + ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + handle_ret(handshake, ret); + return ret; + } + + return MINITLS_OK; +} + +void tls_handshake_clean(tls_handshake_t* handshake) +{ + memset(handshake->random_client, 0, HANDSHAKE_RANDOM_SIZE); + memset(handshake->random_client, 0, HANDSHAKE_RANDOM_SIZE); + + handshake->certificate_requested = false; + //memset(&handshake->ecc_curve, 0, sizeof(crypto_ecc_curve_t*)); + //handshake->ecc_curve_type = 0; +#if CRYPTO_ECC + handshake->key_exchange.ecc.curve = NULL; + memset(&handshake->key_exchange.ecc.server_key, 0, sizeof(crypto_ecc_public_key_t)); + memset(&handshake->key_exchange.ecc.client_key, 0, sizeof(crypto_ecc_private_key_t)); +#endif +#if CRYPTO_RSA + // +#endif + +#if MINITLS_CFG_PROTOCOL_TLS_1_2 + crypto_sha256_init(&handshake->hash.sha256); +#endif +#if (MINITLS_CFG_PROTOCOL_TLS_1_1 || MINITLS_CFG_PROTOCOL_TLS_1_0 || MINITLS_CFG_PROTOCOL_SSL_3) + crypto_sha1_init(&handshake->hash.md5_sha1.sha1); + crypto_md5_init(&handshake->hash.md5_sha1.md5); +#endif + +} + +bool tls_handshake_is_done(tls_handshake_t* handshake) +{ + if(handshake->state == TLS_HANDSHAKE_DONE) + { + return true; + } + return false; +} + +#define HELLO_CLIENT_SIZE 41 +#define HELLO_CLIENT_EXTENSIONS_HEADER_SIZE 2 +#define HELLO_CLIENT_MAX_FRAGMENT_EXTENSION_SIZE 5 +#define HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE 8 +#define HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE 8 + +#if CRYPTO_ECC +const uint8_t TLS_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_CBC_SHA[] = { 0xC0, 0x09 }; +#elif CRYPTO_RSA +const uint8_t TLS_CIPHERSUITE_RSA_WITH_AES_128_CBC_SHA[] = { 0x00, 0x2F }; +#endif + +#define TLS_COMPRESSION_METHOD_NULL 0x00 +#define TLS_EXTENSION_TYPE_MAX_FRAGMENT_LENGTH 1 +#define TLS_EXTENSION_TYPE_SIGNATURE_ALGORITHM 13 +#define TLS_EXTENSION_TYPE_SUPPORTED_ECC_CURVES 10 + +typedef enum __hash_algorithm { + none = (0), md5 = (1), sha1 = (2), sha224 = (3), sha256 = (4), sha384 = (5), + sha512 = (6) +} hash_algorithm_t; + +typedef enum __signature_algorithm { anonymous = (0), rsa = (1), dsa = (2), ecdsa = (3) +} signature_algorithm_t; + +minitls_err_t send_hello_client(tls_handshake_t* handshake, buffer_t* buffer) +{ + //Form hello packet + /* + struct { + ProtocolVersion client_version; + Random random; + SessionID session_id; + CipherSuite cipher_suites<2..2^16-2>; + CompressionMethod compression_methods<1..2^8-1>; + select (extensions_present) { + case false: + struct {}; + case true: + Extension extensions<0..2^16-1>; + }; + } ClientHello; + */ + minitls_err_t ret; + + //Write header + if(handshake->tls_socket->record.max_fragment_size < TLS_DEFAULT_MAX_FRAGMENT_SIZE) + { + ret = prepare_message(handshake, client_hello, buffer, HELLO_CLIENT_SIZE + handshake->tls_socket->session.session_id_length + + HELLO_CLIENT_EXTENSIONS_HEADER_SIZE +#if CRYPTO_ECC + + HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE + HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE +#endif + + HELLO_CLIENT_MAX_FRAGMENT_EXTENSION_SIZE); + } + else + { + ret = prepare_message(handshake, client_hello, buffer, HELLO_CLIENT_SIZE + handshake->tls_socket->session.session_id_length + + HELLO_CLIENT_EXTENSIONS_HEADER_SIZE +#if CRYPTO_ECC + + HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE + HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE +#endif + ); + } + if(ret) + { + return ret; + } + + //Generate 28-bytes long random + crypto_prng_get(handshake->tls_socket->minitls->prng, handshake->random_client, HANDSHAKE_RANDOM_SIZE); + + //Write most recent protocol version supported +#if MINITLS_CFG_PROTOCOL_TLS_1_2 + buffer_nu8_write(buffer, TLS_1_2_VERSION_MAJOR); + buffer_nu8_write(buffer, TLS_1_2_VERSION_MINOR); +#elif MINITLS_CFG_PROTOCOL_TLS_1_1 + buffer_nu8_write(buffer, TLS_1_1_VERSION_MAJOR); + buffer_nu8_write(buffer, TLS_1_1_VERSION_MINOR); +#elif MINITLS_CFG_PROTOCOL_TLS_1_0 + buffer_nu8_write(buffer, TLS_1_0_VERSION_MAJOR); + buffer_nu8_write(buffer, TLS_1_0_VERSION_MINOR); +#elif MINITLS_CFG_PROTOCOL_SSL_3 + buffer_nu8_write(buffer, SSL_3_VERSION_MAJOR); + buffer_nu8_write(buffer, SSL_3_VERSION_MINOR); +#else +#error No SSL/TLS protocol version enabled +#endif + + //Write random + //buffer_nu32_write(buffer, 0); //UNIX Timestamp -- not mandatory, write 0s -- Don't do this, just use a 32bytes long random + buffer_nbytes_write(buffer, handshake->random_client, HANDSHAKE_RANDOM_SIZE); + //Write session ID + buffer_nu8_write(buffer, handshake->tls_socket->session.session_id_length); + buffer_nbytes_write(buffer, handshake->tls_socket->session.session_id, handshake->tls_socket->session.session_id_length); + //Write the one cipher suite we support + buffer_nu16_write(buffer, 2); //2 bytes long +#if CRYPTO_ECC + buffer_nbytes_write(buffer, TLS_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 2); +#elif CRYPTO_RSA + buffer_nbytes_write(buffer, TLS_CIPHERSUITE_RSA_WITH_AES_128_CBC_SHA, 2); +#else +#error You must enable one cipher suite +#endif + //Write the one compression method we support (null) + buffer_nu8_write(buffer, 1); //1 byte long + buffer_nu8_write(buffer, TLS_COMPRESSION_METHOD_NULL); + + if(handshake->tls_socket->record.max_fragment_size < TLS_DEFAULT_MAX_FRAGMENT_SIZE) + { + //3 extensions (ECC) / 1 (RSA) + buffer_nu16_write(buffer, +#if CRYPTO_ECC + + HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE + HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE +#endif + + HELLO_CLIENT_MAX_FRAGMENT_EXTENSION_SIZE); + } + else + { + //2 (ECC) extensions / 0 (RSA) + buffer_nu16_write(buffer, 0 +#if CRYPTO_ECC + + HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE + HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE +#endif + ); + } +#if CRYPTO_ECC + //Use signature algorithm extension + buffer_nu16_write(buffer, TLS_EXTENSION_TYPE_SIGNATURE_ALGORITHM); + /* + enum { + none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), + sha512(6), (255) + } HashAlgorithm; + + enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } + SignatureAlgorithm; + + struct { + HashAlgorithm hash; + SignatureAlgorithm signature; + } SignatureAndHashAlgorithm; + + SignatureAndHashAlgorithm + supported_signature_algorithms<2..2^16-2>; + */ + + buffer_nu16_write(buffer, 4); //2 bytes of list length, 2 bytes of hash and signature algo + buffer_nu16_write(buffer, 2); //2 bytes of hash and signature algo + + //Supported algo is ECDSA-SHA1 + buffer_nu8_write(buffer, sha1); + buffer_nu8_write(buffer, ecdsa); + + //Use supported elliptic curves extensions + + buffer_nu16_write(buffer, TLS_EXTENSION_TYPE_SUPPORTED_ECC_CURVES); + /* + struct { + NamedCurve elliptic_curve_list<1..2^16-1> + } EllipticCurveList; + */ + buffer_nu16_write(buffer, 4); //2 bytes of list length, 2 bytes of curve type + buffer_nu16_write(buffer, 2); //2 bytes of curve type + buffer_nu16_write(buffer, secp192r1); //The one curve we support -- TODO clean this up +#endif + + if(handshake->tls_socket->record.max_fragment_size < TLS_DEFAULT_MAX_FRAGMENT_SIZE) + { + //Use MaxFragmentLength extension to decrease the maximum fragment length (RFC4366) + buffer_nu16_write(buffer, TLS_EXTENSION_TYPE_MAX_FRAGMENT_LENGTH); + + buffer_nu16_write(buffer, 1); //1 byte of data + /* + enum{ + 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255) + } MaxFragmentLength; + */ + if( handshake->tls_socket->record.max_fragment_size >= 4096 ) + { + buffer_nu8_write(buffer, 4); + } + else if( handshake->tls_socket->record.max_fragment_size >= 2048 ) + { + buffer_nu8_write(buffer, 3); + } + else if( handshake->tls_socket->record.max_fragment_size >= 1024 ) + { + buffer_nu8_write(buffer, 2); + } + else if( handshake->tls_socket->record.max_fragment_size >= 512 ) + { + buffer_nu8_write(buffer, 1); + } + else + { + WARN("Fragment size is too small - using 512 bytes fragment size"); + buffer_nu8_write(buffer, 1); + } + } + + ret = send_message(handshake, buffer); + if(ret) + { + return ret; + } + + return MINITLS_OK; +} + +minitls_err_t parse_hello_server(tls_handshake_t* handshake, buffer_t* buffer, bool* resumed) +{ + //Header has already been parsed + /* + struct { + ProtocolVersion server_version; + Random random; + SessionID session_id; + CipherSuite cipher_suite; + CompressionMethod compression_method; + select (extensions_present) { + case false: + struct {}; + case true: + Extension extensions<0..2^16-1>; + }; + } ServerHello; + */ + + if( buffer_length(buffer) < 35) //protocol version + random + session_id length + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + tls_protocol_version_t version; + version.major = buffer_nu8_read(buffer); + version.minor = buffer_nu8_read(buffer); + + //Check that version is one of the supported ones + if( +#if MINITLS_CFG_PROTOCOL_TLS_1_2 + ( (version.major != TLS_1_2_VERSION_MAJOR) || (version.minor != TLS_1_2_VERSION_MINOR) ) && +#endif +#if MINITLS_CFG_PROTOCOL_TLS_1_1 + ( (version.major != TLS_1_1_VERSION_MAJOR) || (version.minor != TLS_1_1_VERSION_MINOR) ) && +#endif +#if MINITLS_CFG_PROTOCOL_TLS_1_0 + ( (version.major != TLS_1_0_VERSION_MAJOR) || (version.minor != TLS_1_0_VERSION_MINOR) ) && +#endif +#if MINITLS_CFG_PROTOCOL_SSL_3 + ( (version.major != SSL_3_VERSION_MAJOR) || (version.minor != SSL_3_VERSION_MINOR) ) && +#endif + true ) + { + ERR("Version mismatch"); + return MINITLS_ERR_PROTOCOL_VERSION; + } + + //Set record version accordingly + tls_record_set_protocol_version(&handshake->tls_socket->record, version.major, version.minor); + + //buffer_nu32_read(buffer); //UNIX Timestamp -- Don't read that, read it as part of the random + buffer_nbytes_read(buffer, handshake->random_server, HANDSHAKE_RANDOM_SIZE); + size_t server_session_id_length = buffer_nu8_read(buffer); + if( buffer_length(buffer) < server_session_id_length + 3 ) //session id + cipher suite + compression mode + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; ///Will raise a "decode error" alert + } + if(server_session_id_length > SESSION_ID_MAX_SIZE) //Buffer overflow attack + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; ///Will raise a "decode error" alert + } + + uint8_t server_session_id[SESSION_ID_MAX_SIZE]; + buffer_nbytes_read(buffer, server_session_id, server_session_id_length); + + if( (handshake->tls_socket->session.session_id_length == 0) + || (server_session_id_length != handshake->tls_socket->session.session_id_length) + || (memcmp(server_session_id, handshake->tls_socket->session.session_id, server_session_id_length) != 0)) + { + //Session ID does not match, start a new one + handshake->tls_socket->session.session_id_length = server_session_id_length; + memcpy(handshake->tls_socket->session.session_id, server_session_id, server_session_id_length); + *resumed = false; + } + else + { + //Resume session + *resumed = true; + } + + uint8_t cipher_suite[2]; + buffer_nbytes_read(buffer, cipher_suite, 2); + uint8_t compression_mode = buffer_nu8_read(buffer); + + if( +#if CRYPTO_ECC + (memcmp(cipher_suite, TLS_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 2) != 0) +#elif CRYPTO_RSA + (memcmp(cipher_suite, TLS_CIPHERSUITE_RSA_WITH_AES_128_CBC_SHA, 2) != 0) +#else +#error +#endif + || (compression_mode != TLS_COMPRESSION_METHOD_NULL) ) + { + ERR("Unsupported cipher suite / compression method"); + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; //Protocol error as we did not advertise any other cipher suite/compression method in the hello client message + } + + if( buffer_length(buffer) > 0 ) + { + //Extensions are present, TODO + DBG("Some extensions are present; ignoring them"); + } + + DBG("Hello Server OK"); + + return MINITLS_OK; +} + +minitls_err_t parse_server_certificate(tls_handshake_t* handshake, buffer_t* buffer) +{ + if( buffer_length(buffer) < 3) //Certificate list size + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + size_t cert_list_size = buffer_nu24_read(buffer); + + if( buffer_length(buffer) < cert_list_size ) //Certificate list + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + if( buffer_length(buffer) < 3) //Certificate size + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + size_t cert_size = buffer_nu24_read(buffer); + + if( (buffer_length(buffer) < cert_size) || (cert_size + 3 > cert_list_size) ) //Certificate + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + //Only parse first certificate in the chain, ignore others + if( cert_size != handshake->tls_socket->minitls->certificate->certificate_size ) + { + ERR("Certificate does not match"); + //Cannot contain certificate, return security error + return MINITLS_ERR_WRONG_CERTIFICATE; + } + + //Do a plain, stupid, comparison with the certificate we have + if( memcmp( handshake->tls_socket->minitls->certificate->certificate, buffer_current_read_position(buffer), handshake->tls_socket->minitls->certificate->certificate_size) != 0 ) + { + ERR("Certificate does not match"); + //This is not the same certificate + return MINITLS_ERR_WRONG_CERTIFICATE; + } + buffer_n_discard(buffer, handshake->tls_socket->minitls->certificate->certificate_size); + + + return MINITLS_OK; +} + +minitls_err_t parse_server_key_exchange(tls_handshake_t* handshake, buffer_t* buffer) +{ +#if CRYPTO_ECC + //For now we only support the ECDHE_ECDSA key exchange algo, so we hardcode the recovery of ephemeral key values here + /* + enum { explicit_prime (1), explicit_char2 (2), + named_curve (3), reserved(248..255) } ECCurveType; + + enum { + sect163k1 (1), sect163r1 (2), sect163r2 (3), + sect193r1 (4), sect193r2 (5), sect233k1 (6), + sect233r1 (7), sect239k1 (8), sect283k1 (9), + sect283r1 (10), sect409k1 (11), sect409r1 (12), + sect571k1 (13), sect571r1 (14), secp160k1 (15), + secp160r1 (16), secp160r2 (17), secp192k1 (18), + secp192r1 (19), secp224k1 (20), secp224r1 (21), + secp256k1 (22), secp256r1 (23), secp384r1 (24), + secp521r1 (25), + reserved (0xFE00..0xFEFF), + arbitrary_explicit_prime_curves(0xFF01), + arbitrary_explicit_char2_curves(0xFF02), + (0xFFFF) + } NamedCurve; + + struct { + ECCurveType curve_type; + select (curve_type) { + case explicit_prime: + opaque prime_p <1..2^8-1>; + ECCurve curve; + ECPoint base; + opaque order <1..2^8-1>; + opaque cofactor <1..2^8-1>; + case explicit_char2: + uint16 m; + ECBasisType basis; + select (basis) { + case ec_trinomial: + opaque k <1..2^8-1>; + case ec_pentanomial: + opaque k1 <1..2^8-1>; + opaque k2 <1..2^8-1>; + opaque k3 <1..2^8-1>; + }; + ECCurve curve; + ECPoint base; + opaque order <1..2^8-1>; + opaque cofactor <1..2^8-1>; + case named_curve: + NamedCurve namedcurve; + }; + } ECParameters; + + struct { + opaque point <1..2^8-1>; + } ECPoint; + + struct { + ECParameters curve_params; + ECPoint public; + } ServerECDHParams; + + //signed_params - TLS1.2 specific + struct { + SignatureAndHashAlgorithm algorithm; + opaque signature<0..2^16-1>; + } DigitallySigned; + + enum { ecdsa } SignatureAlgorithm; + + select (SignatureAlgorithm) { + case ecdsa: + digitally-signed struct { + opaque sha_hash[sha_size]; + }; + } Signature; + + + ServerKeyExchange.signed_params.sha_hash + SHA(ClientHello.random + ServerHello.random + + ServerKeyExchange.params); + + select (KeyExchangeAlgorithm) { + case ec_diffie_hellman: + ServerECDHParams params; + Signature signed_params; + } ServerKeyExchange; + */ + + //Save position as we will have to perform a hash starting from here + uint8_t* params_position = buffer_current_read_position(buffer); + + //Only supporting named curves as we have no means of validating other new curves + if( buffer_length(buffer) < 1 ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + uint8_t curve_desc_type = buffer_nu8_read(buffer); + if(curve_desc_type != 3) //Named curve + { + WARN("Not a named curve"); + return MINITLS_ERR_NOT_IMPLEMENTED; + } + + crypto_ecc_curve_type_t curve_type = buffer_nu16_read(buffer); + + DBG("ECC Curve %d", curve_type); + + minitls_err_t ret = crypto_ecc_curve_get(&handshake->key_exchange.ecc.curve, curve_type); + if(ret) + { + ERR("Could not get curve"); + return ret; + } + + if( buffer_length(buffer) < 1 ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + size_t point_size = buffer_nu8_read(buffer); + + if( buffer_length(buffer) < point_size ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + //Read point + ret = crypto_ecc_ansi_x963_import(&handshake->key_exchange.ecc.server_key, handshake->key_exchange.ecc.curve, buffer_current_read_position(buffer), point_size); + buffer_n_discard(buffer, point_size); + if(ret) + { + ERR("Error decoding ANSI X9.63 key"); + return ret; + } + + size_t params_size = buffer_current_read_position(buffer) - params_position; + + if( buffer_length(buffer) < 2 ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + //Check hash and signature algorithms -- this was introduced in TLS1.2 + uint8_t hash_alg = buffer_nu8_read(buffer); + uint8_t sig_alg = buffer_nu8_read(buffer); + + //We only support SHA1 / ECDSA so check that this is what the server is offering + + if( (hash_alg != sha1) || (sig_alg != ecdsa) ) + { + ERR("Unsupported hash/signature algorithms"); + } + + //Signature length is 2 bytes long + if( buffer_length(buffer) < 2 ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + size_t signature_length = buffer_nu16_read(buffer); + + //Signature length is 2 bytes long + if( buffer_length(buffer) < signature_length ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + //Compute hash + uint8_t hash_result[SHA1_SIZE]; + crypto_sha1_t sha1; + crypto_sha1_init(&sha1); + crypto_sha1_update(&sha1, handshake->random_client, HANDSHAKE_RANDOM_SIZE); + crypto_sha1_update(&sha1, handshake->random_server, HANDSHAKE_RANDOM_SIZE); + crypto_sha1_update(&sha1, params_position, params_size); + crypto_sha1_end(&sha1, hash_result); + + DBG("SHA-1 Hash:"); + DBG_BLOCK( + buffer_t hash_result_buf; + buffer_byref(&hash_result_buf, hash_result, SHA1_SIZE); + buffer_dump(&hash_result_buf); ) + + //Finally check signature + ret = crypto_ecc_dsa_check(&handshake->tls_socket->minitls->certificate->public_key.ecc, hash_result, SHA1_SIZE, buffer_current_read_position(buffer), signature_length); + buffer_n_discard(buffer, signature_length); + if(ret) + { + ERR("Signature check failed"); + return ret; + } + + DBG("Server key exchange parsed"); + + return MINITLS_OK; +#else + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; //Illegal for these cipher suites +#endif +} + +minitls_err_t parse_client_certificate_request(tls_handshake_t* handshake, buffer_t* buffer) +{ + //Ignore that, we won't offer a certificate in return anyway (or an empty one) + return MINITLS_OK; +} + +minitls_err_t parse_hello_done_server(tls_handshake_t* handshake, buffer_t* buffer) +{ + if( buffer_length(buffer) != 0 ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + return MINITLS_OK; +} + +#define CLIENT_CERTIFICATE_NOCERTS_SIZE 3 + +minitls_err_t send_client_certificate(tls_handshake_t* handshake, buffer_t* buffer) +{ + //Send a 0-length certificate structure + + minitls_err_t ret; + + //Write header + ret = prepare_message(handshake, certificate, buffer, CLIENT_CERTIFICATE_NOCERTS_SIZE); + if(ret) + { + return ret; + } + + buffer_nu24_write(buffer, 0); //empty list + + ret = send_message(handshake, buffer); + if(ret) + { + return ret; + } + + return MINITLS_OK; +} + +#define CLIENT_KEY_EXCHANGE_BASE_SIZE 0 + +minitls_err_t send_client_key_exchange(tls_handshake_t* handshake, buffer_t* buffer) +{ + minitls_err_t ret; + + //Write header + ret = prepare_message(handshake, client_key_exchange, buffer, CLIENT_KEY_EXCHANGE_BASE_SIZE); + if(ret) + { + return ret; + } + + +#if CRYPTO_ECC + //Send a 0-length certificate structure + + //Key size + DBG_BLOCK( + size_t key_size = crypto_ecc_get_key_size_for_curve(handshake->ecc_curve); + + DBG("Generating ephemeral key of size %d", key_size); + ) + + //Generate client key + ret = crypto_ecc_generate_key(&handshake->key_exchange.ecc.client_key, handshake->key_exchange.ecc.curve, handshake->tls_socket->minitls->prng); + if(ret) + { + return ret; + } + + DBG("Key generated"); + + //Skip key size + size_t point_size = 0; + buffer_nu8_write(buffer, point_size); + + DBG("Exporting key in X9.63 format"); + + //Write key + ret = crypto_ecc_ansi_x963_export(crypto_ecc_get_public_key(&handshake->key_exchange.ecc.client_key), /*handshake->ecc_curve,*/ buffer_current_write_position(buffer), buffer_space(buffer), &point_size); + if(ret) + { + return ret; + } + + buffer_n_skip(buffer, point_size); + + DBG("Adjusting size"); + + //Rewind, write position and re-adjust + size_t length = buffer_length(buffer); + buffer_set_length(buffer, length - point_size - 1); + + buffer_nu8_write(buffer, point_size); + + buffer_set_length(buffer, length); + + //Adjust message size + ret = prepare_message_adjust_size(handshake, buffer, CLIENT_KEY_EXCHANGE_BASE_SIZE + 1 + point_size); + if(ret) + { + return ret; + } + + ret = send_message(handshake, buffer); + if(ret) + { + return ret; + } +#elif CRYPTO_RSA + /* + struct { + ProtocolVersion client_version; + opaque random[46]; + } PreMasterSecret; + + client_version + The latest (newest) version supported by the client. This is + used to detect version rollback attacks. + + random + 46 securely-generated random bytes. + + struct { + public-key-encrypted PreMasterSecret pre_master_secret; + } EncryptedPreMasterSecret; + + pre_master_secret + This random value is generated by the client and is used to + generate the master secret, as specified in Section 8.1. + */ + + //Premaster key is generated first + +#else +#error +#endif + + DBG("Generating premaster key"); + + uint8_t premaster_key[HANDSHAKE_MAX_PREMASTER_KEY_SIZE]; + size_t premaster_key_size = 0; + +#if CRYPTO_ECC + //At this point we can generate the pre-master key + ret = crypto_ecc_dh_generate_shared_secret(&handshake->key_exchange.ecc.client_key, &handshake->key_exchange.ecc.server_key, premaster_key, HANDSHAKE_MAX_PREMASTER_KEY_SIZE, &premaster_key_size); + if(ret) + { + memset(premaster_key, 0, HANDSHAKE_MAX_PREMASTER_KEY_SIZE); //Don't want to leak this + return ret; + } +#elif CRYPTO_RSA + //The two first bytes should be the SSL/TLS version advertised in ClientHello (prevents version rollback attacks) +#if MINITLS_CFG_PROTOCOL_TLS_1_2 + premaster_key[0] = TLS_1_2_VERSION_MAJOR; + premaster_key[1] = TLS_1_2_VERSION_MINOR; +#elif MINITLS_CFG_PROTOCOL_TLS_1_1 + premaster_key[0] = TLS_1_1_VERSION_MAJOR; + premaster_key[1] = TLS_1_1_VERSION_MINOR; +#elif MINITLS_CFG_PROTOCOL_TLS_1_0 + premaster_key[0] = TLS_1_0_VERSION_MAJOR; + premaster_key[1] = TLS_1_0_VERSION_MINOR; +#elif MINITLS_CFG_PROTOCOL_SSL_3 + premaster_key[0] = SSL_3_VERSION_MAJOR; + premaster_key[1] = SSL_3_VERSION_MINOR; +#else +#error No SSL/TLS protocol version enabled +#endif + + //Other 46 bytes are random + crypto_prng_get(handshake->tls_socket->minitls->prng, &premaster_key[2], HANDSHAKE_RSA_PREMASTER_KEY_SIZE - 2); + premaster_key_size = HANDSHAKE_RSA_PREMASTER_KEY_SIZE; + + //Encrypt it using RSA + uint8_t encrypted_premaster_key[128]; + size_t encrypted_premaster_key_size = 0; + ret = crypto_rsa_encrypt(&handshake->tls_socket->minitls->certificate->public_key.rsa, + premaster_key, premaster_key_size, + encrypted_premaster_key, sizeof(encrypted_premaster_key), &encrypted_premaster_key_size, handshake->tls_socket->minitls->prng); + if(ret) + { + return ret; + } + + DBG("Encrypted premaster key size: %d", encrypted_premaster_key_size); + + //Now send it + buffer_nu16_write(buffer, encrypted_premaster_key_size); + buffer_nbytes_write(buffer, encrypted_premaster_key, encrypted_premaster_key_size); + + //Adjust message size + ret = prepare_message_adjust_size(handshake, buffer, CLIENT_KEY_EXCHANGE_BASE_SIZE + 2 + encrypted_premaster_key_size); + if(ret) + { + return ret; + } + + ret = send_message(handshake, buffer); + if(ret) + { + return ret; + } +#endif + + DBG_BLOCK( + DBG("Premaster key size: %d", premaster_key_size); + + buffer_t dump_buf; + DBG("Premaster key"); + buffer_byref(&dump_buf, premaster_key, premaster_key_size); + buffer_dump(&dump_buf); + + DBG("Random client"); + buffer_byref(&dump_buf, handshake->random_client, HANDSHAKE_RANDOM_SIZE); + buffer_dump(&dump_buf); + + DBG("Random server"); + buffer_byref(&dump_buf, handshake->random_server, HANDSHAKE_RANDOM_SIZE); + buffer_dump(&dump_buf); + ) + + + //Now generate the shared AES128 key + + DBG("Expanding the key"); + + /* + master_secret = PRF(pre_master_secret, "master secret", + ClientHello.random + ServerHello.random) + [0..47]; + */ + + ret = prf_compute(premaster_key, premaster_key_size, "master secret", + handshake->random_client, HANDSHAKE_RANDOM_SIZE, + handshake->random_server, HANDSHAKE_RANDOM_SIZE, + handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE); + memset(premaster_key, 0, HANDSHAKE_MAX_PREMASTER_KEY_SIZE); //Don't want to leak this + if(ret) + { + return ret; + } + + DBG_BLOCK( + DBG("Key expanded into master key"); + buffer_byref(&dump_buf, handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE); + buffer_dump(&dump_buf); + ) + + return MINITLS_OK; +} + +#define VERIFY_DATA_LENGTH 12 +#define FINISHED_SIZE 12 // 12 bytes PRF // -- no 13 //length + 12 bytes prf + +minitls_err_t send_finished(tls_handshake_t* handshake, buffer_t* buffer) +{ + minitls_err_t ret; + + //Write header + ret = prepare_message(handshake, finished, buffer, FINISHED_SIZE); + if(ret) + { + return ret; + } + + crypto_sha256_t hash; + crypto_sha256_copy(&hash, &handshake->hash.sha256); //We need to keep the global hash to check the server's finished message + + //Compute final hash + uint8_t result[SHA256_SIZE]; + crypto_sha256_end(&hash, result); + + /* + struct { + opaque verify_data[verify_data_length]; + } Finished; + + verify_data + PRF(master_secret, finished_label, Hash(handshake_messages)) + [0..verify_data_length-1]; + */ + + //buffer_nu8_write(buffer, VERIFY_DATA_LENGTH); //This is optional but anyway -- NOPE + + ret = prf_compute(handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE, "client finished", + result, SHA256_SIZE, + NULL, 0, + buffer_current_write_position(buffer), VERIFY_DATA_LENGTH); + buffer_n_skip(buffer, VERIFY_DATA_LENGTH); + if(ret) + { + return ret; + } + + ret = send_message(handshake, buffer); + if(ret) + { + return ret; + } + + return MINITLS_OK; +} + +minitls_err_t parse_finished(tls_handshake_t* handshake, buffer_t* buffer) +{ + if(buffer_length(buffer) < VERIFY_DATA_LENGTH) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } +/* + size_t length = VERIFY_DATA_LENGTH; + if(buffer_length(buffer) > VERIFY_DATA_LENGTH) + { + length = buffer_nu8_read(buffer); + } +*/ + if( (buffer_length(buffer)/*length*/ != VERIFY_DATA_LENGTH) ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + crypto_sha256_t hash; + crypto_sha256_copy(&hash, &handshake->hash.sha256); //We need to keep the global hash to check the server's finished message + + //Compute final hash + uint8_t result[SHA256_SIZE]; + crypto_sha256_end(&hash, result); + + /* + struct { + opaque verify_data[verify_data_length]; + } Finished; + + verify_data + PRF(master_secret, finished_label, Hash(handshake_messages)) + [0..verify_data_length-1]; + */ + + uint8_t prf[VERIFY_DATA_LENGTH]; + minitls_err_t ret = prf_compute(handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE, "server finished", + result, SHA256_SIZE, + NULL, 0, + prf, VERIFY_DATA_LENGTH); + if(ret) + { + return ret; + } + + + if(memcmp(prf, buffer_current_read_position(buffer), VERIFY_DATA_LENGTH) != 0) + { + ERR("PRF differs; computed PRF was:"); + + DBG_BLOCK( + buffer_t computed_prf; + buffer_byref(&computed_prf, prf, VERIFY_DATA_LENGTH); + buffer_dump(&computed_prf);) + + return MINITLS_ERR_WRONG_MAC; + } + + buffer_n_discard(buffer, VERIFY_DATA_LENGTH); + + return MINITLS_OK; +} + +minitls_err_t generate_record_keys(tls_handshake_t* handshake) +{ + //Expand master key + + /* + To generate the key material, compute + + key_block = PRF(SecurityParameters.master_secret, + "key expansion", + SecurityParameters.server_random + + SecurityParameters.client_random); + + until enough output has been generated. Then, the key_block is + partitioned as follows: + + client_write_MAC_key[SecurityParameters.mac_key_length] + server_write_MAC_key[SecurityParameters.mac_key_length] + client_write_key[SecurityParameters.enc_key_length] + server_write_key[SecurityParameters.enc_key_length] + client_write_IV[SecurityParameters.fixed_iv_length] + server_write_IV[SecurityParameters.fixed_iv_length] + */ + + /* + Cipher Type Material Size Size + ------------ ------ -------- ---- ----- + AES_128_CBC Block 16 16 16 + + MAC Algorithm mac_length mac_key_length + -------- ----------- ---------- -------------- + SHA HMAC-SHA1 20 20 + */ + + //For our cipher we don't need the initialization vectors + + DBG("Expand master key"); + + //Expand key + uint8_t key_expansion[2*TLS_HMAC_SHA1_KEY_SIZE + 2*AES_128_KEY_SIZE]; + minitls_err_t ret = prf_compute(handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE, "key expansion", + handshake->random_server, HANDSHAKE_RANDOM_SIZE, + handshake->random_client, HANDSHAKE_RANDOM_SIZE, + key_expansion, 2*TLS_HMAC_SHA1_KEY_SIZE + 2*AES_128_KEY_SIZE); + if(ret) + { + return ret; + } + + //Init cipher/mac + tls_record_set_keys(&handshake->tls_socket->record, TLS_SECURITY_TYPE_AES_128_CBC_SHA, + &key_expansion[0*TLS_HMAC_SHA1_KEY_SIZE + 0*AES_128_KEY_SIZE], + &key_expansion[1*TLS_HMAC_SHA1_KEY_SIZE + 0*AES_128_KEY_SIZE], + &key_expansion[2*TLS_HMAC_SHA1_KEY_SIZE + 0*AES_128_KEY_SIZE], + &key_expansion[2*TLS_HMAC_SHA1_KEY_SIZE + 1*AES_128_KEY_SIZE] + ); + + return MINITLS_OK; +} + +#define HANDSHAKE_HEADER_SIZE 4 + +minitls_err_t parse_message(tls_handshake_t* handshake, buffer_t* buffer, handshake_type_t* message_type) +{ + if( buffer_length(buffer) < HANDSHAKE_HEADER_SIZE ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + //Update SHA to use in FINISHED message + if(handshake->state < TLS_HANDSHAKE_FINISHED_SENT) //Don't update if it is the FINISHED message sent from the server + { + //If TLS1.2 or protocol version unknown for now + crypto_sha256_update(&handshake->hash.sha256, buffer_current_read_position(buffer), buffer_length(buffer)); + + //FIXME SSL3-TLS1.1 or protocol version unknown for now + + } + + *message_type = (uint8_t) buffer_nu8_read(buffer); + size_t size = buffer_nu24_read(buffer); + + if( buffer_length(buffer) != size ) + { + return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; + } + + return MINITLS_OK; +} + +minitls_err_t prepare_message(tls_handshake_t* handshake, handshake_type_t message_type, buffer_t* buffer, size_t size) +{ + buffer_reset(buffer); + if( buffer_size(buffer) < size + HANDSHAKE_HEADER_SIZE /* header size*/ ) + { + ERR("Buffer too small"); + return MINITLS_ERR_BUFFER_TOO_SMALL; + } + + buffer_nu8_write(buffer, (uint8_t)message_type); + buffer_nu24_write(buffer, size); + + return MINITLS_OK; +} + +minitls_err_t prepare_message_adjust_size(tls_handshake_t* handshake, buffer_t* buffer, size_t size) +{ + size_t length = buffer_length(buffer); + + buffer_reset(buffer); + if( buffer_size(buffer) < size + HANDSHAKE_HEADER_SIZE /* header size*/ ) + { + return MINITLS_ERR_BUFFER_TOO_SMALL; + } + + buffer_set_length(buffer, 1); //Skip message type + + buffer_nu24_write(buffer, size); + + buffer_set_length(buffer, length); + + return MINITLS_OK; +} + + +minitls_err_t send_message(tls_handshake_t* handshake, buffer_t* buffer) +{ + //Update SHA to use in FINISHED messages + crypto_sha256_update(&handshake->hash.sha256, buffer_current_read_position(buffer), buffer_length(buffer)); + + //DEBUG TODO: check mismatch with length + + minitls_err_t ret = tls_record_send(&handshake->tls_socket->record, TLS_HANDSHAKE, buffer); + if(ret) + { + ERR("Send returned %d", ret); + return ret; + } + return MINITLS_OK; +} + +void handle_ret(tls_handshake_t* handshake, minitls_err_t ret) +{ + ERR("Will return error %d", ret); + + //Make handshake fail + handshake->state = TLS_HANDSHAKE_FAILED; + + //Send alert to other party + switch(ret) + { + case MINITLS_ERR_WRONG_CERTIFICATE: + tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, CERTIFICATE_UNKNOWN, &handshake->tls_socket->record.buffer); + break; + case MINITLS_ERR_PROTOCOL_NON_CONFORMANT: + tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, UNEXPECTED_MESSAGE, &handshake->tls_socket->record.buffer); + break; + case MINITLS_ERR_NOT_IMPLEMENTED: + default: + tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, HANDSHAKE_FAILURE, &handshake->tls_socket->record.buffer); + break; + } +} + + +minitls_err_t prf_compute(const uint8_t* secret, size_t secret_size, + const char* label, + const uint8_t* seed1, size_t seed1_size, + const uint8_t* seed2, size_t seed2_size, + uint8_t* out, size_t out_size) +{ + //PRF TLS1.2 + //TODO add PRF SSL3-TLS1.1 + + //DBG("PRF: Secret %p [%d] - label '%s' - Seed %p [%d] - out %p [%d]", secret, secret_size, label, seed, seed_size, out, out_size); + //We are using the HMAC-SHA256 MAC (non negotiable) + + /* + P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + + HMAC_hash(secret, A(2) + seed) + + HMAC_hash(secret, A(3) + seed) + ... + + A(0) = seed + A(i) = HMAC_hash(secret, A(i-1)) + + PRF(secret, label, seed) = P_hash(secret, label + seed) + + */ + + //MAC_HMAC_SHA1_SIZE + uint8_t mac[HMAC_SHA256_SIZE]; + uint8_t A[HMAC_SHA256_SIZE]; + + //A[0] = seed + crypto_hmac_sha256_t hmac_sha256; + + //minitls_err_t ret; + + size_t current_size = 0; + + while(current_size < out_size) + { + //DBG("Current size %d", current_size); + + //Compute A[n]=f(A[n-1]) + crypto_hmac_sha256_init(&hmac_sha256, secret, secret_size); + + if(current_size == 0) //First iteration + { + crypto_hmac_sha256_update(&hmac_sha256, (const uint8_t*)label, strlen(label)); + + crypto_hmac_sha256_update(&hmac_sha256, seed1, seed1_size); + if(seed2 != NULL) + { + crypto_hmac_sha256_update(&hmac_sha256, seed2, seed2_size); + } + } + else + { + crypto_hmac_sha256_update(&hmac_sha256, A, HMAC_SHA256_SIZE); + } + + crypto_hmac_sha256_end(&hmac_sha256, A); + + //Compute HMAC_hash(secret, A[n] + seed) + crypto_hmac_sha256_init(&hmac_sha256, secret, secret_size); + + crypto_hmac_sha256_update(&hmac_sha256, A, HMAC_SHA256_SIZE); + + crypto_hmac_sha256_update(&hmac_sha256, (const uint8_t*)label, strlen(label)); + + crypto_hmac_sha256_update(&hmac_sha256, seed1, seed1_size); + if(seed2 != NULL) + { + crypto_hmac_sha256_update(&hmac_sha256, seed2, seed2_size); + } + + crypto_hmac_sha256_end(&hmac_sha256, mac); + + //Copy and truncate if needed + size_t append_size = MIN( out_size - current_size, HMAC_SHA256_SIZE ); + memcpy(out + current_size, mac, append_size); + + current_size += append_size; + } + + return MINITLS_OK; +} + +minitls_err_t change_cipher_spec_request(tls_handshake_t* handshake, buffer_t* buffer) +{ + /* + The change cipher spec protocol exists to signal transitions in + ciphering strategies. The protocol consists of a single message, + which is encrypted and compressed under the current (not the pending) + connection state. The message consists of a single byte of value 1. + + struct { + enum { change_cipher_spec(1), (255) } type; + } ChangeCipherSpec; + */ + buffer_reset(buffer); + if( buffer_size(buffer) < 1 ) + { + return MINITLS_ERR_BUFFER_TOO_SMALL; + } + + buffer_nu8_write(buffer, 1); + + minitls_err_t ret = tls_record_send(&handshake->tls_socket->record, TLS_CHANGE_CIPHER_SPEC, buffer); + if(ret) + { + return ret; + } + + return MINITLS_OK; +}