2002-10-25 Sebastien Pouliot <spouliot@videotron.ca>
[mono.git] / mcs / class / corlib / System.Security.Cryptography / HMACSHA1.cs
1 //
2 // HMACSHA1.cs: Handles HMAC with SHA-1
3 //
4 // Author:
5 //      Sebastien Pouliot (spouliot@motus.com)
6 //
7 // (C) 2002 Motus Technologies Inc. (http://www.motus.com)
8 //
9
10 using System;
11 using System.IO;
12 using System.Security.Cryptography;
13
14 namespace System.Security.Cryptography {
15
16 // References:
17 // a.   FIPS PUB 198: The Keyed-Hash Message Authentication Code (HMAC), 2002 March.
18 //      http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf
19 // b.   Internet RFC 2104, HMAC, Keyed-Hashing for Message Authentication
20 //      (include C source for HMAC-MD5)
21 //      http://www.ietf.org/rfc/rfc2104.txt
22 // c.   IETF RFC2202: Test Cases for HMAC-MD5 and HMAC-SHA-1
23 //      (include C source for HMAC-MD5 and HAMAC-SHA1)
24 //      http://www.ietf.org/rfc/rfc2202.txt
25 // d.   ANSI X9.71, Keyed Hash Message Authentication Code.
26 //      not free :-(
27 //      http://webstore.ansi.org/ansidocstore/product.asp?sku=ANSI+X9%2E71%2D2000
28
29 // Generic HMAC mechanisms - most of HMAC work is done in here.
30 // It should work with any hash function e.g. MD5 for HMACMD5 (RFC2104)
31 internal class HMACAlgorithm {
32         private byte[] key;
33         private byte[] hash;
34         private HashAlgorithm algo;
35         private string hashName;
36         private CryptoStream stream;
37
38         public HMACAlgorithm (string algoName) 
39         {
40                 CreateHash (algoName);
41         }
42
43         ~HMACAlgorithm () 
44         {
45                 Dispose ();
46         }
47
48         private void CreateHash (string algoName) 
49         {
50                 algo = (HashAlgorithm) CryptoConfig.CreateFromName (algoName);
51                 hashName = algoName;
52         }
53
54         public void Dispose () 
55         {
56                 ZeroizeKey ();
57         }
58
59         public HashAlgorithm Algo {
60                 get { return algo; }
61         }
62
63         public string HashName {
64                 get { return hashName; }
65                 set { 
66                         // only if its not too late for a change
67                         if (stream == null)
68                                 CreateHash (value);
69                 }
70         }
71
72         public byte[] HashValue {
73                 get { return hash; }
74         }
75
76         public byte[] Key {
77                 get { return key; }
78                 set {
79                         if ((value != null) && (value.Length > 64))
80                                 key = algo.ComputeHash (value);
81                         else
82                                 key = (byte[]) value.Clone();
83                 }
84         }
85
86         public void Initialize () 
87         {
88                 hash = null;
89         }
90
91         private byte[] KeySetup (byte[] key, byte padding) 
92         {
93                 byte[] buf = new byte [64];
94
95                 for (int i = 0; i < key.Length; ++i)
96                         buf [i] = (byte) ((byte) key [i] ^ padding);
97
98                 for (int i = key.Length; i < 64; ++i)
99                         buf [i] = padding;
100                 
101                 return buf;
102         }
103
104         public void Core (byte[] rgb, int ib, int cb) 
105         {
106                 if (stream == null) {
107                         byte[] buf = KeySetup (key, 0x36);
108                         algo.Initialize ();
109                         stream = new CryptoStream (Stream.Null, algo, CryptoStreamMode.Write);
110                         stream.Write (buf, 0, buf.Length);
111                 }
112                 stream.Write (rgb, ib, cb);
113         }
114
115         public byte[] Final () 
116         {
117                 stream.Close ();
118                 stream = null;
119                 byte[] intermediate = algo.Hash;
120                 byte[] buf = KeySetup (key, 0x5C);
121
122                 algo.Initialize ();
123                 stream = new CryptoStream (Stream.Null, algo, CryptoStreamMode.Write);
124                 stream.Write (buf, 0, buf.Length);
125                 stream.Write (intermediate, 0, intermediate.Length);
126                 stream.Close ();
127                 stream = null;
128
129                 hash = algo.Hash;
130                 algo.Clear ();
131                 return hash;
132         }
133
134         // Note: this key is different (well most of the time) from the key
135         // used in KeyHashAlgorithm (this one may be padded or hashed). So
136         // it need to be zeroized independently.
137         public void ZeroizeKey () 
138         {
139                 if (key != null)
140                         Array.Clear (key, 0, key.Length);
141         }
142 }
143
144 public class HMACSHA1: KeyedHashAlgorithm {
145         private HMACAlgorithm hmac;
146
147         public HMACSHA1 () : base ()
148         {
149                 hmac = new HMACAlgorithm ("SHA1");
150                 HashSizeValue = 160;
151                 GenerateKey();
152         }
153
154         public HMACSHA1 (byte[] rgbKey) 
155         {
156                 hmac = new HMACAlgorithm ("SHA1");
157                 HashSizeValue = 160;
158                 hmac.Key = rgbKey;
159         }
160
161         ~HMACSHA1 () 
162         {
163                 Dispose (false);
164         }
165
166         public override byte[] Key {
167                 get { return base.Key; }
168                 set { 
169                         hmac.Key = value; 
170                         base.Key = value;
171                 }
172         } 
173
174         public string HashName {
175                 get { return hmac.HashName; }
176                 set { hmac.HashName = value; }
177         }
178
179         protected override void Dispose (bool disposing) 
180         {
181                 if (hmac != null)
182                         hmac.Dispose();
183                 base.Dispose (disposing);
184         }
185
186         // generate a random 64 bits key
187         private void GenerateKey () 
188         {
189                 KeyValue = new byte[8];
190                 RandomNumberGenerator rng = RandomNumberGenerator.Create();
191                 rng.GetBytes (KeyValue);
192                 hmac.Key = KeyValue;
193         }
194
195         public override void Initialize ()
196         {
197                 State = 0;
198                 hmac.Initialize ();
199         }
200
201         protected override void HashCore (byte[] rgb, int ib, int cb)
202         {
203                 if (State == 0) {
204                         // let us throw an exception if hash name is invalid
205                         // for HMACSHA1 (obviously this can't be done by the 
206                         // generic HMAC class) 
207                         if (! (hmac.Algo is SHA1))
208                                 throw new InvalidCastException ();
209                 }
210                 State = 1;
211                 hmac.Core (rgb, ib, cb);
212         }
213
214         protected override byte[] HashFinal ()
215         {
216                 State = 0;
217                 return hmac.Final ();
218         }
219 }
220
221 }