6f39135fab8ab5ecff5215aac26bd1c8be7cadde
[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
457 /*
458 * Recompute list of acceptable cipher suites. We keep
459 * only suites which are common to the client and server,
460 * with some extra filters.
461 *
462 * Note that when using static ECDH, it is up to the
463 * policy callback to determine whether the curves match
464 * the contents of the certificate.
465 *
466 * We also build a list of common suites for session
467 * resumption: this one may include suites whose
468 * asymmetric crypto is not supported, because session
469 * resumption uses only symmetric crypto.
470 */
471 CommonCipherSuites = new List<int>();
472 List<int> commonSuitesResume = new List<int>();
473 bool canTLS12 = Version >= SSL.TLS12;
474 bool canSignRSA;
475 bool canSignECDSA;
476 if (Version >= SSL.TLS12) {
477 canSignRSA = false;
478 canSignECDSA = false;
479 foreach (int alg in ClientHashAndSign) {
480 int sa = alg & 0xFF;
481 switch (sa) {
482 case SSL.RSA: canSignRSA = true; break;
483 case SSL.ECDSA: canSignECDSA = true; break;
484 }
485 }
486 } else {
487 /*
488 * For pre-1.2, the hash-and-sign configuration does
489 * not matter, only the cipher suites themselves. So
490 * we claim support of both RSA and ECDSA signatures
491 * to avoid trimming the list too much.
492 */
493 canSignRSA = true;
494 canSignECDSA = true;
495 }
496 bool canECDHE = CommonCurves.Count > 0;
497
498 foreach (int cs in clientSuites) {
499 if (!canTLS12 && SSL.IsTLS12(cs)) {
500 continue;
501 }
502 commonSuitesResume.Add(cs);
503 if (!canECDHE && SSL.IsECDHE(cs)) {
504 continue;
505 }
506 if (!canSignRSA && SSL.IsECDHE_RSA(cs)) {
507 continue;
508 }
509 if (!canSignECDSA && SSL.IsECDHE_ECDSA(cs)) {
510 continue;
511 }
512 CommonCipherSuites.Add(cs);
513 }
514 CommonCipherSuites = FilterList(CommonCipherSuites,
515 SupportedCipherSuites, EnforceServerOrder);
516 commonSuitesResume = FilterList(commonSuitesResume,
517 SupportedCipherSuites, EnforceServerOrder);
518
519 /*
520 * If resuming, then use the remembered session parameters,
521 * but only if they are compatible with what the client
522 * sent AND what we currently support.
523 */
524 SSLSessionParameters sp = null;
525 if (idLen > 0 && !NoResume && SessionCache != null) {
526 sp = SessionCache.Retrieve(
527 clientSessionID, ServerName);
528 if (sp != null && sp.ServerName != null
529 && ServerName != null)
530 {
531 /*
532 * When resuming a session, if there is
533 * an explicit name sent by the client,
534 * and the cached parameters also include
535 * an explicit name, then both names
536 * shall match.
537 */
538 string s1 = sp.ServerName.ToLowerInvariant();
539 string s2 = ServerName.ToLowerInvariant();
540 if (s1 != s2) {
541 sp = null;
542 }
543 }
544 }
545 if (sp != null) {
546 bool resumeOK = true;
547 if (sp.Version < VersionMin
548 || sp.Version > VersionMax
549 || sp.Version > ClientVersionMax)
550 {
551 resumeOK = false;
552 }
553 if (!commonSuitesResume.Contains(sp.CipherSuite)) {
554 resumeOK = false;
555 }
556
557 if (resumeOK) {
558 /*
559 * Session resumption is acceptable.
560 */
561 resume = true;
562 sessionID = clientSessionID;
563 Version = sp.Version;
564 CipherSuite = sp.CipherSuite;
565 sessionID = clientSessionID;
566 SetMasterSecret(sp.MasterSecret);
567 return true;
568 }
569 }
570
571 /*
572 * Not resuming. Let's select parameters.
573 * Protocol version was already set.
574 */
575 if (CommonCipherSuites.Count == 0) {
576 throw new SSLException("No common cipher suite");
577 }
578 serverChoices = ServerPolicy.Apply(this);
579 CipherSuite = serverChoices.GetCipherSuite();
580
581 /*
582 * We create a new session ID, even if we don't have a
583 * session cache, because the session parameters could
584 * be extracted manually by the application.
585 */
586 sessionID = new byte[32];
587 RNG.GetBytes(sessionID);
588
589 return true;
590 }
591
592 void ParseExtSNI(byte[] buf, int off, int len)
593 {
594 if (len < 2) {
595 throw new SSLException("Invalid SNI extension");
596 }
597 int tlen = IO.Dec16be(buf, off);
598 off += 2;
599 if ((tlen + 2) != len) {
600 throw new SSLException("Invalid SNI extension");
601 }
602 int lim = off + tlen;
603 bool found = false;
604 while (off < lim) {
605 if ((off + 3) > lim) {
606 throw new SSLException("Invalid SNI extension");
607 }
608 int ntype = buf[off ++];
609 int nlen = IO.Dec16be(buf, off);
610 off += 2;
611 if ((off + nlen) > lim) {
612 throw new SSLException("Invalid SNI extension");
613 }
614 if (ntype == 0) {
615 /*
616 * Name type is "host name". There shall be
617 * only one (at most) in the extension.
618 */
619 if (found) {
620 throw new SSLException("Several host"
621 + " names in SNI extension");
622 }
623 found = true;
624
625 /*
626 * Verify that the name contains only
627 * printable non-space ASCII, and normalise
628 * it to lowercase.
629 */
630 char[] tc = new char[nlen];
631 for (int i = 0; i < nlen; i ++) {
632 int x = buf[off + i];
633 if (x <= 32 || x >= 126) {
634 throw new SSLException(
635 "Invalid SNI hostname");
636 }
637 if (x >= 'A' && x <= 'Z') {
638 x += ('a' - 'A');
639 }
640 tc[i] = (char)x;
641 }
642 ServerName = new string(tc);
643 }
644 off += nlen;
645 }
646 }
647
648 void ParseExtSignatures(byte[] buf, int off, int len)
649 {
650 if (len < 2) {
651 throw new SSLException("Invalid signatures extension");
652 }
653 int tlen = IO.Dec16be(buf, off);
654 off += 2;
655 if (len != (tlen + 2)) {
656 throw new SSLException("Invalid signatures extension");
657 }
658 if ((tlen & 1) != 0) {
659 throw new SSLException("Invalid signatures extension");
660 }
661 ClientHashAndSign = new List<int>();
662 while (tlen > 0) {
663 ClientHashAndSign.Add(IO.Dec16be(buf, off));
664 off += 2;
665 tlen -= 2;
666 }
667 }
668
669 void ParseExtCurves(byte[] buf, int off, int len)
670 {
671 if (len < 2) {
672 throw new SSLException("Invalid curves extension");
673 }
674 int tlen = IO.Dec16be(buf, off);
675 off += 2;
676 if (len != (tlen + 2)) {
677 throw new SSLException("Invalid curves extension");
678 }
679 if ((tlen & 1) != 0) {
680 throw new SSLException("Invalid curves extension");
681 }
682 ClientCurves = new List<int>();
683 while (tlen > 0) {
684 ClientCurves.Add(IO.Dec16be(buf, off));
685 off += 2;
686 tlen -= 2;
687 }
688 }
689
690 void ParseExtSecureReneg(byte[] buf, int off, int len)
691 {
692 if (len < 1 || len != 1 + buf[off]) {
693 throw new SSLException(
694 "Invalid Secure Renegotiation extension");
695 }
696 len --;
697 off ++;
698
699 if (renegSupport == 0) {
700 /*
701 * Initial handshake: extension MUST be empty.
702 */
703 if (len != 0) {
704 throw new SSLException(
705 "Non-empty Secure Renegotation"
706 + " on initial handshake");
707 }
708 renegSupport = 1;
709 } else {
710 /*
711 * Renegotiation: extension MUST contain the
712 * saved client Finished message.
713 */
714 if (len != 12) {
715 throw new SSLException(
716 "Wrong Secure Renegotiation value");
717 }
718 int z = 0;
719 for (int i = 0; i < 12; i ++) {
720 z |= savedClientFinished[i] ^ buf[off + i];
721 }
722 if (z != 0) {
723 throw new SSLException(
724 "Wrong Secure Renegotiation value");
725 }
726 }
727 }
728
729 void SendServerHello()
730 {
731 MemoryStream ms = StartHandshakeMessage(SSL.SERVER_HELLO);
732
733 // Protocol version
734 IO.Write16(ms, Version);
735
736 // Server random
737 ms.Write(serverRandom, 0, serverRandom.Length);
738
739 // Session ID
740 ms.WriteByte((byte)sessionID.Length);
741 ms.Write(sessionID, 0, sessionID.Length);
742
743 // Cipher suite
744 IO.Write16(ms, CipherSuite);
745
746 // Compression
747 ms.WriteByte(0x00);
748
749 // Extensions
750 MemoryStream chExt = new MemoryStream();
751
752 // Secure renegotiation
753 if (!GetQuirkBool("noSecureReneg")) {
754 byte[] exv = null;
755 if (renegSupport > 0) {
756 if (FirstHandshakeDone) {
757 exv = new byte[24];
758 Array.Copy(savedClientFinished, 0,
759 exv, 0, 12);
760 Array.Copy(savedServerFinished, 0,
761 exv, 12, 12);
762 } else {
763 exv = new byte[0];
764 }
765 }
766 if (GetQuirkBool("forceEmptySecureReneg")) {
767 exv = new byte[0];
768 } else if (GetQuirkBool("forceNonEmptySecureReneg")) {
769 exv = new byte[24];
770 } else if (GetQuirkBool("alterNonEmptySecureReneg")) {
771 if (exv.Length > 0) {
772 exv[exv.Length - 1] ^= 0x01;
773 }
774 } else if (GetQuirkBool("oversizedSecureReneg")) {
775 exv = new byte[255];
776 }
777
778 if (exv != null) {
779 IO.Write16(chExt, 0xFF01);
780 IO.Write16(chExt, exv.Length + 1);
781 chExt.WriteByte((byte)exv.Length);
782 chExt.Write(exv, 0, exv.Length);
783 }
784 }
785
786 // Extra extension with random contents.
787 int extraExt = GetQuirkInt("sendExtraExtension", -1);
788 if (extraExt >= 0) {
789 byte[] exv = new byte[extraExt >> 16];
790 RNG.GetBytes(exv);
791 IO.Write16(chExt, extraExt & 0xFFFF);
792 IO.Write16(chExt, exv.Length);
793 chExt.Write(exv, 0, exv.Length);
794 }
795
796 // Max Fragment Length
797 // ALPN
798 // FIXME
799
800 byte[] encExt = chExt.ToArray();
801 if (encExt.Length > 0) {
802 if (encExt.Length > 65535) {
803 throw new SSLException("Oversized extensions");
804 }
805 IO.Write16(ms, encExt.Length);
806 ms.Write(encExt, 0, encExt.Length);
807 }
808
809 EndHandshakeMessage(ms);
810 }
811
812 void SendCertificate()
813 {
814 MemoryStream ms = StartHandshakeMessage(SSL.CERTIFICATE);
815 byte[][] chain = serverChoices.GetCertificateChain();
816 int tlen = 0;
817 foreach (byte[] ec in chain) {
818 tlen += 3 + ec.Length;
819 }
820 if (tlen > 0xFFFFFC) {
821 throw new SSLException("Oversized certificate chain");
822 }
823 IO.Write24(ms, tlen);
824 foreach (byte[] ec in chain) {
825 IO.Write24(ms, ec.Length);
826 ms.Write(ec, 0, ec.Length);
827 }
828 EndHandshakeMessage(ms);
829 }
830
831 void SendServerKeyExchange()
832 {
833 if (CommonCurves.Count == 0) {
834 /*
835 * Since we filter cipher suites when parsing the
836 * ClientHello, this situation may happen only if
837 * the IServerPolicy callback goofed up.
838 */
839 throw new SSLException("No curve for ECDHE");
840 }
841 int curveID = CommonCurves[0];
842 ecdheCurve = SSL.GetCurveByID(curveID);
843
844 /*
845 * Generate our ephemeral ECDH secret and the point to
846 * send to the peer.
847 */
848 ecdheSecret = ecdheCurve.MakeRandomSecret();
849 byte[] P = ecdheCurve.GetGenerator(false);
850 ecdheCurve.Mul(P, ecdheSecret, P, false);
851
852 /*
853 * Generate to-be-signed:
854 * clientRandom 32 bytes
855 * serverRandom 32 bytes
856 * 0x03 curve is a "named curve"
857 * id curve identifier (two bytes)
858 * point public point (one-byte length + value)
859 */
860 byte[] tbs = new byte[64 + 4 + P.Length];
861 Array.Copy(clientRandom, 0, tbs, 0, 32);
862 Array.Copy(serverRandom, 0, tbs, 32, 32);
863 tbs[64] = 0x03;
864 IO.Enc16be(curveID, tbs, 65);
865 tbs[67] = (byte)P.Length;
866 Array.Copy(P, 0, tbs, 68, P.Length);
867
868 /*
869 * Obtain server signature.
870 */
871 int hashAlgo, sigAlgo;
872 byte[] sig = serverChoices.DoSign(tbs,
873 out hashAlgo, out sigAlgo);
874
875 /*
876 * Encode message.
877 */
878 MemoryStream ms = StartHandshakeMessage(
879 SSL.SERVER_KEY_EXCHANGE);
880 ms.Write(tbs, 64, tbs.Length - 64);
881 if (Version >= SSL.TLS12) {
882 ms.WriteByte((byte)hashAlgo);
883 ms.WriteByte((byte)sigAlgo);
884 }
885 IO.Write16(ms, sig.Length);
886 ms.Write(sig, 0, sig.Length);
887 EndHandshakeMessage(ms);
888 }
889
890 void SendServerHelloDone()
891 {
892 MemoryStream ms = StartHandshakeMessage(SSL.SERVER_HELLO_DONE);
893 EndHandshakeMessage(ms);
894 }
895
896 void ParseClientKeyExchange()
897 {
898 byte[] msg = ReadHandshakeMessageExpected(
899 SSL.CLIENT_KEY_EXCHANGE);
900 byte[] pms;
901 if (SSL.IsECDHE(CipherSuite)) {
902 /*
903 * Expecting a curve point; we are doing the
904 * ECDH ourselves.
905 */
906 if (msg.Length < 1 || msg.Length != 1 + msg[0]) {
907 throw new SSLException(
908 "Invalid ClientKeyExchange");
909 }
910 byte[] P = new byte[msg.Length - 1];
911 byte[] D = new byte[ecdheCurve.EncodedLength];
912 Array.Copy(msg, 1, P, 0, P.Length);
913 if (ecdheCurve.Mul(P, ecdheSecret, D, false) == 0) {
914 throw new SSLException(
915 "Invalid ClientKeyExchange");
916 }
917 int xlen;
918 int xoff = ecdheCurve.GetXoff(out xlen);
919 pms = new byte[xlen];
920 Array.Copy(D, xoff, pms, 0, xlen);
921
922 /*
923 * Memory wiping is out of scope for this library,
924 * and is unreliable anyway in the presence of
925 * a moving garbage collector. So we just unlink
926 * the secret array.
927 */
928 ecdheSecret = null;
929 } else {
930 /*
931 * RSA or static ECDH. The crypto operation is done
932 * by the relevant callback.
933 */
934 if (msg.Length < 2) {
935 throw new SSLException(
936 "Invalid ClientKeyExchange");
937 }
938 int off, len;
939 if (SSL.IsRSA(CipherSuite)) {
940 len = IO.Dec16be(msg, 0);
941 off = 2;
942 } else if (SSL.IsECDH(CipherSuite)) {
943 len = msg[0];
944 off = 1;
945 } else {
946 throw new Exception("NYI");
947 }
948 if (msg.Length != off + len) {
949 throw new SSLException(
950 "Invalid ClientKeyExchange");
951 }
952 byte[] cke = new byte[len];
953 Array.Copy(msg, off, cke, 0, len);
954 pms = serverChoices.DoKeyExchange(cke);
955 }
956
957 ComputeMaster(pms);
958 }
959
960 internal override void ProcessExtraHandshake()
961 {
962 /*
963 * If Secure Renegotiation is supported, then we accept
964 * to do a new handshake.
965 */
966 if (renegSupport > 0) {
967 DoHandshake();
968 return;
969 }
970
971 /*
972 * We must read and discard an incoming ClientHello,
973 * then politely refuse.
974 */
975 ReadHandshakeMessageExpected(SSL.CLIENT_HELLO);
976 SendWarning(SSL.NO_RENEGOTIATION);
977 SetAppData();
978 }
979
980 internal override void PrepareRenegotiate()
981 {
982 MemoryStream ms = StartHandshakeMessage(SSL.HELLO_REQUEST);
983 EndHandshakeMessage(ms);
984 FlushSub();
985 }
986
987 /*
988 * Compute the intersection of two lists of integers (the second
989 * list is provided as an array). The intersection is returned
990 * as a new List<int> instance. If enforceV2 is true, then the
991 * order of items in the returned list will be that of v2; otherwise,
992 * it will be that of v1. Duplicates are removed.
993 */
994 static List<int> FilterList(List<int> v1, int[] v2, bool enforceV2)
995 {
996 List<int> r = new List<int>();
997 if (enforceV2) {
998 foreach (int x in v2) {
999 if (v1.Contains(x) && !r.Contains(x)) {
1000 r.Add(x);
1001 }
1002 }
1003 } else {
1004 foreach (int x in v1) {
1005 foreach (int y in v2) {
1006 if (x == y) {
1007 if (!r.Contains(x)) {
1008 r.Add(x);
1009 }
1010 break;
1011 }
1012 }
1013 }
1014 }
1015 return r;
1016 }
1017 }
1018
1019 }