Support Python long integers in X509.set_serial_number and X509.get_serial_number
Thanks to Gael Le Mignot (kilobug) for the patch to fix get_serial_number.
diff --git a/src/crypto/x509.c b/src/crypto/x509.c
index 13a4c71..2149d28 100644
--- a/src/crypto/x509.c
+++ b/src/crypto/x509.c
@@ -71,12 +71,20 @@
crypto_X509_get_serial_number(crypto_X509Obj *self, PyObject *args)
{
ASN1_INTEGER *asn1_i;
+ BIGNUM *bignum;
+ char *hex;
+ PyObject *res;
if (!PyArg_ParseTuple(args, ":get_serial_number"))
return NULL;
asn1_i = X509_get_serialNumber(self->x509);
- return PyInt_FromLong(ASN1_INTEGER_get(asn1_i));
+ bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
+ hex = BN_bn2hex(bignum);
+ res = PyLong_FromString(hex, NULL, 16);
+ BN_free(bignum);
+ free(hex);
+ return res;
}
static char crypto_X509_set_serial_number_doc[] = "\n\
@@ -91,15 +99,91 @@
static PyObject *
crypto_X509_set_serial_number(crypto_X509Obj *self, PyObject *args)
{
- long serial;
+ long small_serial;
+ PyObject *serial = NULL;
+ PyObject *hex = NULL;
+ PyObject *format = NULL;
+ PyObject *format_args = NULL;
+ ASN1_INTEGER *asn1_i = NULL;
+ BIGNUM *bignum = NULL;
- if (!PyArg_ParseTuple(args, "l:set_serial_number", &serial))
+ if (!PyArg_ParseTuple(args, "O:set_serial_number", &serial)) {
return NULL;
+ }
- ASN1_INTEGER_set(X509_get_serialNumber(self->x509), serial);
+ if (!PyInt_Check(serial) && !PyLong_Check(serial)) {
+ PyErr_SetString(
+ PyExc_TypeError, "serial number must be integer");
+ goto err;
+ }
+
+ if ((format_args = Py_BuildValue("(O)", serial)) == NULL) {
+ goto err;
+ }
+
+ if ((format = PyString_FromString("%x")) == NULL) {
+ goto err;
+ }
+
+ if ((hex = PyString_Format(format, format_args)) == NULL) {
+ goto err;
+ }
+
+ /**
+ * BN_hex2bn stores the result in &bignum. Unless it doesn't feel like
+ * it. If bignum is still NULL after this call, then the return value
+ * is actually the result. I hope. -exarkun
+ */
+ small_serial = BN_hex2bn(&bignum, PyString_AsString(hex));
+
+ Py_DECREF(format_args);
+ format_args = NULL;
+ Py_DECREF(format);
+ format = NULL;
+ Py_DECREF(hex);
+ hex = NULL;
+
+ if (bignum == NULL) {
+ if (ASN1_INTEGER_set(X509_get_serialNumber(self->x509), small_serial)) {
+ exception_from_error_queue();
+ goto err;
+ }
+ } else {
+ asn1_i = BN_to_ASN1_INTEGER(bignum, NULL);
+ BN_free(bignum);
+ bignum = NULL;
+ if (asn1_i == NULL) {
+ exception_from_error_queue();
+ goto err;
+ }
+ if (!X509_set_serialNumber(self->x509, asn1_i)) {
+ exception_from_error_queue();
+ goto err;
+ }
+ ASN1_INTEGER_free(asn1_i);
+ asn1_i = NULL;
+ }
Py_INCREF(Py_None);
return Py_None;
+
+ err:
+ if (format_args) {
+ Py_DECREF(format_args);
+ }
+ if (format) {
+ Py_DECREF(format);
+ }
+ if (hex) {
+ Py_DECREF(hex);
+ }
+ if (bignum) {
+ BN_free(bignum);
+ }
+ if (asn1_i) {
+ ASN1_INTEGER_free(asn1_i);
+ }
+ return NULL;
}
static char crypto_X509_get_issuer_doc[] = "\n\
diff --git a/test/test_crypto.py b/test/test_crypto.py
index b88ada2..dfc80f2 100644
--- a/test/test_crypto.py
+++ b/test/test_crypto.py
@@ -5,7 +5,7 @@
from unittest import TestCase
from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
-from OpenSSL.crypto import X509, X509Name, X509NameType
+from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
from OpenSSL.crypto import X509Req, X509ReqType
class _Python23TestCaseHelper:
@@ -293,3 +293,39 @@
del request
subject.commonName = "bar"
self.assertEqual(subject.commonName, "bar")
+
+
+
+class X509Tests(TestCase, _Python23TestCaseHelper):
+ """
+ Tests for L{OpenSSL.crypto.X509}.
+ """
+ def test_construction(self):
+ """
+ L{X509} takes no arguments and returns an instance of L{X509Type}.
+ """
+ certificate = X509()
+ self.assertTrue(
+ isinstance(certificate, X509Type),
+ "%r is of type %r, should be %r" % (certificate,
+ type(certificate),
+ X509Type))
+
+
+ def test_serial_number(self):
+ """
+ The serial number of an L{X509Type} can be retrieved and modified with
+ L{X509Type.get_serial_number} and L{X509Type.set_serial_number}.
+ """
+ certificate = X509()
+ self.assertRaises(TypeError, certificate.set_serial_number)
+ self.assertRaises(TypeError, certificate.set_serial_number, 1, 2)
+ self.assertRaises(TypeError, certificate.set_serial_number, "1")
+ self.assertRaises(TypeError, certificate.set_serial_number, 5.5)
+ self.assertEqual(certificate.get_serial_number(), 0)
+ certificate.set_serial_number(1)
+ self.assertEqual(certificate.get_serial_number(), 1)
+ certificate.set_serial_number(2 ** 32 + 1)
+ self.assertEqual(certificate.get_serial_number(), 2 ** 32 + 1)
+ certificate.set_serial_number(2 ** 64 + 1)
+ self.assertEqual(certificate.get_serial_number(), 2 ** 64 + 1)