Initial commit.
[BoarSSL] / SSLTLS / SSLClient.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 client connection.
36 *
37 * An instance is created over a specified transport stream. SSL session
38 * parameters from a previous connection are optionally specified, to
39 * attempt session resumption. The instance handles the connection but
40 * cannot be "revived" after the connection was closed (the session
41 * parameters, though, can be extracted and used with another instance).
42 */
43
44 public class SSLClient : SSLEngine {
45
46 /*
47 * Create the client over the provided stream. No attempt at
48 * session resumption will be made.
49 */
50 public SSLClient(Stream sub) : this(sub, null)
51 {
52 }
53
54 /*
55 * Create the client over the provided stream. If the
56 * 'sessionParameters' are not null, then the client will try to
57 * resume that session. Note that session parameters may include
58 * a "target server name", in which case the ServerName
59 * property will be set to that name, which will be included
60 * in the ClientHello as the Server Name Indication extension.
61 */
62 public SSLClient(Stream sub, SSLSessionParameters sessionParameters)
63 : base(sub)
64 {
65 sentExtensions = new List<int>();
66 resumeParams = sessionParameters;
67 if (resumeParams != null) {
68 string name = resumeParams.ServerName;
69 if (name != null) {
70 ServerName = name;
71 }
72 }
73 }
74
75 /*
76 * Validator for the server certificate. This callback is
77 * responsible for obtaining the server's public key and making
78 * sure it is the right one. A normal, standard-compliant
79 * implementation should do the following:
80 *
81 * -- Validate the certificate as X.509 mandates (building
82 * a path to a trust anchor, and verifying all signatures, names
83 * and appropriate certificate extensions; also obtaining
84 * proper CRL or OCSP response for a fresh revocation status).
85 *
86 * -- Check that the intended server name (provided in the
87 * 'serverName' parameter) matches that which is found in the
88 * certificate (see RFC 2818 section 3.1 for details; also
89 * consider RFC 6125 section 6.4).
90 *
91 * -- Return the public key found in the server's certificate,
92 * along with its allowed usages (which may depend on the
93 * KeyUsage extensions found in the certificate).
94 *
95 * The certificate chain, as received from the server, is
96 * provided as parameter; it is non-empty (it contains at least
97 * one certificate). The server's certificate is the first one
98 * in the chain.
99 *
100 * The 'serverName' parameter is the intended server name, to
101 * match against the names found in the certificate. If it is
102 * null, then no matching is expected (this correspond to the
103 * ServerName property in this SSLClient instance).
104 *
105 * The 'usage' variable shall be set to a value that qualifies
106 * whether the key may be used for encryption and/or signatures.
107 */
108 public delegate IPublicKey CertValidator(
109 byte[][] chain, string serverName, out KeyUsage usage);
110 public CertValidator ServerCertValidator {
111 get; set;
112 }
113
114 /*
115 * A simple INSECURE certificate "validator" that does not validate
116 * anything: the public key is extracted and returned, with no
117 * other checks. THIS IS FOR TESTS ONLY. Using this validator
118 * basically voids all security properties of SSL.
119 */
120 public static IPublicKey InsecureCertValidator(
121 byte[][] chain, string serverName, out KeyUsage usage)
122 {
123 usage = KeyUsage.EncryptAndSign;
124 return SSL.GetKeyFromCert(chain[0]);
125 }
126
127 List<int> sentExtensions;
128 SSLSessionParameters resumeParams;
129
130 internal override bool IsClient {
131 get {
132 return true;
133 }
134 }
135
136 internal override bool DoHandshake()
137 {
138 CheckConfigHashAndSign();
139
140 ResetHashes();
141 MakeRandom(clientRandom);
142
143 SendClientHello();
144 FlushSub();
145
146 bool resume;
147 if (!ParseServerHello(out resume)) {
148 return false;
149 }
150 HandshakeCount ++;
151 if (resume) {
152 /*
153 * Abbreviated handshake.
154 */
155 ParseCCSAndFinished();
156 SendCCSAndFinished();
157 FlushSub();
158 SetAppData();
159 IsResume = true;
160 return true;
161 }
162
163 KeyUsage usage;
164 IPublicKey pkey = ParseCertificate(out usage);
165
166 ECCurve curve;
167 byte[] serverPoint;
168 if (SSL.IsECDHE_RSA(CipherSuite)) {
169 if (!(pkey is RSAPublicKey)) {
170 throw new SSLException(
171 "ECDHE_RSA needs a RSA public key");
172 }
173 if (usage != KeyUsage.SignOnly
174 && usage != KeyUsage.EncryptAndSign)
175 {
176 throw new SSLException("Server public key"
177 + " unfit for signatures");
178 }
179 serverPoint = ParseServerKeyExchange(out curve, pkey);
180 } else if (SSL.IsECDHE_ECDSA(CipherSuite)) {
181 if (!(pkey is ECPublicKey)) {
182 throw new SSLException(
183 "ECDHE_ECDSA needs an EC public key");
184 }
185 if (usage != KeyUsage.SignOnly
186 && usage != KeyUsage.EncryptAndSign)
187 {
188 throw new SSLException("Server public key"
189 + " unfit for signatures");
190 }
191 serverPoint = ParseServerKeyExchange(out curve, pkey);
192 } else {
193 curve = null;
194 serverPoint = null;
195 }
196
197 bool reqClientCert = false;
198 int mt;
199 byte[] msg = ReadHandshakeMessage(out mt);
200 if (mt == SSL.CERTIFICATE_REQUEST) {
201 /*
202 * FIXME: parse message and select a client
203 * certificate.
204 */
205 reqClientCert = true;
206 msg = ReadHandshakeMessage(out mt);
207 }
208 if (mt != SSL.SERVER_HELLO_DONE) {
209 throw new SSLException(string.Format("Unexpected"
210 + " handshake message {0}"
211 + " (expected: ServerHelloDone)", mt));
212 }
213 if (msg.Length != 0) {
214 throw new SSLException(
215 "Invalid ServerHelloDone (not empty)");
216 }
217
218 if (reqClientCert) {
219 /*
220 * FIXME: right now, we send an empty Certificate
221 * message if the server asks for a client
222 * certificate; i.e., we claim we have none.
223 */
224 MemoryStream ms =
225 StartHandshakeMessage(SSL.CERTIFICATE);
226 EndHandshakeMessage(ms);
227 }
228
229 if (SSL.IsRSA(CipherSuite)) {
230 if (!(pkey is RSAPublicKey)) {
231 throw new SSLException(
232 "Server public key is not RSA");
233 }
234 if (usage != KeyUsage.EncryptOnly
235 && usage != KeyUsage.EncryptAndSign)
236 {
237 throw new SSLException("Server public key is"
238 + " not allowed for encryption");
239 }
240 SendClientKeyExchangeRSA(pkey as RSAPublicKey);
241 } else if (SSL.IsECDH(CipherSuite)) {
242 if (!(pkey is ECPublicKey)) {
243 throw new SSLException(
244 "Server public key is not EC");
245 }
246 if (usage != KeyUsage.EncryptOnly
247 && usage != KeyUsage.EncryptAndSign)
248 {
249 throw new SSLException("Server public key is"
250 + " not allowed for key exchange");
251 }
252 SendClientKeyExchangeECDH(pkey as ECPublicKey);
253 } else if (serverPoint != null) {
254 SendClientKeyExchangeECDH(curve, serverPoint);
255 } else {
256 /*
257 * TODO: Maybe support DHE cipher suites?
258 */
259 throw new Exception("NYI");
260 }
261
262 /*
263 * FIXME: when client certificates are supported, we
264 * will need to send a CertificateVerify message here.
265 */
266
267 SendCCSAndFinished();
268 FlushSub();
269
270 ParseCCSAndFinished();
271 SetAppData();
272 IsResume = false;
273
274 return true;
275 }
276
277 void SendClientHello()
278 {
279 MemoryStream ms = StartHandshakeMessage(SSL.CLIENT_HELLO);
280
281 // Maximum supported protocol version.
282 IO.Write16(ms, VersionMax);
283
284 // Client random.
285 ms.Write(clientRandom, 0, clientRandom.Length);
286
287 // Session ID.
288 if (resumeParams != null) {
289 byte[] id = resumeParams.SessionID;
290 ms.WriteByte((byte)id.Length);
291 ms.Write(id, 0, id.Length);
292 } else {
293 ms.WriteByte(0x00);
294 }
295
296 // List of supported cipher suites.
297 int csLen = SupportedCipherSuites.Length << 1;
298 int extraCS = GetQuirkInt("sendExtraCipherSuite", -1);
299 if (extraCS >= 0) {
300 csLen += 2;
301 }
302 IO.Write16(ms, csLen);
303 foreach (int cs in SupportedCipherSuites) {
304 IO.Write16(ms, cs);
305 }
306 if (extraCS >= 0) {
307 IO.Write16(ms, extraCS);
308 }
309
310 // List of supported compression algorithms.
311 ms.WriteByte(0x01);
312 ms.WriteByte(0x00);
313
314 // Extensions
315 sentExtensions.Clear();
316 MemoryStream chExt = new MemoryStream();
317
318 // Server Name Indication
319 if (ServerName != null && ServerName.Length > 0) {
320 byte[] encName = Encoding.UTF8.GetBytes(ServerName);
321 int elen = encName.Length;
322 if (elen > 65530) {
323 throw new SSLException("Oversized server name");
324 }
325 sentExtensions.Add(0x0000);
326 IO.Write16(chExt, 0x0000); // extension type
327 IO.Write16(chExt, elen + 5); // extension length
328 IO.Write16(chExt, elen + 3); // name list length
329 chExt.WriteByte(0x00); // name type
330 IO.Write16(chExt, elen); // name length
331 chExt.Write(encName, 0, elen);
332 }
333
334 // Supported Curves and Supported Point Formats
335 if (SupportedCurves != null && SupportedCurves.Length > 0) {
336 int len = SupportedCurves.Length;
337 sentExtensions.Add(0x000A);
338 IO.Write16(chExt, 0x000A);
339 IO.Write16(chExt, (len << 1) + 2);
340 IO.Write16(chExt, len << 1);
341 foreach (int cc in SupportedCurves) {
342 IO.Write16(chExt, cc);
343 }
344
345 sentExtensions.Add(0x000B);
346 IO.Write16(chExt, 0x000B);
347 IO.Write16(chExt, 2);
348 chExt.WriteByte(1);
349 chExt.WriteByte(0x00);
350 }
351
352 // Supported Signatures
353 if (VersionMax >= SSL.TLS12 && SupportedHashAndSign != null
354 && SupportedHashAndSign.Length > 0)
355 {
356 sentExtensions.Add(0x000D);
357 IO.Write16(chExt, 0x000D);
358 int num = SupportedHashAndSign.Length;
359 IO.Write16(chExt, 2 + (num << 1));
360 IO.Write16(chExt, num << 1);
361 foreach (int hs in SupportedHashAndSign) {
362 IO.Write16(chExt, hs);
363 }
364 }
365
366 // Secure renegotiation
367 if (!GetQuirkBool("noSecureReneg")) {
368 sentExtensions.Add(0xFF01);
369 IO.Write16(chExt, 0xFF01);
370 byte[] exv;
371 if (renegSupport > 0) {
372 exv = savedClientFinished;
373 } else {
374 exv = new byte[0];
375 }
376
377 if (GetQuirkBool("forceEmptySecureReneg")) {
378 exv = new byte[0];
379 } else if (GetQuirkBool("forceNonEmptySecureReneg")) {
380 exv = new byte[12];
381 } else if (GetQuirkBool("alterNonEmptySecureReneg")) {
382 if (exv.Length > 0) {
383 exv[exv.Length - 1] ^= 0x01;
384 }
385 } else if (GetQuirkBool("oversizedSecureReneg")) {
386 exv = new byte[255];
387 }
388
389 IO.Write16(chExt, exv.Length + 1);
390 chExt.WriteByte((byte)exv.Length);
391 chExt.Write(exv, 0, exv.Length);
392 }
393
394 // Extra extension with random contents.
395 int extraExt = GetQuirkInt("sendExtraExtension", -1);
396 if (extraExt >= 0) {
397 byte[] exv = new byte[extraExt >> 16];
398 RNG.GetBytes(exv);
399 IO.Write16(chExt, extraExt & 0xFFFF);
400 IO.Write16(chExt, exv.Length);
401 chExt.Write(exv, 0, exv.Length);
402 }
403
404 // Max Fragment Length
405 // ALPN
406 // FIXME
407
408 byte[] encExt = chExt.ToArray();
409 if (encExt.Length > 0) {
410 if (encExt.Length > 65535) {
411 throw new SSLException("Oversized extensions");
412 }
413 IO.Write16(ms, encExt.Length);
414 ms.Write(encExt, 0, encExt.Length);
415 }
416
417 EndHandshakeMessage(ms);
418 }
419
420 bool ParseServerHello(out bool resume)
421 {
422 resume = false;
423
424 int mt;
425 byte[] msg = ReadHandshakeMessage(out mt, FirstHandshakeDone);
426 if (msg == null) {
427 /*
428 * Server denies attempt explicitly.
429 */
430 return false;
431 }
432 if (mt != SSL.SERVER_HELLO) {
433 throw new SSLException(string.Format("Unexpected"
434 + " handshake message {0} (expecting a"
435 + " ServerHello)", mt));
436 }
437
438 if (msg.Length < 38) {
439 throw new SSLException("Truncated ServerHello");
440 }
441 Version = IO.Dec16be(msg, 0);
442 if (Version < VersionMin || Version > VersionMax) {
443 throw new SSLException(string.Format(
444 "Unsupported version: 0x{0:X4}", Version));
445 }
446 Array.Copy(msg, 2, serverRandom, 0, 32);
447 int idLen = msg[34];
448 if (idLen > 32) {
449 throw new SSLException("Invalid session ID length");
450 }
451 if (idLen + 38 > msg.Length) {
452 throw new SSLException("Truncated ServerHello");
453 }
454 sessionID = new byte[idLen];
455 Array.Copy(msg, 35, sessionID, 0, idLen);
456 int off = 35 + idLen;
457
458 /*
459 * Cipher suite. It must be one of the suites we sent.
460 */
461 CipherSuite = IO.Dec16be(msg, off);
462 off += 2;
463 bool found = false;
464 foreach (int cs in SupportedCipherSuites) {
465 if (cs == SSL.FALLBACK_SCSV
466 || cs == SSL.EMPTY_RENEGOTIATION_INFO_SCSV)
467 {
468 continue;
469 }
470 if (cs == CipherSuite) {
471 found = true;
472 break;
473 }
474 }
475 if (!found) {
476 throw new SSLException(string.Format(
477 "Server selected cipher suite 0x{0:X4}"
478 + " which we did not advertise", CipherSuite));
479 }
480
481 /*
482 * Compression. Must be 0, since we do not support it.
483 */
484 int comp = msg[off ++];
485 if (comp != 0x00) {
486 throw new SSLException(string.Format(
487 "Server selected compression {0}"
488 + " which we did not advertise", comp));
489 }
490
491 /*
492 * Extensions. Each extension from the server should
493 * correspond to an extension sent in the ClientHello.
494 */
495 bool secReneg = false;
496 if (msg.Length > off) {
497 if (msg.Length == off + 1) {
498 throw new SSLException("Truncated ServerHello");
499 }
500 int tlen = IO.Dec16be(msg, off);
501 off += 2;
502 if (tlen != msg.Length - off) {
503 throw new SSLException(
504 "Invalid extension list length");
505 }
506 while (off < msg.Length) {
507 if ((off + 4) > msg.Length) {
508 throw new SSLException(
509 "Truncated extention");
510 }
511 int etype = IO.Dec16be(msg, off);
512 int elen = IO.Dec16be(msg, off + 2);
513 off += 4;
514 if (elen > msg.Length - off) {
515 throw new SSLException(
516 "Truncated extention");
517 }
518 if (!sentExtensions.Contains(etype)) {
519 throw new SSLException(string.Format(
520 "Server send unadvertised"
521 + " extenstion 0x{0:X4}",
522 etype));
523 }
524
525 /*
526 * We have some processing to do on some
527 * specific server-side extensions.
528 */
529 switch (etype) {
530
531 case 0xFF01:
532 secReneg = true;
533 ParseExtSecureReneg(msg, off, elen);
534 break;
535 }
536
537 off += elen;
538 }
539 }
540
541 /*
542 * Renegotiation support: if we did not get the extension
543 * from the server, then secure renegotiation is
544 * definitely not supported. If it _was_ known as
545 * being supported (from a previous handshake) then this
546 * is a fatal error.
547 */
548 if (!secReneg) {
549 if (renegSupport > 0) {
550 throw new SSLException("Missing Secure"
551 + " Renegotiation extension");
552 }
553 renegSupport = -1;
554 }
555
556 /*
557 * Check whether this is a session resumption: a session
558 * is resumed if we sent a non-empty session ID, and the
559 * ServerHello contained the same session ID.
560 *
561 * In case of resumption, the ServerHello must use the
562 * same version and cipher suite than in the saved
563 * parameters.
564 */
565 if (resumeParams != null) {
566 byte[] id = resumeParams.SessionID;
567 if (id.Length > 0 && IO.Eq(id, sessionID)) {
568 if (Version != resumeParams.Version) {
569 throw new SSLException(
570 "Resume version mismatch");
571 }
572 if (CipherSuite != resumeParams.CipherSuite) {
573 throw new SSLException(
574 "Resume cipher suite mismatch");
575 }
576 SetMasterSecret(resumeParams.MasterSecret);
577 resume = true;
578 }
579 }
580
581 return true;
582 }
583
584 void ParseExtSecureReneg(byte[] buf, int off, int len)
585 {
586 if (len < 1 || len != 1 + buf[off]) {
587 throw new SSLException(
588 "Invalid Secure Renegotiation extension");
589 }
590 len --;
591 off ++;
592
593 if (renegSupport == 0) {
594 /*
595 * Initial handshake: extension MUST be empty.
596 */
597 if (len != 0) {
598 throw new SSLException(
599 "Non-empty Secure Renegotation"
600 + " on initial handshake");
601 }
602 renegSupport = 1;
603 } else {
604 /*
605 * Renegotiation: extension MUST contain the
606 * concatenation of the saved client and
607 * server Finished messages (in that order).
608 */
609 if (len != 24) {
610 throw new SSLException(
611 "Wrong Secure Renegotiation value");
612 }
613 int z = 0;
614 for (int i = 0; i < 12; i ++) {
615 z |= savedClientFinished[i] ^ buf[off + i];
616 z |= savedServerFinished[i] ^ buf[off + 12 + i];
617 }
618 if (z != 0) {
619 throw new SSLException(
620 "Wrong Secure Renegotiation value");
621 }
622 }
623 }
624
625 IPublicKey ParseCertificate(out KeyUsage usage)
626 {
627 byte[] msg = ReadHandshakeMessageExpected(SSL.CERTIFICATE);
628 if (msg.Length < 3) {
629 throw new SSLException("Invalid Certificate message");
630 }
631 int tlen = IO.Dec24be(msg, 0);
632 int off = 3;
633 if (tlen != msg.Length - off) {
634 throw new SSLException("Invalid Certificate message");
635 }
636 List<byte[]> certs = new List<byte[]>();
637 while (off < msg.Length) {
638 if (msg.Length - off < 3) {
639 throw new SSLException(
640 "Invalid Certificate message");
641 }
642 int clen = IO.Dec24be(msg, off);
643 off += 3;
644 if (clen > msg.Length - off) {
645 throw new SSLException(
646 "Invalid Certificate message");
647 }
648 byte[] ec = new byte[clen];
649 Array.Copy(msg, off, ec, 0, clen);
650 off += clen;
651 certs.Add(ec);
652 }
653
654 return ServerCertValidator(
655 certs.ToArray(), ServerName, out usage);
656 }
657
658 byte[] ParseServerKeyExchange(out ECCurve curve, IPublicKey pkey)
659 {
660 byte[] msg = ReadHandshakeMessageExpected(
661 SSL.SERVER_KEY_EXCHANGE);
662 if (msg.Length < 4) {
663 throw new SSLException(
664 "Invalid ServerKeyExchange message");
665 }
666 if (msg[0] != 0x03) {
667 throw new SSLException("Unsupported unnamed curve");
668 }
669 curve = SSL.GetCurveByID(IO.Dec16be(msg, 1));
670 int plen = msg[3];
671 int off = 4;
672 if (msg.Length - off < plen) {
673 throw new SSLException(
674 "Invalid ServerKeyExchange message");
675 }
676 byte[] point = new byte[plen];
677 Array.Copy(msg, off, point, 0, plen);
678 off += plen;
679 int slen = off;
680
681 int hashId, sigId;
682 if (Version >= SSL.TLS12) {
683 if (msg.Length - off < 2) {
684 throw new SSLException(
685 "Invalid ServerKeyExchange message");
686 }
687 hashId = msg[off ++];
688 if (hashId == 0) {
689 throw new SSLException(
690 "Invalid hash identifier");
691 }
692 sigId = msg[off ++];
693 } else {
694 if (pkey is RSAPublicKey) {
695 hashId = 0;
696 sigId = 1;
697 } else if (pkey is ECPublicKey) {
698 hashId = 2;
699 sigId = 3;
700 } else {
701 throw new SSLException(
702 "Unsupported signature key type");
703 }
704 }
705
706 if (msg.Length - off < 2) {
707 throw new SSLException(
708 "Invalid ServerKeyExchange message");
709 }
710 int sigLen = IO.Dec16be(msg, off);
711 off += 2;
712 if (sigLen != msg.Length - off) {
713 throw new SSLException(
714 "Invalid ServerKeyExchange message");
715 }
716 byte[] sig = new byte[sigLen];
717 Array.Copy(msg, off, sig, 0, sigLen);
718
719 byte[] hv;
720 if (hashId == 0) {
721 MD5 md5 = new MD5();
722 SHA1 sha1 = new SHA1();
723 md5.Update(clientRandom);
724 md5.Update(serverRandom);
725 md5.Update(msg, 0, slen);
726 sha1.Update(clientRandom);
727 sha1.Update(serverRandom);
728 sha1.Update(msg, 0, slen);
729 hv = new byte[36];
730 md5.DoFinal(hv, 0);
731 sha1.DoFinal(hv, 16);
732 } else {
733 IDigest h = SSL.GetHashByID(hashId);
734 h.Update(clientRandom);
735 h.Update(serverRandom);
736 h.Update(msg, 0, slen);
737 hv = h.DoFinal();
738 }
739
740 bool ok;
741 if (sigId == 1) {
742 RSAPublicKey rpk = pkey as RSAPublicKey;
743 if (rpk == null) {
744 throw new SSLException(
745 "Wrong public key type for RSA");
746 }
747 if (hashId == 0) {
748 ok = RSA.VerifyND(rpk, hv, sig);
749 } else {
750 byte[] head1, head2;
751
752 switch (hashId) {
753 case 1:
754 head1 = RSA.PKCS1_MD5;
755 head2 = RSA.PKCS1_MD5_ALT;
756 break;
757 case 2:
758 head1 = RSA.PKCS1_SHA1;
759 head2 = RSA.PKCS1_SHA1_ALT;
760 break;
761 case 3:
762 head1 = RSA.PKCS1_SHA224;
763 head2 = RSA.PKCS1_SHA224_ALT;
764 break;
765 case 4:
766 head1 = RSA.PKCS1_SHA256;
767 head2 = RSA.PKCS1_SHA256_ALT;
768 break;
769 case 5:
770 head1 = RSA.PKCS1_SHA384;
771 head2 = RSA.PKCS1_SHA384_ALT;
772 break;
773 case 6:
774 head1 = RSA.PKCS1_SHA512;
775 head2 = RSA.PKCS1_SHA512_ALT;
776 break;
777 default:
778 throw new SSLException(
779 "Unsupported hash algorithm: "
780 + hashId);
781 }
782 ok = RSA.Verify(rpk, head1, head2, hv, sig);
783 }
784 } else if (sigId == 3) {
785 ECPublicKey epk = pkey as ECPublicKey;
786 if (epk == null) {
787 throw new SSLException(
788 "Wrong public key type for ECDSA");
789 }
790 ok = ECDSA.Verify(epk, hv, sig);
791 } else {
792 throw new SSLException(
793 "Unsupported signature type: " + sigId);
794 }
795
796 if (!ok) {
797 throw new SSLException(
798 "Invalid signature on ServerKeyExchange");
799 }
800 return point;
801 }
802
803 void SendClientKeyExchangeRSA(RSAPublicKey pkey)
804 {
805 byte[] pms = new byte[48];
806 IO.Enc16be(Version, pms, 0);
807 RNG.GetBytes(pms, 2, pms.Length - 2);
808 byte[] epms = RSA.Encrypt(pkey, pms);
809 MemoryStream ms =
810 StartHandshakeMessage(SSL.CLIENT_KEY_EXCHANGE);
811 IO.Write16(ms, epms.Length);
812 ms.Write(epms, 0, epms.Length);
813 EndHandshakeMessage(ms);
814 ComputeMaster(pms);
815 }
816
817 void SendClientKeyExchangeECDH(ECPublicKey pkey)
818 {
819 ECCurve curve = pkey.Curve;
820 SendClientKeyExchangeECDH(curve, pkey.Pub);
821 }
822
823 void SendClientKeyExchangeECDH(ECCurve curve, byte[] pub)
824 {
825 byte[] k = curve.MakeRandomSecret();
826
827 /*
828 * Compute the point P = k*G that we will send.
829 */
830 byte[] P = curve.GetGenerator(false);
831 uint good = curve.Mul(P, k, P, false);
832
833 /*
834 * Compute the shared secret Q = k*Pub.
835 */
836 byte[] Q = new byte[P.Length];
837 good &= curve.Mul(pub, k, Q, false);
838
839 if (good == 0) {
840 /*
841 * This might happen only if the server's public
842 * key is not part of the proper subgroup. This
843 * cannot happen with NIST's "P" curves.
844 */
845 throw new SSLException("ECDH failed");
846 }
847
848 /*
849 * Send message.
850 */
851 MemoryStream ms =
852 StartHandshakeMessage(SSL.CLIENT_KEY_EXCHANGE);
853 ms.WriteByte((byte)P.Length);
854 ms.Write(P, 0, P.Length);
855 EndHandshakeMessage(ms);
856
857 /*
858 * Extract premaster secret.
859 */
860 int xlen;
861 int xoff = curve.GetXoff(out xlen);
862 byte[] pms = new byte[xlen];
863 Array.Copy(Q, xoff, pms, 0, xlen);
864 ComputeMaster(pms);
865 }
866
867 internal override void ProcessExtraHandshake()
868 {
869 /*
870 * If we receive a non-empty handshake message, then
871 * it should be an HelloRequest. Note that we expect
872 * the request not to be mixed with application data
873 * records (though it could be split).
874 */
875 ReadHelloRequests();
876
877 /*
878 * We accept to renegotiate only if the server supports
879 * the Secure Renegotiation extension.
880 */
881 if (renegSupport != 1) {
882 SendWarning(SSL.NO_RENEGOTIATION);
883 SetAppData();
884 return;
885 }
886
887 /*
888 * We do a new handshake. We explicitly refuse to reuse
889 * session parameters, because there is no point in
890 * renegotiation if this resumes the same session.
891 */
892 resumeParams = null;
893 DoHandshake();
894 }
895
896 internal override void PrepareRenegotiate()
897 {
898 /*
899 * Nothing to do to trigger a new handshake, the client
900 * just has to send the ClientHello right away.
901 */
902 }
903 }
904
905 }