Recommit change done by Pedro
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / TlsSession.cs
1 /* Transport Security Layer (TLS)
2  * Copyright (c) 2003 Carlos Guzmán Álvarez
3  * 
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:
11  * 
12  * The above copyright notice and this permission notice shall be included 
13  * in all copies or substantial portions of the Software.
14  * 
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.
23  */
24
25 using System;
26 using System.IO;
27 using System.Collections;
28 using System.Text;
29 using System.Net;
30 using System.Net.Sockets;
31 using System.Security.Cryptography;
32
33 using Mono.Security.Protocol.Tls.Alerts;
34
35 namespace Mono.Security.Protocol.Tls
36 {
37         public sealed class TlsSession
38         {
39                 #region EVENTS
40
41                 public event TlsWarningAlertEventHandler WarningAlert;
42
43                 #endregion
44
45                 #region FIELDS
46                 
47                 private byte[]                                          sessionId;
48                 private TlsSessionContext                       context;
49                 private bool                                            helloDone;
50                 private bool                                            handshakeFinished;
51                 private TlsSessionSettings                      settings;
52                 private TlsCipherSuiteCollection        supportedCiphers;
53                 private TlsSocket                                       socket;
54                 private TlsNetworkStream                        networkStream;
55                 private bool                                            isSecure;
56                 private TlsSessionState                         state;
57
58                 #endregion
59
60                 #region PROPERTIES
61
62                 public byte[] SessionId
63                 {
64                         get { return sessionId; }
65                 }
66
67                 public TlsNetworkStream NetworkStream
68                 {
69                         get { return networkStream; }
70                 }
71
72                 public TlsSessionState State
73                 {
74                         get { return state; }
75                 }
76
77                 #endregion
78
79                 #region INTERNAL_PROPERTIES
80
81                 internal TlsSessionContext Context
82                 {
83                         get { return context; }
84                 }
85
86                 internal TlsCipherSuiteCollection SupportedCiphers
87                 {
88                         get { return supportedCiphers; }
89                 }
90                                 
91                 internal bool HelloDone
92                 {
93                         get { return helloDone; }
94                         set { helloDone = value; }
95                 }
96
97                 internal bool HandshakeFinished
98                 {
99                         get { return handshakeFinished; }
100                         set { handshakeFinished = value; }
101                 }
102
103                 internal bool IsSecure
104                 {
105                         get { return isSecure; }
106                         set { isSecure = value; }
107                 }
108
109                 internal TlsSessionSettings Settings
110                 {
111                         get { return settings; }
112                 }
113
114                 internal short MaxFragmentSize
115                 {
116                         get { return (short)System.Math.Pow(2, 14); }
117                 }
118
119                 #endregion
120
121                 #region CONSTRUCTORS
122
123                 public TlsSession(TlsSessionSettings settings)
124                 {
125                         this.settings                   = settings;
126                         this.context                    = new TlsSessionContext();
127                         this.sessionId                  = new byte[0];
128                                                 
129                         // Initialize socket for connection
130                         this.initializeSocket();
131                 }
132
133                 #endregion
134
135                 #region EXCEPTION_METHODS
136
137                 internal TlsException CreateException(TlsAlertLevel alertLevel, TlsAlertDescription alertDesc)
138                 {
139                         return CreateException(TlsAlert.GetAlertMessage(alertDesc));
140                 }
141
142                 internal TlsException CreateException(string format, params object[] args)
143                 {
144                         StringBuilder message = new StringBuilder();
145                         message.AppendFormat(format, args);
146
147                         return CreateException(message.ToString());
148                 }
149
150                 internal TlsException CreateException(string message)
151                 {
152                         this.state = TlsSessionState.Broken;
153
154                         // Throw an exception will made the connection unavailable
155                         // for this both streams will be closed
156                         closeStreams();
157
158                         return new TlsException(message);
159                 }
160
161                 #endregion
162
163                 #region METHODS
164
165                 public void Open()
166                 {
167                         try
168                         {
169                                 this.context.Protocol                   = settings.Protocol;
170                                 this.context.CompressionMethod  = settings.CompressionMethod;
171                                 this.state                                              = TlsSessionState.OpeningSecure;
172                                 this.supportedCiphers                   = TlsCipherSuiteFactory.GetSupportedCiphers(context.Protocol);
173                                 this.socket.DoHandshake();
174                                 this.state                              = TlsSessionState.OpenSecure;
175                         }
176                         catch (TlsException ex)
177                         {
178                                 this.state = TlsSessionState.Broken;
179                                 throw ex;
180                         }
181                         catch (Exception ex)
182                         {
183                                 this.state = TlsSessionState.Broken;
184                                 this.closeStreams();
185                                 throw ex;
186                         }
187                 }
188
189                 public void Close()
190                 {
191                         try
192                         {
193                                 this.state = TlsSessionState.Closing;
194
195                                 if (isSecure)
196                                 {
197                                         TlsCloseNotifyAlert alert = new TlsCloseNotifyAlert(this);
198
199                                         // Write close notify
200                                         this.socket.SendAlert(alert);
201
202                                         // Check that the session is finished by the client and by server
203                                         if (!this.context.ConnectionEnd)
204                                         {
205                                                 throw new TlsException("Invalid session termination");
206                                         }
207                                 }                               
208                         }
209                         catch (Exception ex)
210                         {
211                                 this.state = TlsSessionState.Broken;
212                                 throw ex;
213                         }
214                         finally
215                         {
216                                 // Close streams
217                                 closeStreams();
218
219                                 this.state = TlsSessionState.Closed;
220                         }
221                 }
222
223                 #endregion
224
225                 #region INTERNAL_METHODS
226
227                 internal void RaiseWarningAlert(TlsAlertLevel level, TlsAlertDescription description)
228                 {
229                         if (WarningAlert != null)
230                         {
231                                 WarningAlert(this, new TlsWarningAlertEventArgs(level, description));
232                         }
233                 }
234
235                 internal void SetSessionId(byte[] sessionId)
236                 {
237                         this.sessionId = sessionId;
238                 }
239
240                 #endregion
241
242                 #region PRIVATE_METHODS
243
244                 private void initializeSocket()
245                 {
246                         try
247                         {
248                                 this.state = TlsSessionState.Opening;
249
250                                 // Initialize socket
251                                 IPAddress       hostadd = Dns.Resolve(settings.ServerName).AddressList[0];
252                                 IPEndPoint      EPhost  = new IPEndPoint(hostadd, settings.ServerPort);
253
254                                 // Create the socket
255                                 socket = new TlsSocket(
256                                         this,
257                                         AddressFamily.InterNetwork,
258                                         SocketType.Stream,
259                                         ProtocolType.IP);
260
261                                 // Make the socket to connect to the Server
262                                 socket.Connect(EPhost);                                 
263                                 networkStream = new TlsNetworkStream(socket, true);
264
265                                 this.state = TlsSessionState.Open;
266                         }
267                         catch (Exception ex)
268                         {
269                                 this.state = TlsSessionState.Broken;
270                                 throw ex;
271                         }                       
272                 }
273
274                 private void closeStreams()
275                 {
276                         // Reset session state
277                         this.context.IsActual = false;
278
279                         // Close the socket and the networkStream
280                         this.networkStream.Close();
281
282                         // Reset session information
283                         this.isSecure                   = false;
284                         this.helloDone                  = false;
285                         this.handshakeFinished  = false;
286                         this.context                    = new TlsSessionContext();
287                         this.sessionId                  = new byte[0];                  
288                 }
289
290                 #endregion
291         }
292 }