TLS protocol: add handshake state validation
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / Context.cs
1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24
25 using System;
26 using System.Text;
27 using System.Collections;
28 using System.Security.Cryptography;
29 using System.Security.Cryptography.X509Certificates;
30
31 using Mono.Security.Cryptography;
32 using Mono.Security.Protocol.Tls.Handshake;
33
34 namespace Mono.Security.Protocol.Tls
35 {
36         internal abstract class Context
37         {
38                 #region Internal Constants
39
40                 internal const short MAX_FRAGMENT_SIZE  = 16384; // 2^14
41                 internal const short TLS1_PROTOCOL_CODE = (0x03 << 8) | 0x01;
42                 internal const short SSL3_PROTOCOL_CODE = (0x03 << 8) | 0x00;
43                 internal const long  UNIX_BASE_TICKS    = 621355968000000000;
44
45                 #endregion
46
47                 #region Fields
48                 
49                 // Protocol version
50                 private SecurityProtocolType securityProtocol;
51                 
52                 // Sesison ID
53                 private byte[] sessionId;
54
55                 // Compression method
56                 private SecurityCompressionType compressionMethod;
57
58                 // Information sent and request by the server in the Handshake protocol
59                 private TlsServerSettings serverSettings;
60
61                 // Client configuration
62                 private TlsClientSettings clientSettings;
63
64                 // Cipher suite information
65                 private SecurityParameters current;
66                 private SecurityParameters negotiating;
67                 private SecurityParameters read;
68                 private SecurityParameters write;
69                 private CipherSuiteCollection supportedCiphers;
70
71                 // Last handshake message received
72                 private HandshakeType lastHandshakeMsg;
73
74                 // Handshake negotiation state
75                 private HandshakeState handshakeState;
76
77                 // Misc
78                 private bool    abbreviatedHandshake;
79                 private bool    receivedConnectionEnd;
80                 private bool    sentConnectionEnd;
81                 private bool    protocolNegotiated;
82                 
83                 // Sequence numbers
84                 private ulong   writeSequenceNumber;
85                 private ulong   readSequenceNumber;
86
87                 // Random data
88                 private byte[]  clientRandom;
89                 private byte[]  serverRandom;
90                 private byte[]  randomCS;
91                 private byte[]  randomSC;
92
93                 // Key information
94                 private byte[]  masterSecret;
95                 private byte[]  clientWriteKey;
96                 private byte[]  serverWriteKey;
97                 private byte[]  clientWriteIV;
98                 private byte[]  serverWriteIV;
99                 
100                 // Handshake hashes
101                 private TlsStream handshakeMessages;
102                 
103                 // Secure Random generator              
104                 private RandomNumberGenerator random;
105
106                 // Record protocol
107                 private RecordProtocol recordProtocol;
108
109                 #endregion
110
111                 #region Properties
112
113                 public bool AbbreviatedHandshake
114                 {
115                         get { return abbreviatedHandshake; }
116                         set { abbreviatedHandshake = value; }
117                 }
118
119                 public bool     ProtocolNegotiated
120                 {
121                         get { return this.protocolNegotiated; }
122                         set { this.protocolNegotiated = value; }
123                 }
124
125                 public bool ChangeCipherSpecDone { get; set; }
126
127                 public SecurityProtocolType SecurityProtocol
128                 {
129                         get 
130                         {
131                                 if ((this.securityProtocol & SecurityProtocolType.Tls) == SecurityProtocolType.Tls ||   
132                                         (this.securityProtocol & SecurityProtocolType.Default) == SecurityProtocolType.Default)
133                                 {
134                                         return SecurityProtocolType.Tls;
135                                 }
136                                 else
137                                 {
138                                         if ((this.securityProtocol & SecurityProtocolType.Ssl3) == SecurityProtocolType.Ssl3)
139                                         {
140                                                 return SecurityProtocolType.Ssl3;
141                                         }
142                                 }
143
144                                 throw new NotSupportedException("Unsupported security protocol type");
145                         }
146
147                         set { this.securityProtocol = value; }
148                 }
149
150                 public SecurityProtocolType SecurityProtocolFlags
151                 {
152                         get { return this.securityProtocol; }
153                 }
154
155                 public short Protocol
156                 {
157                         get 
158                         { 
159                                 switch (this.SecurityProtocol)
160                                 {
161                                         case SecurityProtocolType.Tls:
162                                         case SecurityProtocolType.Default:
163                                                 return Context.TLS1_PROTOCOL_CODE;
164
165                                         case SecurityProtocolType.Ssl3:
166                                                 return Context.SSL3_PROTOCOL_CODE;
167
168                                         case SecurityProtocolType.Ssl2:
169                                         default:
170                                                 throw new NotSupportedException("Unsupported security protocol type");
171                                 }
172                         }
173                 }
174
175                 public byte[] SessionId
176                 {
177                         get { return this.sessionId; }
178                         set { this.sessionId = value; }
179                 }
180
181                 public SecurityCompressionType CompressionMethod
182                 {
183                         get { return this.compressionMethod; }
184                         set { this.compressionMethod = value; }
185                 }
186
187                 public TlsServerSettings ServerSettings
188                 {
189                         get { return this.serverSettings; }
190                 }
191
192                 public TlsClientSettings ClientSettings
193                 {
194                         get { return this.clientSettings; }
195                 }
196
197                 public HandshakeType LastHandshakeMsg
198                 {
199                         get { return this.lastHandshakeMsg; }
200                         set { this.lastHandshakeMsg = value; }
201                 }
202
203                 public  HandshakeState HandshakeState
204                 {
205                         get { return this.handshakeState; }
206                         set { this.handshakeState = value; }
207                 }
208
209                 public bool ReceivedConnectionEnd
210                 {
211                         get { return this.receivedConnectionEnd; }
212                         set { this.receivedConnectionEnd = value; }
213                 }
214
215                 public bool SentConnectionEnd
216                 {
217                         get { return this.sentConnectionEnd; }
218                         set { this.sentConnectionEnd = value; }
219                 }
220
221                 public CipherSuiteCollection SupportedCiphers
222                 {
223                         get { return supportedCiphers; }
224                         set { supportedCiphers = value; }
225                 }
226
227                 public TlsStream HandshakeMessages
228                 {
229                         get { return this.handshakeMessages; }
230                 }
231
232                 public ulong WriteSequenceNumber
233                 {
234                         get { return this.writeSequenceNumber; }
235                         set { this.writeSequenceNumber = value; }
236                 }
237
238                 public ulong ReadSequenceNumber
239                 {
240                         get { return this.readSequenceNumber; }
241                         set { this.readSequenceNumber = value; }
242                 }
243
244                 public byte[] ClientRandom
245                 {
246                         get { return this.clientRandom; }
247                         set { this.clientRandom = value; }
248                 }
249
250                 public byte[] ServerRandom
251                 {
252                         get { return this.serverRandom; }
253                         set { this.serverRandom = value; }
254                 }
255
256                 public byte[] RandomCS
257                 {
258                         get { return this.randomCS; }
259                         set { this.randomCS = value; }
260                 }
261
262                 public byte[] RandomSC
263                 {
264                         get { return this.randomSC; }
265                         set { this.randomSC = value; }
266                 }
267
268                 public byte[] MasterSecret
269                 {
270                         get { return this.masterSecret; }
271                         set { this.masterSecret = value; }
272                 }
273
274                 public byte[] ClientWriteKey
275                 {
276                         get { return this.clientWriteKey; }
277                         set { this.clientWriteKey = value; }
278                 }
279
280                 public byte[] ServerWriteKey
281                 {
282                         get { return this.serverWriteKey; }
283                         set { this.serverWriteKey = value; }
284                 }
285
286                 public byte[] ClientWriteIV
287                 {
288                         get { return this.clientWriteIV; }
289                         set { this.clientWriteIV = value; }
290                 }
291
292                 public byte[] ServerWriteIV
293                 {
294                         get { return this.serverWriteIV; }
295                         set { this.serverWriteIV = value; }
296                 }
297
298                 public RecordProtocol RecordProtocol
299                 {
300                         get { return this.recordProtocol; }
301                         set { this.recordProtocol = value; }
302                 }
303
304                 #endregion
305
306                 #region Constructors
307
308                 public Context(SecurityProtocolType securityProtocolType)
309                 {
310                         this.SecurityProtocol   = securityProtocolType;
311                         this.compressionMethod  = SecurityCompressionType.None;
312                         this.serverSettings             = new TlsServerSettings();
313                         this.clientSettings             = new TlsClientSettings();
314                         this.handshakeMessages  = new TlsStream();
315                         this.sessionId                  = null;
316                         this.handshakeState             = HandshakeState.None;
317                         this.random                             = RandomNumberGenerator.Create();
318                 }
319
320                 #endregion
321
322                 #region Methods
323                 
324                 public int GetUnixTime()
325                 {
326                         DateTime now = DateTime.UtcNow;
327                                                                                                                                                      
328                         return (int)((now.Ticks - UNIX_BASE_TICKS) / TimeSpan.TicksPerSecond);
329                 }
330
331                 public byte[] GetSecureRandomBytes(int count)
332                 {
333                         byte[] secureBytes = new byte[count];
334
335                         this.random.GetNonZeroBytes(secureBytes);
336                         
337                         return secureBytes;
338                 }
339
340                 public virtual void Clear()
341                 {
342                         this.compressionMethod  = SecurityCompressionType.None;
343                         this.serverSettings             = new TlsServerSettings();
344                         this.clientSettings             = new TlsClientSettings();
345                         this.handshakeMessages  = new TlsStream();
346                         this.sessionId                  = null;
347                         this.handshakeState             = HandshakeState.None;
348
349                         this.ClearKeyInfo();
350                 }
351
352                 public virtual void ClearKeyInfo()
353                 {
354                         // Clear Master Secret
355                         if (masterSecret != null) {
356                                 Array.Clear (masterSecret, 0, masterSecret.Length);
357                                 masterSecret = null;
358                         }
359
360                         // Clear client and server random
361                         if (clientRandom != null) {
362                                 Array.Clear (clientRandom, 0, clientRandom.Length);
363                                 clientRandom = null;
364                         }
365                         if (serverRandom != null) {
366                                 Array.Clear (serverRandom, 0, serverRandom.Length);
367                                 serverRandom = null;
368                         }
369                         if (randomCS != null) {
370                                 Array.Clear (randomCS, 0, randomCS.Length);
371                                 randomCS = null;
372                         }
373                         if (randomSC != null) {
374                                 Array.Clear (randomSC, 0, randomSC.Length);
375                                 randomSC = null;
376                         }
377
378                         // Clear client keys
379                         if (clientWriteKey != null) {
380                                 Array.Clear (clientWriteKey, 0, clientWriteKey.Length);
381                                 clientWriteKey = null;
382                         }
383                         if (clientWriteIV != null) {
384                                 Array.Clear (clientWriteIV, 0, clientWriteIV.Length);
385                                 clientWriteIV = null;
386                         }
387                         
388                         // Clear server keys
389                         if (serverWriteKey != null) {
390                                 Array.Clear (serverWriteKey, 0, serverWriteKey.Length);
391                                 serverWriteKey = null;
392                         }
393                         if (serverWriteIV != null) {
394                                 Array.Clear (serverWriteIV, 0, serverWriteIV.Length);
395                                 serverWriteIV = null;
396                         }
397
398                         // Reset handshake messages
399                         this.handshakeMessages.Reset();
400
401                         // Clear MAC keys if protocol is different than Ssl3
402                         // SSLv3 needs them inside Mono.Security.Protocol.Tls.SslCipherSuite.Compute[Client|Server]RecordMAC
403                         if (this.securityProtocol != SecurityProtocolType.Ssl3)
404                         {
405 //                              this.clientWriteMAC = null;
406 //                              this.serverWriteMAC = null;
407                         }
408                 }
409
410                 public SecurityProtocolType DecodeProtocolCode (short code, bool allowFallback = false)
411                 {
412                         switch (code)
413                         {
414                                 case Context.TLS1_PROTOCOL_CODE:
415                                         return SecurityProtocolType.Tls;
416
417                                 case Context.SSL3_PROTOCOL_CODE:
418                                         return SecurityProtocolType.Ssl3;
419
420                                 default:
421                                         // if allowed we'll continue using TLS (1.0) even if the other side is capable of using a newer
422                                         // version of the TLS protocol
423                                         if (allowFallback && (code > (short) Context.TLS1_PROTOCOL_CODE))
424                                                 return SecurityProtocolType.Tls;
425                                         throw new NotSupportedException("Unsupported security protocol type");
426                         }
427                 }
428
429                 public void ChangeProtocol(short protocol)
430                 {
431                         SecurityProtocolType protocolType = this.DecodeProtocolCode(protocol);
432
433                         if ((protocolType & this.SecurityProtocolFlags) == protocolType ||
434                                 (this.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default)
435                         {
436                                 this.SecurityProtocol = protocolType;
437                                 this.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers ((this is ServerContext), protocolType);
438                         }
439                         else
440                         {
441                                 throw new TlsException(AlertDescription.ProtocolVersion, "Incorrect protocol version received from server");
442                         }
443                 }
444
445
446                 public SecurityParameters Current
447                 {
448                         get
449                         {
450                                 if (current == null)
451                                         current = new SecurityParameters ();
452                                 if (current.Cipher != null)
453                                         current.Cipher.Context = this;
454                                 return current;
455                         }
456                 }
457
458                 public SecurityParameters Negotiating
459                 {
460                         get
461                         {
462                                 if (negotiating == null)
463                                         negotiating = new SecurityParameters ();
464                                 if (negotiating.Cipher != null)
465                                         negotiating.Cipher.Context = this;
466                                 return negotiating;
467                         }
468                 }
469
470                 public SecurityParameters Read
471                 {
472                         get { return read; }
473                 }
474
475                 public SecurityParameters Write
476                 {
477                         get { return write; }
478                 }
479
480                 public void StartSwitchingSecurityParameters (bool client)
481                 {
482                         if (client) {
483                                 // everything we write from now on is encrypted
484                                 write = negotiating;
485                                 // but we still read with the older cipher until we 
486                                 // receive the ChangeCipherSpec message
487                                 read = current;
488                         } else {
489                                 // everything we read from now on is encrypted
490                                 read = negotiating;
491                                 // but we still write with the older cipher until we 
492                                 // receive the ChangeCipherSpec message
493                                 write = current;
494                         }
495                         current = negotiating;
496                 }
497
498                 public void EndSwitchingSecurityParameters (bool client)
499                 {
500                         SecurityParameters temp;
501                         if (client) {
502                                 temp = read;
503                                 // we now read with the new, negotiated, security parameters
504                                 read = current;
505                         } else {
506                                 temp = write;
507                                 // we now write with the new, negotiated, security parameters
508                                 write = current;
509                         }
510                         // so we clear the old one (last reference)
511                         if (temp != null)
512                                 temp.Clear ();
513                         negotiating = temp;
514                         // and are now ready for a future renegotiation
515                 }
516
517                 #endregion
518         }
519 }