comment as X509Certificate2 is in System.dll, which cause a circular dependency issue :(
[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 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
4 //
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:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
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.
23 //
24
25 using System;
26 using System.IO;
27 using System.Security.Cryptography;
28
29 namespace Mono.Security.Protocol.Tls
30 {
31         internal class TlsCipherSuite : CipherSuite
32         {
33                 private const int MacHeaderLength = 13;
34                 private byte[] header;
35
36                 #region Constructors
37                 
38                 public TlsCipherSuite(
39                         short code, string name, CipherAlgorithmType cipherAlgorithmType, 
40                         HashAlgorithmType hashAlgorithmType, ExchangeAlgorithmType exchangeAlgorithmType,
41                         bool exportable, bool blockMode, byte keyMaterialSize, 
42                         byte expandedKeyMaterialSize, short effectiveKeyBytes, 
43                         byte ivSize, byte blockSize) 
44                         :base(code, name, cipherAlgorithmType, hashAlgorithmType, 
45                         exchangeAlgorithmType, exportable, blockMode, keyMaterialSize, 
46                         expandedKeyMaterialSize, effectiveKeyBytes, ivSize, blockSize)
47                 {
48                 }
49
50                 #endregion
51
52                 #region MAC Generation Methods
53
54                 public override byte[] ComputeServerRecordMAC(ContentType contentType, byte[] fragment)
55                 {
56                         if (header == null)
57                                 header = new byte [MacHeaderLength];
58
59                         ulong seqnum = (Context is ClientContext) ? Context.ReadSequenceNumber : Context.WriteSequenceNumber;
60                         Write (header, 0, seqnum);
61                         header [8] = (byte) contentType;
62                         Write (header, 9, this.Context.Protocol);
63                         Write (header, 11, (short)fragment.Length);
64
65                         HashAlgorithm mac = this.ServerHMAC;
66                         mac.TransformBlock (header, 0, header.Length, header, 0);
67                         mac.TransformBlock (fragment, 0, fragment.Length, fragment, 0);
68                         // hack, else the method will allocate a new buffer of the same length (negative half the optimization)
69                         mac.TransformFinalBlock (CipherSuite.EmptyArray, 0, 0);
70                         return mac.Hash;
71                 }
72
73                 public override byte[] ComputeClientRecordMAC(ContentType contentType, byte[] fragment)
74                 {
75                         if (header == null)
76                                 header = new byte [MacHeaderLength];
77
78                         ulong seqnum = (Context is ClientContext) ? Context.WriteSequenceNumber : Context.ReadSequenceNumber;
79                         Write (header, 0, seqnum);
80                         header [8] = (byte) contentType;
81                         Write (header, 9, this.Context.Protocol);
82                         Write (header, 11, (short)fragment.Length);
83
84                         HashAlgorithm mac = this.ClientHMAC;
85                         mac.TransformBlock (header, 0, header.Length, header, 0);
86                         mac.TransformBlock (fragment, 0, fragment.Length, fragment, 0);
87                         // hack, else the method will allocate a new buffer of the same length (negative half the optimization)
88                         mac.TransformFinalBlock (CipherSuite.EmptyArray, 0, 0);
89                         return mac.Hash;
90                 }
91
92                 #endregion
93
94                 #region Key Generation Methods
95
96                 public override void ComputeMasterSecret(byte[] preMasterSecret)
97                 {
98                         // Create master secret
99                         this.Context.MasterSecret = new byte[preMasterSecret.Length];
100                         this.Context.MasterSecret = this.PRF(
101                                 preMasterSecret, "master secret", this.Context.RandomCS, 48);
102
103                         DebugHelper.WriteLine(">>>> MasterSecret", this.Context.MasterSecret);
104                 }
105
106                 public override void ComputeKeys()
107                 {
108                         // Create keyblock
109                         TlsStream keyBlock = new TlsStream(
110                                 this.PRF(
111                                 this.Context.MasterSecret, 
112                                 "key expansion",
113                                 this.Context.RandomSC,
114                                 this.KeyBlockSize));
115
116                         this.Context.Negotiating.ClientWriteMAC = keyBlock.ReadBytes(this.HashSize);
117                         this.Context.Negotiating.ServerWriteMAC = keyBlock.ReadBytes(this.HashSize);
118                         this.Context.ClientWriteKey = keyBlock.ReadBytes(this.KeyMaterialSize);
119                         this.Context.ServerWriteKey = keyBlock.ReadBytes(this.KeyMaterialSize);
120
121                         if (!this.IsExportable)
122                         {
123                                 if (this.IvSize != 0)
124                                 {
125                                         this.Context.ClientWriteIV = keyBlock.ReadBytes(this.IvSize);
126                                         this.Context.ServerWriteIV = keyBlock.ReadBytes(this.IvSize);
127                                 }
128                                 else
129                                 {
130                                         this.Context.ClientWriteIV = CipherSuite.EmptyArray;
131                                         this.Context.ServerWriteIV = CipherSuite.EmptyArray;
132                                 }
133                         }
134                         else
135                         {
136                                 // Generate final write keys
137                                 byte[] finalClientWriteKey      = PRF(this.Context.ClientWriteKey, "client write key", this.Context.RandomCS, this.ExpandedKeyMaterialSize);
138                                 byte[] finalServerWriteKey      = PRF(this.Context.ServerWriteKey, "server write key", this.Context.RandomCS, this.ExpandedKeyMaterialSize);
139                                 
140                                 this.Context.ClientWriteKey     = finalClientWriteKey;
141                                 this.Context.ServerWriteKey     = finalServerWriteKey;
142
143                                 if (this.IvSize > 0) 
144                                 {
145                                         // Generate IV block
146                                         byte[] ivBlock = PRF(CipherSuite.EmptyArray, "IV block", this.Context.RandomCS, this.IvSize*2);
147
148                                         // Generate IV keys
149                                         this.Context.ClientWriteIV = new byte[this.IvSize];                             
150                                         Buffer.BlockCopy(ivBlock, 0, this.Context.ClientWriteIV, 0, this.Context.ClientWriteIV.Length);
151
152                                         this.Context.ServerWriteIV = new byte[this.IvSize];
153                                         Buffer.BlockCopy(ivBlock, this.IvSize, this.Context.ServerWriteIV, 0, this.Context.ServerWriteIV.Length);
154                                 }
155                                 else 
156                                 {
157                                         this.Context.ClientWriteIV = CipherSuite.EmptyArray;
158                                         this.Context.ServerWriteIV = CipherSuite.EmptyArray;
159                                 }
160                         }
161
162                         DebugHelper.WriteLine(">>>> KeyBlock", keyBlock.ToArray());
163                         DebugHelper.WriteLine(">>>> ClientWriteKey", this.Context.ClientWriteKey);
164                         DebugHelper.WriteLine(">>>> ClientWriteIV", this.Context.ClientWriteIV);
165                         DebugHelper.WriteLine(">>>> ClientWriteMAC", this.Context.Negotiating.ClientWriteMAC);
166                         DebugHelper.WriteLine(">>>> ServerWriteKey", this.Context.ServerWriteKey);
167                         DebugHelper.WriteLine(">>>> ServerWriteIV", this.Context.ServerWriteIV);
168                         DebugHelper.WriteLine(">>>> ServerWriteMAC", this.Context.Negotiating.ServerWriteMAC);
169
170                         ClientSessionCache.SetContextInCache (this.Context);
171                         // Clear no more needed data
172                         keyBlock.Reset();
173                 }
174
175                 #endregion
176         }
177 }