aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rpkid/ext/POW.c895
1 files changed, 877 insertions, 18 deletions
diff --git a/rpkid/ext/POW.c b/rpkid/ext/POW.c
index a749bf6e..8ed3a24e 100644
--- a/rpkid/ext/POW.c
+++ b/rpkid/ext/POW.c
@@ -81,22 +81,6 @@
/* $Id: rcynic.c 4613 2012-07-30 23:24:15Z sra $ */
-#warning Still need PKCS10 type and methods
-/*
- * From rpki.x509.PKCS10, it looks like we need to:
- * get/set basicConstraints, SIA, and keyUsage (check RFC)
- * get/set version
- * get/set subject name
- * get/set subject public key / sign with private key
- *
- * The last two are integrated even POW.pkix, almost certainly they
- * are in OpenSSL as well. See: X509_REQ_ in x509/x509.h,
- * x509/x509_req.c, x509/x509rset.c, x509/x_all.c.
- *
- * Looks like Xt09_REQ_add_extensions() wants to add all extensions as
- * a group. Yum.
- */
-
#warning Consider making ROA and Manifest C/API-level subclasses of CMS
/*
* This would be a major change to the Python code but really seems
@@ -258,7 +242,8 @@ static PyTypeObject
POW_IPAddress_Type,
POW_ROA_Type,
POW_Manifest_Type,
- POW_ROA_Type;
+ POW_ROA_Type,
+ POW_PKCS10_Type;
/*
* Object internals.
@@ -313,6 +298,12 @@ typedef struct {
Manifest *manifest;
} manifest_object;
+typedef struct {
+ PyObject_HEAD
+ X509_REQ *pkcs10;
+ STACK_OF(X509_EXTENSION) *exts;
+} pkcs10_object;
+
/*
@@ -1355,7 +1346,7 @@ x509_object_der_read_helper(PyTypeObject *type, BIO *bio)
if ((self = (x509_object *) x509_object_new(type, NULL, NULL)) == NULL)
goto error;
- if(!d2i_X509_bio(bio, &self->x509))
+ if (!d2i_X509_bio(bio, &self->x509))
lose_openssl_error("Couldn't load DER encoded certificate");
return (PyObject *) self;
@@ -2055,6 +2046,7 @@ x509_object_set_aki(x509_object *self, PyObject *args)
else
return NULL;
}
+
static char x509_object_get_key_usage__doc__[] =
"This method returns a FrozenSet of strings representing the KeyUsage\n"
"settings for this certificate, or None if the certificate has no\n"
@@ -6618,6 +6610,872 @@ static PyTypeObject POW_ROA_Type = {
/*
+ * PKCS10 object.
+ */
+
+static PyObject *
+pkcs10_object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ pkcs10_object *self;
+
+ if ((self = (pkcs10_object *) type->tp_alloc(type, 0)) != NULL &&
+ (self->pkcs10 = X509_REQ_new()) != NULL &&
+ (self->exts = sk_X509_EXTENSION_new_null()) != NULL)
+ return (PyObject *) self;
+
+ Py_XDECREF(self);
+ return NULL;
+}
+
+static PyObject *
+pkcs10_object_pem_read_helper(PyTypeObject *type, BIO *in)
+{
+ pkcs10_object *self = NULL;
+
+ if ((self = (pkcs10_object *) pkcs10_object_new(type, NULL, NULL)) == NULL)
+ goto error;
+
+ X509_REQ_free(self->pkcs10);
+ sk_X509_EXTENSION_pop_free(self->exts, X509_EXTENSION_free);
+ self->pkcs10 = NULL;
+ self->exts = NULL;
+
+ if ((self->pkcs10 = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL)) == NULL)
+ lose_openssl_error("Couldn't load PEM encoded PKCS#10 request");
+
+ self->exts = X509_REQ_get_extensions(self->pkcs10);
+
+ return (PyObject *) self;
+
+ error:
+
+ Py_XDECREF(self);
+ return NULL;
+}
+
+static PyObject *
+pkcs10_object_der_read_helper(PyTypeObject *type, BIO *bio)
+{
+ pkcs10_object *self;
+
+ if ((self = (pkcs10_object *) pkcs10_object_new(type, NULL, NULL)) == NULL)
+ goto error;
+
+ sk_X509_EXTENSION_pop_free(self->exts, X509_EXTENSION_free);
+ self->exts = NULL;
+
+ if (!d2i_X509_REQ_bio(bio, &self->pkcs10))
+ lose_openssl_error("Couldn't load DER encoded PKCS#10 request");
+
+ self->exts = X509_REQ_get_extensions(self->pkcs10);
+
+ return (PyObject *) self;
+
+ error:
+ Py_XDECREF(self);
+ return NULL;
+}
+
+static char pkcs10_object_pem_read__doc__[] =
+ "Class method to read a PEM-encoded PKCS#10 object from a string.\n"
+ ;
+
+static PyObject *
+pkcs10_object_pem_read(PyTypeObject *type, PyObject *args)
+{
+ return read_from_string_helper(pkcs10_object_pem_read_helper, type, args);
+}
+
+static char pkcs10_object_pem_read_file__doc__[] =
+ "Class method to read a PEM-encoded PKCS#10 object from a file.\n"
+ ;
+
+static PyObject *
+pkcs10_object_pem_read_file(PyTypeObject *type, PyObject *args)
+{
+ return read_from_file_helper(pkcs10_object_pem_read_helper, type, args);
+}
+
+static char pkcs10_object_der_read__doc__[] =
+ "Class method to read a DER-encoded PKCS#10 object from a string.\n"
+ ;
+
+static PyObject *
+pkcs10_object_der_read(PyTypeObject *type, PyObject *args)
+{
+ return read_from_string_helper(pkcs10_object_der_read_helper, type, args);
+}
+
+static char pkcs10_object_der_read_file__doc__[] =
+ "Class method to read a DER-encoded PKCS#10 object from a file.\n"
+ ;
+
+static PyObject *
+pkcs10_object_der_read_file(PyTypeObject *type, PyObject *args)
+{
+ return read_from_file_helper(pkcs10_object_der_read_helper, type, args);
+}
+
+static char pkcs10_object_pem_write__doc__[] =
+ "This method returns a PEM-encoded PKCS#10 object as a string.\n"
+ ;
+
+static PyObject *
+pkcs10_object_pem_write(pkcs10_object *self)
+{
+ PyObject *result = NULL;
+ BIO *bio = NULL;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ lose_no_memory();
+
+ if (!PEM_write_bio_X509_REQ(bio, self->pkcs10))
+ lose_openssl_error("Unable to write PKCS#10 request");
+
+ result = BIO_to_PyString_helper(bio);
+
+ error: /* Fall through */
+ BIO_free(bio);
+ return result;
+}
+
+static char pkcs10_object_der_write__doc__[] =
+ "This method returns a DER-encoded PKCS#10 object as a string.\n"
+ ;
+
+static PyObject *
+pkcs10_object_der_write(pkcs10_object *self)
+{
+ PyObject *result = NULL;
+ BIO *bio = NULL;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ lose_no_memory();
+
+ if (!i2d_X509_REQ_bio(bio, self->pkcs10))
+ lose_openssl_error("Unable to write PKCS#10 request");
+
+ result = BIO_to_PyString_helper(bio);
+
+ error: /* Fall through */
+ BIO_free(bio);
+ return result;
+}
+
+static char pkcs10_object_get_public_key__doc__[] =
+ "This method gets the public key for this PKCS#10 request.\n"
+ ;
+
+static PyObject *
+pkcs10_object_get_public_key(pkcs10_object *self)
+{
+ PyTypeObject *type = &POW_Asymmetric_Type;
+ asymmetric_object *asym = NULL;
+
+ if ((asym = (asymmetric_object *) type->tp_alloc(type, 0)) == NULL)
+ goto error;
+
+ if ((asym->pkey = X509_REQ_get_pubkey(self->pkcs10)) == NULL)
+ lose_openssl_error("Couldn't extract public key from PKCS#10 request");
+
+ return (PyObject *) asym;
+
+ error:
+ Py_XDECREF(asym);
+ return NULL;
+}
+
+static char pkcs10_object_set_public_key__doc__[] =
+ "This method sets the public key for this PKCS#10 request.\n"
+ "The \"key\" parameter should be an instance of the Asymmetric class,\n"
+ "containing a public key.\n"
+ ;
+
+static PyObject *
+pkcs10_object_set_public_key(pkcs10_object *self, PyObject *args)
+{
+ asymmetric_object *asym;
+
+ if (!PyArg_ParseTuple(args, "O!", &POW_Asymmetric_Type, &asym))
+ goto error;
+
+ if (!X509_REQ_set_pubkey(self->pkcs10, asym->pkey))
+ lose_openssl_error("Couldn't set certificate's PKCS#10 request");
+
+ Py_RETURN_NONE;
+
+ error:
+ return NULL;
+}
+
+static char pkcs10_object_sign__doc__[] =
+ "This method signs a PKCS#10 request with a private key.\n"
+ "\n"
+ "The \"key\" parameter should be an instance of the Asymmetric class,\n"
+ "containing a private key.\n"
+ "\n"
+ "The optional \"digest\" parameter indicates which digest to compute and\n"
+ "sign, and should be one of the following:\n"
+ "\n"
+ "* MD5_DIGEST\n"
+ "* SHA_DIGEST\n"
+ "* SHA1_DIGEST\n"
+ "* SHA256_DIGEST\n"
+ "* SHA384_DIGEST\n"
+ "* SHA512_DIGEST\n"
+ "\n"
+ "The default digest algorithm is SHA-256.\n"
+ ;
+
+static PyObject *
+pkcs10_object_sign(pkcs10_object *self, PyObject *args)
+{
+ asymmetric_object *asym;
+ int digest_type = SHA256_DIGEST;
+ const EVP_MD *digest_method = NULL;
+
+ if (!PyArg_ParseTuple(args, "O!|i", &POW_Asymmetric_Type, &asym, &digest_type))
+ goto error;
+
+ if ((digest_method = evp_digest_factory(digest_type)) == NULL)
+ lose("Unsupported digest algorithm");
+
+ /*
+ * Not sure whether we should do this or not, but without it we end
+ * up creating a second attribute if one already exists, which
+ * confuses at least OpenSSL. RFCs are not much help. Will a PKIX
+ * expert next time I see one in the hallway....
+ */
+#warning Confirm proper PKCS10 attribute behavior
+#if 0
+ while (X509_REQ_get_attr_count(self->pkcs10) > 0)
+ X509_ATTRIBUTE_free(X509_REQ_delete_attr(self->pkcs10, 0));
+#endif
+
+ if (sk_X509_EXTENSION_num(self->exts) > 0 &&
+ !X509_REQ_add_extensions(self->pkcs10, self->exts))
+ lose_openssl_error("Couldn't add extensions block to PKCS#10 request");
+
+ if (!X509_REQ_sign(self->pkcs10, asym->pkey, digest_method))
+ lose_openssl_error("Couldn't sign PKCS#10 request");
+
+ Py_RETURN_NONE;
+
+ error:
+ return NULL;
+}
+
+static char pkcs10_object_verify__doc__[] =
+ "Verify a PKCS#10 request.\n"
+ ;
+
+static PyObject *
+pkcs10_object_verify(pkcs10_object *self)
+{
+ EVP_PKEY *pkey = NULL;
+ int status;
+
+ if ((pkey = X509_REQ_get_pubkey(self->pkcs10)) == NULL)
+ lose_openssl_error("Couldn't extract public key from PKCS#10 for verification");
+
+ if ((status = X509_REQ_verify(self->pkcs10, pkey)) < 0)
+ lose_openssl_error("Couldn't verify PKCS#10 signature");
+
+ EVP_PKEY_free(pkey);
+ return PyBool_FromLong(status);
+
+ error:
+ EVP_PKEY_free(pkey);
+ return NULL;
+}
+
+static char pkcs10_object_get_version__doc__[] =
+ "This method returns the version number from the version field of this PKCS#10 request.\n"
+ ;
+
+static PyObject *
+pkcs10_object_get_version(pkcs10_object *self)
+{
+ return Py_BuildValue("l", X509_REQ_get_version(self->pkcs10));
+}
+
+static char pkcs10_object_set_version__doc__[] =
+ "This method sets the version number in the version field of this PKCS#10 request.\n"
+ "The \"version\" parameter should be an integer, but the only value is zero, so\n"
+ "this field is optional and defaults to zero.\n"
+;
+
+static PyObject *
+pkcs10_object_set_version(pkcs10_object *self, PyObject *args)
+{
+ long version = 0;
+
+ if (!PyArg_ParseTuple(args, "|l", &version))
+ goto error;
+
+ if (version != 0)
+ lose("RFC 6487 6.1.1 forbids non-zero values for this field");
+
+ if (!X509_REQ_set_version(self->pkcs10, version))
+ lose("Couldn't set certificate version");
+
+ Py_RETURN_NONE;
+
+ error:
+
+ return NULL;
+}
+
+static char pkcs10_object_get_subject__doc__[] =
+ "This method returns a tuple containing the subject's name. See\n"
+ "the X509.getIssuer() method for details of the return value\n"
+ "and use of the optional \"format\" parameter.\n"
+ ;
+
+static PyObject *
+pkcs10_object_get_subject(pkcs10_object *self, PyObject *args)
+{
+ PyObject *result = NULL;
+ int format = OIDNAME_FORMAT;
+
+ if (!PyArg_ParseTuple(args, "|i", &format))
+ goto error;
+
+ result = x509_object_helper_get_name(X509_REQ_get_subject_name(self->pkcs10),
+ format);
+
+ error: /* Fall through */
+ return result;
+}
+
+static char pkcs10_object_set_subject__doc__[] =
+ "This method is used to set the PKCS#10 request's subject name.\n"
+ "The \"name\" parameter should be in the same format as the return\n"
+ "value from the \"getSubject\" method.\n"
+ ;
+
+static PyObject *
+pkcs10_object_set_subject(pkcs10_object *self, PyObject *args)
+{
+ PyObject *name_sequence = NULL;
+ X509_NAME *name = NULL;
+
+ if (!PyArg_ParseTuple(args, "O", &name_sequence))
+ goto error;
+
+ if (!PySequence_Check(name_sequence))
+ lose_type_error("Inapropriate type");
+
+ if ((name = x509_object_helper_set_name(name_sequence)) == NULL)
+ goto error;
+
+ if (!X509_REQ_set_subject_name(self->pkcs10, name))
+ lose("Unable to set subject name");
+
+ X509_NAME_free(name);
+
+ Py_RETURN_NONE;
+
+ error:
+ X509_NAME_free(name);
+ return NULL;
+}
+
+static char pkcs10_object_get_key_usage__doc__[] =
+ "This method returns a FrozenSet of strings representing the KeyUsage\n"
+ "settings for this PKCS#10 request, or None if the request has no\n"
+ "KeyUsage extension. The bits have the same names as in RFC 5280.\n"
+ ;
+
+static PyObject *
+pkcs10_object_get_key_usage(pkcs10_object *self)
+{
+ extern X509V3_EXT_METHOD v3_key_usage;
+ BIT_STRING_BITNAME *bit_name;
+ ASN1_BIT_STRING *ext = NULL;
+ PyObject *result = NULL;
+ PyObject *token = NULL;
+
+ if ((ext = X509V3_get_d2i(self->exts, NID_key_usage, NULL, NULL)) == NULL)
+ Py_RETURN_NONE;
+
+ if ((result = PyFrozenSet_New(NULL)) == NULL)
+ goto error;
+
+ for (bit_name = v3_key_usage.usr_data; bit_name->sname != NULL; bit_name++) {
+ if (ASN1_BIT_STRING_get_bit(ext, bit_name->bitnum) &&
+ ((token = PyString_FromString(bit_name->sname)) == NULL ||
+ PySet_Add(result, token) < 0))
+ goto error;
+ Py_XDECREF(token);
+ token = NULL;
+ }
+
+ ASN1_BIT_STRING_free(ext);
+ return result;
+
+ error:
+ ASN1_BIT_STRING_free(ext);
+ Py_XDECREF(token);
+ Py_XDECREF(result);
+ return NULL;
+}
+
+static char pkcs10_object_set_key_usage__doc__[] =
+ "This method sets the KeyUsage extension for this PKCS#10 request.\n"
+ "\n"
+ "Argument \"iterable\" should be an iterable object which returns zero or more\n"
+ "strings naming bits to be enabled. The bits have the same names as in RFC 5280.\n"
+ "\n"
+ "Optional argument \"critical\" is a boolean indicating whether the extension\n"
+ "should be marked as critical or not. RFC 5280 4.2.1.3 says this extension SHOULD\n"
+ "be marked as critical when used, so the default is True.\n"
+ ;
+
+static PyObject *
+pkcs10_object_set_key_usage(pkcs10_object *self, PyObject *args)
+{
+ extern X509V3_EXT_METHOD v3_key_usage;
+ BIT_STRING_BITNAME *bit_name;
+ ASN1_BIT_STRING *ext = NULL;
+ PyObject *iterable = NULL;
+ PyObject *critical = Py_True;
+ PyObject *iterator = NULL;
+ PyObject *token = NULL;
+ const char *t;
+ int ok = 0;
+
+ if ((ext = ASN1_BIT_STRING_new()) == NULL)
+ lose_no_memory();
+
+ if (!PyArg_ParseTuple(args, "O|O", &iterable, &critical) ||
+ (iterator = PyObject_GetIter(iterable)) == NULL)
+ goto error;
+
+ while ((token = PyIter_Next(iterator)) != NULL) {
+
+ if ((t = PyString_AsString(token)) == NULL)
+ goto error;
+
+ for (bit_name = v3_key_usage.usr_data; bit_name->sname != NULL; bit_name++)
+ if (!strcmp(t, bit_name->sname))
+ break;
+
+ if (bit_name->sname == NULL)
+ lose("Unrecognized KeyUsage token");
+
+ if (!ASN1_BIT_STRING_set_bit(ext, bit_name->bitnum, 1))
+ lose_no_memory();
+
+ Py_XDECREF(token);
+ token = NULL;
+ }
+
+ if (!X509V3_add1_i2d(&self->exts, NID_key_usage, ext,
+ PyObject_IsTrue(critical),
+ X509V3_ADD_REPLACE))
+ lose_openssl_error("Couldn't add KeyUsage extension to certificate");
+
+ ok = 1;
+
+ error: /* Fall through */
+ ASN1_BIT_STRING_free(ext);
+ Py_XDECREF(iterator);
+ Py_XDECREF(token);
+
+ if (ok)
+ Py_RETURN_NONE;
+ else
+ return NULL;
+}
+
+static char pkcs10_object_get_basic_constraints__doc__[] =
+ "Get BasicConstraints value for this PKCS#10 request. If the request\n"
+ "has no BasicConstraints extension, this method returns None.\n"
+ "Otherwise, it returns a two-element tuple. The first element of the\n"
+ "tuple is a boolean representing the extension's cA value; the second\n"
+ "element of the tuple is either an integer representing the\n"
+ "pathLenConstraint value or None if there is no pathLenConstraint.\n"
+ ;
+
+static PyObject *
+pkcs10_object_get_basic_constraints(pkcs10_object *self)
+{
+ BASIC_CONSTRAINTS *ext = NULL;
+ PyObject *result;
+
+ if ((ext = X509V3_get_d2i(self->exts, NID_basic_constraints, NULL, NULL)) == NULL)
+ Py_RETURN_NONE;
+
+ if (ext->pathlen == NULL)
+ result = Py_BuildValue("(NO)", PyBool_FromLong(ext->ca), Py_None);
+ else
+ result = Py_BuildValue("(Nl)", PyBool_FromLong(ext->ca), ASN1_INTEGER_get(ext->pathlen));
+
+ BASIC_CONSTRAINTS_free(ext);
+ return result;
+}
+
+static char pkcs10_object_set_basic_constraints__doc__[] =
+ "Set BasicConstraints value for this PKCS#10 request.\n"
+ "\n"
+ "First argument \"ca\" is a boolean indicating whether the request\n"
+ "is for a CA certificate or not.\n"
+ "\n"
+ "Optional second argument \"pathLenConstraint\" is a non-negative integer\n"
+ "specifying the pathLenConstraint value for this certificate; this value\n"
+ "may only be set for CA certificates."
+ "\n"
+ "Optional third argument \"critical\" specifies whether the extension\n"
+ "should be marked as critical. RFC 5280 4.2.1.9 requires that CA\n"
+ "certificates mark this extension as critical, so the default is True.\n"
+ ;
+
+static PyObject *
+pkcs10_object_set_basic_constraints(pkcs10_object *self, PyObject *args)
+{
+ BASIC_CONSTRAINTS *ext = NULL;
+ PyObject *is_ca = NULL;
+ PyObject *pathlen_obj = Py_None;
+ PyObject *critical = Py_True;
+ long pathlen = -1;
+ int ok = 0;
+
+ if (!PyArg_ParseTuple(args, "O|OO", &is_ca, &pathlen_obj, &critical))
+ goto error;
+
+ if (pathlen_obj != Py_None && (pathlen = PyInt_AsLong(pathlen_obj)) < 0)
+ lose_type_error("Bad pathLenConstraint value");
+
+ if ((ext = BASIC_CONSTRAINTS_new()) == NULL)
+ lose_no_memory();
+
+ ext->ca = PyObject_IsTrue(is_ca) ? 0xFF : 0;
+
+ if (pathlen_obj != Py_None &&
+ ((ext->pathlen == NULL && (ext->pathlen = ASN1_INTEGER_new()) == NULL) ||
+ !ASN1_INTEGER_set(ext->pathlen, pathlen)))
+ lose_no_memory();
+
+ if (!X509V3_add1_i2d(&self->exts, NID_basic_constraints, ext,
+ PyObject_IsTrue(critical), X509V3_ADD_REPLACE))
+ lose_openssl_error("Couldn't add BasicConstraints extension to certificate");
+
+ ok = 1;
+
+ error:
+ BASIC_CONSTRAINTS_free(ext);
+
+ if (ok)
+ Py_RETURN_NONE;
+ else
+ return NULL;
+}
+
+static char pkcs10_object_get_sia__doc__[] =
+ "Get SIA values for this PKCS#10 request. If the request\n"
+ "has no SIA extension, this method returns None.\n"
+ "Otherwise, it returns a tuple containing three sequences:\n"
+ "caRepository URIs, rpkiManifest URIs, and signedObject URIs.\n"
+ "Any other accessMethods are ignored, as are any non-URI\n"
+ "accessLocations.\n"
+ ;
+
+static PyObject *
+pkcs10_object_get_sia(pkcs10_object *self)
+{
+ AUTHORITY_INFO_ACCESS *ext = NULL;
+ PyObject *result = NULL;
+ PyObject *result_caRepository = NULL;
+ PyObject *result_rpkiManifest = NULL;
+ PyObject *result_signedObject = NULL;
+ int n_caRepository = 0;
+ int n_rpkiManifest = 0;
+ int n_signedObject = 0;
+ const char *uri;
+ PyObject *obj;
+ int i, nid;
+
+ if ((ext = X509V3_get_d2i(self->exts, NID_sinfo_access, NULL, NULL)) == NULL)
+ Py_RETURN_NONE;
+
+ /*
+ * Easiest to do this in two passes, first pass just counts URIs.
+ */
+
+ for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ext); i++) {
+ ACCESS_DESCRIPTION *a = sk_ACCESS_DESCRIPTION_value(ext, i);
+ if (a->location->type != GEN_URI)
+ continue;
+ nid = OBJ_obj2nid(a->method);
+ if (nid == NID_caRepository) {
+ n_caRepository++;
+ continue;
+ }
+ if (nid == NID_rpkiManifest) {
+ n_rpkiManifest++;
+ continue;
+ }
+ if (nid == NID_signedObject) {
+ n_signedObject++;
+ continue;
+ }
+ }
+
+ if (((result_caRepository = PyTuple_New(n_caRepository)) == NULL) ||
+ ((result_rpkiManifest = PyTuple_New(n_rpkiManifest)) == NULL) ||
+ ((result_signedObject = PyTuple_New(n_signedObject)) == NULL))
+ goto error;
+
+ n_caRepository = n_rpkiManifest = n_signedObject = 0;
+
+ for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ext); i++) {
+ ACCESS_DESCRIPTION *a = sk_ACCESS_DESCRIPTION_value(ext, i);
+ if (a->location->type != GEN_URI)
+ continue;
+ nid = OBJ_obj2nid(a->method);
+ uri = (char *) ASN1_STRING_data(a->location->d.uniformResourceIdentifier);
+ if (nid == NID_caRepository) {
+ if ((obj = PyString_FromString(uri)) == NULL)
+ goto error;
+ PyTuple_SET_ITEM(result_caRepository, n_caRepository++, obj);
+ continue;
+ }
+ if (nid == NID_rpkiManifest) {
+ if ((obj = PyString_FromString(uri)) == NULL)
+ goto error;
+ PyTuple_SET_ITEM(result_rpkiManifest, n_rpkiManifest++, obj);
+ continue;
+ }
+ if (nid == NID_signedObject) {
+ if ((obj = PyString_FromString(uri)) == NULL)
+ goto error;
+ PyTuple_SET_ITEM(result_signedObject, n_signedObject++, obj);
+ continue;
+ }
+ }
+
+ result = Py_BuildValue("(OOO)",
+ result_caRepository,
+ result_rpkiManifest,
+ result_signedObject);
+
+ error:
+ AUTHORITY_INFO_ACCESS_free(ext);
+ Py_XDECREF(result_caRepository);
+ Py_XDECREF(result_rpkiManifest);
+ Py_XDECREF(result_signedObject);
+ return result;
+}
+
+static char pkcs10_object_set_sia__doc__[] =
+ "Set SIA values for this PKCS#10 request. Takes three arguments:\n"
+ "caRepository URIs, rpkiManifest URIs, and signedObject URIs.\n"
+ "Each of these should be an iterable which returns URIs.\n"
+ "None is acceptable as an alternate way of specifying an empty\n"
+ "sequence of URIs for a particular argument.\n"
+ ;
+
+static PyObject *
+pkcs10_object_set_sia(pkcs10_object *self, PyObject *args)
+{
+ AUTHORITY_INFO_ACCESS *ext = NULL;
+ PyObject *caRepository = NULL;
+ PyObject *rpkiManifest = NULL;
+ PyObject *signedObject = NULL;
+ PyObject *iterator = NULL;
+ ASN1_OBJECT *oid = NULL;
+ PyObject **pobj = NULL;
+ PyObject *item = NULL;
+ ACCESS_DESCRIPTION *a = NULL;
+ int i, nid = NID_undef, ok = 0;
+ Py_ssize_t urilen;
+ char *uri;
+
+ if (!PyArg_ParseTuple(args, "OOO", &caRepository, &rpkiManifest, &signedObject))
+ goto error;
+
+ if ((ext = AUTHORITY_INFO_ACCESS_new()) == NULL)
+ lose_no_memory();
+
+ /*
+ * This is going to want refactoring, because it's ugly, because we
+ * want to reuse code for AIA, and because it'd be nice to support a
+ * single URI as an abbreviation for a sequence containing one URI.
+ */
+
+ for (i = 0; i < 3; i++) {
+ switch (i) {
+ case 0: pobj = &caRepository; nid = NID_caRepository; break;
+ case 1: pobj = &rpkiManifest; nid = NID_rpkiManifest; break;
+ case 2: pobj = &signedObject; nid = NID_signedObject; break;
+ }
+
+ if (*pobj == Py_None)
+ continue;
+
+ if ((oid = OBJ_nid2obj(nid)) == NULL)
+ lose_openssl_error("Couldn't find SIA accessMethod OID");
+
+ if ((iterator = PyObject_GetIter(*pobj)) == NULL)
+ goto error;
+
+ while ((item = PyIter_Next(iterator)) != NULL) {
+
+ if (PyString_AsStringAndSize(item, &uri, &urilen) < 0)
+ goto error;
+
+ if ((a = ACCESS_DESCRIPTION_new()) == NULL ||
+ (a->method = OBJ_dup(oid)) == NULL ||
+ (a->location->d.uniformResourceIdentifier = ASN1_IA5STRING_new()) == NULL ||
+ !ASN1_OCTET_STRING_set(a->location->d.uniformResourceIdentifier, (unsigned char *) uri, urilen))
+ lose_no_memory();
+
+ a->location->type = GEN_URI;
+
+ if (!sk_ACCESS_DESCRIPTION_push(ext, a))
+ lose_no_memory();
+
+ a = NULL;
+ Py_XDECREF(item);
+ item = NULL;
+ }
+
+ Py_XDECREF(iterator);
+ iterator = NULL;
+ }
+
+ if (!X509V3_add1_i2d(&self->exts, NID_sinfo_access, ext, 0, X509V3_ADD_REPLACE))
+ lose_openssl_error("Couldn't add SIA extension to certificate");
+
+ ok = 1;
+
+ error:
+ AUTHORITY_INFO_ACCESS_free(ext);
+ ACCESS_DESCRIPTION_free(a);
+ Py_XDECREF(item);
+ Py_XDECREF(iterator);
+
+ if (ok)
+ Py_RETURN_NONE;
+ else
+ return NULL;
+}
+
+/*
+ * May want EKU handlers eventually, skip for now.
+ */
+
+static char pkcs10_object_pprint__doc__[] =
+ "This method returns a pretty-printed rendition of the PKCS#10 request.\n"
+ ;
+
+static PyObject *
+pkcs10_object_pprint(pkcs10_object *self)
+{
+ PyObject *result = NULL;
+ BIO *bio = NULL;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ lose_no_memory();
+
+ if (!X509_REQ_print(bio, self->pkcs10))
+ lose_openssl_error("Unable to pretty-print PKCS#10 request");
+
+ result = BIO_to_PyString_helper(bio);
+
+ error: /* Fall through */
+ BIO_free(bio);
+ return result;
+}
+
+static struct PyMethodDef pkcs10_object_methods[] = {
+ Define_Method(pemWrite, pkcs10_object_pem_write, METH_NOARGS),
+ Define_Method(derWrite, pkcs10_object_der_write, METH_NOARGS),
+ Define_Method(sign, pkcs10_object_sign, METH_VARARGS),
+ Define_Method(verify, pkcs10_object_verify, METH_NOARGS),
+ Define_Method(getPublicKey, pkcs10_object_get_public_key, METH_NOARGS),
+ Define_Method(setPublicKey, pkcs10_object_set_public_key, METH_VARARGS),
+ Define_Method(getVersion, pkcs10_object_get_version, METH_NOARGS),
+ Define_Method(setVersion, pkcs10_object_set_version, METH_VARARGS),
+ Define_Method(getSubject, pkcs10_object_get_subject, METH_VARARGS),
+ Define_Method(setSubject, pkcs10_object_set_subject, METH_VARARGS),
+ Define_Method(pprint, pkcs10_object_pprint, METH_NOARGS),
+ Define_Method(getKeyUsage, pkcs10_object_get_key_usage, METH_NOARGS),
+ Define_Method(setKeyUsage, pkcs10_object_set_key_usage, METH_VARARGS),
+ Define_Method(getBasicConstraints, pkcs10_object_get_basic_constraints, METH_NOARGS),
+ Define_Method(setBasicConstraints, pkcs10_object_set_basic_constraints, METH_VARARGS),
+ Define_Method(getSIA, pkcs10_object_get_sia, METH_NOARGS),
+ Define_Method(setSIA, pkcs10_object_set_sia, METH_VARARGS),
+ Define_Class_Method(pemRead, pkcs10_object_pem_read, METH_VARARGS),
+ Define_Class_Method(pemReadFile, pkcs10_object_pem_read_file, METH_VARARGS),
+ Define_Class_Method(derRead, pkcs10_object_der_read, METH_VARARGS),
+ Define_Class_Method(derReadFile, pkcs10_object_der_read_file, METH_VARARGS),
+ {NULL}
+};
+
+static void
+pkcs10_object_dealloc(pkcs10_object *self)
+{
+ X509_REQ_free(self->pkcs10);
+ sk_X509_EXTENSION_pop_free(self->exts, X509_EXTENSION_free);
+ self->ob_type->tp_free((PyObject*) self);
+}
+
+static char POW_PKCS10_Type__doc__[] =
+ "This class represents a PKCS#10 request.\n"
+ "\n"
+ LAME_DISCLAIMER_IN_ALL_CLASS_DOCUMENTATION
+ ;
+
+static PyTypeObject POW_PKCS10_Type = {
+ PyObject_HEAD_INIT(0)
+ 0, /* ob_size */
+ "POW.PKCS10", /* tp_name */
+ sizeof(pkcs10_object), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)pkcs10_object_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ POW_PKCS10_Type__doc__, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ pkcs10_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ pkcs10_object_new, /* tp_new */
+};
+
+
+
+
+
+
+/*
* Module functions.
*/
@@ -6823,6 +7681,7 @@ init_POW(void)
Define_Class(POW_IPAddress_Type);
Define_Class(POW_Manifest_Type);
Define_Class(POW_ROA_Type);
+ Define_Class(POW_PKCS10_Type);
#undef Define_Class