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