1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
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.
28 using System.Security.Cryptography;
29 using System.Security.Cryptography.X509Certificates;
32 using Mono.Security.Cryptography;
34 namespace Mono.Security.Protocol.Tls
36 internal class SslCipherSuite : CipherSuite
47 public SslCipherSuite(
48 short code, string name, CipherAlgorithmType cipherAlgorithmType,
49 HashAlgorithmType hashAlgorithmType, ExchangeAlgorithmType exchangeAlgorithmType,
50 bool exportable, bool blockMode, byte keyMaterialSize,
51 byte expandedKeyMaterialSize, short effectiveKeyBytes,
52 byte ivSize, byte blockSize) :
53 base(code, name, cipherAlgorithmType, hashAlgorithmType,
54 exchangeAlgorithmType, exportable, blockMode, keyMaterialSize,
55 expandedKeyMaterialSize, effectiveKeyBytes, ivSize, blockSize)
58 int padLength = (hashAlgorithmType == HashAlgorithmType.Md5) ? 48 : 40;
61 this.pad1 = new byte[padLength];
62 this.pad2 = new byte[padLength];
64 /* Pad the key for inner and outer digest */
65 for (int i = 0; i < padLength; ++i)
74 #region MAC Generation Methods
76 public override byte[] ComputeServerRecordMAC(ContentType contentType, byte[] fragment)
78 HashAlgorithm hash = HashAlgorithm.Create(this.HashAlgorithmName);
79 TlsStream block = new TlsStream();
81 block.Write(this.Context.ServerWriteMAC);
82 block.Write(this.pad1);
83 if (this.Context is ClientContext)
85 block.Write(this.Context.ReadSequenceNumber);
89 block.Write(this.Context.WriteSequenceNumber);
91 block.Write((byte)contentType);
92 block.Write((short)fragment.Length);
93 block.Write(fragment);
95 hash.ComputeHash(block.ToArray(), 0, (int)block.Length);
97 byte[] blockHash = hash.Hash;
101 block.Write(this.Context.ServerWriteMAC);
102 block.Write(this.pad2);
103 block.Write(blockHash);
105 hash.ComputeHash(block.ToArray(), 0, (int)block.Length);
112 public override byte[] ComputeClientRecordMAC(ContentType contentType, byte[] fragment)
114 HashAlgorithm hash = HashAlgorithm.Create(this.HashAlgorithmName);
115 TlsStream block = new TlsStream();
117 block.Write(this.Context.ClientWriteMAC);
118 block.Write(this.pad1);
119 if (this.Context is ClientContext)
121 block.Write(this.Context.WriteSequenceNumber);
125 block.Write(this.Context.ReadSequenceNumber);
127 block.Write((byte)contentType);
128 block.Write((short)fragment.Length);
129 block.Write(fragment);
131 hash.ComputeHash(block.ToArray(), 0, (int)block.Length);
133 byte[] blockHash = hash.Hash;
137 block.Write(this.Context.ClientWriteMAC);
138 block.Write(this.pad2);
139 block.Write(blockHash);
141 hash.ComputeHash(block.ToArray(), 0, (int)block.Length);
150 #region Key Generation Methods
152 public override void ComputeMasterSecret(byte[] preMasterSecret)
154 TlsStream masterSecret = new TlsStream();
156 masterSecret.Write(this.prf(preMasterSecret, "A", this.Context.RandomCS));
157 masterSecret.Write(this.prf(preMasterSecret, "BB", this.Context.RandomCS));
158 masterSecret.Write(this.prf(preMasterSecret, "CCC", this.Context.RandomCS));
160 this.Context.MasterSecret = masterSecret.ToArray();
162 DebugHelper.WriteLine(">>>> MasterSecret", this.Context.MasterSecret);
165 public override void ComputeKeys()
168 TlsStream tmp = new TlsStream();
170 char labelChar = 'A';
173 while (tmp.Length < this.KeyBlockSize)
175 string label = String.Empty;
177 for (int i = 0; i < count; i++)
179 label += labelChar.ToString();
182 byte[] block = this.prf(this.Context.MasterSecret, label.ToString(), this.Context.RandomSC);
184 int size = (tmp.Length + block.Length) > this.KeyBlockSize ? (this.KeyBlockSize - (int)tmp.Length) : block.Length;
186 tmp.Write(block, 0, size);
193 TlsStream keyBlock = new TlsStream(tmp.ToArray());
195 this.Context.ClientWriteMAC = keyBlock.ReadBytes(this.HashSize);
196 this.Context.ServerWriteMAC = keyBlock.ReadBytes(this.HashSize);
197 this.Context.ClientWriteKey = keyBlock.ReadBytes(this.KeyMaterialSize);
198 this.Context.ServerWriteKey = keyBlock.ReadBytes(this.KeyMaterialSize);
200 if (!this.IsExportable)
202 if (this.IvSize != 0)
204 this.Context.ClientWriteIV = keyBlock.ReadBytes(this.IvSize);
205 this.Context.ServerWriteIV = keyBlock.ReadBytes(this.IvSize);
209 this.Context.ClientWriteIV = CipherSuite.EmptyArray;
210 this.Context.ServerWriteIV = CipherSuite.EmptyArray;
215 HashAlgorithm md5 = MD5.Create();
217 int keySize = (md5.HashSize >> 3); //in bytes not bits
218 byte[] temp = new byte [keySize];
220 // Generate final write keys
221 md5.TransformBlock(this.Context.ClientWriteKey, 0, this.Context.ClientWriteKey.Length, temp, 0);
222 md5.TransformFinalBlock(this.Context.RandomCS, 0, this.Context.RandomCS.Length);
223 byte[] finalClientWriteKey = new byte[this.ExpandedKeyMaterialSize];
224 Buffer.BlockCopy(md5.Hash, 0, finalClientWriteKey, 0, this.ExpandedKeyMaterialSize);
227 md5.TransformBlock(this.Context.ServerWriteKey, 0, this.Context.ServerWriteKey.Length, temp, 0);
228 md5.TransformFinalBlock(this.Context.RandomSC, 0, this.Context.RandomSC.Length);
229 byte[] finalServerWriteKey = new byte[this.ExpandedKeyMaterialSize];
230 Buffer.BlockCopy(md5.Hash, 0, finalServerWriteKey, 0, this.ExpandedKeyMaterialSize);
232 this.Context.ClientWriteKey = finalClientWriteKey;
233 this.Context.ServerWriteKey = finalServerWriteKey;
239 temp = md5.ComputeHash(this.Context.RandomCS, 0, this.Context.RandomCS.Length);
240 this.Context.ClientWriteIV = new byte[this.IvSize];
241 Buffer.BlockCopy(temp, 0, this.Context.ClientWriteIV, 0, this.IvSize);
244 temp = md5.ComputeHash(this.Context.RandomSC, 0, this.Context.RandomSC.Length);
245 this.Context.ServerWriteIV = new byte[this.IvSize];
246 Buffer.BlockCopy(temp, 0, this.Context.ServerWriteIV, 0, this.IvSize);
250 this.Context.ClientWriteIV = CipherSuite.EmptyArray;
251 this.Context.ServerWriteIV = CipherSuite.EmptyArray;
255 DebugHelper.WriteLine(">>>> KeyBlock", keyBlock.ToArray());
256 DebugHelper.WriteLine(">>>> ClientWriteKey", this.Context.ClientWriteKey);
257 DebugHelper.WriteLine(">>>> ClientWriteIV", this.Context.ClientWriteIV);
258 DebugHelper.WriteLine(">>>> ClientWriteMAC", this.Context.ClientWriteMAC);
259 DebugHelper.WriteLine(">>>> ServerWriteKey", this.Context.ServerWriteKey);
260 DebugHelper.WriteLine(">>>> ServerWriteIV", this.Context.ServerWriteIV);
261 DebugHelper.WriteLine(">>>> ServerWriteMAC", this.Context.ServerWriteMAC);
263 ClientSessionCache.SetContextInCache (this.Context);
264 // Clear no more needed data
271 #region Private Methods
273 private byte[] prf(byte[] secret, string label, byte[] random)
275 HashAlgorithm md5 = MD5.Create();
276 HashAlgorithm sha = SHA1.Create();
279 TlsStream block = new TlsStream();
280 block.Write(Encoding.ASCII.GetBytes(label));
284 byte[] shaHash = sha.ComputeHash(block.ToArray(), 0, (int)block.Length);
290 block.Write(shaHash);
292 byte[] result = md5.ComputeHash(block.ToArray(), 0, (int)block.Length);