Add unit test for AggregateException.GetBaseException that works on .net but is broke...
[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 SecurityProtocolType SecurityProtocol
126                 {
127                         get 
128                         {
129                                 if ((this.securityProtocol & SecurityProtocolType.Tls) == SecurityProtocolType.Tls ||   
130                                         (this.securityProtocol & SecurityProtocolType.Default) == SecurityProtocolType.Default)
131                                 {
132                                         return SecurityProtocolType.Tls;
133                                 }
134                                 else
135                                 {
136                                         if ((this.securityProtocol & SecurityProtocolType.Ssl3) == SecurityProtocolType.Ssl3)
137                                         {
138                                                 return SecurityProtocolType.Ssl3;
139                                         }
140                                 }
141
142                                 throw new NotSupportedException("Unsupported security protocol type");
143                         }
144
145                         set { this.securityProtocol = value; }
146                 }
147
148                 public SecurityProtocolType SecurityProtocolFlags
149                 {
150                         get { return this.securityProtocol; }
151                 }
152
153                 public short Protocol
154                 {
155                         get 
156                         { 
157                                 switch (this.SecurityProtocol)
158                                 {
159                                         case SecurityProtocolType.Tls:
160                                         case SecurityProtocolType.Default:
161                                                 return Context.TLS1_PROTOCOL_CODE;
162
163                                         case SecurityProtocolType.Ssl3:
164                                                 return Context.SSL3_PROTOCOL_CODE;
165
166                                         case SecurityProtocolType.Ssl2:
167                                         default:
168                                                 throw new NotSupportedException("Unsupported security protocol type");
169                                 }
170                         }
171                 }
172
173                 public byte[] SessionId
174                 {
175                         get { return this.sessionId; }
176                         set { this.sessionId = value; }
177                 }
178
179                 public SecurityCompressionType CompressionMethod
180                 {
181                         get { return this.compressionMethod; }
182                         set { this.compressionMethod = value; }
183                 }
184
185                 public TlsServerSettings ServerSettings
186                 {
187                         get { return this.serverSettings; }
188                 }
189
190                 public TlsClientSettings ClientSettings
191                 {
192                         get { return this.clientSettings; }
193                 }
194
195                 public HandshakeType LastHandshakeMsg
196                 {
197                         get { return this.lastHandshakeMsg; }
198                         set { this.lastHandshakeMsg = value; }
199                 }
200
201                 public  HandshakeState HandshakeState
202                 {
203                         get { return this.handshakeState; }
204                         set { this.handshakeState = value; }
205                 }
206
207                 public bool ReceivedConnectionEnd
208                 {
209                         get { return this.receivedConnectionEnd; }
210                         set { this.receivedConnectionEnd = value; }
211                 }
212
213                 public bool SentConnectionEnd
214                 {
215                         get { return this.sentConnectionEnd; }
216                         set { this.sentConnectionEnd = value; }
217                 }
218
219                 public CipherSuiteCollection SupportedCiphers
220                 {
221                         get { return supportedCiphers; }
222                         set { supportedCiphers = value; }
223                 }
224
225                 public TlsStream HandshakeMessages
226                 {
227                         get { return this.handshakeMessages; }
228                 }
229
230                 public ulong WriteSequenceNumber
231                 {
232                         get { return this.writeSequenceNumber; }
233                         set { this.writeSequenceNumber = value; }
234                 }
235
236                 public ulong ReadSequenceNumber
237                 {
238                         get { return this.readSequenceNumber; }
239                         set { this.readSequenceNumber = value; }
240                 }
241
242                 public byte[] ClientRandom
243                 {
244                         get { return this.clientRandom; }
245                         set { this.clientRandom = value; }
246                 }
247
248                 public byte[] ServerRandom
249                 {
250                         get { return this.serverRandom; }
251                         set { this.serverRandom = value; }
252                 }
253
254                 public byte[] RandomCS
255                 {
256                         get { return this.randomCS; }
257                         set { this.randomCS = value; }
258                 }
259
260                 public byte[] RandomSC
261                 {
262                         get { return this.randomSC; }
263                         set { this.randomSC = value; }
264                 }
265
266                 public byte[] MasterSecret
267                 {
268                         get { return this.masterSecret; }
269                         set { this.masterSecret = value; }
270                 }
271
272                 public byte[] ClientWriteKey
273                 {
274                         get { return this.clientWriteKey; }
275                         set { this.clientWriteKey = value; }
276                 }
277
278                 public byte[] ServerWriteKey
279                 {
280                         get { return this.serverWriteKey; }
281                         set { this.serverWriteKey = value; }
282                 }
283
284                 public byte[] ClientWriteIV
285                 {
286                         get { return this.clientWriteIV; }
287                         set { this.clientWriteIV = value; }
288                 }
289
290                 public byte[] ServerWriteIV
291                 {
292                         get { return this.serverWriteIV; }
293                         set { this.serverWriteIV = value; }
294                 }
295
296                 public RecordProtocol RecordProtocol
297                 {
298                         get { return this.recordProtocol; }
299                         set { this.recordProtocol = value; }
300                 }
301
302                 #endregion
303
304                 #region Constructors
305
306                 public Context(SecurityProtocolType securityProtocolType)
307                 {
308                         this.SecurityProtocol   = securityProtocolType;
309                         this.compressionMethod  = SecurityCompressionType.None;
310                         this.serverSettings             = new TlsServerSettings();
311                         this.clientSettings             = new TlsClientSettings();
312                         this.handshakeMessages  = new TlsStream();
313                         this.sessionId                  = null;
314                         this.handshakeState             = HandshakeState.None;
315                         this.random                             = RandomNumberGenerator.Create();
316                 }
317
318                 #endregion
319
320                 #region Methods
321                 
322                 public int GetUnixTime()
323                 {
324                         DateTime now = DateTime.UtcNow;
325                                                                                                                                                      
326                         return (int)((now.Ticks - UNIX_BASE_TICKS) / TimeSpan.TicksPerSecond);
327                 }
328
329                 public byte[] GetSecureRandomBytes(int count)
330                 {
331                         byte[] secureBytes = new byte[count];
332
333                         this.random.GetNonZeroBytes(secureBytes);
334                         
335                         return secureBytes;
336                 }
337
338                 public virtual void Clear()
339                 {
340                         this.compressionMethod  = SecurityCompressionType.None;
341                         this.serverSettings             = new TlsServerSettings();
342                         this.clientSettings             = new TlsClientSettings();
343                         this.handshakeMessages  = new TlsStream();
344                         this.sessionId                  = null;
345                         this.handshakeState             = HandshakeState.None;
346
347                         this.ClearKeyInfo();
348                 }
349
350                 public virtual void ClearKeyInfo()
351                 {
352                         // Clear Master Secret
353                         if (masterSecret != null) {
354                                 Array.Clear (masterSecret, 0, masterSecret.Length);
355                                 masterSecret = null;
356                         }
357
358                         // Clear client and server random
359                         if (clientRandom != null) {
360                                 Array.Clear (clientRandom, 0, clientRandom.Length);
361                                 clientRandom = null;
362                         }
363                         if (serverRandom != null) {
364                                 Array.Clear (serverRandom, 0, serverRandom.Length);
365                                 serverRandom = null;
366                         }
367                         if (randomCS != null) {
368                                 Array.Clear (randomCS, 0, randomCS.Length);
369                                 randomCS = null;
370                         }
371                         if (randomSC != null) {
372                                 Array.Clear (randomSC, 0, randomSC.Length);
373                                 randomSC = null;
374                         }
375
376                         // Clear client keys
377                         if (clientWriteKey != null) {
378                                 Array.Clear (clientWriteKey, 0, clientWriteKey.Length);
379                                 clientWriteKey = null;
380                         }
381                         if (clientWriteIV != null) {
382                                 Array.Clear (clientWriteIV, 0, clientWriteIV.Length);
383                                 clientWriteIV = null;
384                         }
385                         
386                         // Clear server keys
387                         if (serverWriteKey != null) {
388                                 Array.Clear (serverWriteKey, 0, serverWriteKey.Length);
389                                 serverWriteKey = null;
390                         }
391                         if (serverWriteIV != null) {
392                                 Array.Clear (serverWriteIV, 0, serverWriteIV.Length);
393                                 serverWriteIV = null;
394                         }
395
396                         // Reset handshake messages
397                         this.handshakeMessages.Reset();
398
399                         // Clear MAC keys if protocol is different than Ssl3
400                         // SSLv3 needs them inside Mono.Security.Protocol.Tls.SslCipherSuite.Compute[Client|Server]RecordMAC
401                         if (this.securityProtocol != SecurityProtocolType.Ssl3)
402                         {
403 //                              this.clientWriteMAC = null;
404 //                              this.serverWriteMAC = null;
405                         }
406                 }
407
408                 public SecurityProtocolType DecodeProtocolCode(short code)
409                 {
410                         switch (code)
411                         {
412                                 case Context.TLS1_PROTOCOL_CODE:
413                                         return SecurityProtocolType.Tls;
414
415                                 case Context.SSL3_PROTOCOL_CODE:
416                                         return SecurityProtocolType.Ssl3;
417
418                                 default:
419                                         throw new NotSupportedException("Unsupported security protocol type");
420                         }
421                 }
422
423                 public void ChangeProtocol(short protocol)
424                 {
425                         SecurityProtocolType protocolType = this.DecodeProtocolCode(protocol);
426
427                         if ((protocolType & this.SecurityProtocolFlags) == protocolType ||
428                                 (this.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default)
429                         {
430                                 this.SecurityProtocol = protocolType;
431                                 this.SupportedCiphers.Clear();
432                                 this.SupportedCiphers = null;
433                                 this.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(protocolType);
434                         }
435                         else
436                         {
437                                 throw new TlsException(AlertDescription.ProtocolVersion, "Incorrect protocol version received from server");
438                         }
439                 }
440
441
442                 public SecurityParameters Current
443                 {
444                         get
445                         {
446                                 if (current == null)
447                                         current = new SecurityParameters ();
448                                 if (current.Cipher != null)
449                                         current.Cipher.Context = this;
450                                 return current;
451                         }
452                 }
453
454                 public SecurityParameters Negotiating
455                 {
456                         get
457                         {
458                                 if (negotiating == null)
459                                         negotiating = new SecurityParameters ();
460                                 if (negotiating.Cipher != null)
461                                         negotiating.Cipher.Context = this;
462                                 return negotiating;
463                         }
464                 }
465
466                 public SecurityParameters Read
467                 {
468                         get { return read; }
469                 }
470
471                 public SecurityParameters Write
472                 {
473                         get { return write; }
474                 }
475
476                 public void StartSwitchingSecurityParameters (bool client)
477                 {
478                         if (client) {
479                                 // everything we write from now on is encrypted
480                                 write = negotiating;
481                                 // but we still read with the older cipher until we 
482                                 // receive the ChangeCipherSpec message
483                                 read = current;
484                         } else {
485                                 // everything we read from now on is encrypted
486                                 read = negotiating;
487                                 // but we still write with the older cipher until we 
488                                 // receive the ChangeCipherSpec message
489                                 write = current;
490                         }
491                         current = negotiating;
492                 }
493
494                 public void EndSwitchingSecurityParameters (bool client)
495                 {
496                         SecurityParameters temp;
497                         if (client) {
498                                 temp = read;
499                                 // we now read with the new, negotiated, security parameters
500                                 read = current;
501                         } else {
502                                 temp = write;
503                                 // we now write with the new, negotiated, security parameters
504                                 write = current;
505                         }
506                         // so we clear the old one (last reference)
507                         if (temp != null)
508                                 temp.Clear ();
509                         negotiating = temp;
510                         // and are now ready for a future renegotiation
511                 }
512
513                 #endregion
514         }
515 }