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 }
;
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
)
78 else if (hash
is SHA256
)
80 else if (hash
is SHA384
)
82 else if (hash
is SHA512
)
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
);
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
)
109 while ((x
[i
++] == 0x00) && (i
< x
.Length
)) {
110 // confuse compiler into reporting a warning with {}
114 byte[] result
= new byte [x
.Length
- i
];
115 Buffer
.BlockCopy (x
, i
, result
, 0, result
.Length
);
122 // PKCS #1 v.2.1, Section 5.1.1
123 public static byte[] RSAEP (RSA rsa
, byte[] m
)
126 return rsa
.EncryptValue (m
);
129 // PKCS #1 v.2.1, Section 5.1.2
130 public static byte[] RSADP (RSA rsa
, byte[] c
)
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
)
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
];
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)
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))
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
];
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
);
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))
267 // PS is a minimum of 8 bytes + 2 bytes for header
268 while ((EM
[mPos
] != 0x00) && (mPos
< EM
.Length
))
270 if (EM
[mPos
] != 0x00)
273 byte[] M
= new byte [EM
.Length
- mPos
];
274 Buffer
.BlockCopy (EM
, mPos
, M
, 0, M
.Length
);
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
);
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
);
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))
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
);
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
327 string oid
= CryptoConfig
.MapNameToOID (hash
.ToString ());
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 ();
342 // There are no valid OID, in this case t = hashValue
343 // This is the case of the MD5SHA hash algorithm
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];
355 for (int i
=2; i
< PSLength
+ 2; i
++)
357 Buffer
.BlockCopy (t
, 0, EM
, PSLength
+ 3, t
.Length
);
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!
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)
376 // 2. Let T be the empty octet string.
377 byte[] T
= new byte [iterations
* hLen
];
379 byte[] toBeHashed
= new byte [mgfSeedLength
+ 4];
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
);