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