1189251Ssam/* 2189251Ssam * TLSv1 server - write handshake message 3252726Srpaulo * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12214734Srpaulo#include "crypto/md5.h" 13214734Srpaulo#include "crypto/sha1.h" 14252726Srpaulo#include "crypto/sha256.h" 15214734Srpaulo#include "crypto/tls.h" 16252726Srpaulo#include "crypto/random.h" 17189251Ssam#include "x509v3.h" 18189251Ssam#include "tlsv1_common.h" 19189251Ssam#include "tlsv1_record.h" 20189251Ssam#include "tlsv1_server.h" 21189251Ssam#include "tlsv1_server_i.h" 22189251Ssam 23189251Ssam 24189251Ssamstatic size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) 25189251Ssam{ 26189251Ssam size_t len = 0; 27189251Ssam struct x509_certificate *cert; 28189251Ssam 29189251Ssam cert = conn->cred->cert; 30189251Ssam while (cert) { 31189251Ssam len += 3 + cert->cert_len; 32189251Ssam if (x509_certificate_self_signed(cert)) 33189251Ssam break; 34189251Ssam cert = x509_certificate_get_subject(conn->cred->trusted_certs, 35189251Ssam &cert->issuer); 36189251Ssam } 37189251Ssam 38189251Ssam return len; 39189251Ssam} 40189251Ssam 41189251Ssam 42189251Ssamstatic int tls_write_server_hello(struct tlsv1_server *conn, 43189251Ssam u8 **msgpos, u8 *end) 44189251Ssam{ 45189251Ssam u8 *pos, *rhdr, *hs_start, *hs_length; 46189251Ssam struct os_time now; 47189251Ssam size_t rlen; 48189251Ssam 49189251Ssam pos = *msgpos; 50189251Ssam 51189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello"); 52189251Ssam rhdr = pos; 53189251Ssam pos += TLS_RECORD_HEADER_LEN; 54189251Ssam 55189251Ssam os_get_time(&now); 56189251Ssam WPA_PUT_BE32(conn->server_random, now.sec); 57252726Srpaulo if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { 58189251Ssam wpa_printf(MSG_ERROR, "TLSv1: Could not generate " 59189251Ssam "server_random"); 60189251Ssam return -1; 61189251Ssam } 62189251Ssam wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", 63189251Ssam conn->server_random, TLS_RANDOM_LEN); 64189251Ssam 65189251Ssam conn->session_id_len = TLS_SESSION_ID_MAX_LEN; 66252726Srpaulo if (random_get_bytes(conn->session_id, conn->session_id_len)) { 67189251Ssam wpa_printf(MSG_ERROR, "TLSv1: Could not generate " 68189251Ssam "session_id"); 69189251Ssam return -1; 70189251Ssam } 71189251Ssam wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", 72189251Ssam conn->session_id, conn->session_id_len); 73189251Ssam 74189251Ssam /* opaque fragment[TLSPlaintext.length] */ 75189251Ssam 76189251Ssam /* Handshake */ 77189251Ssam hs_start = pos; 78189251Ssam /* HandshakeType msg_type */ 79189251Ssam *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO; 80189251Ssam /* uint24 length (to be filled) */ 81189251Ssam hs_length = pos; 82189251Ssam pos += 3; 83189251Ssam /* body - ServerHello */ 84189251Ssam /* ProtocolVersion server_version */ 85252726Srpaulo WPA_PUT_BE16(pos, conn->rl.tls_version); 86189251Ssam pos += 2; 87189251Ssam /* Random random: uint32 gmt_unix_time, opaque random_bytes */ 88189251Ssam os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); 89189251Ssam pos += TLS_RANDOM_LEN; 90189251Ssam /* SessionID session_id */ 91189251Ssam *pos++ = conn->session_id_len; 92189251Ssam os_memcpy(pos, conn->session_id, conn->session_id_len); 93189251Ssam pos += conn->session_id_len; 94189251Ssam /* CipherSuite cipher_suite */ 95189251Ssam WPA_PUT_BE16(pos, conn->cipher_suite); 96189251Ssam pos += 2; 97189251Ssam /* CompressionMethod compression_method */ 98189251Ssam *pos++ = TLS_COMPRESSION_NULL; 99189251Ssam 100189251Ssam if (conn->session_ticket && conn->session_ticket_cb) { 101189251Ssam int res = conn->session_ticket_cb( 102189251Ssam conn->session_ticket_cb_ctx, 103189251Ssam conn->session_ticket, conn->session_ticket_len, 104189251Ssam conn->client_random, conn->server_random, 105189251Ssam conn->master_secret); 106189251Ssam if (res < 0) { 107189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " 108189251Ssam "indicated failure"); 109189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 110189251Ssam TLS_ALERT_HANDSHAKE_FAILURE); 111189251Ssam return -1; 112189251Ssam } 113189251Ssam conn->use_session_ticket = res; 114189251Ssam 115189251Ssam if (conn->use_session_ticket) { 116189251Ssam if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) { 117189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to " 118189251Ssam "derive keys"); 119189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 120189251Ssam TLS_ALERT_INTERNAL_ERROR); 121189251Ssam return -1; 122189251Ssam } 123189251Ssam } 124189251Ssam 125189251Ssam /* 126189251Ssam * RFC 4507 specifies that server would include an empty 127189251Ssam * SessionTicket extension in ServerHello and a 128189251Ssam * NewSessionTicket message after the ServerHello. However, 129189251Ssam * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket 130189251Ssam * extension at the moment, does not use such extensions. 131189251Ssam * 132189251Ssam * TODO: Add support for configuring RFC 4507 behavior and make 133189251Ssam * EAP-FAST disable it. 134189251Ssam */ 135189251Ssam } 136189251Ssam 137189251Ssam WPA_PUT_BE24(hs_length, pos - hs_length - 3); 138189251Ssam tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); 139189251Ssam 140189251Ssam if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, 141252726Srpaulo rhdr, end - rhdr, hs_start, pos - hs_start, 142252726Srpaulo &rlen) < 0) { 143189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); 144189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 145189251Ssam TLS_ALERT_INTERNAL_ERROR); 146189251Ssam return -1; 147189251Ssam } 148189251Ssam pos = rhdr + rlen; 149189251Ssam 150189251Ssam *msgpos = pos; 151189251Ssam 152189251Ssam return 0; 153189251Ssam} 154189251Ssam 155189251Ssam 156189251Ssamstatic int tls_write_server_certificate(struct tlsv1_server *conn, 157189251Ssam u8 **msgpos, u8 *end) 158189251Ssam{ 159189251Ssam u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; 160189251Ssam size_t rlen; 161189251Ssam struct x509_certificate *cert; 162189251Ssam const struct tls_cipher_suite *suite; 163189251Ssam 164189251Ssam suite = tls_get_cipher_suite(conn->rl.cipher_suite); 165189251Ssam if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { 166189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when " 167189251Ssam "using anonymous DH"); 168189251Ssam return 0; 169189251Ssam } 170189251Ssam 171189251Ssam pos = *msgpos; 172189251Ssam 173189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); 174189251Ssam rhdr = pos; 175189251Ssam pos += TLS_RECORD_HEADER_LEN; 176189251Ssam 177189251Ssam /* opaque fragment[TLSPlaintext.length] */ 178189251Ssam 179189251Ssam /* Handshake */ 180189251Ssam hs_start = pos; 181189251Ssam /* HandshakeType msg_type */ 182189251Ssam *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; 183189251Ssam /* uint24 length (to be filled) */ 184189251Ssam hs_length = pos; 185189251Ssam pos += 3; 186189251Ssam /* body - Certificate */ 187189251Ssam /* uint24 length (to be filled) */ 188189251Ssam cert_start = pos; 189189251Ssam pos += 3; 190189251Ssam cert = conn->cred->cert; 191189251Ssam while (cert) { 192189251Ssam if (pos + 3 + cert->cert_len > end) { 193189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " 194189251Ssam "for Certificate (cert_len=%lu left=%lu)", 195189251Ssam (unsigned long) cert->cert_len, 196189251Ssam (unsigned long) (end - pos)); 197189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 198189251Ssam TLS_ALERT_INTERNAL_ERROR); 199189251Ssam return -1; 200189251Ssam } 201189251Ssam WPA_PUT_BE24(pos, cert->cert_len); 202189251Ssam pos += 3; 203189251Ssam os_memcpy(pos, cert->cert_start, cert->cert_len); 204189251Ssam pos += cert->cert_len; 205189251Ssam 206189251Ssam if (x509_certificate_self_signed(cert)) 207189251Ssam break; 208189251Ssam cert = x509_certificate_get_subject(conn->cred->trusted_certs, 209189251Ssam &cert->issuer); 210189251Ssam } 211189251Ssam if (cert == conn->cred->cert || cert == NULL) { 212189251Ssam /* 213189251Ssam * Server was not configured with all the needed certificates 214189251Ssam * to form a full certificate chain. The client may fail to 215189251Ssam * validate the chain unless it is configured with all the 216189251Ssam * missing CA certificates. 217189251Ssam */ 218189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain " 219189251Ssam "not configured - validation may fail"); 220189251Ssam } 221189251Ssam WPA_PUT_BE24(cert_start, pos - cert_start - 3); 222189251Ssam 223189251Ssam WPA_PUT_BE24(hs_length, pos - hs_length - 3); 224189251Ssam 225189251Ssam if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, 226252726Srpaulo rhdr, end - rhdr, hs_start, pos - hs_start, 227252726Srpaulo &rlen) < 0) { 228189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); 229189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 230189251Ssam TLS_ALERT_INTERNAL_ERROR); 231189251Ssam return -1; 232189251Ssam } 233189251Ssam pos = rhdr + rlen; 234189251Ssam 235189251Ssam tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); 236189251Ssam 237189251Ssam *msgpos = pos; 238189251Ssam 239189251Ssam return 0; 240189251Ssam} 241189251Ssam 242189251Ssam 243189251Ssamstatic int tls_write_server_key_exchange(struct tlsv1_server *conn, 244189251Ssam u8 **msgpos, u8 *end) 245189251Ssam{ 246189251Ssam tls_key_exchange keyx; 247189251Ssam const struct tls_cipher_suite *suite; 248189251Ssam u8 *pos, *rhdr, *hs_start, *hs_length; 249189251Ssam size_t rlen; 250189251Ssam u8 *dh_ys; 251189251Ssam size_t dh_ys_len; 252189251Ssam 253189251Ssam suite = tls_get_cipher_suite(conn->rl.cipher_suite); 254189251Ssam if (suite == NULL) 255189251Ssam keyx = TLS_KEY_X_NULL; 256189251Ssam else 257189251Ssam keyx = suite->key_exchange; 258189251Ssam 259189251Ssam if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { 260189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed"); 261189251Ssam return 0; 262189251Ssam } 263189251Ssam 264189251Ssam if (keyx != TLS_KEY_X_DH_anon) { 265189251Ssam /* TODO? */ 266189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet " 267189251Ssam "supported with key exchange type %d", keyx); 268189251Ssam return -1; 269189251Ssam } 270189251Ssam 271189251Ssam if (conn->cred == NULL || conn->cred->dh_p == NULL || 272189251Ssam conn->cred->dh_g == NULL) { 273189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for " 274189251Ssam "ServerKeyExhcange"); 275189251Ssam return -1; 276189251Ssam } 277189251Ssam 278189251Ssam os_free(conn->dh_secret); 279189251Ssam conn->dh_secret_len = conn->cred->dh_p_len; 280189251Ssam conn->dh_secret = os_malloc(conn->dh_secret_len); 281189251Ssam if (conn->dh_secret == NULL) { 282189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " 283189251Ssam "memory for secret (Diffie-Hellman)"); 284189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 285189251Ssam TLS_ALERT_INTERNAL_ERROR); 286189251Ssam return -1; 287189251Ssam } 288252726Srpaulo if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) { 289189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " 290189251Ssam "data for Diffie-Hellman"); 291189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 292189251Ssam TLS_ALERT_INTERNAL_ERROR); 293189251Ssam os_free(conn->dh_secret); 294189251Ssam conn->dh_secret = NULL; 295189251Ssam return -1; 296189251Ssam } 297189251Ssam 298189251Ssam if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) > 299189251Ssam 0) 300189251Ssam conn->dh_secret[0] = 0; /* make sure secret < p */ 301189251Ssam 302189251Ssam pos = conn->dh_secret; 303189251Ssam while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0) 304189251Ssam pos++; 305189251Ssam if (pos != conn->dh_secret) { 306189251Ssam os_memmove(conn->dh_secret, pos, 307189251Ssam conn->dh_secret_len - (pos - conn->dh_secret)); 308189251Ssam conn->dh_secret_len -= pos - conn->dh_secret; 309189251Ssam } 310189251Ssam wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value", 311189251Ssam conn->dh_secret, conn->dh_secret_len); 312189251Ssam 313189251Ssam /* Ys = g^secret mod p */ 314189251Ssam dh_ys_len = conn->cred->dh_p_len; 315189251Ssam dh_ys = os_malloc(dh_ys_len); 316189251Ssam if (dh_ys == NULL) { 317189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for " 318189251Ssam "Diffie-Hellman"); 319189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 320189251Ssam TLS_ALERT_INTERNAL_ERROR); 321189251Ssam return -1; 322189251Ssam } 323189251Ssam if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len, 324189251Ssam conn->dh_secret, conn->dh_secret_len, 325189251Ssam conn->cred->dh_p, conn->cred->dh_p_len, 326189251Ssam dh_ys, &dh_ys_len)) { 327189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 328189251Ssam TLS_ALERT_INTERNAL_ERROR); 329189251Ssam os_free(dh_ys); 330189251Ssam return -1; 331189251Ssam } 332189251Ssam 333189251Ssam wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", 334189251Ssam dh_ys, dh_ys_len); 335189251Ssam 336189251Ssam /* 337189251Ssam * struct { 338189251Ssam * select (KeyExchangeAlgorithm) { 339189251Ssam * case diffie_hellman: 340189251Ssam * ServerDHParams params; 341189251Ssam * Signature signed_params; 342189251Ssam * case rsa: 343189251Ssam * ServerRSAParams params; 344189251Ssam * Signature signed_params; 345189251Ssam * }; 346189251Ssam * } ServerKeyExchange; 347189251Ssam * 348189251Ssam * struct { 349189251Ssam * opaque dh_p<1..2^16-1>; 350189251Ssam * opaque dh_g<1..2^16-1>; 351189251Ssam * opaque dh_Ys<1..2^16-1>; 352189251Ssam * } ServerDHParams; 353189251Ssam */ 354189251Ssam 355189251Ssam pos = *msgpos; 356189251Ssam 357189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange"); 358189251Ssam rhdr = pos; 359189251Ssam pos += TLS_RECORD_HEADER_LEN; 360189251Ssam 361189251Ssam /* opaque fragment[TLSPlaintext.length] */ 362189251Ssam 363189251Ssam /* Handshake */ 364189251Ssam hs_start = pos; 365189251Ssam /* HandshakeType msg_type */ 366189251Ssam *pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE; 367189251Ssam /* uint24 length (to be filled) */ 368189251Ssam hs_length = pos; 369189251Ssam pos += 3; 370189251Ssam 371189251Ssam /* body - ServerDHParams */ 372189251Ssam /* dh_p */ 373189251Ssam if (pos + 2 + conn->cred->dh_p_len > end) { 374189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " 375189251Ssam "dh_p"); 376189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 377189251Ssam TLS_ALERT_INTERNAL_ERROR); 378189251Ssam os_free(dh_ys); 379189251Ssam return -1; 380189251Ssam } 381189251Ssam WPA_PUT_BE16(pos, conn->cred->dh_p_len); 382189251Ssam pos += 2; 383189251Ssam os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len); 384189251Ssam pos += conn->cred->dh_p_len; 385189251Ssam 386189251Ssam /* dh_g */ 387189251Ssam if (pos + 2 + conn->cred->dh_g_len > end) { 388189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " 389189251Ssam "dh_g"); 390189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 391189251Ssam TLS_ALERT_INTERNAL_ERROR); 392189251Ssam os_free(dh_ys); 393189251Ssam return -1; 394189251Ssam } 395189251Ssam WPA_PUT_BE16(pos, conn->cred->dh_g_len); 396189251Ssam pos += 2; 397189251Ssam os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len); 398189251Ssam pos += conn->cred->dh_g_len; 399189251Ssam 400189251Ssam /* dh_Ys */ 401189251Ssam if (pos + 2 + dh_ys_len > end) { 402189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " 403189251Ssam "dh_Ys"); 404189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 405189251Ssam TLS_ALERT_INTERNAL_ERROR); 406189251Ssam os_free(dh_ys); 407189251Ssam return -1; 408189251Ssam } 409189251Ssam WPA_PUT_BE16(pos, dh_ys_len); 410189251Ssam pos += 2; 411189251Ssam os_memcpy(pos, dh_ys, dh_ys_len); 412189251Ssam pos += dh_ys_len; 413189251Ssam os_free(dh_ys); 414189251Ssam 415189251Ssam WPA_PUT_BE24(hs_length, pos - hs_length - 3); 416189251Ssam 417189251Ssam if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, 418252726Srpaulo rhdr, end - rhdr, hs_start, pos - hs_start, 419252726Srpaulo &rlen) < 0) { 420189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); 421189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 422189251Ssam TLS_ALERT_INTERNAL_ERROR); 423189251Ssam return -1; 424189251Ssam } 425189251Ssam pos = rhdr + rlen; 426189251Ssam 427189251Ssam tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); 428189251Ssam 429189251Ssam *msgpos = pos; 430189251Ssam 431189251Ssam return 0; 432189251Ssam} 433189251Ssam 434189251Ssam 435189251Ssamstatic int tls_write_server_certificate_request(struct tlsv1_server *conn, 436189251Ssam u8 **msgpos, u8 *end) 437189251Ssam{ 438189251Ssam u8 *pos, *rhdr, *hs_start, *hs_length; 439189251Ssam size_t rlen; 440189251Ssam 441189251Ssam if (!conn->verify_peer) { 442189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed"); 443189251Ssam return 0; 444189251Ssam } 445189251Ssam 446189251Ssam pos = *msgpos; 447189251Ssam 448189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest"); 449189251Ssam rhdr = pos; 450189251Ssam pos += TLS_RECORD_HEADER_LEN; 451189251Ssam 452189251Ssam /* opaque fragment[TLSPlaintext.length] */ 453189251Ssam 454189251Ssam /* Handshake */ 455189251Ssam hs_start = pos; 456189251Ssam /* HandshakeType msg_type */ 457189251Ssam *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST; 458189251Ssam /* uint24 length (to be filled) */ 459189251Ssam hs_length = pos; 460189251Ssam pos += 3; 461189251Ssam /* body - CertificateRequest */ 462189251Ssam 463189251Ssam /* 464189251Ssam * enum { 465189251Ssam * rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4), 466189251Ssam * (255) 467189251Ssam * } ClientCertificateType; 468189251Ssam * ClientCertificateType certificate_types<1..2^8-1> 469189251Ssam */ 470189251Ssam *pos++ = 1; 471189251Ssam *pos++ = 1; /* rsa_sign */ 472189251Ssam 473189251Ssam /* 474189251Ssam * opaque DistinguishedName<1..2^16-1> 475189251Ssam * DistinguishedName certificate_authorities<3..2^16-1> 476189251Ssam */ 477189251Ssam /* TODO: add support for listing DNs for trusted CAs */ 478189251Ssam WPA_PUT_BE16(pos, 0); 479189251Ssam pos += 2; 480189251Ssam 481189251Ssam WPA_PUT_BE24(hs_length, pos - hs_length - 3); 482189251Ssam 483189251Ssam if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, 484252726Srpaulo rhdr, end - rhdr, hs_start, pos - hs_start, 485252726Srpaulo &rlen) < 0) { 486189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); 487189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 488189251Ssam TLS_ALERT_INTERNAL_ERROR); 489189251Ssam return -1; 490189251Ssam } 491189251Ssam pos = rhdr + rlen; 492189251Ssam 493189251Ssam tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); 494189251Ssam 495189251Ssam *msgpos = pos; 496189251Ssam 497189251Ssam return 0; 498189251Ssam} 499189251Ssam 500189251Ssam 501189251Ssamstatic int tls_write_server_hello_done(struct tlsv1_server *conn, 502189251Ssam u8 **msgpos, u8 *end) 503189251Ssam{ 504252726Srpaulo u8 *pos; 505189251Ssam size_t rlen; 506252726Srpaulo u8 payload[4]; 507189251Ssam 508189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); 509189251Ssam 510189251Ssam /* opaque fragment[TLSPlaintext.length] */ 511189251Ssam 512189251Ssam /* Handshake */ 513252726Srpaulo pos = payload; 514189251Ssam /* HandshakeType msg_type */ 515189251Ssam *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE; 516252726Srpaulo /* uint24 length */ 517252726Srpaulo WPA_PUT_BE24(pos, 0); 518189251Ssam pos += 3; 519189251Ssam /* body - ServerHelloDone (empty) */ 520189251Ssam 521189251Ssam if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, 522252726Srpaulo *msgpos, end - *msgpos, payload, pos - payload, 523252726Srpaulo &rlen) < 0) { 524189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); 525189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 526189251Ssam TLS_ALERT_INTERNAL_ERROR); 527189251Ssam return -1; 528189251Ssam } 529189251Ssam 530252726Srpaulo tls_verify_hash_add(&conn->verify, payload, pos - payload); 531189251Ssam 532252726Srpaulo *msgpos += rlen; 533189251Ssam 534189251Ssam return 0; 535189251Ssam} 536189251Ssam 537189251Ssam 538189251Ssamstatic int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, 539189251Ssam u8 **msgpos, u8 *end) 540189251Ssam{ 541189251Ssam size_t rlen; 542252726Srpaulo u8 payload[1]; 543189251Ssam 544252726Srpaulo wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); 545189251Ssam 546252726Srpaulo payload[0] = TLS_CHANGE_CIPHER_SPEC; 547252726Srpaulo 548189251Ssam if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, 549252726Srpaulo *msgpos, end - *msgpos, payload, sizeof(payload), 550252726Srpaulo &rlen) < 0) { 551189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); 552189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 553189251Ssam TLS_ALERT_INTERNAL_ERROR); 554189251Ssam return -1; 555189251Ssam } 556189251Ssam 557189251Ssam if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { 558189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " 559189251Ssam "record layer"); 560189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 561189251Ssam TLS_ALERT_INTERNAL_ERROR); 562189251Ssam return -1; 563189251Ssam } 564189251Ssam 565252726Srpaulo *msgpos += rlen; 566189251Ssam 567189251Ssam return 0; 568189251Ssam} 569189251Ssam 570189251Ssam 571189251Ssamstatic int tls_write_server_finished(struct tlsv1_server *conn, 572189251Ssam u8 **msgpos, u8 *end) 573189251Ssam{ 574252726Srpaulo u8 *pos, *hs_start; 575189251Ssam size_t rlen, hlen; 576252726Srpaulo u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; 577189251Ssam u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; 578189251Ssam 579189251Ssam pos = *msgpos; 580189251Ssam 581189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); 582189251Ssam 583189251Ssam /* Encrypted Handshake Message: Finished */ 584189251Ssam 585252726Srpaulo#ifdef CONFIG_TLSV12 586252726Srpaulo if (conn->rl.tls_version >= TLS_VERSION_1_2) { 587252726Srpaulo hlen = SHA256_MAC_LEN; 588252726Srpaulo if (conn->verify.sha256_server == NULL || 589252726Srpaulo crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) 590252726Srpaulo < 0) { 591252726Srpaulo conn->verify.sha256_server = NULL; 592252726Srpaulo tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 593252726Srpaulo TLS_ALERT_INTERNAL_ERROR); 594252726Srpaulo return -1; 595252726Srpaulo } 596252726Srpaulo conn->verify.sha256_server = NULL; 597252726Srpaulo } else { 598252726Srpaulo#endif /* CONFIG_TLSV12 */ 599252726Srpaulo 600189251Ssam hlen = MD5_MAC_LEN; 601189251Ssam if (conn->verify.md5_server == NULL || 602189251Ssam crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { 603189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 604189251Ssam TLS_ALERT_INTERNAL_ERROR); 605189251Ssam conn->verify.md5_server = NULL; 606189251Ssam crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); 607189251Ssam conn->verify.sha1_server = NULL; 608189251Ssam return -1; 609189251Ssam } 610189251Ssam conn->verify.md5_server = NULL; 611189251Ssam hlen = SHA1_MAC_LEN; 612189251Ssam if (conn->verify.sha1_server == NULL || 613189251Ssam crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, 614189251Ssam &hlen) < 0) { 615189251Ssam conn->verify.sha1_server = NULL; 616189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 617189251Ssam TLS_ALERT_INTERNAL_ERROR); 618189251Ssam return -1; 619189251Ssam } 620189251Ssam conn->verify.sha1_server = NULL; 621252726Srpaulo hlen = MD5_MAC_LEN + SHA1_MAC_LEN; 622189251Ssam 623252726Srpaulo#ifdef CONFIG_TLSV12 624252726Srpaulo } 625252726Srpaulo#endif /* CONFIG_TLSV12 */ 626252726Srpaulo 627252726Srpaulo if (tls_prf(conn->rl.tls_version, 628252726Srpaulo conn->master_secret, TLS_MASTER_SECRET_LEN, 629252726Srpaulo "server finished", hash, hlen, 630252726Srpaulo verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { 631189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); 632189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 633189251Ssam TLS_ALERT_INTERNAL_ERROR); 634189251Ssam return -1; 635189251Ssam } 636189251Ssam wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", 637252726Srpaulo verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); 638189251Ssam 639189251Ssam /* Handshake */ 640252726Srpaulo pos = hs_start = verify_data; 641189251Ssam /* HandshakeType msg_type */ 642189251Ssam *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; 643252726Srpaulo /* uint24 length */ 644252726Srpaulo WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); 645189251Ssam pos += 3; 646189251Ssam pos += TLS_VERIFY_DATA_LEN; 647189251Ssam tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); 648189251Ssam 649189251Ssam if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, 650252726Srpaulo *msgpos, end - *msgpos, hs_start, pos - hs_start, 651252726Srpaulo &rlen) < 0) { 652189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); 653189251Ssam tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, 654189251Ssam TLS_ALERT_INTERNAL_ERROR); 655189251Ssam return -1; 656189251Ssam } 657189251Ssam 658252726Srpaulo *msgpos += rlen; 659189251Ssam 660189251Ssam return 0; 661189251Ssam} 662189251Ssam 663189251Ssam 664189251Ssamstatic u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) 665189251Ssam{ 666189251Ssam u8 *msg, *end, *pos; 667189251Ssam size_t msglen; 668189251Ssam 669189251Ssam *out_len = 0; 670189251Ssam 671189251Ssam msglen = 1000 + tls_server_cert_chain_der_len(conn); 672189251Ssam 673189251Ssam msg = os_malloc(msglen); 674189251Ssam if (msg == NULL) 675189251Ssam return NULL; 676189251Ssam 677189251Ssam pos = msg; 678189251Ssam end = msg + msglen; 679189251Ssam 680189251Ssam if (tls_write_server_hello(conn, &pos, end) < 0) { 681189251Ssam os_free(msg); 682189251Ssam return NULL; 683189251Ssam } 684189251Ssam 685189251Ssam if (conn->use_session_ticket) { 686189251Ssam /* Abbreviated handshake using session ticket; RFC 4507 */ 687189251Ssam if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || 688189251Ssam tls_write_server_finished(conn, &pos, end) < 0) { 689189251Ssam os_free(msg); 690189251Ssam return NULL; 691189251Ssam } 692189251Ssam 693189251Ssam *out_len = pos - msg; 694189251Ssam 695189251Ssam conn->state = CHANGE_CIPHER_SPEC; 696189251Ssam 697189251Ssam return msg; 698189251Ssam } 699189251Ssam 700189251Ssam /* Full handshake */ 701189251Ssam if (tls_write_server_certificate(conn, &pos, end) < 0 || 702189251Ssam tls_write_server_key_exchange(conn, &pos, end) < 0 || 703189251Ssam tls_write_server_certificate_request(conn, &pos, end) < 0 || 704189251Ssam tls_write_server_hello_done(conn, &pos, end) < 0) { 705189251Ssam os_free(msg); 706189251Ssam return NULL; 707189251Ssam } 708189251Ssam 709189251Ssam *out_len = pos - msg; 710189251Ssam 711189251Ssam conn->state = CLIENT_CERTIFICATE; 712189251Ssam 713189251Ssam return msg; 714189251Ssam} 715189251Ssam 716189251Ssam 717189251Ssamstatic u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn, 718189251Ssam size_t *out_len) 719189251Ssam{ 720189251Ssam u8 *msg, *end, *pos; 721189251Ssam 722189251Ssam *out_len = 0; 723189251Ssam 724189251Ssam msg = os_malloc(1000); 725189251Ssam if (msg == NULL) 726189251Ssam return NULL; 727189251Ssam 728189251Ssam pos = msg; 729189251Ssam end = msg + 1000; 730189251Ssam 731189251Ssam if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || 732189251Ssam tls_write_server_finished(conn, &pos, end) < 0) { 733189251Ssam os_free(msg); 734189251Ssam return NULL; 735189251Ssam } 736189251Ssam 737189251Ssam *out_len = pos - msg; 738189251Ssam 739189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully"); 740189251Ssam conn->state = ESTABLISHED; 741189251Ssam 742189251Ssam return msg; 743189251Ssam} 744189251Ssam 745189251Ssam 746189251Ssamu8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len) 747189251Ssam{ 748189251Ssam switch (conn->state) { 749189251Ssam case SERVER_HELLO: 750189251Ssam return tls_send_server_hello(conn, out_len); 751189251Ssam case SERVER_CHANGE_CIPHER_SPEC: 752189251Ssam return tls_send_change_cipher_spec(conn, out_len); 753189251Ssam default: 754189251Ssam if (conn->state == ESTABLISHED && conn->use_session_ticket) { 755189251Ssam /* Abbreviated handshake was already completed. */ 756189251Ssam return NULL; 757189251Ssam } 758189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " 759189251Ssam "generating reply", conn->state); 760189251Ssam return NULL; 761189251Ssam } 762189251Ssam} 763189251Ssam 764189251Ssam 765189251Ssamu8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, 766189251Ssam u8 description, size_t *out_len) 767189251Ssam{ 768189251Ssam u8 *alert, *pos, *length; 769189251Ssam 770189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); 771189251Ssam *out_len = 0; 772189251Ssam 773189251Ssam alert = os_malloc(10); 774189251Ssam if (alert == NULL) 775189251Ssam return NULL; 776189251Ssam 777189251Ssam pos = alert; 778189251Ssam 779189251Ssam /* TLSPlaintext */ 780189251Ssam /* ContentType type */ 781189251Ssam *pos++ = TLS_CONTENT_TYPE_ALERT; 782189251Ssam /* ProtocolVersion version */ 783252726Srpaulo WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : 784252726Srpaulo TLS_VERSION); 785189251Ssam pos += 2; 786189251Ssam /* uint16 length (to be filled) */ 787189251Ssam length = pos; 788189251Ssam pos += 2; 789189251Ssam /* opaque fragment[TLSPlaintext.length] */ 790189251Ssam 791189251Ssam /* Alert */ 792189251Ssam /* AlertLevel level */ 793189251Ssam *pos++ = level; 794189251Ssam /* AlertDescription description */ 795189251Ssam *pos++ = description; 796189251Ssam 797189251Ssam WPA_PUT_BE16(length, pos - length - 2); 798189251Ssam *out_len = pos - alert; 799189251Ssam 800189251Ssam return alert; 801189251Ssam} 802