2004-03-10 Carlos Guzman Alvarez <carlosga@telefonica.net>
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / TlsCipherSuite.cs
1 /* Transport Security Layer (TLS)
2  * Copyright (c) 2003-2004 Carlos Guzman Alvarez
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.Text;
28 using System.Security.Cryptography;
29 using System.Security.Cryptography.X509Certificates;
30
31 using Mono.Security;
32 using Mono.Security.Cryptography;
33
34 namespace Mono.Security.Protocol.Tls
35 {
36         internal class TlsCipherSuite : CipherSuite
37         {
38                 #region Constructors
39                 
40                 public TlsCipherSuite(
41                         short code, string name, CipherAlgorithmType cipherAlgorithmType, 
42                         HashAlgorithmType hashAlgorithmType, ExchangeAlgorithmType exchangeAlgorithmType,
43                         bool exportable, bool blockMode, byte keyMaterialSize, 
44                         byte expandedKeyMaterialSize, short effectiveKeyBytes, 
45                         byte ivSize, byte blockSize) 
46                         :base(code, name, cipherAlgorithmType, hashAlgorithmType, 
47                         exchangeAlgorithmType, exportable, blockMode, keyMaterialSize, 
48                         expandedKeyMaterialSize, effectiveKeyBytes, ivSize, blockSize)
49                 {
50                 }
51
52                 #endregion
53
54                 #region MAC Generation Methods
55
56                 public override byte[] ComputeServerRecordMAC(ContentType contentType, byte[] fragment)
57                 {
58                         TlsStream       data    = new TlsStream();
59                         byte[]          result  = null;
60
61                         if (this.Context is ClientContext)
62                         {
63                                 data.Write(this.Context.ReadSequenceNumber);
64                         }
65                         else
66                         {
67                                 data.Write(this.Context.WriteSequenceNumber);
68                         }
69
70                         data.Write((byte)contentType);
71                         data.Write(this.Context.Protocol);
72                         data.Write((short)fragment.Length);
73                         data.Write(fragment);
74
75                         result = this.ServerHMAC.ComputeHash(data.ToArray());
76
77                         data.Reset();
78
79                         return result;
80                 }
81
82                 public override byte[] ComputeClientRecordMAC(ContentType contentType, byte[] fragment)
83                 {
84                         TlsStream       data    = new TlsStream();
85                         byte[]          result  = null;
86
87                         if (this.Context is ClientContext)
88                         {
89                                 data.Write(this.Context.WriteSequenceNumber);
90                         }
91                         else
92                         {
93                                 data.Write(this.Context.ReadSequenceNumber);
94                         }
95
96                         data.Write((byte)contentType);
97                         data.Write(this.Context.Protocol);
98                         data.Write((short)fragment.Length);
99                         data.Write(fragment);
100
101                         result = this.ClientHMAC.ComputeHash(data.ToArray());
102
103                         data.Reset();
104
105                         return result;
106                 }
107
108                 #endregion
109
110                 #region Key Generation Methods
111
112                 public override void ComputeMasterSecret(byte[] preMasterSecret)
113                 {
114                         // Create master secret
115                         this.Context.MasterSecret = new byte[preMasterSecret.Length];
116                         this.Context.MasterSecret = this.PRF(
117                                 preMasterSecret, "master secret", this.Context.RandomCS, 48);
118                 }
119
120                 public override void ComputeKeys()
121                 {
122                         // Create keyblock
123                         TlsStream keyBlock = new TlsStream(
124                                 this.PRF(
125                                 this.Context.MasterSecret, 
126                                 "key expansion",
127                                 this.Context.RandomSC,
128                                 this.KeyBlockSize));
129
130                         this.Context.ClientWriteMAC = keyBlock.ReadBytes(this.HashSize);
131                         this.Context.ServerWriteMAC = keyBlock.ReadBytes(this.HashSize);
132                         this.Context.ClientWriteKey = keyBlock.ReadBytes(this.KeyMaterialSize);
133                         this.Context.ServerWriteKey = keyBlock.ReadBytes(this.KeyMaterialSize);
134
135                         if (!this.IsExportable)
136                         {
137                                 if (this.IvSize != 0)
138                                 {
139                                         this.Context.ClientWriteIV = keyBlock.ReadBytes(this.IvSize);
140                                         this.Context.ServerWriteIV = keyBlock.ReadBytes(this.IvSize);
141                                 }
142                                 else
143                                 {
144                                         this.Context.ClientWriteIV = CipherSuite.EmptyArray;
145                                         this.Context.ServerWriteIV = CipherSuite.EmptyArray;
146                                 }
147                         }
148                         else
149                         {
150                                 // Generate final write keys
151                                 byte[] finalClientWriteKey      = PRF(this.Context.ClientWriteKey, "client write key", this.Context.RandomCS, this.KeyMaterialSize);
152                                 byte[] finalServerWriteKey      = PRF(this.Context.ServerWriteKey, "server write key", this.Context.RandomCS, this.KeyMaterialSize);
153                                 
154                                 this.Context.ClientWriteKey     = finalClientWriteKey;
155                                 this.Context.ServerWriteKey     = finalServerWriteKey;
156
157                                 // Generate IV block
158                                 byte[] ivBlock = PRF(new byte[]{}, "IV block", this.Context.RandomCS, this.IvSize*2);
159
160                                 // Generate IV keys
161                                 this.Context.ClientWriteIV = new byte[this.IvSize];                             
162                                 System.Array.Copy(ivBlock, 0, this.Context.ClientWriteIV, 0, this.Context.ClientWriteIV.Length);
163
164                                 this.Context.ServerWriteIV = new byte[this.IvSize];
165                                 System.Array.Copy(ivBlock, this.IvSize, this.Context.ServerWriteIV, 0, this.Context.ServerWriteIV.Length);
166                         }
167
168                         // Clear no more needed data
169                         keyBlock.Reset();
170                 }
171
172                 #endregion
173         }
174 }