1 /* Transport Security Layer (TLS)
2 * Copyright (c) 2003 Carlos Guzmán Álvarez
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
28 using System.Collections;
29 using System.Net.Sockets;
30 using System.Security.Cryptography;
32 using Mono.Security.Protocol.Tls;
33 using Mono.Security.Protocol.Tls.Alerts;
34 using Mono.Security.Protocol.Tls.Handshake;
35 using Mono.Security.Protocol.Tls.Handshake.Client;
37 namespace Mono.Security.Protocol.Tls
39 public sealed class TlsSocket : Socket
43 private TlsSession session;
44 private BufferedStream inputBuffer;
50 internal TlsSession Session
52 get { return this.session; }
55 internal BufferedStream InputBuffer
57 get { return this.inputBuffer; }
65 AddressFamily addressFamily,
66 SocketType socketType,
67 ProtocolType protocolType
68 ) : base(addressFamily, socketType, protocolType)
70 this.inputBuffer = new BufferedStream(new MemoryStream());
75 AddressFamily addressFamily,
76 SocketType socketType,
77 ProtocolType protocolType
78 ) : this(addressFamily, socketType, protocolType)
80 this.session = session;
85 #region REPLACED_METHODS
87 public new void Close()
91 if (this.session.State != TlsSessionState.Closing &&
92 this.session.State != TlsSessionState.Closed)
98 public new int Receive(byte[] buffer)
100 return this.Receive(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None);
103 public new int Receive(byte[] buffer, SocketFlags socketFlags)
105 return this.Receive(buffer, 0, buffer != null ? buffer.Length : 0, socketFlags);
108 public new int Receive(byte[] buffer, int size, SocketFlags socketFlags)
110 return this.Receive(buffer, 0, size, socketFlags);
113 public new int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags)
115 if (!this.session.IsSecure)
117 return base.Receive(buffer, offset, size, socketFlags);
120 // If actual buffer is full readed reset it
121 if (this.inputBuffer.Position == this.inputBuffer.Length)
126 // Check if we have space in the middle buffer
127 // if not Read next TLS record and update the inputBuffer
128 while ((this.inputBuffer.Length - this.inputBuffer.Position) < size)
130 // Read next record and write it into the inputBuffer
131 long position = this.inputBuffer.Position;
132 byte[] record = this.receiveRecord();
134 if (record.Length > 0)
136 // Write new data to the inputBuffer
137 this.inputBuffer.Seek(0, SeekOrigin.End);
138 this.inputBuffer.Write(record, 0, record.Length);
140 // Restore buffer position
141 this.inputBuffer.Seek(position, SeekOrigin.Begin);
144 if (base.Available == 0)
150 return this.inputBuffer.Read(buffer, offset, size);
153 public new int Send(byte[] buffer)
155 return this.Send(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None);
158 public new int Send(byte[] buffer, SocketFlags socketFlags)
160 return this.Send(buffer, 0, buffer != null ? buffer.Length : 0, socketFlags);
163 public new int Send(byte[] buffer, int size, SocketFlags socketFlags)
165 return this.Send(buffer, 0, size, socketFlags);
168 public new int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags)
170 if (!this.session.IsSecure)
172 return base.Send(buffer, offset, size, socketFlags);
175 // Send the buffer as a TLS record
176 byte[] recordData = new byte[size];
177 System.Array.Copy(buffer, offset, recordData, 0, size);
179 return this.sendRecord(TlsContentType.ApplicationData, recordData);
184 #region TLS_RECORD_METHODS
186 private byte[] receiveRecord()
188 if (this.session.Context.ConnectionEnd)
190 throw this.session.CreateException("The session is finished and it's no longer valid.");
193 TlsContentType contentType = (TlsContentType)this.ReadByte();
194 TlsProtocol protocol = (TlsProtocol)this.ReadShort();
195 short length = this.ReadShort();
199 byte[] buffer = new byte[length];
200 while (received != length)
202 received += base.Receive(
203 buffer, received, buffer.Length - received, SocketFlags.None);
206 TlsStream message = new TlsStream(buffer);
208 // Check that the message has a valid protocol version
209 if (protocol != this.session.Context.Protocol)
211 throw session.CreateException("Invalid protocol version on message received from server");
214 // Decrypt message contents if needed
215 if (contentType == TlsContentType.Alert && length == 2)
220 if (session.Context.IsActual &&
221 contentType != TlsContentType.ChangeCipherSpec)
223 message = this.decryptRecordFragment(
230 byte[] result = message.ToArray();
235 case TlsContentType.Alert:
236 this.processAlert((TlsAlertLevel)message.ReadByte(),
237 (TlsAlertDescription)message.ReadByte());
240 case TlsContentType.ChangeCipherSpec:
241 // Reset sequence numbers
242 this.session.Context.ReadSequenceNumber = 0;
245 case TlsContentType.ApplicationData:
248 case TlsContentType.Handshake:
251 this.processHandshakeMessage(message);
253 // Update handshakes of current messages
254 this.session.Context.HandshakeMessages.Write(message.ToArray());
258 throw session.CreateException("Unknown record received from server.");
266 #region TLS_CRYPTO_METHODS
268 private byte[] encryptRecordFragment(TlsContentType contentType, byte[] fragment)
270 // Calculate message MAC
271 byte[] mac = this.session.Context.Cipher.ComputeClientRecordMAC(contentType, fragment);
273 // Encrypt the message
274 byte[] ecr = this.session.Context.Cipher.EncryptRecord(fragment, mac);
277 if (this.session.Context.Cipher.CipherMode == CipherMode.CBC)
279 byte[] iv = new byte[this.session.Context.Cipher.IvSize];
280 System.Array.Copy(ecr, ecr.Length - iv.Length, iv, 0, iv.Length);
281 this.session.Context.Cipher.UpdateClientCipherIV(iv);
284 // Update sequence number
285 this.session.Context.WriteSequenceNumber++;
290 private TlsStream decryptRecordFragment(TlsContentType contentType,
291 TlsProtocol protocol,
294 byte[] dcrFragment = null;
295 byte[] dcrMAC = null;
298 this.session.Context.Cipher.DecryptRecord(fragment, ref dcrFragment, ref dcrMAC);
301 if (this.session.Context.Cipher.CipherMode == CipherMode.CBC)
303 byte[] iv = new byte[session.Context.Cipher.IvSize];
304 System.Array.Copy(fragment, fragment.Length - iv.Length, iv, 0, iv.Length);
305 this.session.Context.Cipher.UpdateServerCipherIV(iv);
309 byte[] mac = this.session.Context.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);
311 // Check that the mac is correct
312 if (mac.Length != dcrMAC.Length)
314 throw new TlsException("Invalid MAC received from server.");
316 for (int i = 0; i < mac.Length; i++)
318 if (mac[i] != dcrMAC[i])
320 throw new TlsException("Invalid MAC received from server.");
324 // Update sequence number
325 this.session.Context.ReadSequenceNumber++;
327 return new TlsStream(dcrFragment);
332 #region TLS_SEND_METHODS
334 internal int SendAlert(TlsAlert alert)
337 int bytesSent = this.sendRecord(TlsContentType.Alert, alert.ToArray());
340 alert.UpdateSession();
342 // Reset message contents
348 private int sendRecord(TlsHandshakeType type)
350 TlsHandshakeMessage msg = createClientHandshakeMessage(type);
353 int bytesSent = this.sendRecord(msg.ContentType, msg.EncodeMessage());
358 // Reset message contents
364 private int sendChangeCipherSpec()
366 // Send Change Cipher Spec message
367 int bytesSent = this.sendRecord(TlsContentType.ChangeCipherSpec, new byte[] {1});
369 // Reset sequence numbers
370 this.session.Context.WriteSequenceNumber = 0;
372 // Make the pending state to be the current state
373 this.session.Context.IsActual = true;
375 // Send Finished message
376 bytesSent += this.sendRecord(TlsHandshakeType.Finished);
381 private int sendRecord(TlsContentType contentType, byte[] recordData)
383 if (this.session.Context.ConnectionEnd)
385 throw this.session.CreateException("The session is finished and it's no longer valid.");
389 byte[][] fragments = fragmentData(recordData);
390 for (int i = 0; i < fragments.Length; i++)
392 byte[] fragment = fragments[i];
394 if (this.session.Context.IsActual)
397 fragment = this.encryptRecordFragment(contentType, fragment);
401 TlsStream record = new TlsStream();
402 record.Write((byte)contentType);
403 record.Write((short)this.session.Context.Protocol);
404 record.Write((short)fragment.Length);
405 record.Write(fragment);
408 bytesSent += base.Send(record.ToArray());
417 private byte[][] fragmentData(byte[] messageData)
419 ArrayList d = new ArrayList();
423 while (position < messageData.Length)
425 short fragmentLength = 0;
427 if ((messageData.Length - position) > TlsSessionContext.MAX_FRAGMENT_SIZE)
429 fragmentLength = TlsSessionContext.MAX_FRAGMENT_SIZE;
433 fragmentLength = (short)(messageData.Length - position);
435 fragmentData = new byte[fragmentLength];
437 System.Array.Copy(messageData, position, fragmentData, 0, fragmentLength);
441 position += fragmentLength;
444 byte[][] result = new byte[d.Count][];
445 for (int i = 0; i < d.Count; i++)
447 result[i] = (byte[])d[i];
455 #region MESSAGE_PROCESSING
457 private void processHandshakeMessage(TlsStream handMsg)
459 TlsHandshakeType handshakeType = (TlsHandshakeType)handMsg.ReadByte();
460 TlsHandshakeMessage message = null;
462 // Read message length
463 int length = handMsg.ReadInt24();
466 byte[] data = new byte[length];
467 handMsg.Read(data, 0, length);
469 // Create and process the server message
470 message = this.createServerHandshakeMessage(handshakeType, data);
475 message.UpdateSession();
479 private void processAlert(TlsAlertLevel alertLevel, TlsAlertDescription alertDesc)
483 case TlsAlertLevel.Fatal:
484 throw this.session.CreateException(alertLevel, alertDesc);
486 case TlsAlertLevel.Warning:
490 case TlsAlertDescription.CloseNotify:
491 this.session.Context.ConnectionEnd = true;
495 this.session.RaiseWarningAlert(alertLevel, alertDesc);
506 private void resetBuffer()
508 this.inputBuffer.SetLength(0);
509 this.inputBuffer.Position = 0;
512 private byte ReadByte()
514 byte[] b = new byte[1];
520 private short ReadShort()
522 byte[] b = new byte[2];
525 short val = BitConverter.ToInt16(b, 0);
527 return System.Net.IPAddress.HostToNetworkOrder(val);
532 #region HANDSHAKE_METHODS
537 ClientHello -------->
542 <-------- ServerHelloDone
550 Application Data <-------> Application Data
552 Fig. 1 - Message flow for a full handshake
555 internal void DoHandshake()
557 // Reset isSecure field
558 this.session.IsSecure = false;
561 this.sendRecord(TlsHandshakeType.ClientHello);
563 // Read server response
564 while (!this.session.Context.HelloDone)
567 this.receiveRecord();
570 // Send client certificate if requested
571 if (this.session.Context.ServerSettings.CertificateRequest)
573 this.sendRecord(TlsHandshakeType.Certificate);
576 // Send Client Key Exchange
577 this.sendRecord(TlsHandshakeType.ClientKeyExchange);
579 // Now initialize session cipher with the generated keys
580 this.session.Context.Cipher.InitializeCipher();
582 // Send certificate verify if requested
583 if (this.session.Context.ServerSettings.CertificateRequest)
585 this.sendRecord(TlsHandshakeType.CertificateVerify);
588 // Send Cipher Spec protocol
589 this.sendChangeCipherSpec();
591 // Read Cipher Spec protocol
592 this.receiveRecord();
594 // Read server finished
595 if (!this.session.Context.HandshakeFinished)
597 this.receiveRecord();
601 this.session.Context.ClearKeyInfo();
604 this.session.IsSecure = true;
607 private TlsHandshakeMessage createClientHandshakeMessage(TlsHandshakeType type)
611 case TlsHandshakeType.ClientHello:
612 return new TlsClientHello(session);
614 case TlsHandshakeType.Certificate:
615 return new TlsClientCertificate(session);
617 case TlsHandshakeType.ClientKeyExchange:
618 return new TlsClientKeyExchange(session);
620 case TlsHandshakeType.CertificateVerify:
621 return new TlsClientCertificateVerify(session);
623 case TlsHandshakeType.Finished:
624 return new TlsClientFinished(session);
627 throw new InvalidOperationException("Unknown client handshake message type: " + type.ToString() );
631 private TlsHandshakeMessage createServerHandshakeMessage(TlsHandshakeType type, byte[] buffer)
635 case TlsHandshakeType.HelloRequest:
636 this.sendRecord(TlsHandshakeType.ClientHello);
639 case TlsHandshakeType.ServerHello:
640 return new TlsServerHello(session, buffer);
642 case TlsHandshakeType.Certificate:
643 return new TlsServerCertificate(session, buffer);
645 case TlsHandshakeType.ServerKeyExchange:
646 return new TlsServerKeyExchange(session, buffer);
648 case TlsHandshakeType.CertificateRequest:
649 return new TlsServerCertificateRequest(session, buffer);
651 case TlsHandshakeType.ServerHelloDone:
652 return new TlsServerHelloDone(session, buffer);
654 case TlsHandshakeType.Finished:
655 return new TlsServerFinished(session, buffer);
658 throw this.session.CreateException("Unknown server handshake message received ({0})", type.ToString());