2003-10-30 Sebastien Pouliot <spouliot@videotron.ca>
[mono.git] / mcs / class / Mono.Security / Mono.Security.Cryptography / TlsHMAC.cs
1 /* Transport Security Layer (TLS)\r
2  * Copyright (c) 2003 Carlos Guzmán Álvarez\r
3  * \r
4  * Permission is hereby granted, free of charge, to any person \r
5  * obtaining a copy of this software and associated documentation \r
6  * files (the "Software"), to deal in the Software without restriction, \r
7  * including without limitation the rights to use, copy, modify, merge, \r
8  * publish, distribute, sublicense, and/or sell copies of the Software, \r
9  * and to permit persons to whom the Software is furnished to do so, \r
10  * subject to the following conditions:\r
11  * \r
12  * The above copyright notice and this permission notice shall be included \r
13  * in all copies or substantial portions of the Software.\r
14  * \r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, \r
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES \r
17  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
19  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
20  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \r
22  * DEALINGS IN THE SOFTWARE.\r
23  */\r
24 \r
25 using System;\r
26 using System.Security.Cryptography;\r
27 \r
28 namespace Mono.Security.Cryptography\r
29 {\r
30    /*\r
31         * References:\r
32         *               RFC 2104 (http://www.ietf.org/rfc/rfc2104.txt)\r
33         *               RFC 2202 (http://www.ietf.org/rfc/rfc2202.txt)\r
34         * MSDN:\r
35         * \r
36         *               Extending the KeyedHashAlgorithm Class (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconextendingkeyedhashalgorithmclass.asp)\r
37         */\r
38         internal class HMAC : System.Security.Cryptography.KeyedHashAlgorithm\r
39         {\r
40                 #region FIELDS\r
41 \r
42                 private HashAlgorithm   hash;\r
43                 private bool                    hashing;\r
44 \r
45                 private byte[]                  innerPad;\r
46                 private byte[]                  outerPad;\r
47 \r
48                 #endregion\r
49 \r
50                 #region PROPERTIES\r
51         \r
52                 public override byte[] Key\r
53                 {\r
54                         get { return (byte[])KeyValue.Clone(); }\r
55                         set\r
56                         {\r
57                                 if (hashing)\r
58                                 {\r
59                                         throw new Exception("Cannot change key during hash operation.");\r
60                                 }\r
61 \r
62                                 /* if key is longer than 64 bytes reset it to rgbKey = Hash(rgbKey) */\r
63                                 if (value.Length > 64)\r
64                                 {\r
65                                         KeyValue = hash.ComputeHash(value);\r
66                                 }\r
67                                 else\r
68                                 {\r
69                                         KeyValue = (byte[])value.Clone();\r
70                                 }\r
71 \r
72                                 initializePad();\r
73                         }\r
74                 }\r
75 \r
76                 #endregion\r
77 \r
78                 #region CONSTRUCTORS\r
79 \r
80                 public HMAC()\r
81                 {\r
82                         // Create the hash\r
83                         hash = HashAlgorithm.Create("MD5");\r
84                         // Set HashSizeValue\r
85                         HashSizeValue = hash.HashSize;\r
86 \r
87                         // Generate a radom key\r
88                         byte[] rgbKey = new byte[64];\r
89                         RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();\r
90                         rng.GetNonZeroBytes(rgbKey);\r
91 \r
92                         KeyValue = (byte[])rgbKey.Clone();\r
93 \r
94                         initializePad();\r
95                 }\r
96 \r
97                 public HMAC(string hashName, byte[] rgbKey)\r
98                 {\r
99                         // Create the hash\r
100                         if (hashName == null || hashName.Length == 0)\r
101                         {\r
102                                 hashName = "MD5";\r
103                         }\r
104                         hash = HashAlgorithm.Create(hashName);\r
105                         // Set HashSizeValue\r
106                         HashSizeValue = hash.HashSize;\r
107 \r
108                         /* if key is longer than 64 bytes reset it to rgbKey = Hash(rgbKey) */\r
109                         if (rgbKey.Length > 64)\r
110                         {\r
111                                 KeyValue = hash.ComputeHash(rgbKey);\r
112                         }\r
113                         else\r
114                         {\r
115                                 KeyValue = (byte[])rgbKey.Clone();\r
116                         }\r
117 \r
118                         initializePad();\r
119                 }\r
120 \r
121                 #endregion\r
122 \r
123                 #region METHODS\r
124 \r
125                 public override void Initialize()\r
126                 {\r
127                         hash.Initialize();\r
128                         initializePad();\r
129                         hashing = false;\r
130                 }\r
131 \r
132                 protected override byte[] HashFinal()\r
133                 {\r
134                         if (!hashing)\r
135                         {\r
136                                 hash.TransformBlock(innerPad, 0, innerPad.Length, innerPad, 0);\r
137                                 hashing = true;\r
138                         }\r
139                         // Finalize the original hash\r
140                         hash.TransformFinalBlock(new byte[0], 0, 0);\r
141 \r
142                         byte[] firstResult = hash.Hash;\r
143 \r
144                         hash.Initialize();\r
145                         hash.TransformBlock(outerPad, 0, outerPad.Length, outerPad, 0);\r
146                         hash.TransformFinalBlock(firstResult, 0, firstResult.Length);\r
147                         \r
148                         Initialize();\r
149 \r
150                         return hash.Hash;\r
151                 }\r
152 \r
153                 protected override void HashCore(\r
154                         byte[] array,\r
155                         int ibStart,\r
156                         int cbSize)\r
157                 {\r
158                         if (!hashing)\r
159                         {\r
160                                 hash.TransformBlock(innerPad, 0, innerPad.Length, innerPad, 0);\r
161                                 hashing = true;\r
162                         }\r
163                         hash.TransformBlock(array, ibStart, cbSize, array, ibStart);\r
164                 }\r
165 \r
166                 #endregion\r
167 \r
168                 #region PRIVATE_METHODS\r
169 \r
170                 private void initializePad()\r
171                 {\r
172                         // Fill pad arrays\r
173                         innerPad = new byte[64];\r
174                         outerPad = new byte[64];\r
175 \r
176                         /* Pad the key for inner and outer digest */\r
177                         for (int i = 0 ; i < KeyValue.Length; ++i)\r
178                         {\r
179                                 innerPad[i] = (byte)(KeyValue[i] ^ 0x36);\r
180                                 outerPad[i] = (byte)(KeyValue[i] ^ 0x5C);\r
181                         }\r
182                         for (int i = KeyValue.Length; i < 64; ++i) \r
183                         {\r
184                                 innerPad[i] = 0x36;\r
185                                 outerPad[i] = 0x5C;\r
186                         }\r
187                 }\r
188 \r
189                 #endregion\r
190         }\r
191 }\r