2 // PKCS1.cs - Implements PKCS#1 primitives.
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
31 using System
.Security
.Cryptography
;
33 namespace Mono
.Security
.Cryptography
{
36 // a. PKCS#1: RSA Cryptography Standard
37 // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html
50 private static bool Compare (byte[] array1
, byte[] array2
)
52 bool result
= (array1
.Length
== array2
.Length
);
54 for (int i
=0; i
< array1
.Length
; i
++)
55 if (array1
[i
] != array2
[i
])
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
]);
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 }
;
72 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 }
;
73 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 }
;
76 private static byte[] GetEmptyHash (HashAlgorithm hash
)
80 else if (hash
is SHA256
)
83 else if (hash
is SHA384
)
85 else if (hash
is SHA512
)
89 return hash
.ComputeHash ((byte[])null);
92 // PKCS #1 v.2.1, Section 4.1
93 // I2OSP converts a non-negative integer to an octet string of a specified length.
94 public static byte[] I2OSP (int x
, int size
)
96 byte[] array
= BitConverterLE
.GetBytes (x
);
97 Array
.Reverse (array
, 0, array
.Length
);
98 return I2OSP (array
, size
);
101 public static byte[] I2OSP (byte[] x
, int size
)
103 byte[] result
= new byte [size
];
104 Buffer
.BlockCopy (x
, 0, result
, (result
.Length
- x
.Length
), x
.Length
);
108 // PKCS #1 v.2.1, Section 4.2
109 // OS2IP converts an octet string to a nonnegative integer.
110 public static byte[] OS2IP (byte[] x
)
113 while ((x
[i
++] == 0x00) && (i
< x
.Length
)) {
114 // confuse compiler into reporting a warning with {}
118 byte[] result
= new byte [x
.Length
- i
];
119 Buffer
.BlockCopy (x
, i
, result
, 0, result
.Length
);
126 // PKCS #1 v.2.1, Section 5.1.1
127 public static byte[] RSAEP (RSA rsa
, byte[] m
)
130 return rsa
.EncryptValue (m
);
133 // PKCS #1 v.2.1, Section 5.1.2
134 public static byte[] RSADP (RSA rsa
, byte[] c
)
137 // Decrypt value may apply CRT optimizations
138 return rsa
.DecryptValue (c
);
141 // PKCS #1 v.2.1, Section 5.2.1
142 public static byte[] RSASP1 (RSA rsa
, byte[] m
)
144 // first form: s = m^d mod n
145 // Decrypt value may apply CRT optimizations
146 return rsa
.DecryptValue (m
);
149 // PKCS #1 v.2.1, Section 5.2.2
150 public static byte[] RSAVP1 (RSA rsa
, byte[] s
)
153 return rsa
.EncryptValue (s
);
156 // PKCS #1 v.2.1, Section 7.1.1
157 // RSAES-OAEP-ENCRYPT ((n, e), M, L)
158 public static byte[] Encrypt_OAEP (RSA rsa
, HashAlgorithm hash
, RandomNumberGenerator rng
, byte[] M
)
160 int size
= rsa
.KeySize
/ 8;
161 int hLen
= hash
.HashSize
/ 8;
162 if (M
.Length
> size
- 2 * hLen
- 2)
163 throw new CryptographicException ("message too long");
164 // empty label L SHA1 hash
165 byte[] lHash
= GetEmptyHash (hash
);
166 int PSLength
= (size
- M
.Length
- 2 * hLen
- 2);
167 // DB = lHash || PS || 0x01 || M
168 byte[] DB
= new byte [lHash
.Length
+ PSLength
+ 1 + M
.Length
];
169 Buffer
.BlockCopy (lHash
, 0, DB
, 0, lHash
.Length
);
170 DB
[(lHash
.Length
+ PSLength
)] = 0x01;
171 Buffer
.BlockCopy (M
, 0, DB
, (DB
.Length
- M
.Length
), M
.Length
);
173 byte[] seed
= new byte [hLen
];
176 byte[] dbMask
= MGF1 (hash
, seed
, size
- hLen
- 1);
177 byte[] maskedDB
= xor (DB
, dbMask
);
178 byte[] seedMask
= MGF1 (hash
, maskedDB
, hLen
);
179 byte[] maskedSeed
= xor (seed
, seedMask
);
180 // EM = 0x00 || maskedSeed || maskedDB
181 byte[] EM
= new byte [maskedSeed
.Length
+ maskedDB
.Length
+ 1];
182 Buffer
.BlockCopy (maskedSeed
, 0, EM
, 1, maskedSeed
.Length
);
183 Buffer
.BlockCopy (maskedDB
, 0, EM
, maskedSeed
.Length
+ 1, maskedDB
.Length
);
185 byte[] m
= OS2IP (EM
);
186 byte[] c
= RSAEP (rsa
, m
);
187 return I2OSP (c
, size
);
190 // PKCS #1 v.2.1, Section 7.1.2
191 // RSAES-OAEP-DECRYPT (K, C, L)
192 public static byte[] Decrypt_OAEP (RSA rsa
, HashAlgorithm hash
, byte[] C
)
194 int size
= rsa
.KeySize
/ 8;
195 int hLen
= hash
.HashSize
/ 8;
196 if ((size
< (2 * hLen
+ 2)) || (C
.Length
!= size
))
197 throw new CryptographicException ("decryption error");
199 byte[] c
= OS2IP (C
);
200 byte[] m
= RSADP (rsa
, c
);
201 byte[] EM
= I2OSP (m
, size
);
203 // split EM = Y || maskedSeed || maskedDB
204 byte[] maskedSeed
= new byte [hLen
];
205 Buffer
.BlockCopy (EM
, 1, maskedSeed
, 0, maskedSeed
.Length
);
206 byte[] maskedDB
= new byte [size
- hLen
- 1];
207 Buffer
.BlockCopy (EM
, (EM
.Length
- maskedDB
.Length
), maskedDB
, 0, maskedDB
.Length
);
209 byte[] seedMask
= MGF1 (hash
, maskedDB
, hLen
);
210 byte[] seed
= xor (maskedSeed
, seedMask
);
211 byte[] dbMask
= MGF1 (hash
, seed
, size
- hLen
- 1);
212 byte[] DB
= xor (maskedDB
, dbMask
);
214 byte[] lHash
= GetEmptyHash (hash
);
215 // split DB = lHash' || PS || 0x01 || M
216 byte[] dbHash
= new byte [lHash
.Length
];
217 Buffer
.BlockCopy (DB
, 0, dbHash
, 0, dbHash
.Length
);
218 bool h
= Compare (lHash
, dbHash
);
220 // find separator 0x01
221 int nPos
= lHash
.Length
;
222 while (DB
[nPos
] == 0)
225 int Msize
= DB
.Length
- nPos
- 1;
226 byte[] M
= new byte [Msize
];
227 Buffer
.BlockCopy (DB
, (nPos
+ 1), M
, 0, Msize
);
229 // we could have returned EM[0] sooner but would be helping a timing attack
230 if ((EM
[0] != 0) || (!h
) || (DB
[nPos
] != 0x01))
235 // PKCS #1 v.2.1, Section 7.2.1
236 // RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)
237 public static byte[] Encrypt_v15 (RSA rsa
, RandomNumberGenerator rng
, byte[] M
)
239 int size
= rsa
.KeySize
/ 8;
240 if (M
.Length
> size
- 11)
241 throw new CryptographicException ("message too long");
242 int PSLength
= System
.Math
.Max (8, (size
- M
.Length
- 3));
243 byte[] PS
= new byte [PSLength
];
244 rng
.GetNonZeroBytes (PS
);
245 byte[] EM
= new byte [size
];
247 Buffer
.BlockCopy (PS
, 0, EM
, 2, PSLength
);
248 Buffer
.BlockCopy (M
, 0, EM
, (size
- M
.Length
), M
.Length
);
250 byte[] m
= OS2IP (EM
);
251 byte[] c
= RSAEP (rsa
, m
);
252 byte[] C
= I2OSP (c
, size
);
256 // PKCS #1 v.2.1, Section 7.2.2
257 // RSAES-PKCS1-V1_5-DECRYPT (K, C)
258 public static byte[] Decrypt_v15 (RSA rsa
, byte[] C
)
260 int size
= rsa
.KeySize
>> 3; // div by 8
261 if ((size
< 11) || (C
.Length
> size
))
262 throw new CryptographicException ("decryption error");
263 byte[] c
= OS2IP (C
);
264 byte[] m
= RSADP (rsa
, c
);
265 byte[] EM
= I2OSP (m
, size
);
267 if ((EM
[0] != 0x00) || (EM
[1] != 0x02))
271 // PS is a minimum of 8 bytes + 2 bytes for header
272 while ((EM
[mPos
] != 0x00) && (mPos
< EM
.Length
))
274 if (EM
[mPos
] != 0x00)
277 byte[] M
= new byte [EM
.Length
- mPos
];
278 Buffer
.BlockCopy (EM
, mPos
, M
, 0, M
.Length
);
282 // PKCS #1 v.2.1, Section 8.2.1
283 // RSASSA-PKCS1-V1_5-SIGN (K, M)
284 public static byte[] Sign_v15 (RSA rsa
, HashAlgorithm hash
, byte[] hashValue
)
286 int size
= (rsa
.KeySize
>> 3); // div 8
287 byte[] EM
= Encode_v15 (hash
, hashValue
, size
);
288 byte[] m
= OS2IP (EM
);
289 byte[] s
= RSASP1 (rsa
, m
);
290 byte[] S
= I2OSP (s
, size
);
294 // PKCS #1 v.2.1, Section 8.2.2
295 // RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S)
296 public static bool Verify_v15 (RSA rsa
, HashAlgorithm hash
, byte[] hashValue
, byte[] signature
)
298 return Verify_v15 (rsa
, hash
, hashValue
, signature
, false);
301 // DO NOT USE WITHOUT A VERY GOOD REASON
302 public static bool Verify_v15 (RSA rsa
, HashAlgorithm hash
, byte [] hashValue
, byte [] signature
, bool tryNonStandardEncoding
)
304 int size
= (rsa
.KeySize
>> 3); // div 8
305 byte[] s
= OS2IP (signature
);
306 byte[] m
= RSAVP1 (rsa
, s
);
307 byte[] EM2
= I2OSP (m
, size
);
308 byte[] EM
= Encode_v15 (hash
, hashValue
, size
);
309 bool result
= Compare (EM
, EM2
);
310 if (result
|| !tryNonStandardEncoding
)
313 // NOTE: some signatures don't include the hash OID (pretty lame but real)
314 // and compatible with MS implementation. E.g. Verisign Authenticode Timestamps
316 // we're making this "as safe as possible"
317 if ((EM2
[0] != 0x00) || (EM2
[1] != 0x01))
320 for (i
= 2; i
< EM2
.Length
- hashValue
.Length
- 1; i
++) {
324 if (EM2
[i
++] != 0x00)
327 byte [] decryptedHash
= new byte [hashValue
.Length
];
328 Buffer
.BlockCopy (EM2
, i
, decryptedHash
, 0, decryptedHash
.Length
);
329 return Compare (decryptedHash
, hashValue
);
332 // PKCS #1 v.2.1, Section 9.2
333 // EMSA-PKCS1-v1_5-Encode
334 public static byte[] Encode_v15 (HashAlgorithm hash
, byte[] hashValue
, int emLength
)
336 if (hashValue
.Length
!= (hash
.HashSize
>> 3))
337 throw new CryptographicException ("bad hash length for " + hash
.ToString ());
339 // DigestInfo ::= SEQUENCE {
340 // digestAlgorithm AlgorithmIdentifier,
341 // digest OCTET STRING
346 string oid
= CryptoConfig
.MapNameToOID (hash
.ToString ());
349 ASN1 digestAlgorithm
= new ASN1 (0x30);
350 digestAlgorithm
.Add (new ASN1 (CryptoConfig
.EncodeOID (oid
)));
351 digestAlgorithm
.Add (new ASN1 (0x05)); // NULL
352 ASN1 digest
= new ASN1 (0x04, hashValue
);
353 ASN1 digestInfo
= new ASN1 (0x30);
354 digestInfo
.Add (digestAlgorithm
);
355 digestInfo
.Add (digest
);
357 t
= digestInfo
.GetBytes ();
361 // There are no valid OID, in this case t = hashValue
362 // This is the case of the MD5SHA hash algorithm
366 Buffer
.BlockCopy (hashValue
, 0, t
, t
.Length
- hashValue
.Length
, hashValue
.Length
);
368 int PSLength
= System
.Math
.Max (8, emLength
- t
.Length
- 3);
369 // PS = PSLength of 0xff
371 // EM = 0x00 | 0x01 | PS | 0x00 | T
372 byte[] EM
= new byte [PSLength
+ t
.Length
+ 3];
374 for (int i
=2; i
< PSLength
+ 2; i
++)
376 Buffer
.BlockCopy (t
, 0, EM
, PSLength
+ 3, t
.Length
);
381 // PKCS #1 v.2.1, Section B.2.1
382 public static byte[] MGF1 (HashAlgorithm hash
, byte[] mgfSeed
, int maskLen
)
384 // 1. If maskLen > 2^32 hLen, output "mask too long" and stop.
385 // easy - this is impossible by using a int (31bits) as parameter ;-)
386 // BUT with a signed int we do have to check for negative values!
388 throw new OverflowException();
390 int mgfSeedLength
= mgfSeed
.Length
;
391 int hLen
= (hash
.HashSize
>> 3); // from bits to bytes
392 int iterations
= (maskLen
/ hLen
);
393 if (maskLen
% hLen
!= 0)
395 // 2. Let T be the empty octet string.
396 byte[] T
= new byte [iterations
* hLen
];
398 byte[] toBeHashed
= new byte [mgfSeedLength
+ 4];
400 // 3. For counter from 0 to \ceil (maskLen / hLen) - 1, do the following:
401 for (int counter
= 0; counter
< iterations
; counter
++) {
402 // a. Convert counter to an octet string C of length 4 octets
403 byte[] C
= I2OSP (counter
, 4);
405 // b. Concatenate the hash of the seed mgfSeed and C to the octet string T:
406 // T = T || Hash (mgfSeed || C)
407 Buffer
.BlockCopy (mgfSeed
, 0, toBeHashed
, 0, mgfSeedLength
);
408 Buffer
.BlockCopy (C
, 0, toBeHashed
, mgfSeedLength
, 4);
409 byte[] output
= hash
.ComputeHash (toBeHashed
);
410 Buffer
.BlockCopy (output
, 0, T
, pos
, hLen
);
411 pos
+= mgfSeedLength
;
414 // 4. Output the leading maskLen octets of T as the octet string mask.
415 byte[] mask
= new byte [maskLen
];
416 Buffer
.BlockCopy (T
, 0, mask
, 0, maskLen
);