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
.Security
.Cryptography
;
30 namespace Mono
.Security
.Protocol
.Tls
32 internal class SslCipherSuite
: CipherSuite
39 private const int MacHeaderLength
= 11;
40 private byte[] header
;
46 public SslCipherSuite(
47 short code
, string name
, CipherAlgorithmType cipherAlgorithmType
,
48 HashAlgorithmType hashAlgorithmType
, ExchangeAlgorithmType exchangeAlgorithmType
,
49 bool exportable
, bool blockMode
, byte keyMaterialSize
,
50 byte expandedKeyMaterialSize
, short effectiveKeyBytes
,
51 byte ivSize
, byte blockSize
) :
52 base(code
, name
, cipherAlgorithmType
, hashAlgorithmType
,
53 exchangeAlgorithmType
, exportable
, blockMode
, keyMaterialSize
,
54 expandedKeyMaterialSize
, effectiveKeyBytes
, ivSize
, blockSize
)
57 int padLength
= (hashAlgorithmType
== HashAlgorithmType
.Md5
) ? 48 : 40;
60 this.pad1
= new byte[padLength
];
61 this.pad2
= new byte[padLength
];
63 /* Pad the key for inner and outer digest */
64 for (int i
= 0; i
< padLength
; ++i
)
73 #region MAC Generation Methods
75 public override byte[] ComputeServerRecordMAC(ContentType contentType
, byte[] fragment
)
77 HashAlgorithm hash
= HashAlgorithm
.Create(this.HashAlgorithmName
);
79 byte[] smac
= this.Context
.Read
.ServerWriteMAC
;
80 hash
.TransformBlock (smac
, 0, smac
.Length
, smac
, 0);
81 hash
.TransformBlock (pad1
, 0, pad1
.Length
, pad1
, 0);
84 header
= new byte [MacHeaderLength
];
86 ulong seqnum
= (Context
is ClientContext
) ? Context
.ReadSequenceNumber
: Context
.WriteSequenceNumber
;
87 Write (header
, 0, seqnum
);
88 header
[8] = (byte) contentType
;
89 Write (header
, 9, (short)fragment
.Length
);
90 hash
.TransformBlock (header
, 0, header
.Length
, header
, 0);
91 hash
.TransformBlock (fragment
, 0, fragment
.Length
, fragment
, 0);
92 // hack, else the method will allocate a new buffer of the same length (negative half the optimization)
93 hash
.TransformFinalBlock (CipherSuite
.EmptyArray
, 0, 0);
95 byte[] blockHash
= hash
.Hash
;
99 hash
.TransformBlock (smac
, 0, smac
.Length
, smac
, 0);
100 hash
.TransformBlock (pad2
, 0, pad2
.Length
, pad2
, 0);
101 hash
.TransformBlock (blockHash
, 0, blockHash
.Length
, blockHash
, 0);
103 hash
.TransformFinalBlock (CipherSuite
.EmptyArray
, 0, 0);
108 public override byte[] ComputeClientRecordMAC(ContentType contentType
, byte[] fragment
)
110 HashAlgorithm hash
= HashAlgorithm
.Create(this.HashAlgorithmName
);
112 byte[] cmac
= this.Context
.Current
.ClientWriteMAC
;
113 hash
.TransformBlock (cmac
, 0, cmac
.Length
, cmac
, 0);
114 hash
.TransformBlock (pad1
, 0, pad1
.Length
, pad1
, 0);
117 header
= new byte [MacHeaderLength
];
119 ulong seqnum
= (Context
is ClientContext
) ? Context
.WriteSequenceNumber
: Context
.ReadSequenceNumber
;
120 Write (header
, 0, seqnum
);
121 header
[8] = (byte) contentType
;
122 Write (header
, 9, (short)fragment
.Length
);
123 hash
.TransformBlock (header
, 0, header
.Length
, header
, 0);
124 hash
.TransformBlock (fragment
, 0, fragment
.Length
, fragment
, 0);
125 // hack, else the method will allocate a new buffer of the same length (negative half the optimization)
126 hash
.TransformFinalBlock (CipherSuite
.EmptyArray
, 0, 0);
128 byte[] blockHash
= hash
.Hash
;
132 hash
.TransformBlock (cmac
, 0, cmac
.Length
, cmac
, 0);
133 hash
.TransformBlock (pad2
, 0, pad2
.Length
, pad2
, 0);
134 hash
.TransformBlock (blockHash
, 0, blockHash
.Length
, blockHash
, 0);
136 hash
.TransformFinalBlock (CipherSuite
.EmptyArray
, 0, 0);
143 #region Key Generation Methods
145 public override void ComputeMasterSecret(byte[] preMasterSecret
)
147 TlsStream masterSecret
= new TlsStream();
149 masterSecret
.Write(this.prf(preMasterSecret
, "A", this.Context
.RandomCS
));
150 masterSecret
.Write(this.prf(preMasterSecret
, "BB", this.Context
.RandomCS
));
151 masterSecret
.Write(this.prf(preMasterSecret
, "CCC", this.Context
.RandomCS
));
153 this.Context
.MasterSecret
= masterSecret
.ToArray();
155 DebugHelper
.WriteLine(">>>> MasterSecret", this.Context
.MasterSecret
);
158 public override void ComputeKeys()
161 TlsStream tmp
= new TlsStream();
163 char labelChar
= 'A';
166 while (tmp
.Length
< this.KeyBlockSize
)
168 string label
= String
.Empty
;
170 for (int i
= 0; i
< count
; i
++)
172 label
+= labelChar
.ToString();
175 byte[] block
= this.prf(this.Context
.MasterSecret
, label
.ToString(), this.Context
.RandomSC
);
177 int size
= (tmp
.Length
+ block
.Length
) > this.KeyBlockSize
? (this.KeyBlockSize
- (int)tmp
.Length
) : block
.Length
;
179 tmp
.Write(block
, 0, size
);
186 TlsStream keyBlock
= new TlsStream(tmp
.ToArray());
188 this.Context
.Negotiating
.ClientWriteMAC
= keyBlock
.ReadBytes(this.HashSize
);
189 this.Context
.Negotiating
.ServerWriteMAC
= keyBlock
.ReadBytes(this.HashSize
);
190 this.Context
.ClientWriteKey
= keyBlock
.ReadBytes(this.KeyMaterialSize
);
191 this.Context
.ServerWriteKey
= keyBlock
.ReadBytes(this.KeyMaterialSize
);
193 if (!this.IsExportable
)
195 if (this.IvSize
!= 0)
197 this.Context
.ClientWriteIV
= keyBlock
.ReadBytes(this.IvSize
);
198 this.Context
.ServerWriteIV
= keyBlock
.ReadBytes(this.IvSize
);
202 this.Context
.ClientWriteIV
= CipherSuite
.EmptyArray
;
203 this.Context
.ServerWriteIV
= CipherSuite
.EmptyArray
;
208 HashAlgorithm md5
= MD5
.Create();
210 int keySize
= (md5
.HashSize
>> 3); //in bytes not bits
211 byte[] temp
= new byte [keySize
];
213 // Generate final write keys
214 md5
.TransformBlock(this.Context
.ClientWriteKey
, 0, this.Context
.ClientWriteKey
.Length
, temp
, 0);
215 md5
.TransformFinalBlock(this.Context
.RandomCS
, 0, this.Context
.RandomCS
.Length
);
216 byte[] finalClientWriteKey
= new byte[this.ExpandedKeyMaterialSize
];
217 Buffer
.BlockCopy(md5
.Hash
, 0, finalClientWriteKey
, 0, this.ExpandedKeyMaterialSize
);
220 md5
.TransformBlock(this.Context
.ServerWriteKey
, 0, this.Context
.ServerWriteKey
.Length
, temp
, 0);
221 md5
.TransformFinalBlock(this.Context
.RandomSC
, 0, this.Context
.RandomSC
.Length
);
222 byte[] finalServerWriteKey
= new byte[this.ExpandedKeyMaterialSize
];
223 Buffer
.BlockCopy(md5
.Hash
, 0, finalServerWriteKey
, 0, this.ExpandedKeyMaterialSize
);
225 this.Context
.ClientWriteKey
= finalClientWriteKey
;
226 this.Context
.ServerWriteKey
= finalServerWriteKey
;
232 temp
= md5
.ComputeHash(this.Context
.RandomCS
, 0, this.Context
.RandomCS
.Length
);
233 this.Context
.ClientWriteIV
= new byte[this.IvSize
];
234 Buffer
.BlockCopy(temp
, 0, this.Context
.ClientWriteIV
, 0, this.IvSize
);
237 temp
= md5
.ComputeHash(this.Context
.RandomSC
, 0, this.Context
.RandomSC
.Length
);
238 this.Context
.ServerWriteIV
= new byte[this.IvSize
];
239 Buffer
.BlockCopy(temp
, 0, this.Context
.ServerWriteIV
, 0, this.IvSize
);
243 this.Context
.ClientWriteIV
= CipherSuite
.EmptyArray
;
244 this.Context
.ServerWriteIV
= CipherSuite
.EmptyArray
;
248 DebugHelper
.WriteLine(">>>> KeyBlock", keyBlock
.ToArray());
249 DebugHelper
.WriteLine(">>>> ClientWriteKey", this.Context
.ClientWriteKey
);
250 DebugHelper
.WriteLine(">>>> ClientWriteIV", this.Context
.ClientWriteIV
);
251 DebugHelper
.WriteLine(">>>> ClientWriteMAC", this.Context
.Negotiating
.ClientWriteMAC
);
252 DebugHelper
.WriteLine(">>>> ServerWriteKey", this.Context
.ServerWriteKey
);
253 DebugHelper
.WriteLine(">>>> ServerWriteIV", this.Context
.ServerWriteIV
);
254 DebugHelper
.WriteLine(">>>> ServerWriteMAC", this.Context
.Negotiating
.ServerWriteMAC
);
256 ClientSessionCache
.SetContextInCache (this.Context
);
257 // Clear no more needed data
264 #region Private Methods
266 private byte[] prf(byte[] secret
, string label
, byte[] random
)
268 HashAlgorithm md5
= MD5
.Create();
269 HashAlgorithm sha
= SHA1
.Create();
272 TlsStream block
= new TlsStream();
273 block
.Write(Encoding
.ASCII
.GetBytes(label
));
277 byte[] shaHash
= sha
.ComputeHash(block
.ToArray(), 0, (int)block
.Length
);
283 block
.Write(shaHash
);
285 byte[] result
= md5
.ComputeHash(block
.ToArray(), 0, (int)block
.Length
);