1 /**
2 * TLS Server
3 * 
4 * Copyright:
5 * (C) 2004-2011,2015 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.tls.server;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_TLS):
15 
16 public import botan.tls.channel;
17 import botan.tls.credentials_manager;
18 import botan.tls.handshake_state;
19 import botan.tls.messages;
20 import botan.tls.alert;
21 import botan.rng.rng;
22 import memutils.dictionarylist;
23 import memutils.hashmap;
24 import botan.utils.types;
25 import std.datetime;
26 
27 alias NextProtocolHandler = string delegate(in Vector!string);
28 alias SNIHandler = SNIContextSwitchInfo delegate(string);
29 
30 struct SNIContextSwitchInfo
31 {
32 	TLSSessionManager session_manager;
33 	TLSCredentialsManager credentials;
34 	TLSPolicy policy;
35 	NextProtocolHandler next_proto;
36 	void* user_data;
37 }
38 
39 
40 /**
41 * TLS Server
42 */
43 final class TLSServer : TLSChannel
44 {
45 public:
46 
47     /**
48     * TLSServer initialization
49     */
50     this(DataWriter output_fn,
51          OnClearData data_cb,
52          OnAlert alert_cb,
53          OnHandshakeComplete handshake_cb,
54          TLSSessionManager session_manager,
55          TLSCredentialsManager creds,
56          TLSPolicy policy,
57          RandomNumberGenerator rng,
58          NextProtocolHandler next_proto = null,
59 		 SNIHandler sni_handler = null,
60          bool is_datagram = false,
61          size_t io_buf_sz = 16*1024) 
62     {
63         super(output_fn, data_cb, alert_cb, handshake_cb, session_manager, rng, is_datagram, io_buf_sz);
64 		m_policy = policy;
65         m_creds = creds;
66         m_choose_next_protocol = next_proto;
67 		m_sni_handler = sni_handler;
68     }
69 
70 	void* getUserData() { return m_user_data; }
71 
72 protected:
73     override Vector!X509Certificate getPeerCertChain(in HandshakeState state) const
74     {
75         if (state.clientCerts())
76             return state.clientCerts().certChain().dup();
77         return Vector!X509Certificate();
78     }
79 
80     /*
81     * Send a hello request to the client
82     */
83     override void initiateHandshake(HandshakeState state, bool force_full_renegotiation)
84     {
85         (cast(ServerHandshakeState)state).allow_session_resumption = !force_full_renegotiation;
86         
87         auto hello_req = scoped!HelloRequest(state.handshakeIo());
88     }
89 
90     /*
91     * Process a handshake message
92     */
93     override void processHandshakeMsg(in HandshakeState active_state,
94                                       HandshakeState state_base,
95                                       HandshakeType type,
96                                       const ref Vector!ubyte contents)
97     {
98         ServerHandshakeState state = cast(ServerHandshakeState)(state_base);
99         
100         state.confirmTransitionTo(type);
101         
102         /*
103         * The change cipher spec message isn't technically a handshake
104         * message so it's not included in the hash. The finished and
105         * certificate verify messages are verified based on the current
106         * state of the hash *before* this message so we delay adding them
107         * to the hash computation until we've processed them below.
108         */
109         if (type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY)
110         {
111             state.hash().update(state.handshakeIo().format(contents, type));
112         }
113         
114         if (type == CLIENT_HELLO)
115         {
116             const bool initial_handshake = !active_state;
117             
118             if (!m_policy.allowInsecureRenegotiation() &&
119                 !(initial_handshake || secureRenegotiationSupported()))
120             {
121                 sendWarningAlert(TLSAlert.NO_RENEGOTIATION);
122                 return;
123             }
124             state.clientHello(new ClientHello(contents, type));
125 
126 			// choose a proper context depending on hostname and register the userdata
127 			if (m_sni_handler) {
128 				const string sni_hostname = state.clientHello().sniHostname();
129 				SNIContextSwitchInfo ctx = m_sni_handler(sni_hostname);
130 				if (ctx !is SNIContextSwitchInfo.init)
131 					switchContext(ctx);
132 			}
133 
134             const TLSProtocolVersion client_version = state.clientHello().Version();
135             
136             TLSProtocolVersion negotiated_version;
137 
138             const TLSProtocolVersion latest_supported = m_policy.latestSupportedVersion(client_version.isDatagramProtocol());
139 
140             if ((initial_handshake && client_version.knownVersion()) ||
141                 (!initial_handshake && client_version == active_state.Version()))
142             {
143                 /*
144                 Common cases: new client hello with some known version, or a
145                 renegotiation using the same version as previously
146                 negotiated.
147                 */
148                 
149                 negotiated_version = client_version;
150             }
151             else if (!initial_handshake && (client_version != active_state.Version()))
152             {
153                 /*
154                 * If this is a renegotiation, and the client has offered a
155                 * later version than what it initially negotiated, negotiate
156                 * the old version. This matches OpenSSL's behavior. If the
157                 * client is offering a version earlier than what it initially
158                 * negotiated, reject as a probable attack.
159                 */
160                 if (active_state.Version() > client_version)
161                 {
162                     throw new TLSException(TLSAlert.PROTOCOL_VERSION,
163                                             "TLSClient negotiated " ~
164                                             active_state.Version().toString() ~
165                                             " then renegotiated with " ~
166                                             client_version.toString());
167                 }
168                 else
169                     negotiated_version = active_state.Version();
170             }
171             else
172             {
173                 /*
174                 New negotiation using a version we don't know. Offer
175                 them the best we currently know and support.
176                 */
177                 negotiated_version = latest_supported;
178             }
179             
180             if (!m_policy.acceptableProtocolVersion(negotiated_version))
181             {
182                 throw new TLSException(TLSAlert.PROTOCOL_VERSION, "Client version " ~ negotiated_version.toString() ~ " is unacceptable by policy");
183             }
184 
185             if (state.clientHello().sentFallbackSCSV())
186             {
187                 if (latest_supported > client_version)
188                     throw new TLSException(TLSAlert.INAPPROPRIATE_FALLBACK, "Client signalled fallback SCSV, possible attack");
189             }
190 
191             secureRenegotiationCheck(state.clientHello());
192             
193             state.setVersion(negotiated_version);
194 
195             TLSSession session_info;
196 			scope(exit) destroy(session_info);
197             const bool resuming = state.allow_session_resumption &&
198                                     checkForResume(session_info,
199                                                    sessionManager(),
200                                                    m_creds,
201                                                    state.clientHello(),
202                                                    m_policy.sessionTicketLifetime());
203             
204             bool have_session_ticket_key = false;
205             
206 			if (m_creds.hasPsk()) try
207             {
208                 have_session_ticket_key = m_creds.psk("tls-server", "session-ticket", "").length > 0;
209             }
210             catch (Exception) {}
211 
212             m_application_protocol = "";
213 
214             if (m_choose_next_protocol && state.clientHello().supportsAlpn())
215                 m_application_protocol = m_choose_next_protocol(state.clientHello().nextProtocols());
216 
217             if (resuming)
218             {
219                 // resume session
220                 
221                 const bool offer_new_session_ticket = (state.clientHello().supportsSessionTicket() &&
222                                                         state.clientHello().sessionTicket().empty &&
223                                                         have_session_ticket_key);
224                 
225                 state.serverHello(new ServerHello(state.handshakeIo(),
226                                                   state.hash(),
227                                                   m_policy,
228                                                   state.clientHello().sessionId().dup,
229                                                   session_info.Version(),
230                                                   session_info.ciphersuiteCode(),
231                                                   session_info.compressionMethod(),
232                                                   session_info.fragmentSize(),
233                                                   state.clientHello().secureRenegotiation(),
234                                                   state.clientHello().supportsExtendedMasterSecret(),
235                                                   secureRenegotiationDataForServerHello(),
236                                                   offer_new_session_ticket,
237                                                   state.clientHello().supportsAlpn(),
238                                                   m_application_protocol,
239                                                   state.clientHello().supportsHeartbeats(),
240                                                   rng()));
241 
242                 // Load ChannelID Original Handshake Hash
243                 if (state.clientHello().supportsChannelID() && state.serverHello().supportsChannelID()) 
244                 {
245                     TLSSession sess;
246                     scope(exit) if (sess) sess.destroy();
247                     if (sessionManager() && sessionManager().loadFromSessionId(state.clientHello().sessionId(), sess))
248                     {
249                         state.setOriginalHandshakeHash(sess.originalHandshakeHash().dup());
250                     }
251                 }
252 
253                 secureRenegotiationCheck(state.serverHello());
254                 
255                 state.computeSessionKeys(session_info.masterSecret().dup);
256                 
257                 if (!saveSession(session_info))
258                 {
259                     auto entry = &session_info.sessionId();
260                     sessionManager().removeEntry(*entry);
261                     
262                     if (state.serverHello().supportsSessionTicket()) // send an empty ticket
263                     {
264                         state.newSessionTicket(new NewSessionTicket(state.handshakeIo(), state.hash()));
265                     }
266                 }
267                 
268                 if (state.serverHello().supportsSessionTicket() && !state.newSessionTicket())
269                 {
270 					if (m_creds.hasPsk()) try
271                     {
272 
273                         const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
274                         
275                         state.newSessionTicket(new NewSessionTicket(state.handshakeIo(),
276                                                                         state.hash(),
277                                                                         session_info.encrypt(ticket_key, rng()),
278                                                                         m_policy.sessionTicketLifetime()));
279                     }
280                     catch (Exception) {}
281                     
282                     if (!state.newSessionTicket())
283                     {
284                         state.newSessionTicket(new NewSessionTicket(state.handshakeIo(), state.hash()));
285                     }
286                 }
287                 
288                 state.handshakeIo().send(scoped!ChangeCipherSpec());
289                 
290                 changeCipherSpecWriter(SERVER);
291                 
292                 state.serverFinished(new Finished(state.handshakeIo(), state, SERVER));
293                 
294                 state.setExpectedNext(HANDSHAKE_CCS);
295             }
296             else // new session
297             {
298                 HashMapRef!(string, Array!X509Certificate) cert_chains;
299                 
300                 const string sni_hostname = state.clientHello().sniHostname();
301                 
302                 cert_chains = getServerCerts(sni_hostname, m_creds);
303                 
304                 if (sni_hostname != "" && cert_chains.length == 0)
305                 {
306                     cert_chains = getServerCerts("", m_creds);
307                         
308                     /*
309                     * Only send the unrecognized_name alert if we couldn't
310                     * find any certs for the requested name but did find at
311                     * least one cert to use in general. That avoids sending an
312                     * unrecognized_name when a server is configured for purely
313                     * anonymous operation.
314                     */
315                     if (cert_chains.length != 0)
316                         sendAlert(TLSAlert(TLSAlert.UNRECOGNIZED_NAME));
317                 }
318                 state.serverHello(
319                     new ServerHello(    state.handshakeIo(),
320                                         state.hash(),
321                                         m_policy,
322                                         makeHelloRandom(rng(), m_policy), // new session ID
323                                         state.Version(),
324                                         chooseCiphersuite(m_policy, state.Version(), m_creds, cert_chains, state.clientHello()),
325                                         chooseCompression(m_policy, state.clientHello().compressionMethods()),
326                                         state.clientHello().fragmentSize(),
327                                         state.clientHello().secureRenegotiation(),
328                                         state.clientHello().supportsExtendedMasterSecret(),
329                                         secureRenegotiationDataForServerHello(),
330                                         state.clientHello().supportsSessionTicket() && have_session_ticket_key,
331                                         state.clientHello().supportsAlpn(),
332                                         m_application_protocol,
333                                         state.clientHello().supportsHeartbeats(),
334                                         rng()
335                     )
336                 );
337 
338                 secureRenegotiationCheck(state.serverHello());
339                 
340                 const string sig_algo = state.ciphersuite().sigAlgo();
341                 const string kex_algo = state.ciphersuite().kexAlgo();
342                 
343                 if (sig_algo != "")
344                 {
345                     assert(!cert_chains[sig_algo].empty,
346                     "Attempting to send empty certificate chain");
347                     
348                     state.serverCerts(new Certificate(state.handshakeIo(), state.hash(), cert_chains[sig_algo])
349                     );
350                 }
351                 
352                 PrivateKey priv_key = null;
353                 
354                 if (kex_algo == "RSA" || sig_algo != "")
355                 {
356                     priv_key = m_creds.privateKeyFor(state.serverCerts().certChain()[0],
357                                                      "tls-server",
358                                                      sni_hostname );
359                     
360                     if (!priv_key)
361                         throw new InternalError("No private key located for associated server cert");
362                 }
363                 
364                 if (kex_algo == "RSA")
365                 {
366                     state.server_rsa_kex_key = priv_key;
367                 }
368                 else
369                 {
370                     state.serverKex(
371                         new ServerKeyExchange(state.handshakeIo(),
372                                                 state,
373                                                 m_policy,
374                                                 m_creds,
375                                                 rng(),
376                                                 priv_key)
377                         );
378                 }
379                 
380                 auto trusted_CAs = m_creds.trustedCertificateAuthorities("tls-server", sni_hostname);
381                 
382                 Vector!X509DN client_auth_CAs;
383                 
384                 foreach (store; trusted_CAs[])
385                 {
386                     auto subjects = store.allSubjects();
387                     client_auth_CAs ~= subjects[];
388                 }
389                 
390                 if (!client_auth_CAs.empty && state.ciphersuite().sigAlgo() != "")
391                 {
392                     state.certReq(new CertificateReq(state.handshakeIo(), state.hash(), m_policy,
393                                                      client_auth_CAs.move(), state.Version()));
394                     
395                     state.setExpectedNext(CERTIFICATE);
396                 }
397                 
398                 /*
399                 * If the client doesn't have a cert they want to use they are
400                 * allowed to send either an empty cert message or proceed
401                 * directly to the client key exchange, so allow either case.
402                 */
403                 state.setExpectedNext(CLIENT_KEX);
404                 
405                 state.serverHelloDone(new ServerHelloDone(state.handshakeIo(), state.hash()));
406             }
407         }
408         else if (type == CERTIFICATE)
409         {
410             state.clientCerts(new Certificate(contents));
411             
412             state.setExpectedNext(CLIENT_KEX);
413         }
414         else if (type == CLIENT_KEX)
415         {
416             if (state.receivedHandshakeMsg(CERTIFICATE) && !state.clientCerts().empty)
417                 state.setExpectedNext(CERTIFICATE_VERIFY);
418             else
419                 state.setExpectedNext(HANDSHAKE_CCS);
420             
421             state.clientKex(
422                 new ClientKeyExchange(contents, state, state.server_rsa_kex_key, m_creds, m_policy, rng())
423             );
424             
425             state.computeSessionKeys();
426         }
427         else if (type == CERTIFICATE_VERIFY)
428         {
429             state.clientVerify(new CertificateVerify(contents, state.Version()));
430             
431             const(Vector!X509Certificate)* client_certs = &state.clientCerts().certChain();
432             
433             const bool sig_valid = state.clientVerify().verify((*client_certs)[0], state);
434             
435             state.hash().update(state.handshakeIo().format(contents, type));
436             
437             /*
438             * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for
439             * "A handshake cryptographic operation failed, including being
440             * unable to correctly verify a signature, ..."
441             */
442             if (!sig_valid)
443                 throw new TLSException(TLSAlert.DECRYPT_ERROR, "TLSClient cert verify failed");
444             
445             try
446             {
447                 m_creds.verifyCertificateChain("tls-server", "", *client_certs);
448             }
449             catch(Exception e)
450             {
451                 throw new TLSException(TLSAlert.BAD_CERTIFICATE, e.msg);
452             }
453             
454             state.setExpectedNext(HANDSHAKE_CCS);
455         }
456         else if (type == HANDSHAKE_CCS)
457         {
458 
459             if (state.originalHandshakeHash().length == 0)
460                 state.setExpectedNext(FINISHED);
461             else state.setExpectedNext(CHANNEL_ID);
462 
463             changeCipherSpecReader(SERVER);            
464         }
465         else if (type == CHANNEL_ID) {
466             // TODO: decode and verify
467 
468             state.setExpectedNext(FINISHED);
469         }
470         else if (type == FINISHED)
471         {
472             state.setExpectedNext(HANDSHAKE_NONE);
473             
474             state.clientFinished(new Finished(contents.dup));
475             
476             if (!state.clientFinished().verify(state, CLIENT))
477                 throw new TLSException(TLSAlert.DECRYPT_ERROR, "Finished message didn't verify");
478             
479             if (!state.serverFinished())
480             {
481 
482                 // already sent finished if resuming, so this is a new session
483                 
484                 state.hash().update(state.handshakeIo().format(contents, type));
485 
486                 // Save Handshake Hash for ChannelID validation
487                 if (state.clientHello().supportsChannelID() && state.serverHello().supportsChannelID())
488                     state.setOriginalHandshakeHash(state.hash().flushInto(state.Version(), state.ciphersuite().prfAlgo()));
489 
490                 auto session_info =   new TLSSession(state.serverHello().sessionId().dup,
491                                                      state.sessionKeys().masterSecret().dup,
492                                                      state.originalHandshakeHash().dup,
493                                                      state.serverHello().Version(),
494                                                      state.serverHello().ciphersuite(),
495                                                      state.serverHello().compressionMethod(),
496                                                      SERVER,
497                                                      state.serverHello().fragmentSize(),
498                                                      state.serverHello().supportsExtendedMasterSecret(),
499                                                      getPeerCertChain(state),
500                                                      Vector!ubyte(),
501                                                      TLSServerInformation(state.clientHello().sniHostname()),
502                                                      state.srpIdentifier()
503                     );
504                 
505                 if (saveSession(session_info))
506                 {
507                     if (state.serverHello().supportsSessionTicket())
508                     {
509                         try
510                         {
511                             const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
512                             
513                             state.newSessionTicket(
514                                 new NewSessionTicket(state.handshakeIo(),
515                                                      state.hash(),
516                                                      session_info.encrypt(ticket_key, rng()),
517                                                      m_policy.sessionTicketLifetime())
518                                 );
519                         }
520                         catch (Exception) {}
521                     }
522                     else
523                         sessionManager().save(session_info);
524                 }
525                 
526                 if (!state.newSessionTicket() && state.serverHello().supportsSessionTicket())
527                 {
528                     state.newSessionTicket(new NewSessionTicket(state.handshakeIo(), state.hash()));
529                 }
530                 
531                 state.handshakeIo().send(scoped!ChangeCipherSpec());
532                 
533                 changeCipherSpecWriter(SERVER);
534                 
535                 state.serverFinished(new Finished(state.handshakeIo(), state, SERVER));
536             }
537             activateSession();
538         }
539         else
540             throw new TLSUnexpectedMessage("Unknown handshake message received");
541     }
542 
543     override HandshakeState newHandshakeState(HandshakeIO io)
544     {
545         HandshakeState state = new ServerHandshakeState(io);
546         state.setExpectedNext(CLIENT_HELLO);
547         return state;
548     }
549 
550 	void switchContext(SNIContextSwitchInfo info)
551 	{
552 		m_creds = info.credentials;
553 		m_session_manager = info.session_manager;
554 		m_policy = info.policy;
555 		m_choose_next_protocol = info.next_proto;
556 		m_user_data = info.user_data;
557 	}
558 
559 private:
560     TLSPolicy m_policy;
561     TLSCredentialsManager m_creds;
562 
563     NextProtocolHandler m_choose_next_protocol;
564 	SNIHandler m_sni_handler;
565 	void* m_user_data;
566 }
567 
568 private:
569 
570 bool checkForResume(ref TLSSession session_info,
571                     TLSSessionManager session_manager,
572                     TLSCredentialsManager credentials,
573                     in ClientHello clientHello,
574                     Duration session_ticket_lifetime)
575 {
576     const(Vector!ubyte)* client_session_id = &clientHello.sessionId();
577     const Vector!ubyte session_ticket = clientHello.sessionTicket();
578     
579     if (session_ticket.empty)
580     {
581         if (client_session_id.empty) // not resuming
582             return false;
583         
584         // not found
585         if (!session_manager.loadFromSessionId(*client_session_id, session_info))
586             return false;
587     }
588     else
589     {
590         // If a session ticket was sent, ignore client session ID
591         try
592         {
593             session_info = TLSSession.decrypt(session_ticket,
594             credentials.psk("tls-server", "session-ticket", ""));
595             
596             if (session_ticket_lifetime != Duration.init &&
597                 session_info.sessionAge() > session_ticket_lifetime)
598                 return false; // ticket has expired
599         }
600         catch (Exception)
601         {
602             return false;
603         }
604     }
605     
606 	if (!session_info) 
607 		return false;
608 
609     // wrong version
610     if (clientHello.Version() != session_info.Version())
611         return false;
612 	import std.algorithm : canFind;
613     // client didn't send original ciphersuite
614     if (!clientHello.ciphersuitesData().canFind(session_info.ciphersuiteCode()))
615         return false;
616     
617     // client didn't send original compression method
618     if (!valueExists(clientHello.compressionMethods(),
619                       session_info.compressionMethod()))
620         return false;
621     
622     // client sent a different SRP identity
623     if (clientHello.srpIdentifier() != "")
624     {
625         if (clientHello.srpIdentifier() != session_info.srpIdentifier())
626             return false;
627     }
628     
629     // client sent a different SNI hostname
630     if (clientHello.sniHostname() != "")
631     {
632         if (clientHello.sniHostname() != session_info.serverInfo().hostname())
633             return false;
634     }
635 
636 	if (clientHello.supportsExtendedMasterSecret() != session_info.supportsExtendedMasterSecret())
637 	{
638 		if (!session_info.supportsExtendedMasterSecret())
639 			return false;
640 		else {
641 			throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "Client resumed extended ms session without sending extension");
642 		}
643 	}
644 
645     return true;
646 }
647 
648 /*
649 * Choose which ciphersuite to use
650 */
651 ushort chooseCiphersuite(in TLSPolicy policy,
652                          TLSProtocolVersion _version,
653                          TLSCredentialsManager creds,
654                          in HashMapRef!(string, Array!X509Certificate) cert_chains,
655                          in ClientHello client_hello)
656 {
657 	import std.algorithm : canFind;
658     const bool our_choice = policy.serverUsesOwnCiphersuitePreferences();
659     
660     const bool have_srp = creds.attemptSrp("tls-server", client_hello.sniHostname());
661     
662     ushort[] client_suites = cast(ushort[])client_hello.ciphersuitesData();
663     
664     const Vector!ushort server_suites = policy.ciphersuiteList(_version, have_srp);
665     
666     if (server_suites.empty)
667         throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSPolicy forbids us from negotiating any ciphersuite");
668     
669     const bool have_shared_ecc_curve = (policy.chooseCurve(client_hello.supportedEccCurves()) != "");
670     
671     ushort[] pref_list = cast()server_suites[];
672        
673     if (!our_choice)
674         pref_list = client_suites;
675 	debug {
676 		string dbg_msg;
677 		string dbg_sig;
678 	}
679     foreach (suite_id; pref_list)
680     {
681         if (!client_suites.canFind(suite_id))
682             continue;
683         
684         TLSCiphersuite suite = TLSCiphersuite.byId(suite_id);
685         
686         if (!have_shared_ecc_curve && suite.eccCiphersuite())
687             continue;
688         
689         if (suite.sigAlgo() != "" && cert_chains.get(suite.sigAlgo(), Array!X509Certificate(0)) == Array!X509Certificate(0))
690 		{
691 			debug  {
692 				dbg_msg = "Server Certificate type did not match a type that was accepted from policy: ";
693 				dbg_sig = suite.sigAlgo;
694 			}
695             continue;
696 		}
697         /*
698         The client may offer SRP cipher suites in the hello message but
699         omit the SRP extension.  If the server would like to select an
700         SRP cipher suite in this case, the server SHOULD return a fatal
701         "unknown_psk_identity" alert immediately after processing the
702         client hello message.
703          - RFC 5054 section 2.5.1.2
704         */
705         if (suite.kexAlgo() == "SRP_SHA" && client_hello.srpIdentifier() == "")
706             throw new TLSException(TLSAlert.UNKNOWN_PSK_IDENTITY,
707                                     "TLSClient wanted SRP but did not send username");
708         
709         return suite_id;
710     }
711 	debug logDebug(dbg_msg, dbg_sig);
712     throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "Can't agree on a ciphersuite with client");
713 }
714 
715 /*
716 * Choose which compression algorithm to use
717 */
718 ubyte chooseCompression(in TLSPolicy policy, const ref Vector!ubyte c_comp)
719 {
720     Vector!ubyte s_comp = policy.compression();
721     
722     for (size_t i = 0; i != s_comp.length; ++i)
723         for (size_t j = 0; j != c_comp.length; ++j)
724             if (s_comp[i] == c_comp[j])
725                 return s_comp[i];
726     
727     return NO_COMPRESSION;
728 }
729 
730 HashMapRef!(string, Array!X509Certificate) 
731     getServerCerts(in string hostname, TLSCredentialsManager creds)
732 {
733     string[] cert_types = [ "RSA", "DSA", "ECDSA", null ];
734     
735     HashMapRef!(string, Array!X509Certificate) cert_chains;
736     
737     for (size_t i = 0; cert_types[i]; ++i)
738     {
739         Vector!X509Certificate certs = creds.certChainSingleType(cert_types[i], "tls-server", hostname);
740         
741         if (!certs.empty)
742             cert_chains[cert_types[i]] = certs.dupr;
743     }
744     
745     return cert_chains;
746 }
747 
748 private final class ServerHandshakeState : HandshakeState
749 {
750 public:    
751     this(HandshakeIO io)
752     {
753         super(io);
754     }
755     
756     // Used by the server only, in case of RSA key exchange. Not owned
757     PrivateKey server_rsa_kex_key = null;
758     
759     /*
760     * Used by the server to know if resumption should be allowed on
761     * a server-initiated renegotiation
762     */
763     bool allow_session_resumption = true;
764 }