Added test for a server choosing a TLS-1.2 cipher suite with a pre-1.2 protocol versi...
[BoarSSL] / SSLTLS / SSLServer.cs
1 /*
2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 using System;
26 using System.Collections.Generic;
27 using System.IO;
28 using System.Text;
29
30 using Crypto;
31
32 namespace SSLTLS {
33
34 /*
35 * Class for a SSL server connection.
36 *
37 * An instance is created over a specified transport stream. An optional
38 * cache for session parameters can be provided, to support session
39 * resumption. The instance handles the connection but cannot be
40 * "revived" after the connection was closed (the session parameters,
41 * though, can be extracted and used with another instance).
42 */
43
44 public class SSLServer : SSLEngine {
45
46 /*
47 * If true, then the server will enforce its own preference
48 * order for cipher suite selection; otherwise, it will follow
49 * the client's preferences. Default value is false.
50 */
51 public bool EnforceServerOrder {
52 get; set;
53 }
54
55 /*
56 * Server policy object, that selects cipher suite and certificate
57 * chain to send to client. Such a policy object MUST be set
58 * before the initial handshake takes place. This property is
59 * initialised to the value provided as second argument to the
60 * SSLServer constructor.
61 */
62 public IServerPolicy ServerPolicy {
63 get; set;
64 }
65
66 /*
67 * Optional session cache for SSL sessions. If null, then no
68 * cache is used. Default value is null.
69 */
70 public ISessionCache SessionCache {
71 get; set;
72 }
73
74 /*
75 * If this flag is set to true, then session resumption will be
76 * rejected; all handshakes will be full handshakes. Main
77 * intended usage is when a server wants to renegotiate and ask
78 * for a client certificate. Note that even if that flag is set,
79 * each session resulting from a full handshake is still pushed
80 * to the session cache (if configured in SessionCache).
81 * Default value is false, meaning that session resumption is
82 * allowed (but won't happen anyway if no session cache was
83 * set in SessionCache).
84 */
85 public bool NoResume {
86 get; set;
87 }
88
89 /*
90 * Get the maximum supported version announced by the client
91 * in its ClientHello message.
92 */
93 public int ClientVersionMax {
94 get; private set;
95 }
96
97 /*
98 * Get the list of hash and signature algorithms supported by the
99 * client. Each value is a 16-bit integer, with the high byte
100 * being the hash algorithm, and the low byte the signature
101 * algorithm.
102 *
103 * The list is trimmed to include only hash and signature algorithms
104 * that are supported by both client and server. It is ordered
105 * by client or server preference, depending on the value of
106 * the EnforceServerOrder flag.
107 *
108 * If the client did not send the dedicated extension, then the
109 * list is inferred from the sent cipher suite, as specified
110 * by RFC 5246, section 7.4.1.4.1.
111 */
112 public List<int> ClientHashAndSign {
113 get; internal set;
114 }
115
116 /*
117 * Get the list of elliptic curves supported by the client. Each
118 * entry is a 16-bit integer that identifies a named curve. The
119 * list is ordered by client preferences.
120 *
121 * If the client did not send the Supported Curves extension,
122 * then the list will be inferred to contain NIST P-256 only
123 * (if the client supports at least one ECDH, ECDHE or ECDSA
124 * cipher suite), or to be empty (if the client does not support
125 * any EC-based cipher suite).
126 */
127 public List<int> ClientCurves {
128 get; internal set;
129 }
130
131 /*
132 * Get the list of elliptic curves supported by both client and
133 * server. Each entry is a 16-bit integer that identifies a
134 * named curve. The list is ordered by preference (client or
135 * server, depending on configuration). This list is the one
136 * used for curve selection for ECDHE.
137 */
138 public List<int> CommonCurves {
139 get; internal set;
140 }
141
142 /*
143 * Get the list of cipher suites supported by the client. The
144 * order matches the configured preferences (client or server
145 * preference order, depending on the EnforceServerOrder flag).
146 * Moreover, the list is trimmed:
147 *
148 * - Signalling cipher suites ("SCSV") have been removed.
149 *
150 * - Only suites supported by both client and server are kept.
151 *
152 * - Suites that require TLS 1.2 are omitted if the selected
153 * protocol version is TLS 1.0 or 1.1.
154 *
155 * - Suites that require client support for RSA signatures are
156 * removed if there is no common support for RSA signatures.
157 *
158 * - Suites that require client support for ECDSA signatures
159 * are removed if there is no common support for ECDSA
160 * signatures.
161 *
162 * - ECDHE suites are removed if there is no common support for
163 * elliptic curves.
164 */
165 public List<int> CommonCipherSuites {
166 get; internal set;
167 }
168
169 IServerChoices serverChoices;
170 ECCurve ecdheCurve;
171 byte[] ecdheSecret;
172
173 /*
174 * Create an SSL server instance, over the provided stream.
175 * The 'serverPolicy' parameter is used as initial value to
176 * the ServerPolicy property.
177 */
178 public SSLServer(Stream sub, IServerPolicy serverPolicy)
179 : base(sub)
180 {
181 EnforceServerOrder = false;
182 ServerPolicy = serverPolicy;
183 }
184
185 internal override bool IsClient {
186 get {
187 return false;
188 }
189 }
190
191 internal override bool DoHandshake()
192 {
193 CheckConfigHashAndSign();
194
195 ResetHashes();
196 MakeRandom(serverRandom);
197
198 bool resume;
199 if (!ParseClientHello(out resume)) {
200 return false;
201 }
202 HandshakeCount ++;
203 SetOutputRecordVersion(Version);
204 SetInputRecordVersion(Version);
205
206 if (resume) {
207 SendServerHello();
208 SendCCSAndFinished();
209 FlushSub();
210 ParseCCSAndFinished();
211 SetAppData();
212 IsResume = true;
213 return true;
214 }
215
216 SendServerHello();
217 SendCertificate();
218 if (SSL.IsECDHE(CipherSuite)) {
219 SendServerKeyExchange();
220 }
221 SendServerHelloDone();
222 FlushSub();
223
224 ParseClientKeyExchange();
225 ParseCCSAndFinished();
226 SendCCSAndFinished();
227 FlushSub();
228 SetAppData();
229 IsResume = false;
230 if (SessionCache != null) {
231 SessionCache.Store(SessionParameters);
232 }
233 return true;
234 }
235
236 bool ParseClientHello(out bool resume)
237 {
238 resume = false;
239 int mt;
240 byte[] msg = ReadHandshakeMessage(out mt, FirstHandshakeDone);
241 if (msg == null) {
242 /*
243 * Client rejected renegotiation attempt. This cannot
244 * happen if we are invoked from
245 * ProcessExtraHandshake() because that method is
246 * invoked only when there is buffered handshake
247 * data.
248 */
249 return false;
250 }
251 if (mt != SSL.CLIENT_HELLO) {
252 throw new SSLException(string.Format("Unexpected"
253 + " handshake message {0} (expecting a"
254 + " ClientHello)", mt));
255 }
256
257 /*
258 * Maximum protocol version supported by the client.
259 */
260 if (msg.Length < 35) {
261 throw new SSLException("Invalid ClientHello");
262 }
263 ClientVersionMax = IO.Dec16be(msg, 0);
264 if (ClientVersionMax < VersionMin) {
265 throw new SSLException(string.Format(
266 "No acceptable version (client max = 0x{0:X4})",
267 ClientVersionMax));
268 }
269
270 /*
271 * Client random (32 bytes).
272 */
273 Array.Copy(msg, 2, clientRandom, 0, 32);
274
275 /*
276 * Session ID sent by the client: at most 32 bytes.
277 */
278 int idLen = msg[34];
279 int off = 35;
280 if (idLen > 32 || (off + idLen) > msg.Length) {
281 throw new SSLException("Invalid ClientHello");
282 }
283 byte[] clientSessionID = new byte[idLen];
284 Array.Copy(msg, off, clientSessionID, 0, idLen);
285 off += idLen;
286
287 /*
288 * List of client cipher suites.
289 */
290 if ((off + 2) > msg.Length) {
291 throw new SSLException("Invalid ClientHello");
292 }
293 int csLen = IO.Dec16be(msg, off);
294 off += 2;
295 if ((off + csLen) > msg.Length) {
296 throw new SSLException("Invalid ClientHello");
297 }
298 List<int> clientSuites = new List<int>();
299 bool seenReneg = false;
300 while (csLen > 0) {
301 int cs = IO.Dec16be(msg, off);
302 off += 2;
303 csLen -= 2;
304 if (cs == SSL.FALLBACK_SCSV) {
305 if (ClientVersionMax < VersionMax) {
306 throw new SSLException(
307 "Undue fallback detected");
308 }
309 } else if (cs == SSL.EMPTY_RENEGOTIATION_INFO_SCSV) {
310 if (FirstHandshakeDone) {
311 throw new SSLException(
312 "Reneg SCSV in renegotiation");
313 }
314 seenReneg = true;
315 } else {
316 clientSuites.Add(cs);
317 }
318 }
319
320 /*
321 * List of compression methods. We only accept method 0
322 * (no compression).
323 */
324 if ((off + 1) > msg.Length) {
325 throw new SSLException("Invalid ClientHello");
326 }
327 int compLen = msg[off ++];
328 if ((off + compLen) > msg.Length) {
329 throw new SSLException("Invalid ClientHello");
330 }
331 bool foundUncompressed = false;
332 while (compLen -- > 0) {
333 if (msg[off ++] == 0x00) {
334 foundUncompressed = true;
335 }
336 }
337 if (!foundUncompressed) {
338 throw new SSLException("No common compression support");
339 }
340
341 /*
342 * Extensions.
343 */
344 ClientHashAndSign = null;
345 ClientCurves = null;
346 if (off < msg.Length) {
347 if ((off + 2) > msg.Length) {
348 throw new SSLException("Invalid ClientHello");
349 }
350 int tlen = IO.Dec16be(msg, off);
351 off += 2;
352 if ((off + tlen) != msg.Length) {
353 throw new SSLException("Invalid ClientHello");
354 }
355 while (off < msg.Length) {
356 if ((off + 4) > msg.Length) {
357 throw new SSLException(
358 "Invalid ClientHello");
359 }
360 int etype = IO.Dec16be(msg, off);
361 int elen = IO.Dec16be(msg, off + 2);
362 off += 4;
363 if ((off + elen) > msg.Length) {
364 throw new SSLException(
365 "Invalid ClientHello");
366 }
367 switch (etype) {
368
369 case 0x0000:
370 ParseExtSNI(msg, off, elen);
371 break;
372
373 case 0x000D:
374 ParseExtSignatures(msg, off, elen);
375 break;
376
377 case 0x000A:
378 ParseExtCurves(msg, off, elen);
379 break;
380
381 case 0xFF01:
382 ParseExtSecureReneg(msg, off, elen);
383 seenReneg = true;
384 break;
385
386 // Max Frag Length
387 // ALPN
388 // FIXME
389 }
390
391 off += elen;
392 }
393 }
394
395 /*
396 * If we are renegotiating and we did not see the
397 * Secure Renegotiation extension, then this is an error.
398 */
399 if (FirstHandshakeDone && !seenReneg) {
400 throw new SSLException(
401 "Missing Secure Renegotiation extension");
402 }
403
404 /*
405 * Use prescribed default values for supported algorithms
406 * and curves, when not otherwise advertised by the client.
407 */
408 if (ClientCurves == null) {
409 ClientCurves = new List<int>();
410 foreach (int cs in clientSuites) {
411 if (SSL.IsECDH(cs) || SSL.IsECDHE(cs)) {
412 ClientCurves.Add(SSL.NIST_P256);
413 break;
414 }
415 }
416 }
417 if (ClientHashAndSign == null) {
418 bool withRSA = false;
419 bool withECDSA = false;
420 foreach (int cs in clientSuites) {
421 if (SSL.IsRSA(cs)
422 || SSL.IsECDH_RSA(cs)
423 || SSL.IsECDHE_RSA(cs))
424 {
425 withRSA = true;
426 }
427 if (SSL.IsECDH_ECDSA(cs)
428 || SSL.IsECDHE_ECDSA(cs))
429 {
430 withECDSA = true;
431 }
432 }
433 ClientHashAndSign = new List<int>();
434 if (withRSA) {
435 ClientHashAndSign.Add(SSL.RSA_SHA1);
436 }
437 if (withECDSA) {
438 ClientHashAndSign.Add(SSL.ECDSA_SHA1);
439 }
440 }
441
442 /*
443 * Filter curves and algorithms with regards to our own
444 * configuration.
445 */
446 CommonCurves = FilterList(ClientCurves,
447 SupportedCurves, EnforceServerOrder);
448 ClientHashAndSign = FilterList(ClientHashAndSign,
449 SupportedHashAndSign, EnforceServerOrder);
450
451 /*
452 * Selected protocol version (can be overridden by
453 * resumption).
454 */
455 Version = Math.Min(ClientVersionMax, VersionMax);
456 string forcedVersion = GetQuirkString("forceVersion");
457 if (forcedVersion != null) {
458 switch (forcedVersion) {
459 case "TLS10": Version = SSL.TLS10; break;
460 case "TLS11": Version = SSL.TLS11; break;
461 case "TLS12": Version = SSL.TLS12; break;
462 default:
463 throw new Exception(string.Format(
464 "Unknown forced version: '{0}'",
465 forcedVersion));
466 }
467 }
468
469 /*
470 * Recompute list of acceptable cipher suites. We keep
471 * only suites which are common to the client and server,
472 * with some extra filters.
473 *
474 * Note that when using static ECDH, it is up to the
475 * policy callback to determine whether the curves match
476 * the contents of the certificate.
477 *
478 * We also build a list of common suites for session
479 * resumption: this one may include suites whose
480 * asymmetric crypto is not supported, because session
481 * resumption uses only symmetric crypto.
482 */
483 CommonCipherSuites = new List<int>();
484 List<int> commonSuitesResume = new List<int>();
485 bool canTLS12 = Version >= SSL.TLS12;
486 bool mustTLS12 = false;
487 if (GetQuirkBool("forceTls12CipherSuite")) {
488 canTLS12 = true;
489 mustTLS12 = true;
490 }
491 bool canSignRSA;
492 bool canSignECDSA;
493 if (Version >= SSL.TLS12) {
494 canSignRSA = false;
495 canSignECDSA = false;
496 foreach (int alg in ClientHashAndSign) {
497 int sa = alg & 0xFF;
498 switch (sa) {
499 case SSL.RSA: canSignRSA = true; break;
500 case SSL.ECDSA: canSignECDSA = true; break;
501 }
502 }
503 } else {
504 /*
505 * For pre-1.2, the hash-and-sign configuration does
506 * not matter, only the cipher suites themselves. So
507 * we claim support of both RSA and ECDSA signatures
508 * to avoid trimming the list too much.
509 */
510 canSignRSA = true;
511 canSignECDSA = true;
512 }
513 bool canECDHE = CommonCurves.Count > 0;
514
515 foreach (int cs in clientSuites) {
516 if (!canTLS12 && SSL.IsTLS12(cs)) {
517 continue;
518 }
519 if (mustTLS12 && !SSL.IsTLS12(cs)) {
520 continue;
521 }
522 commonSuitesResume.Add(cs);
523 if (!canECDHE && SSL.IsECDHE(cs)) {
524 continue;
525 }
526 if (!canSignRSA && SSL.IsECDHE_RSA(cs)) {
527 continue;
528 }
529 if (!canSignECDSA && SSL.IsECDHE_ECDSA(cs)) {
530 continue;
531 }
532 CommonCipherSuites.Add(cs);
533 }
534 CommonCipherSuites = FilterList(CommonCipherSuites,
535 SupportedCipherSuites, EnforceServerOrder);
536 commonSuitesResume = FilterList(commonSuitesResume,
537 SupportedCipherSuites, EnforceServerOrder);
538
539 /*
540 * If resuming, then use the remembered session parameters,
541 * but only if they are compatible with what the client
542 * sent AND what we currently support.
543 */
544 SSLSessionParameters sp = null;
545 if (idLen > 0 && !NoResume && SessionCache != null) {
546 sp = SessionCache.Retrieve(
547 clientSessionID, ServerName);
548 if (sp != null && sp.ServerName != null
549 && ServerName != null)
550 {
551 /*
552 * When resuming a session, if there is
553 * an explicit name sent by the client,
554 * and the cached parameters also include
555 * an explicit name, then both names
556 * shall match.
557 */
558 string s1 = sp.ServerName.ToLowerInvariant();
559 string s2 = ServerName.ToLowerInvariant();
560 if (s1 != s2) {
561 sp = null;
562 }
563 }
564 }
565 if (sp != null) {
566 bool resumeOK = true;
567 if (sp.Version < VersionMin
568 || sp.Version > VersionMax
569 || sp.Version > ClientVersionMax)
570 {
571 resumeOK = false;
572 }
573 if (!commonSuitesResume.Contains(sp.CipherSuite)) {
574 resumeOK = false;
575 }
576
577 if (resumeOK) {
578 /*
579 * Session resumption is acceptable.
580 */
581 resume = true;
582 sessionID = clientSessionID;
583 Version = sp.Version;
584 CipherSuite = sp.CipherSuite;
585 sessionID = clientSessionID;
586 SetMasterSecret(sp.MasterSecret);
587 return true;
588 }
589 }
590
591 /*
592 * Not resuming. Let's select parameters.
593 * Protocol version was already set.
594 */
595 if (CommonCipherSuites.Count == 0) {
596 throw new SSLException("No common cipher suite");
597 }
598 serverChoices = ServerPolicy.Apply(this);
599 CipherSuite = serverChoices.GetCipherSuite();
600
601 /*
602 * We create a new session ID, even if we don't have a
603 * session cache, because the session parameters could
604 * be extracted manually by the application.
605 */
606 sessionID = new byte[32];
607 RNG.GetBytes(sessionID);
608
609 return true;
610 }
611
612 void ParseExtSNI(byte[] buf, int off, int len)
613 {
614 if (len < 2) {
615 throw new SSLException("Invalid SNI extension");
616 }
617 int tlen = IO.Dec16be(buf, off);
618 off += 2;
619 if ((tlen + 2) != len) {
620 throw new SSLException("Invalid SNI extension");
621 }
622 int lim = off + tlen;
623 bool found = false;
624 while (off < lim) {
625 if ((off + 3) > lim) {
626 throw new SSLException("Invalid SNI extension");
627 }
628 int ntype = buf[off ++];
629 int nlen = IO.Dec16be(buf, off);
630 off += 2;
631 if ((off + nlen) > lim) {
632 throw new SSLException("Invalid SNI extension");
633 }
634 if (ntype == 0) {
635 /*
636 * Name type is "host name". There shall be
637 * only one (at most) in the extension.
638 */
639 if (found) {
640 throw new SSLException("Several host"
641 + " names in SNI extension");
642 }
643 found = true;
644
645 /*
646 * Verify that the name contains only
647 * printable non-space ASCII, and normalise
648 * it to lowercase.
649 */
650 char[] tc = new char[nlen];
651 for (int i = 0; i < nlen; i ++) {
652 int x = buf[off + i];
653 if (x <= 32 || x >= 126) {
654 throw new SSLException(
655 "Invalid SNI hostname");
656 }
657 if (x >= 'A' && x <= 'Z') {
658 x += ('a' - 'A');
659 }
660 tc[i] = (char)x;
661 }
662 ServerName = new string(tc);
663 }
664 off += nlen;
665 }
666 }
667
668 void ParseExtSignatures(byte[] buf, int off, int len)
669 {
670 if (len < 2) {
671 throw new SSLException("Invalid signatures extension");
672 }
673 int tlen = IO.Dec16be(buf, off);
674 off += 2;
675 if (len != (tlen + 2)) {
676 throw new SSLException("Invalid signatures extension");
677 }
678 if ((tlen & 1) != 0) {
679 throw new SSLException("Invalid signatures extension");
680 }
681 ClientHashAndSign = new List<int>();
682 while (tlen > 0) {
683 ClientHashAndSign.Add(IO.Dec16be(buf, off));
684 off += 2;
685 tlen -= 2;
686 }
687 }
688
689 void ParseExtCurves(byte[] buf, int off, int len)
690 {
691 if (len < 2) {
692 throw new SSLException("Invalid curves extension");
693 }
694 int tlen = IO.Dec16be(buf, off);
695 off += 2;
696 if (len != (tlen + 2)) {
697 throw new SSLException("Invalid curves extension");
698 }
699 if ((tlen & 1) != 0) {
700 throw new SSLException("Invalid curves extension");
701 }
702 ClientCurves = new List<int>();
703 while (tlen > 0) {
704 ClientCurves.Add(IO.Dec16be(buf, off));
705 off += 2;
706 tlen -= 2;
707 }
708 }
709
710 void ParseExtSecureReneg(byte[] buf, int off, int len)
711 {
712 if (len < 1 || len != 1 + buf[off]) {
713 throw new SSLException(
714 "Invalid Secure Renegotiation extension");
715 }
716 len --;
717 off ++;
718
719 if (renegSupport == 0) {
720 /*
721 * Initial handshake: extension MUST be empty.
722 */
723 if (len != 0) {
724 throw new SSLException(
725 "Non-empty Secure Renegotation"
726 + " on initial handshake");
727 }
728 renegSupport = 1;
729 } else {
730 /*
731 * Renegotiation: extension MUST contain the
732 * saved client Finished message.
733 */
734 if (len != 12) {
735 throw new SSLException(
736 "Wrong Secure Renegotiation value");
737 }
738 int z = 0;
739 for (int i = 0; i < 12; i ++) {
740 z |= savedClientFinished[i] ^ buf[off + i];
741 }
742 if (z != 0) {
743 throw new SSLException(
744 "Wrong Secure Renegotiation value");
745 }
746 }
747 }
748
749 void SendServerHello()
750 {
751 MemoryStream ms = StartHandshakeMessage(SSL.SERVER_HELLO);
752
753 // Protocol version
754 IO.Write16(ms, Version);
755
756 // Server random
757 ms.Write(serverRandom, 0, serverRandom.Length);
758
759 // Session ID
760 ms.WriteByte((byte)sessionID.Length);
761 ms.Write(sessionID, 0, sessionID.Length);
762
763 // Cipher suite
764 IO.Write16(ms, CipherSuite);
765
766 // Compression
767 ms.WriteByte(0x00);
768
769 // Extensions
770 MemoryStream chExt = new MemoryStream();
771
772 // Secure renegotiation
773 if (!GetQuirkBool("noSecureReneg")) {
774 byte[] exv = null;
775 if (renegSupport > 0) {
776 if (FirstHandshakeDone) {
777 exv = new byte[24];
778 Array.Copy(savedClientFinished, 0,
779 exv, 0, 12);
780 Array.Copy(savedServerFinished, 0,
781 exv, 12, 12);
782 } else {
783 exv = new byte[0];
784 }
785 }
786 if (GetQuirkBool("forceEmptySecureReneg")) {
787 exv = new byte[0];
788 } else if (GetQuirkBool("forceNonEmptySecureReneg")) {
789 exv = new byte[24];
790 } else if (GetQuirkBool("alterNonEmptySecureReneg")) {
791 if (exv.Length > 0) {
792 exv[exv.Length - 1] ^= 0x01;
793 }
794 } else if (GetQuirkBool("oversizedSecureReneg")) {
795 exv = new byte[255];
796 }
797
798 if (exv != null) {
799 IO.Write16(chExt, 0xFF01);
800 IO.Write16(chExt, exv.Length + 1);
801 chExt.WriteByte((byte)exv.Length);
802 chExt.Write(exv, 0, exv.Length);
803 }
804 }
805
806 // Extra extension with random contents.
807 int extraExt = GetQuirkInt("sendExtraExtension", -1);
808 if (extraExt >= 0) {
809 byte[] exv = new byte[extraExt >> 16];
810 RNG.GetBytes(exv);
811 IO.Write16(chExt, extraExt & 0xFFFF);
812 IO.Write16(chExt, exv.Length);
813 chExt.Write(exv, 0, exv.Length);
814 }
815
816 // Max Fragment Length
817 // ALPN
818 // FIXME
819
820 byte[] encExt = chExt.ToArray();
821 if (encExt.Length > 0) {
822 if (encExt.Length > 65535) {
823 throw new SSLException("Oversized extensions");
824 }
825 IO.Write16(ms, encExt.Length);
826 ms.Write(encExt, 0, encExt.Length);
827 }
828
829 EndHandshakeMessage(ms);
830 }
831
832 void SendCertificate()
833 {
834 MemoryStream ms = StartHandshakeMessage(SSL.CERTIFICATE);
835 byte[][] chain = serverChoices.GetCertificateChain();
836 int tlen = 0;
837 foreach (byte[] ec in chain) {
838 tlen += 3 + ec.Length;
839 }
840 if (tlen > 0xFFFFFC) {
841 throw new SSLException("Oversized certificate chain");
842 }
843 IO.Write24(ms, tlen);
844 foreach (byte[] ec in chain) {
845 IO.Write24(ms, ec.Length);
846 ms.Write(ec, 0, ec.Length);
847 }
848 EndHandshakeMessage(ms);
849 }
850
851 void SendServerKeyExchange()
852 {
853 if (CommonCurves.Count == 0) {
854 /*
855 * Since we filter cipher suites when parsing the
856 * ClientHello, this situation may happen only if
857 * the IServerPolicy callback goofed up.
858 */
859 throw new SSLException("No curve for ECDHE");
860 }
861 int curveID = CommonCurves[0];
862 ecdheCurve = SSL.GetCurveByID(curveID);
863
864 /*
865 * Generate our ephemeral ECDH secret and the point to
866 * send to the peer.
867 */
868 ecdheSecret = ecdheCurve.MakeRandomSecret();
869 byte[] P = ecdheCurve.GetGenerator(false);
870 ecdheCurve.Mul(P, ecdheSecret, P, false);
871
872 /*
873 * Generate to-be-signed:
874 * clientRandom 32 bytes
875 * serverRandom 32 bytes
876 * 0x03 curve is a "named curve"
877 * id curve identifier (two bytes)
878 * point public point (one-byte length + value)
879 */
880 byte[] tbs = new byte[64 + 4 + P.Length];
881 Array.Copy(clientRandom, 0, tbs, 0, 32);
882 Array.Copy(serverRandom, 0, tbs, 32, 32);
883 tbs[64] = 0x03;
884 IO.Enc16be(curveID, tbs, 65);
885 tbs[67] = (byte)P.Length;
886 Array.Copy(P, 0, tbs, 68, P.Length);
887
888 /*
889 * Obtain server signature.
890 */
891 int hashAlgo, sigAlgo;
892 byte[] sig = serverChoices.DoSign(tbs,
893 out hashAlgo, out sigAlgo);
894
895 /*
896 * Encode message.
897 */
898 MemoryStream ms = StartHandshakeMessage(
899 SSL.SERVER_KEY_EXCHANGE);
900 ms.Write(tbs, 64, tbs.Length - 64);
901 if (Version >= SSL.TLS12) {
902 ms.WriteByte((byte)hashAlgo);
903 ms.WriteByte((byte)sigAlgo);
904 }
905 IO.Write16(ms, sig.Length);
906 ms.Write(sig, 0, sig.Length);
907 EndHandshakeMessage(ms);
908 }
909
910 void SendServerHelloDone()
911 {
912 MemoryStream ms = StartHandshakeMessage(SSL.SERVER_HELLO_DONE);
913 EndHandshakeMessage(ms);
914 }
915
916 void ParseClientKeyExchange()
917 {
918 byte[] msg = ReadHandshakeMessageExpected(
919 SSL.CLIENT_KEY_EXCHANGE);
920 byte[] pms;
921 if (SSL.IsECDHE(CipherSuite)) {
922 /*
923 * Expecting a curve point; we are doing the
924 * ECDH ourselves.
925 */
926 if (msg.Length < 1 || msg.Length != 1 + msg[0]) {
927 throw new SSLException(
928 "Invalid ClientKeyExchange");
929 }
930 byte[] P = new byte[msg.Length - 1];
931 byte[] D = new byte[ecdheCurve.EncodedLength];
932 Array.Copy(msg, 1, P, 0, P.Length);
933 if (ecdheCurve.Mul(P, ecdheSecret, D, false) == 0) {
934 throw new SSLException(
935 "Invalid ClientKeyExchange");
936 }
937 int xlen;
938 int xoff = ecdheCurve.GetXoff(out xlen);
939 pms = new byte[xlen];
940 Array.Copy(D, xoff, pms, 0, xlen);
941
942 /*
943 * Memory wiping is out of scope for this library,
944 * and is unreliable anyway in the presence of
945 * a moving garbage collector. So we just unlink
946 * the secret array.
947 */
948 ecdheSecret = null;
949 } else {
950 /*
951 * RSA or static ECDH. The crypto operation is done
952 * by the relevant callback.
953 */
954 if (msg.Length < 2) {
955 throw new SSLException(
956 "Invalid ClientKeyExchange");
957 }
958 int off, len;
959 if (SSL.IsRSA(CipherSuite)) {
960 len = IO.Dec16be(msg, 0);
961 off = 2;
962 } else if (SSL.IsECDH(CipherSuite)) {
963 len = msg[0];
964 off = 1;
965 } else {
966 throw new Exception("NYI");
967 }
968 if (msg.Length != off + len) {
969 throw new SSLException(
970 "Invalid ClientKeyExchange");
971 }
972 byte[] cke = new byte[len];
973 Array.Copy(msg, off, cke, 0, len);
974 pms = serverChoices.DoKeyExchange(cke);
975 }
976
977 ComputeMaster(pms);
978 }
979
980 internal override void ProcessExtraHandshake()
981 {
982 /*
983 * If Secure Renegotiation is supported, then we accept
984 * to do a new handshake.
985 */
986 if (renegSupport > 0) {
987 DoHandshake();
988 return;
989 }
990
991 /*
992 * We must read and discard an incoming ClientHello,
993 * then politely refuse.
994 */
995 ReadHandshakeMessageExpected(SSL.CLIENT_HELLO);
996 SendWarning(SSL.NO_RENEGOTIATION);
997 SetAppData();
998 }
999
1000 internal override void PrepareRenegotiate()
1001 {
1002 MemoryStream ms = StartHandshakeMessage(SSL.HELLO_REQUEST);
1003 EndHandshakeMessage(ms);
1004 FlushSub();
1005 }
1006
1007 /*
1008 * Compute the intersection of two lists of integers (the second
1009 * list is provided as an array). The intersection is returned
1010 * as a new List<int> instance. If enforceV2 is true, then the
1011 * order of items in the returned list will be that of v2; otherwise,
1012 * it will be that of v1. Duplicates are removed.
1013 */
1014 static List<int> FilterList(List<int> v1, int[] v2, bool enforceV2)
1015 {
1016 List<int> r = new List<int>();
1017 if (enforceV2) {
1018 foreach (int x in v2) {
1019 if (v1.Contains(x) && !r.Contains(x)) {
1020 r.Add(x);
1021 }
1022 }
1023 } else {
1024 foreach (int x in v1) {
1025 foreach (int y in v2) {
1026 if (x == y) {
1027 if (!r.Contains(x)) {
1028 r.Add(x);
1029 }
1030 break;
1031 }
1032 }
1033 }
1034 }
1035 return r;
1036 }
1037 }
1038
1039 }