**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / Mono.Security.Cryptography / PKCS1.cs
blob934e0867d437de17ce34d457e8b19606d6ec6e2c
1 //
2 // PKCS1.cs - Implements PKCS#1 primitives.
3 //
4 // Author:
5 // Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System;
31 using System.Security.Cryptography;
33 namespace Mono.Security.Cryptography {
35 // References:
36 // a. PKCS#1: RSA Cryptography Standard
37 // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html
39 #if INSIDE_CORLIB
40 internal
41 #else
42 public
43 #endif
44 sealed class PKCS1 {
46 private PKCS1 ()
50 private static bool Compare (byte[] array1, byte[] array2)
52 bool result = (array1.Length == array2.Length);
53 if (result) {
54 for (int i=0; i < array1.Length; i++)
55 if (array1[i] != array2[i])
56 return false;
58 return result;
61 private static byte[] xor (byte[] array1, byte[] array2)
63 byte[] result = new byte [array1.Length];
64 for (int i=0; i < result.Length; i++)
65 result[i] = (byte) (array1[i] ^ array2[i]);
66 return result;
69 private static byte[] emptySHA1 = { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 };
70 private static byte[] emptySHA256 = { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 };
71 private static byte[] emptySHA384 = { 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b };
72 private static byte[] emptySHA512 = { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e };
74 private static byte[] GetEmptyHash (HashAlgorithm hash)
76 if (hash is SHA1)
77 return emptySHA1;
78 else if (hash is SHA256)
79 return emptySHA256;
80 else if (hash is SHA384)
81 return emptySHA384;
82 else if (hash is SHA512)
83 return emptySHA512;
84 else
85 return hash.ComputeHash ((byte[])null);
88 // PKCS #1 v.2.1, Section 4.1
89 // I2OSP converts a non-negative integer to an octet string of a specified length.
90 public static byte[] I2OSP (int x, int size)
92 byte[] array = BitConverterLE.GetBytes (x);
93 Array.Reverse (array, 0, array.Length);
94 return I2OSP (array, size);
97 public static byte[] I2OSP (byte[] x, int size)
99 byte[] result = new byte [size];
100 Buffer.BlockCopy (x, 0, result, (result.Length - x.Length), x.Length);
101 return result;
104 // PKCS #1 v.2.1, Section 4.2
105 // OS2IP converts an octet string to a nonnegative integer.
106 public static byte[] OS2IP (byte[] x)
108 int i = 0;
109 while ((x [i++] == 0x00) && (i < x.Length)) {
110 // confuse compiler into reporting a warning with {}
112 i--;
113 if (i > 0) {
114 byte[] result = new byte [x.Length - i];
115 Buffer.BlockCopy (x, i, result, 0, result.Length);
116 return result;
118 else
119 return x;
122 // PKCS #1 v.2.1, Section 5.1.1
123 public static byte[] RSAEP (RSA rsa, byte[] m)
125 // c = m^e mod n
126 return rsa.EncryptValue (m);
129 // PKCS #1 v.2.1, Section 5.1.2
130 public static byte[] RSADP (RSA rsa, byte[] c)
132 // m = c^d mod n
133 // Decrypt value may apply CRT optimizations
134 return rsa.DecryptValue (c);
137 // PKCS #1 v.2.1, Section 5.2.1
138 public static byte[] RSASP1 (RSA rsa, byte[] m)
140 // first form: s = m^d mod n
141 // Decrypt value may apply CRT optimizations
142 return rsa.DecryptValue (m);
145 // PKCS #1 v.2.1, Section 5.2.2
146 public static byte[] RSAVP1 (RSA rsa, byte[] s)
148 // m = s^e mod n
149 return rsa.EncryptValue (s);
152 // PKCS #1 v.2.1, Section 7.1.1
153 // RSAES-OAEP-ENCRYPT ((n, e), M, L)
154 public static byte[] Encrypt_OAEP (RSA rsa, HashAlgorithm hash, RandomNumberGenerator rng, byte[] M)
156 int size = rsa.KeySize / 8;
157 int hLen = hash.HashSize / 8;
158 if (M.Length > size - 2 * hLen - 2)
159 throw new CryptographicException ("message too long");
160 // empty label L SHA1 hash
161 byte[] lHash = GetEmptyHash (hash);
162 int PSLength = (size - M.Length - 2 * hLen - 2);
163 // DB = lHash || PS || 0x01 || M
164 byte[] DB = new byte [lHash.Length + PSLength + 1 + M.Length];
165 Buffer.BlockCopy (lHash, 0, DB, 0, lHash.Length);
166 DB [(lHash.Length + PSLength)] = 0x01;
167 Buffer.BlockCopy (M, 0, DB, (DB.Length - M.Length), M.Length);
169 byte[] seed = new byte [hLen];
170 rng.GetBytes (seed);
172 byte[] dbMask = MGF1 (hash, seed, size - hLen - 1);
173 byte[] maskedDB = xor (DB, dbMask);
174 byte[] seedMask = MGF1 (hash, maskedDB, hLen);
175 byte[] maskedSeed = xor (seed, seedMask);
176 // EM = 0x00 || maskedSeed || maskedDB
177 byte[] EM = new byte [maskedSeed.Length + maskedDB.Length + 1];
178 Buffer.BlockCopy (maskedSeed, 0, EM, 1, maskedSeed.Length);
179 Buffer.BlockCopy (maskedDB, 0, EM, maskedSeed.Length + 1, maskedDB.Length);
181 byte[] m = OS2IP (EM);
182 byte[] c = RSAEP (rsa, m);
183 return I2OSP (c, size);
186 // PKCS #1 v.2.1, Section 7.1.2
187 // RSAES-OAEP-DECRYPT (K, C, L)
188 public static byte[] Decrypt_OAEP (RSA rsa, HashAlgorithm hash, byte[] C)
190 int size = rsa.KeySize / 8;
191 int hLen = hash.HashSize / 8;
192 if ((size < (2 * hLen + 2)) || (C.Length != size))
193 throw new CryptographicException ("decryption error");
195 byte[] c = OS2IP (C);
196 byte[] m = RSADP (rsa, c);
197 byte[] EM = I2OSP (m, size);
199 // split EM = Y || maskedSeed || maskedDB
200 byte[] maskedSeed = new byte [hLen];
201 Buffer.BlockCopy (EM, 1, maskedSeed, 0, maskedSeed.Length);
202 byte[] maskedDB = new byte [size - hLen - 1];
203 Buffer.BlockCopy (EM, (EM.Length - maskedDB.Length), maskedDB, 0, maskedDB.Length);
205 byte[] seedMask = MGF1 (hash, maskedDB, hLen);
206 byte[] seed = xor (maskedSeed, seedMask);
207 byte[] dbMask = MGF1 (hash, seed, size - hLen - 1);
208 byte[] DB = xor (maskedDB, dbMask);
210 byte[] lHash = GetEmptyHash (hash);
211 // split DB = lHash' || PS || 0x01 || M
212 byte[] dbHash = new byte [lHash.Length];
213 Buffer.BlockCopy (DB, 0, dbHash, 0, dbHash.Length);
214 bool h = Compare (lHash, dbHash);
216 // find separator 0x01
217 int nPos = lHash.Length;
218 while (DB[nPos] == 0)
219 nPos++;
221 int Msize = DB.Length - nPos - 1;
222 byte[] M = new byte [Msize];
223 Buffer.BlockCopy (DB, (nPos + 1), M, 0, Msize);
225 // we could have returned EM[0] sooner but would be helping a timing attack
226 if ((EM[0] != 0) || (!h) || (DB[nPos] != 0x01))
227 return null;
228 return M;
231 // PKCS #1 v.2.1, Section 7.2.1
232 // RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)
233 public static byte[] Encrypt_v15 (RSA rsa, RandomNumberGenerator rng, byte[] M)
235 int size = rsa.KeySize / 8;
236 if (M.Length > size - 11)
237 throw new CryptographicException ("message too long");
238 int PSLength = System.Math.Max (8, (size - M.Length - 3));
239 byte[] PS = new byte [PSLength];
240 rng.GetNonZeroBytes (PS);
241 byte[] EM = new byte [size];
242 EM [1] = 0x02;
243 Buffer.BlockCopy (PS, 0, EM, 2, PSLength);
244 Buffer.BlockCopy (M, 0, EM, (size - M.Length), M.Length);
246 byte[] m = OS2IP (EM);
247 byte[] c = RSAEP (rsa, m);
248 byte[] C = I2OSP (c, size);
249 return C;
252 // PKCS #1 v.2.1, Section 7.2.2
253 // RSAES-PKCS1-V1_5-DECRYPT (K, C)
254 public static byte[] Decrypt_v15 (RSA rsa, byte[] C)
256 int size = rsa.KeySize / 8;
257 if ((size < 11) || (C.Length != size))
258 throw new CryptographicException ("decryption error");
259 byte[] c = OS2IP (C);
260 byte[] m = RSADP (rsa, c);
261 byte[] EM = I2OSP (m, size);
263 if ((EM [0] != 0x00) || (EM [1] != 0x02))
264 return null;
266 int mPos = 10;
267 // PS is a minimum of 8 bytes + 2 bytes for header
268 while ((EM [mPos] != 0x00) && (mPos < EM.Length))
269 mPos++;
270 if (EM [mPos] != 0x00)
271 return null;
272 mPos++;
273 byte[] M = new byte [EM.Length - mPos];
274 Buffer.BlockCopy (EM, mPos, M, 0, M.Length);
275 return M;
278 // PKCS #1 v.2.1, Section 8.2.1
279 // RSASSA-PKCS1-V1_5-SIGN (K, M)
280 public static byte[] Sign_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue)
282 int size = (rsa.KeySize >> 3); // div 8
283 byte[] EM = Encode_v15 (hash, hashValue, size);
284 byte[] m = OS2IP (EM);
285 byte[] s = RSASP1 (rsa, m);
286 byte[] S = I2OSP (s, size);
287 return S;
290 // PKCS #1 v.2.1, Section 8.2.2
291 // RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S)
292 public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue, byte[] signature)
294 int size = (rsa.KeySize >> 3); // div 8
295 byte[] s = OS2IP (signature);
296 byte[] m = RSAVP1 (rsa, s);
297 byte[] EM2 = I2OSP (m, size);
298 byte[] EM = Encode_v15 (hash, hashValue, size);
299 bool result = Compare (EM, EM2);
300 if (!result) {
301 // NOTE: some signatures don't include the hash OID (pretty lame but real)
302 // and compatible with MS implementation
303 if ((EM2 [0] != 0x00) || (EM2 [1] != 0x01))
304 return false;
305 // TODO: add more validation
306 byte[] decryptedHash = new byte [hashValue.Length];
307 Buffer.BlockCopy (EM2, EM2.Length - hashValue.Length, decryptedHash, 0, decryptedHash.Length);
308 result = Compare (decryptedHash, hashValue);
310 return result;
313 // PKCS #1 v.2.1, Section 9.2
314 // EMSA-PKCS1-v1_5-Encode
315 public static byte[] Encode_v15 (HashAlgorithm hash, byte[] hashValue, int emLength)
317 if (hashValue.Length != (hash.HashSize >> 3))
318 throw new CryptographicException ("bad hash length for " + hash.ToString ());
320 // DigestInfo ::= SEQUENCE {
321 // digestAlgorithm AlgorithmIdentifier,
322 // digest OCTET STRING
323 // }
325 byte[] t = null;
327 string oid = CryptoConfig.MapNameToOID (hash.ToString ());
328 if (oid != null)
330 ASN1 digestAlgorithm = new ASN1 (0x30);
331 digestAlgorithm.Add (new ASN1 (CryptoConfig.EncodeOID (oid)));
332 digestAlgorithm.Add (new ASN1 (0x05)); // NULL
333 ASN1 digest = new ASN1 (0x04, hashValue);
334 ASN1 digestInfo = new ASN1 (0x30);
335 digestInfo.Add (digestAlgorithm);
336 digestInfo.Add (digest);
338 t = digestInfo.GetBytes ();
340 else
342 // There are no valid OID, in this case t = hashValue
343 // This is the case of the MD5SHA hash algorithm
344 t = hashValue;
347 Buffer.BlockCopy (hashValue, 0, t, t.Length - hashValue.Length, hashValue.Length);
349 int PSLength = System.Math.Max (8, emLength - t.Length - 3);
350 // PS = PSLength of 0xff
352 // EM = 0x00 | 0x01 | PS | 0x00 | T
353 byte[] EM = new byte [PSLength + t.Length + 3];
354 EM [1] = 0x01;
355 for (int i=2; i < PSLength + 2; i++)
356 EM[i] = 0xff;
357 Buffer.BlockCopy (t, 0, EM, PSLength + 3, t.Length);
359 return EM;
362 // PKCS #1 v.2.1, Section B.2.1
363 public static byte[] MGF1 (HashAlgorithm hash, byte[] mgfSeed, int maskLen)
365 // 1. If maskLen > 2^32 hLen, output "mask too long" and stop.
366 // easy - this is impossible by using a int (31bits) as parameter ;-)
367 // BUT with a signed int we do have to check for negative values!
368 if (maskLen < 0)
369 throw new OverflowException();
371 int mgfSeedLength = mgfSeed.Length;
372 int hLen = (hash.HashSize >> 3); // from bits to bytes
373 int iterations = (maskLen / hLen);
374 if (maskLen % hLen != 0)
375 iterations++;
376 // 2. Let T be the empty octet string.
377 byte[] T = new byte [iterations * hLen];
379 byte[] toBeHashed = new byte [mgfSeedLength + 4];
380 int pos = 0;
381 // 3. For counter from 0 to \ceil (maskLen / hLen) - 1, do the following:
382 for (int counter = 0; counter < iterations; counter++) {
383 // a. Convert counter to an octet string C of length 4 octets
384 byte[] C = I2OSP (counter, 4);
386 // b. Concatenate the hash of the seed mgfSeed and C to the octet string T:
387 // T = T || Hash (mgfSeed || C)
388 Buffer.BlockCopy (mgfSeed, 0, toBeHashed, 0, mgfSeedLength);
389 Buffer.BlockCopy (C, 0, toBeHashed, mgfSeedLength, 4);
390 byte[] output = hash.ComputeHash (toBeHashed);
391 Buffer.BlockCopy (output, 0, T, pos, hLen);
392 pos += mgfSeedLength;
395 // 4. Output the leading maskLen octets of T as the octet string mask.
396 byte[] mask = new byte [maskLen];
397 Buffer.BlockCopy (T, 0, mask, 0, maskLen);
398 return mask;