change some parameters in the library to meet the needs of the website httpbin.org
Fork of MiniTLS-GPL by
tls/tls_handshake.c
- Committer:
- shiyilei
- Date:
- 2015-02-06
- Revision:
- 5:95f70ebfe61f
- Parent:
- 3:eb324ffffd2b
File content as of revision 5:95f70ebfe61f:
/* 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__ 4 #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 #if MINITLS_CFG_KEY_RSA_2048 #define RSA_PREMASTER_KEY_SIZE 256 #elif MINITLS_CFG_KEY_RSA_1024 #define RSA_PREMASTER_KEY_SIZE 128 #endif 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 }; const uint8_t TLS_CIPHERSUITE_RSA_WITH_ARC4_SHA[] = { 0x00, 0x05 }; #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 #if CRYPTO_ECC buffer_nu16_write(buffer, 2); //2 bytes long buffer_nbytes_write(buffer, TLS_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 2); #elif CRYPTO_RSA buffer_nu16_write(buffer, (CRYPTO_AES_128 + CRYPTO_ARC4)*2); //2 or 4 bytes long #if CRYPTO_AES_128 buffer_nbytes_write(buffer, TLS_CIPHERSUITE_RSA_WITH_AES_128_CBC_SHA, 2); #endif #if CRYPTO_ARC4 buffer_nbytes_write(buffer, TLS_CIPHERSUITE_RSA_WITH_ARC4_SHA, 2); #endif #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 CRYPTO_ECC if(memcmp(cipher_suite, TLS_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 2) == 0) { handshake->target_security = TLS_SECURITY_TYPE_AES_128_CBC_SHA; } else #elif CRYPTO_RSA if(memcmp(cipher_suite, TLS_CIPHERSUITE_RSA_WITH_AES_128_CBC_SHA, 2) == 0) { handshake->target_security = TLS_SECURITY_TYPE_AES_128_CBC_SHA; } else if(memcmp(cipher_suite, TLS_CIPHERSUITE_RSA_WITH_ARC4_SHA, 2) == 0) { handshake->target_security = TLS_SECURITY_TYPE_ARC4_SHA; } else #else #error #endif { ERR("Unsupported cipher suite"); 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( compression_mode != TLS_COMPRESSION_METHOD_NULL ) { ERR("Unsupported 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[RSA_PREMASTER_KEY_SIZE]; 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) { WARN("Error %d", 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, handshake->target_security, &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; }