1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
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:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
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.
27 using System.Collections;
28 using System.Security.Cryptography;
29 using System.Security.Cryptography.X509Certificates;
31 using Mono.Security.Cryptography;
32 using Mono.Security.Protocol.Tls.Handshake;
34 namespace Mono.Security.Protocol.Tls
36 internal abstract class Context
38 #region Internal Constants
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;
50 private SecurityProtocolType securityProtocol;
53 private byte[] sessionId;
56 private SecurityCompressionType compressionMethod;
58 // Information sent and request by the server in the Handshake protocol
59 private TlsServerSettings serverSettings;
61 // Client configuration
62 private TlsClientSettings clientSettings;
64 // Cipher suite information
65 private SecurityParameters current;
66 private SecurityParameters negotiating;
67 private SecurityParameters read;
68 private SecurityParameters write;
69 private CipherSuiteCollection supportedCiphers;
71 // Last handshake message received
72 private HandshakeType lastHandshakeMsg;
74 // Handshake negotiation state
75 private HandshakeState handshakeState;
78 private bool abbreviatedHandshake;
79 private bool receivedConnectionEnd;
80 private bool sentConnectionEnd;
81 private bool protocolNegotiated;
84 private ulong writeSequenceNumber;
85 private ulong readSequenceNumber;
88 private byte[] clientRandom;
89 private byte[] serverRandom;
90 private byte[] randomCS;
91 private byte[] randomSC;
94 private byte[] masterSecret;
95 private byte[] clientWriteKey;
96 private byte[] serverWriteKey;
97 private byte[] clientWriteIV;
98 private byte[] serverWriteIV;
101 private TlsStream handshakeMessages;
103 // Secure Random generator
104 private RandomNumberGenerator random;
107 private RecordProtocol recordProtocol;
113 public bool AbbreviatedHandshake
115 get { return abbreviatedHandshake; }
116 set { abbreviatedHandshake = value; }
119 public bool ProtocolNegotiated
121 get { return this.protocolNegotiated; }
122 set { this.protocolNegotiated = value; }
125 public SecurityProtocolType SecurityProtocol
129 if ((this.securityProtocol & SecurityProtocolType.Tls) == SecurityProtocolType.Tls ||
130 (this.securityProtocol & SecurityProtocolType.Default) == SecurityProtocolType.Default)
132 return SecurityProtocolType.Tls;
136 if ((this.securityProtocol & SecurityProtocolType.Ssl3) == SecurityProtocolType.Ssl3)
138 return SecurityProtocolType.Ssl3;
142 throw new NotSupportedException("Unsupported security protocol type");
145 set { this.securityProtocol = value; }
148 public SecurityProtocolType SecurityProtocolFlags
150 get { return this.securityProtocol; }
153 public short Protocol
157 switch (this.SecurityProtocol)
159 case SecurityProtocolType.Tls:
160 case SecurityProtocolType.Default:
161 return Context.TLS1_PROTOCOL_CODE;
163 case SecurityProtocolType.Ssl3:
164 return Context.SSL3_PROTOCOL_CODE;
166 case SecurityProtocolType.Ssl2:
168 throw new NotSupportedException("Unsupported security protocol type");
173 public byte[] SessionId
175 get { return this.sessionId; }
176 set { this.sessionId = value; }
179 public SecurityCompressionType CompressionMethod
181 get { return this.compressionMethod; }
182 set { this.compressionMethod = value; }
185 public TlsServerSettings ServerSettings
187 get { return this.serverSettings; }
190 public TlsClientSettings ClientSettings
192 get { return this.clientSettings; }
195 public HandshakeType LastHandshakeMsg
197 get { return this.lastHandshakeMsg; }
198 set { this.lastHandshakeMsg = value; }
201 public HandshakeState HandshakeState
203 get { return this.handshakeState; }
204 set { this.handshakeState = value; }
207 public bool ReceivedConnectionEnd
209 get { return this.receivedConnectionEnd; }
210 set { this.receivedConnectionEnd = value; }
213 public bool SentConnectionEnd
215 get { return this.sentConnectionEnd; }
216 set { this.sentConnectionEnd = value; }
219 public CipherSuiteCollection SupportedCiphers
221 get { return supportedCiphers; }
222 set { supportedCiphers = value; }
225 public TlsStream HandshakeMessages
227 get { return this.handshakeMessages; }
230 public ulong WriteSequenceNumber
232 get { return this.writeSequenceNumber; }
233 set { this.writeSequenceNumber = value; }
236 public ulong ReadSequenceNumber
238 get { return this.readSequenceNumber; }
239 set { this.readSequenceNumber = value; }
242 public byte[] ClientRandom
244 get { return this.clientRandom; }
245 set { this.clientRandom = value; }
248 public byte[] ServerRandom
250 get { return this.serverRandom; }
251 set { this.serverRandom = value; }
254 public byte[] RandomCS
256 get { return this.randomCS; }
257 set { this.randomCS = value; }
260 public byte[] RandomSC
262 get { return this.randomSC; }
263 set { this.randomSC = value; }
266 public byte[] MasterSecret
268 get { return this.masterSecret; }
269 set { this.masterSecret = value; }
272 public byte[] ClientWriteKey
274 get { return this.clientWriteKey; }
275 set { this.clientWriteKey = value; }
278 public byte[] ServerWriteKey
280 get { return this.serverWriteKey; }
281 set { this.serverWriteKey = value; }
284 public byte[] ClientWriteIV
286 get { return this.clientWriteIV; }
287 set { this.clientWriteIV = value; }
290 public byte[] ServerWriteIV
292 get { return this.serverWriteIV; }
293 set { this.serverWriteIV = value; }
296 public RecordProtocol RecordProtocol
298 get { return this.recordProtocol; }
299 set { this.recordProtocol = value; }
306 public Context(SecurityProtocolType securityProtocolType)
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();
322 public int GetUnixTime()
324 DateTime now = DateTime.UtcNow;
326 return (int)((now.Ticks - UNIX_BASE_TICKS) / TimeSpan.TicksPerSecond);
329 public byte[] GetSecureRandomBytes(int count)
331 byte[] secureBytes = new byte[count];
333 this.random.GetNonZeroBytes(secureBytes);
338 public virtual void Clear()
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;
350 public virtual void ClearKeyInfo()
352 // Clear Master Secret
353 if (masterSecret != null) {
354 Array.Clear (masterSecret, 0, masterSecret.Length);
358 // Clear client and server random
359 if (clientRandom != null) {
360 Array.Clear (clientRandom, 0, clientRandom.Length);
363 if (serverRandom != null) {
364 Array.Clear (serverRandom, 0, serverRandom.Length);
367 if (randomCS != null) {
368 Array.Clear (randomCS, 0, randomCS.Length);
371 if (randomSC != null) {
372 Array.Clear (randomSC, 0, randomSC.Length);
377 if (clientWriteKey != null) {
378 Array.Clear (clientWriteKey, 0, clientWriteKey.Length);
379 clientWriteKey = null;
381 if (clientWriteIV != null) {
382 Array.Clear (clientWriteIV, 0, clientWriteIV.Length);
383 clientWriteIV = null;
387 if (serverWriteKey != null) {
388 Array.Clear (serverWriteKey, 0, serverWriteKey.Length);
389 serverWriteKey = null;
391 if (serverWriteIV != null) {
392 Array.Clear (serverWriteIV, 0, serverWriteIV.Length);
393 serverWriteIV = null;
396 // Reset handshake messages
397 this.handshakeMessages.Reset();
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)
403 // this.clientWriteMAC = null;
404 // this.serverWriteMAC = null;
408 public SecurityProtocolType DecodeProtocolCode (short code, bool allowFallback = false)
412 case Context.TLS1_PROTOCOL_CODE:
413 return SecurityProtocolType.Tls;
415 case Context.SSL3_PROTOCOL_CODE:
416 return SecurityProtocolType.Ssl3;
419 // if allowed we'll continue using TLS (1.0) even if the other side is capable of using a newer
420 // version of the TLS protocol
421 if (allowFallback && (code > (short) Context.TLS1_PROTOCOL_CODE))
422 return SecurityProtocolType.Tls;
423 throw new NotSupportedException("Unsupported security protocol type");
427 public void ChangeProtocol(short protocol)
429 SecurityProtocolType protocolType = this.DecodeProtocolCode(protocol);
431 if ((protocolType & this.SecurityProtocolFlags) == protocolType ||
432 (this.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default)
434 this.SecurityProtocol = protocolType;
435 this.SupportedCiphers.Clear();
436 this.SupportedCiphers = null;
437 this.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(protocolType);
441 throw new TlsException(AlertDescription.ProtocolVersion, "Incorrect protocol version received from server");
446 public SecurityParameters Current
451 current = new SecurityParameters ();
452 if (current.Cipher != null)
453 current.Cipher.Context = this;
458 public SecurityParameters Negotiating
462 if (negotiating == null)
463 negotiating = new SecurityParameters ();
464 if (negotiating.Cipher != null)
465 negotiating.Cipher.Context = this;
470 public SecurityParameters Read
475 public SecurityParameters Write
477 get { return write; }
480 public void StartSwitchingSecurityParameters (bool client)
483 // everything we write from now on is encrypted
485 // but we still read with the older cipher until we
486 // receive the ChangeCipherSpec message
489 // everything we read from now on is encrypted
491 // but we still write with the older cipher until we
492 // receive the ChangeCipherSpec message
495 current = negotiating;
498 public void EndSwitchingSecurityParameters (bool client)
500 SecurityParameters temp;
503 // we now read with the new, negotiated, security parameters
507 // we now write with the new, negotiated, security parameters
510 // so we clear the old one (last reference)
514 // and are now ready for a future renegotiation