Add PKey.check method
diff --git a/ChangeLog b/ChangeLog
index 9b8dea6..dffd05d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2011-06-12  Jean-Paul Calderone  <exarkun@twistedmatrix.com>
 
+	* OpenSSL/crypto/pkey.c: Add the PKey.check method, mostly
+	  implemented by Rick Dean, to verify the internal consistency of a
+	  PKey instance.
+
+2011-06-12  Jean-Paul Calderone  <exarkun@twistedmatrix.com>
+
 	* OpenSSL/crypto/crypto.c: Fix the sign and verify functions so
 	  they handle data with embedded NULs.  Fix by David Brodsky
 	  <lp:~lihalla>.
diff --git a/OpenSSL/crypto/pkey.c b/OpenSSL/crypto/pkey.c
index 0a13aa3..1f78682 100644
--- a/OpenSSL/crypto/pkey.c
+++ b/OpenSSL/crypto/pkey.c
@@ -107,6 +107,37 @@
     return PyLong_FromLong(self->pkey->type);
 }
 
+static char crypto_PKey_check_doc[] = "\n\
+Check the consistency of an RSA private key.\n\
+\n\
+@return: True if key is consistent.\n\
+@raise Error: if the key is inconsistent.\n\
+@raise TypeError: if the key is of a type which cannot be checked.\n\
+    Only RSA keys can currently be checked.\n\
+";
+
+static PyObject *
+crypto_PKey_check(crypto_PKeyObj *self, PyObject *args) {
+    int r;
+
+    if (!PyArg_ParseTuple(args, ":check")) {
+        return NULL;
+    }
+
+    if (self->pkey->type == EVP_PKEY_RSA) {
+        RSA *rsa;
+        rsa = EVP_PKEY_get1_RSA(self->pkey);
+        r = RSA_check_key(rsa);
+        if (r == 1) {
+            return PyBool_FromLong(1L);
+        } else {
+            FAIL();
+        }
+    } else {
+        PyErr_SetString(PyExc_TypeError, "key type unsupported");
+        return NULL;
+    }
+}
 
 /*
  * ADD_METHOD(name) expands to a correct PyMethodDef declaration
@@ -120,6 +151,7 @@
     ADD_METHOD(generate_key),
     ADD_METHOD(bits),
     ADD_METHOD(type),
+    ADD_METHOD(check),
     { NULL, NULL }
 };
 #undef ADD_METHOD
diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py
index 71da5c3..5cdcefb 100644
--- a/OpenSSL/test/test_crypto.py
+++ b/OpenSSL/test/test_crypto.py
@@ -207,6 +207,7 @@
 11n8RkgFIQA0AhuKSIg3CbuartRsJnWOLwgLTzsrKYL4yRog1RJrtw==
 -----END RSA PRIVATE KEY-----
 """)
+
 encryptedPrivateKeyPEMPassphrase = b("foobar")
 
 # Some PKCS#7 stuff.  Generated with the openssl command line:
@@ -250,6 +251,21 @@
 -----END X509 CRL-----
 """)
 
+
+# A broken RSA private key which can be used to test the error path through
+# PKey.check.
+inconsistentPrivateKeyPEM = b("""-----BEGIN RSA PRIVATE KEY-----
+MIIBPAIBAAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh
+5kwIzOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEaAQJBAIqm/bz4NA1H++Vx5Ewx
+OcKp3w19QSaZAwlGRtsUxrP7436QjnREM3Bm8ygU11BjkPVmtrKm6AayQfCHqJoT
+zIECIQDW0BoMoL0HOYM/mrTLhaykYAVqgIeJsPjvkEhTFXWBuQIhAM3deFAvWNu4
+nklUQ37XsCT2c9tmNt1LAT+slG2JOTTRAiAuXDtC/m3NYVwyHfFm+zKHRzHkClk2
+HjubeEgjpj32AQIhAJqMGTaZVOwevTXvvHwNeH+vRWsAYU/gbx+OQB+7VOcBAiEA
+oolb6NMg/R3enNPvS1O4UU1H8wpaF77L4yiSWlE0p4w=
+-----END RSA PRIVATE KEY-----
+""")
+
+
 class X509ExtTests(TestCase):
     """
     Tests for L{OpenSSL.crypto.X509Extension}.
@@ -512,11 +528,13 @@
     def test_pregeneration(self):
         """
         L{PKeyType.bits} and L{PKeyType.type} return C{0} before the key is
+        generated.  L{PKeyType.check} raises L{TypeError} before the key is
         generated.
         """
         key = PKey()
         self.assertEqual(key.type(), 0)
         self.assertEqual(key.bits(), 0)
+        self.assertRaises(TypeError, key.check)
 
 
     def test_failedGeneration(self):
@@ -564,6 +582,7 @@
         key.generate_key(TYPE_RSA, bits)
         self.assertEqual(key.type(), TYPE_RSA)
         self.assertEqual(key.bits(), bits)
+        self.assertTrue(key.check())
 
 
     def test_dsaGeneration(self):
@@ -579,6 +598,7 @@
         key.generate_key(TYPE_DSA, bits)
         self.assertEqual(key.type(), TYPE_DSA)
         self.assertEqual(key.bits(), bits)
+        self.assertRaises(TypeError, key.check)
 
 
     def test_regeneration(self):
@@ -593,6 +613,23 @@
              self.assertEqual(key.bits(), bits)
 
 
+    def test_inconsistentKey(self):
+        """
+        L{PKeyType.check} returns C{False} if the key is not consistent.
+        """
+        key = load_privatekey(FILETYPE_PEM, inconsistentPrivateKeyPEM)
+        self.assertRaises(Error, key.check)
+
+
+    def test_check_wrong_args(self):
+        """
+        L{PKeyType.check} raises L{TypeError} if called with any arguments.
+        """
+        self.assertRaises(TypeError, PKey().check, None)
+        self.assertRaises(TypeError, PKey().check, object())
+        self.assertRaises(TypeError, PKey().check, 1)
+
+
 
 class X509NameTests(TestCase):
     """
@@ -2077,6 +2114,7 @@
         L{dump_privatekey} writes a PEM, DER, and text.
         """
         key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+        self.assertTrue(key.check())
         dumped_pem = dump_privatekey(FILETYPE_PEM, key)
         self.assertEqual(dumped_pem, cleartextPrivateKeyPEM)
         dumped_der = dump_privatekey(FILETYPE_ASN1, key)
diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex
index 4e00c14..6a49748 100644
--- a/doc/pyOpenSSL.tex
+++ b/doc/pyOpenSSL.tex
@@ -555,6 +555,12 @@
 Return the type of the key.
 \end{methoddesc}
 
+\begin{methoddesc}[PKey]{check}{}
+Check the consistency of this key, returning True if it is consistent and
+raising an exception otherwise.  This is only valid for RSA keys.  See the
+OpenSSL RSA_check_key man page for further limitations.
+\end{methoddesc}
+
 \subsubsection{PKCS7 objects \label{openssl-pkcs7}}
 
 PKCS7 objects have the following methods: