2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / corlib / Mono.Security.Cryptography / RSAManaged.cs
index 82b16f6b291ec3ac5983b6f196be95545548c3ea..29ee75a21ced7c9ee8d28fe8ce1b0a1fd1271057 100644 (file)
@@ -7,15 +7,11 @@
 //
 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
 // Portions (C) 2003 Ben Maurer
-// (C) 2004 Novell (http://www.novell.com)
+// Copyright (C) 2004,2006 Novell, Inc (http://www.novell.com)
 //
 // Key generation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
 // See bouncycastle.txt for license.
 //
-
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
-//
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // "Software"), to deal in the Software without restriction, including
@@ -38,6 +34,7 @@
 
 using System;
 using System.Security.Cryptography;
+using System.Text;
 
 using Mono.Math;
 
@@ -61,6 +58,7 @@ namespace Mono.Security.Cryptography {
                private const int defaultKeySize = 1024;
 
                private bool isCRTpossible = false;
+               private bool keyBlinding = true;
                private bool keypairGenerated = false;
                private bool m_disposed = false;
 
@@ -79,9 +77,9 @@ namespace Mono.Security.Cryptography {
 
                public RSAManaged (int keySize) 
                {
-                       KeySizeValue = keySize;
                        LegalKeySizesValue = new KeySizes [1];
                        LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
+                       base.KeySize = keySize;
                }
 
                ~RSAManaged () 
@@ -163,10 +161,10 @@ namespace Mono.Security.Cryptography {
                        get { return "RSA-PKCS1-KeyEx"; }
                }
 
-               // note: this property will exist in RSACryptoServiceProvider in
-               // version 1.2 of the framework
+               // note: when (if) we generate a keypair then it will have both
+               // the public and private keys
                public bool PublicOnly {
-                       get { return ((d == null) || (n == null)); }
+                       get { return (keypairGenerated && ((d == null) || (n == null))); }
                }
 
                public override string SignatureAlgorithm {
@@ -183,6 +181,16 @@ namespace Mono.Security.Cryptography {
                                GenerateKeyPair ();
 
                        BigInteger input = new BigInteger (rgb);
+                       BigInteger r = null;
+
+                       // we use key blinding (by default) against timing attacks
+                       if (keyBlinding) {
+                               // x = (r^e * g) mod n 
+                               // *new* random number (so it's timing is also random)
+                               r = BigInteger.GenerateRandom (n.BitCount ());
+                               input = r.ModPow (e, n) * input % n;
+                       }
+
                        BigInteger output;
                        // decrypt (which uses the private key) can be 
                        // optimized by using CRT (Chinese Remainder Theorem)
@@ -196,20 +204,30 @@ namespace Mono.Security.Cryptography {
                                        // thanks to benm!
                                        h = p - ((m2 - m1) * qInv % p);
                                        output = m2 + q * h;
-                               }
-                               else {
+                               } else {
                                        // h = (m1 - m2) * qInv mod p
                                        h = (m1 - m2) * qInv % p;
                                        // m = m2 + q * h;
                                        output = m2 + q * h;
                                }
-                       }
-                       else {
+                       } else if (!PublicOnly) {
                                // m = c^d mod n
                                output = input.ModPow (d, n);
+                       } else {
+                               throw new CryptographicException (Locale.GetText ("Missing private key to decrypt value."));
                        }
-                       byte[] result = output.GetBytes ();
-                       // zeroize value
+
+                       if (keyBlinding) {
+                               // Complete blinding
+                               // x^e / r mod n
+                               output = output * r.ModInverse (n) % n;
+                               r.Clear ();
+                       }
+
+                       // it's sometimes possible for the results to be a byte short
+                       // and this can break some software (see #79502) so we 0x00 pad the result
+                       byte[] result = GetPaddedValue (output, (KeySize >> 3));
+                       // zeroize values
                        input.Clear (); 
                        output.Clear ();
                        return result;
@@ -225,17 +243,21 @@ namespace Mono.Security.Cryptography {
 
                        BigInteger input = new BigInteger (rgb);
                        BigInteger output = input.ModPow (e, n);
-                       byte[] result = output.GetBytes ();
+                       // it's sometimes possible for the results to be a byte short
+                       // and this can break some software (see #79502) so we 0x00 pad the result
+                       byte[] result = GetPaddedValue (output, (KeySize >> 3));
                        // zeroize value
                        input.Clear (); 
                        output.Clear ();
                        return result;
                }
 
+
+
                public override RSAParameters ExportParameters (bool includePrivateParameters) 
                {
                        if (m_disposed)
-                               throw new ObjectDisposedException ("");
+                               throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
 
                        if (!keypairGenerated)
                                GenerateKeyPair ();
@@ -245,7 +267,7 @@ namespace Mono.Security.Cryptography {
                        param.Modulus = n.GetBytes ();
                        if (includePrivateParameters) {
                                // some parameters are required for exporting the private key
-                               if ((d == null) || (p == null) || (q == null))
+                               if (d == null)
                                        throw new CryptographicException ("Missing private key");
                                param.D = d.GetBytes ();
                                // hack for bugzilla #57941 where D wasn't provided
@@ -254,14 +276,15 @@ namespace Mono.Security.Cryptography {
                                        Buffer.BlockCopy (param.D, 0, normalizedD, (normalizedD.Length - param.D.Length), param.D.Length);
                                        param.D = normalizedD;
                                }
-                               param.P = p.GetBytes ();
-                               param.Q = q.GetBytes ();
                                // but CRT parameters are optionals
-                               if ((dp != null) && (dq != null) && (qInv != null)) {
+                               if ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null)) {
                                        // and we include them only if we have them all
-                                       param.DP = dp.GetBytes ();
-                                       param.DQ = dq.GetBytes ();
-                                       param.InverseQ = qInv.GetBytes ();
+                                       int length = (KeySize >> 4);
+                                       param.P = GetPaddedValue (p, length);
+                                       param.Q = GetPaddedValue (q, length);
+                                       param.DP = GetPaddedValue (dp, length);
+                                       param.DQ = GetPaddedValue (dq, length);
+                                       param.InverseQ = GetPaddedValue (qInv, length);
                                }
                        }
                        return param;
@@ -270,13 +293,13 @@ namespace Mono.Security.Cryptography {
                public override void ImportParameters (RSAParameters parameters) 
                {
                        if (m_disposed)
-                               throw new ObjectDisposedException ("");
+                               throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
 
                        // if missing "mandatory" parameters
                        if (parameters.Exponent == null) 
-                               throw new CryptographicException ("Missing Exponent");
+                               throw new CryptographicException (Locale.GetText ("Missing Exponent"));
                        if (parameters.Modulus == null)
-                               throw new CryptographicException ("Missing Modulus");
+                               throw new CryptographicException (Locale.GetText ("Missing Modulus"));
        
                        e = new BigInteger (parameters.Exponent);
                        n = new BigInteger (parameters.Modulus);
@@ -293,10 +316,44 @@ namespace Mono.Security.Cryptography {
                                p = new BigInteger (parameters.P);
                        if (parameters.Q != null)
                                q = new BigInteger (parameters.Q);
-                       
+
                        // we now have a keypair
                        keypairGenerated = true;
-                       isCRTpossible = ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null));
+                       bool privateKey = ((p != null) && (q != null) && (dp != null));
+                       isCRTpossible = (privateKey && (dq != null) && (qInv != null));
+
+                       // check if the public/private keys match
+                       // the way the check is made allows a bad D to work if CRT is available (like MS does, see unit tests)
+                       if (!privateKey)
+                               return;
+
+                       // always check n == p * q
+                       bool ok = (n == (p * q));
+                       if (ok) {
+                               // we now know that p and q are correct, so (p - 1), (q - 1) and phi will be ok too
+                               BigInteger pSub1 = (p - 1);
+                               BigInteger qSub1 = (q - 1);
+                               BigInteger phi = pSub1 * qSub1;
+                               // e is fairly static but anyway we can ensure it makes sense by recomputing d
+                               BigInteger dcheck = e.ModInverse (phi);
+
+                               // now if our new d(check) is different than the d we're provided then we cannot
+                               // be sure if 'd' or 'e' is invalid... (note that, from experience, 'd' is more 
+                               // likely to be invalid since it's twice as large as DP (or DQ) and sits at the
+                               // end of the structure (e.g. truncation).
+                               ok = (d == dcheck);
+
+                               // ... unless we have the pre-computed CRT parameters
+                               if (!ok && isCRTpossible) {
+                                       // we can override the previous decision since Mono always prefer, for 
+                                       // performance reasons, using the CRT algorithm
+                                       ok = (dp == (dcheck % pSub1)) && (dq == (dcheck % qSub1)) && 
+                                               (qInv == q.ModInverse (p));
+                               }
+                       }
+
+                       if (!ok)
+                               throw new CryptographicException (Locale.GetText ("Private/public key mismatch"));
                }
 
                protected override void Dispose (bool disposing) 
@@ -348,5 +405,112 @@ namespace Mono.Security.Cryptography {
                public delegate void KeyGeneratedEventHandler (object sender, EventArgs e);
 
                public event KeyGeneratedEventHandler KeyGenerated;
+
+               public override string ToXmlString (bool includePrivateParameters) 
+               {
+                       StringBuilder sb = new StringBuilder ();
+                       RSAParameters rsaParams = ExportParameters (includePrivateParameters);
+                       try {
+                               sb.Append ("<RSAKeyValue>");
+                               
+                               sb.Append ("<Modulus>");
+                               sb.Append (Convert.ToBase64String (rsaParams.Modulus));
+                               sb.Append ("</Modulus>");
+
+                               sb.Append ("<Exponent>");
+                               sb.Append (Convert.ToBase64String (rsaParams.Exponent));
+                               sb.Append ("</Exponent>");
+
+                               if (includePrivateParameters) {
+                                       if (rsaParams.P != null) {
+                                               sb.Append ("<P>");
+                                               sb.Append (Convert.ToBase64String (rsaParams.P));
+                                               sb.Append ("</P>");
+                                       }
+                                       if (rsaParams.Q != null) {
+                                               sb.Append ("<Q>");
+                                               sb.Append (Convert.ToBase64String (rsaParams.Q));
+                                               sb.Append ("</Q>");
+                                       }
+                                       if (rsaParams.DP != null) {
+                                               sb.Append ("<DP>");
+                                               sb.Append (Convert.ToBase64String (rsaParams.DP));
+                                               sb.Append ("</DP>");
+                                       }
+                                       if (rsaParams.DQ != null) {
+                                               sb.Append ("<DQ>");
+                                               sb.Append (Convert.ToBase64String (rsaParams.DQ));
+                                               sb.Append ("</DQ>");
+                                       }
+                                       if (rsaParams.InverseQ != null) {
+                                               sb.Append ("<InverseQ>");
+                                               sb.Append (Convert.ToBase64String (rsaParams.InverseQ));
+                                               sb.Append ("</InverseQ>");
+                                       }
+                                       sb.Append ("<D>");
+                                       sb.Append (Convert.ToBase64String (rsaParams.D));
+                                       sb.Append ("</D>");
+                               }
+                               
+                               sb.Append ("</RSAKeyValue>");
+                       }
+                       catch {
+                               if (rsaParams.P != null)
+                                       Array.Clear (rsaParams.P, 0, rsaParams.P.Length);
+                               if (rsaParams.Q != null)
+                                       Array.Clear (rsaParams.Q, 0, rsaParams.Q.Length);
+                               if (rsaParams.DP != null)
+                                       Array.Clear (rsaParams.DP, 0, rsaParams.DP.Length);
+                               if (rsaParams.DQ != null)
+                                       Array.Clear (rsaParams.DQ, 0, rsaParams.DQ.Length);
+                               if (rsaParams.InverseQ != null)
+                                       Array.Clear (rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
+                               if (rsaParams.D != null)
+                                       Array.Clear (rsaParams.D, 0, rsaParams.D.Length);
+                               throw;
+                       }
+                       
+                       return sb.ToString ();
+               }
+
+               // internal for Mono 1.0.x in order to preserve public contract
+               // they are public for Mono 1.1.x (for 1.2) as the API isn't froze ATM
+
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               bool UseKeyBlinding {
+                       get { return keyBlinding; }
+                       // you REALLY shoudn't touch this (true is fine ;-)
+                       set { keyBlinding = value; }
+               }
+
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               bool IsCrtPossible {
+                       // either the key pair isn't generated (and will be 
+                       // generated with CRT parameters) or CRT is (or isn't)
+                       // possible (in case the key was imported)
+                       get { return (!keypairGenerated || isCRTpossible); }
+               }
+
+               private byte[] GetPaddedValue (BigInteger value, int length)
+               {
+                       byte[] result = value.GetBytes ();
+                       if (result.Length >= length)
+                               return result;
+
+                       // left-pad 0x00 value on the result (same integer, correct length)
+                       byte[] padded = new byte[length];
+                       Buffer.BlockCopy (result, 0, padded, (length - result.Length), result.Length);
+                       // temporary result may contain decrypted (plaintext) data, clear it
+                       Array.Clear (result, 0, result.Length);
+                       return padded;
+               }
        }
 }