2004-02-09 Carlos Guzm��n ��lvarez <carlosga@telefonica.net>
[mono.git] / mcs / class / Mono.Security / Mono.Security.Cryptography / TlsHMAC.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.Security.Cryptography;
27
28 namespace Mono.Security.Cryptography
29 {
30    /*
31         * References:
32         *               RFC 2104 (http://www.ietf.org/rfc/rfc2104.txt)
33         *               RFC 2202 (http://www.ietf.org/rfc/rfc2202.txt)
34         * MSDN:
35         * 
36         *               Extending the KeyedHashAlgorithm Class (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconextendingkeyedhashalgorithmclass.asp)
37         */
38         internal class HMAC : System.Security.Cryptography.KeyedHashAlgorithm
39         {
40                 #region Fields
41
42                 private HashAlgorithm   hash;
43                 private bool                    hashing;
44
45                 private byte[]                  innerPad;
46                 private byte[]                  outerPad;
47
48                 #endregion
49
50                 #region Properties
51         
52                 public override byte[] Key
53                 {
54                         get { return (byte[])KeyValue.Clone(); }
55                         set
56                         {
57                                 if (hashing)
58                                 {
59                                         throw new Exception("Cannot change key during hash operation.");
60                                 }
61
62                                 /* if key is longer than 64 bytes reset it to rgbKey = Hash(rgbKey) */
63                                 if (value.Length > 64)
64                                 {
65                                         KeyValue = hash.ComputeHash(value);
66                                 }
67                                 else
68                                 {
69                                         KeyValue = (byte[])value.Clone();
70                                 }
71
72                                 initializePad();
73                         }
74                 }
75
76                 #endregion
77
78                 #region Constructors
79
80                 public HMAC()
81                 {
82                         // Create the hash
83                         hash = MD5.Create();
84                         // Set HashSizeValue
85                         HashSizeValue = hash.HashSize;
86
87                         // Generate a radom key
88                         byte[] rgbKey = new byte[64];
89                         RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
90                         rng.GetNonZeroBytes(rgbKey);
91
92                         KeyValue = (byte[])rgbKey.Clone();
93
94                         this.Initialize();
95                 }
96
97                 public HMAC(string hashName, byte[] rgbKey)
98                 {
99                         // Create the hash
100                         if (hashName == null || hashName.Length == 0)
101                         {
102                                 hashName = "MD5";
103                         }
104                         hash = HashAlgorithm.Create(hashName);
105                         // Set HashSizeValue
106                         HashSizeValue = hash.HashSize;
107
108                         /* if key is longer than 64 bytes reset it to rgbKey = Hash(rgbKey) */
109                         if (rgbKey.Length > 64)
110                         {
111                                 KeyValue = hash.ComputeHash(rgbKey);
112                         }
113                         else
114                         {
115                                 KeyValue = (byte[])rgbKey.Clone();
116                         }
117
118                         this.Initialize();
119                 }
120
121                 #endregion
122
123                 #region Methods
124
125                 public override void Initialize()
126                 {
127                         hash.Initialize();
128                         initializePad();
129                         hashing = false;
130                 }
131
132                 protected override byte[] HashFinal()
133                 {
134                         if (!hashing)
135                         {
136                                 hash.TransformBlock(innerPad, 0, innerPad.Length, innerPad, 0);
137                                 hashing = true;
138                         }
139                         // Finalize the original hash
140                         hash.TransformFinalBlock(new byte[0], 0, 0);
141
142                         byte[] firstResult = hash.Hash;
143
144                         hash.Initialize();
145                         hash.TransformBlock(outerPad, 0, outerPad.Length, outerPad, 0);
146                         hash.TransformFinalBlock(firstResult, 0, firstResult.Length);
147                         
148                         Initialize();
149
150                         return hash.Hash;
151                 }
152
153                 protected override void HashCore(
154                         byte[] array,
155                         int ibStart,
156                         int cbSize)
157                 {
158                         if (!hashing)
159                         {
160                                 hash.TransformBlock(innerPad, 0, innerPad.Length, innerPad, 0);
161                                 hashing = true;
162                         }
163                         hash.TransformBlock(array, ibStart, cbSize, array, ibStart);
164                 }
165
166                 #endregion
167
168                 #region Private Methods
169
170                 private void initializePad()
171                 {
172                         // Fill pad arrays
173                         innerPad = new byte[64];
174                         outerPad = new byte[64];
175
176                         /* Pad the key for inner and outer digest */
177                         for (int i = 0 ; i < KeyValue.Length; ++i)
178                         {
179                                 innerPad[i] = (byte)(KeyValue[i] ^ 0x36);
180                                 outerPad[i] = (byte)(KeyValue[i] ^ 0x5C);
181                         }
182                         for (int i = KeyValue.Length; i < 64; ++i) 
183                         {
184                                 innerPad[i] = 0x36;
185                                 outerPad[i] = 0x5C;
186                         }
187                 }
188
189                 #endregion
190         }
191 }