diff options
Diffstat (limited to 'rpkid/ext/POW.c')
-rw-r--r-- | rpkid/ext/POW.c | 12508 |
1 files changed, 6127 insertions, 6381 deletions
diff --git a/rpkid/ext/POW.c b/rpkid/ext/POW.c index 5584e874..231b5802 100644 --- a/rpkid/ext/POW.c +++ b/rpkid/ext/POW.c @@ -1,40 +1,88 @@ -/*****************************************************************************/ -/* */ -/* Copyright (c) 2001, 2002, Peter Shannon */ -/* All rights reserved. */ -/* */ -/* Redistribution and use in source and binary forms, with or without */ -/* modification, are permitted provided that the following conditions */ -/* are met: */ -/* */ -/* * Redistributions of source code must retain the above */ -/* copyright notice, this list of conditions and the following */ -/* disclaimer. */ -/* */ -/* * Redistributions in binary form must reproduce the above */ -/* copyright notice, this list of conditions and the following */ -/* disclaimer in the documentation and/or other materials */ -/* provided with the distribution. */ -/* */ -/* * The name of the contributors may be used to endorse or promote */ -/* products derived from this software without specific prior */ -/* written permission. */ -/* */ -/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ -/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ -/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */ -/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS */ -/* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ -/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ -/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ -/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY */ -/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ -/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ -/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* */ -/*****************************************************************************/ +/* + * This module started out as the core of Peter Shannon's "Python + * OpenSSL Wrappers" package, an excellent but somewhat dated package + * which I encountered while looking for some halfway sane way to cram + * RFC 3779 certificate support code into Python. + * + * At this point enough of the code has been added or rewritten that + * it's unclear (either way) whether this code properly qualifies as a + * derivative work. Given that both Peter's original code and all of + * subsequent changes to it were done under something equivalent to a + * BSD license, this may not matter very much, but the following + * attempts to give proper credit to all concerned. + * + **** + * + * Copyright (C) 2009--2012 Internet Systems Consortium ("ISC") + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + **** + * + * Portions copyright (C) 2006--2008 American Registry for Internet + * Numbers ("ARIN") + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + **** + * + * Portions Copyright (c) 2001, 2002, Peter Shannon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * The name of the contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $Id: rcynic.c 4613 2012-07-30 23:24:15Z sra $ */ #include <Python.h> +#include <datetime.h> #include <openssl/opensslconf.h> #include <openssl/crypto.h> @@ -43,199 +91,189 @@ #include <openssl/x509.h> #include <openssl/x509v3.h> #include <openssl/pem.h> -#include <openssl/ssl.h> #include <openssl/evp.h> #include <openssl/err.h> #include <openssl/md5.h> -#ifndef OPENSSL_NO_MD2 -#include <openssl/md2.h> -#endif #include <openssl/sha.h> -#include <openssl/hmac.h> -#include <openssl/ripemd.h> #include <openssl/cms.h> +#include <rpki/roa.h> +#include <rpki/manifest.h> + #include <time.h> +#include <string.h> +#include <arpa/inet.h> +#include <sys/socket.h> -// Symmetric ciphers -#define DES_ECB 1 -#define DES_EDE 2 -#define DES_EDE3 3 -#define DES_CFB 4 -#define DES_EDE_CFB 5 -#define DES_EDE3_CFB 6 -#define DES_OFB 7 -#define DES_EDE_OFB 8 -#define DES_EDE3_OFB 9 -#define DES_CBC 10 -#define DES_EDE_CBC 11 -#define DES_EDE3_CBC 12 -#define DESX_CBC 13 -#define RC4 14 -#define RC4_40 15 -#define IDEA_ECB 16 -#define IDEA_CFB 17 -#define IDEA_OFB 18 -#define IDEA_CBC 19 -#define RC2_ECB 20 -#define RC2_CBC 21 -#define RC2_40_CBC 22 -#define RC2_CFB 23 -#define RC2_OFB 24 -#define BF_ECB 25 -#define BF_CBC 26 -#define BF_CFB 27 -#define BF_OFB 28 -#define CAST5_ECB 29 -#define CAST5_CBC 30 -#define CAST5_CFB 31 -#define CAST5_OFB 32 -#define RC5_32_12_16_CBC 33 -#define RC5_32_12_16_CFB 34 -#define RC5_32_12_16_ECB 35 -#define RC5_32_12_16_OFB 36 - -// SSL connection methods -#define SSLV2_SERVER_METHOD 1 -#define SSLV2_CLIENT_METHOD 2 -#define SSLV2_METHOD 3 -#define SSLV3_SERVER_METHOD 4 -#define SSLV3_CLIENT_METHOD 5 -#define SSLV3_METHOD 6 -#define TLSV1_SERVER_METHOD 7 -#define TLSV1_CLIENT_METHOD 8 -#define TLSV1_METHOD 9 -#define SSLV23_SERVER_METHOD 10 -#define SSLV23_CLIENT_METHOD 11 -#define SSLV23_METHOD 12 - -// SSL connection states - -// PEM encoded data types -#define RSA_PUBLIC_KEY 1 -#define RSA_PRIVATE_KEY 2 -#define DSA_PUBLIC_KEY 3 -#define DSA_PRIVATE_KEY 4 -#define DH_PUBLIC_KEY 5 -#define DH_PRIVATE_KEY 6 -#define X509_CERTIFICATE 7 -#define X_X509_CRL 8 // X509_CRL already used by OpenSSL library -#define CMS_MESSAGE 9 - -// Asymmetric ciphers -#define RSA_CIPHER 1 -#define DSA_CIPHER 2 -#define DH_CIPHER 3 -//#define NO_DSA -//#define NO_DH - -// Digests -#ifndef OPENSSL_NO_MD2 -#define MD2_DIGEST 1 +/* + * GCC attribute to let us tell GCC not to whine about unused formal + * parameters when we're in maximal warning mode. + */ +#ifdef __GNUC__ +#define GCC_UNUSED __attribute__((unused)) +#else +define GCC_UNUSED #endif + +/* + * Maximum size of a raw IP (v4 or v6) address, in bytes. + */ +#define RAW_IPADDR_BUFLEN 16 + +/* + * Maximum size of an ASN.1 Integer converted from a Python Long, in bytes. + */ +#define MAX_ASN1_INTEGER_LEN 20 + +/* Asymmetric ciphers */ +#define RSA_CIPHER 1 + +/* Digests */ #define MD5_DIGEST 2 #define SHA_DIGEST 3 #define SHA1_DIGEST 4 -#define RIPEMD160_DIGEST 5 #define SHA256_DIGEST 6 #define SHA384_DIGEST 7 #define SHA512_DIGEST 8 -// Object format +/* Object format */ #define SHORTNAME_FORMAT 1 #define LONGNAME_FORMAT 2 +#define OIDNAME_FORMAT 3 -// Output format +/* Output format */ #define PEM_FORMAT 1 #define DER_FORMAT 2 -// Object check functions -#define X_X509_Check(op) ((op)->ob_type == &x509type) -#define X_X509_store_Check(op) ((op)->ob_type == &x509_storetype) -#define X_X509_crl_Check(op) ((op)->ob_type == &x509_crltype) -#define X_X509_revoked_Check(op) ((op)->ob_type == &x509_revokedtype) -#define X_asymmetric_Check(op) ((op)->ob_type == &asymmetrictype) -#define X_symmetric_Check(op) ((op)->ob_type == &symmetrictype) -#define X_digest_Check(op) ((op)->ob_type == &digesttype) -#define X_hmac_Check(op) ((op)->ob_type == &hmactype) -#define X_ssl_Check(op) ((op)->ob_type == &ssltype) -#define X_cms_Check(op) ((op)->ob_type == &cmstype) - -// Symbolic representation of "no SSL shutdown mode requested" -#define SSL_NO_SHUTDOWN 0 +/* Object check functions */ +#define POW_X509_Check(op) PyObject_TypeCheck(op, &POW_X509_Type) +#define POW_X509Store_Check(op) PyObject_TypeCheck(op, &POW_X509Store_Type) +#define POW_CRL_Check(op) PyObject_TypeCheck(op, &POW_CRL_Type) +#define POW_Asymmetric_Check(op) PyObject_TypeCheck(op, &POW_Asymmetric_Type) +#define POW_Digest_Check(op) PyObject_TypeCheck(op, &POW_Digest_Type) +#define POW_CMS_Check(op) PyObject_TypeCheck(op, &POW_CMS_Type) +#define POW_IPAddress_Check(op) PyObject_TypeCheck(op, &POW_IPAddress_Type) +#define POW_ROA_Check(op) PyObject_TypeCheck(op, &POW_ROA_Type) +#define POW_Manifest_Check(op) PyObject_TypeCheck(op, &POW_Manifest_Type) +#define POW_ROA_Check(op) PyObject_TypeCheck(op, &POW_ROA_Type) static char pow_module__doc__ [] = -"<moduleDescription>\n" -" <header>\n" -" <name>POW</name>\n" -" <author>Peter Shannon</author>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This third major release of POW addresses the most critical missing\n" -" parts of functionality, X509v3 support. Initially I thought adding\n" -" support via the OpenSSL code would be the easiest option but this\n" -" proved to be incorrect mainly due to the way I have chosen to handle\n" -" the complex data such as <classname>directoryNames</classname> and\n" -" <classname>generalNames</classname>. It is easier in python to\n" -" construct complex sets of data using lists and dictionaries than\n" -" coordinate large numbers of objects and method calls. This is no\n" -" criticism, it is just extremely easy. Coding complex data such as the\n" -" <classname>certificatePolicies</classname> coding coding routines in C\n" -" to handle the data proved laborous and ultimately error prone.\n" -" </para>\n" -" <para>\n" -" PKIX structures are supported by a few operations on the relevant POW\n" -" objects and through a Python library which is modelled on the DER\n" -" encoding rules. Modeling DER does expose some of the complexities of\n" -" the ASN1 specifications but avoids coding many assumptions into the\n" -" data structures and the interface for the objects. For an example of\n" -" overly complex definitions take a look at the\n" -" <classname>Name</classname> object in RFC3280. It is equally\n" -" important that modeling DER in the way leads to a library which is\n" -" trivial to extend to support new objects - simple objects are one\n" -" liners and complex objects only require the definition of a new\n" -" constructor.\n" -" </para>\n" -" <para>\n" -" functionality have been plugged. The <classname>Ssl</classname> class has received\n" -" several new features relating to security. Other areas have been\n" -" improved: PRNG support, certificate and CRL signing, certificate chain\n" -" and client verification. Many bugs have been fixed, and certain\n" -" parts of code re-written where necessary. I hope you enjoy using POW\n" -" and please feel free to send me feature requests and bug reports.\n" -" </para>\n" -" </body>\n" -"</moduleDescription>\n" -; + "Python interface to RFC-3779-enabled OpenSSL. This code is intended\n" + "to support the rpki.net toolset.\n" + "\n" + "This code started out life as Peter Shannon's excellent \"Python OpenSSL\n" + "Wrappers\" package. It has been extensively modified since then, to add\n" + "support for things needed for the RPKI protocols, to upgrade the code\n" + "to use modern (circa Python 2.7) classes, and to remove code not\n" + "needed for RPKI.\n" + ; + +#define LAME_DISCLAIMER_IN_ALL_CLASS_DOCUMENTATION \ + "The documentation for this class used to provide a nice example of how\n" \ + "to use the class. Sadly, most of what was in that example is now\n" \ + "obsolete due to recent or impending API changes. Once the new API is\n" \ + "stable, this documentation should be rewritten to provide such examples.\n" + +/* + * Handle NIDs we wish OpenSSL knew about. This is carefully (we + * hope) written to do nothing at all for any NID that OpenSSL knows + * about; the intent is just to add definitions for things OpenSSL + * doesn't know about yet. Of necessity, this is a bit gross, since + * it confounds runtime static variables with predefined macro names, + * but we try to put all the magic associated with this in one place. + */ + +#ifndef NID_rpkiManifest +static int NID_rpkiManifest; +#endif + +#ifndef NID_signedObject +static int NID_signedObject; +#endif + +static const struct { + int *nid; + const char *oid; + const char *sn; + const char *ln; +} missing_nids[] = { + +#ifndef NID_rpkiManifest + {&NID_rpkiManifest, "1.3.6.1.5.5.7.48.10", "id-ad-rpkiManifest", "RPKI Manifest"}, +#endif + +#ifndef NID_signedObject + {&NID_signedObject, "1.3.6.1.5.5.7.48.11", "id-ad-signedObject", "Signed Object"} +#endif + +}; + +/* + * IP versions. + */ + +typedef struct ipaddress_version { + unsigned version; + unsigned afi; + unsigned af; + unsigned length; +} ipaddress_version; + +static const ipaddress_version ipaddress_version_4 = { + 4, IANA_AFI_IPV4, AF_INET, 4 +}; + +static const ipaddress_version ipaddress_version_6 = { + 6, IANA_AFI_IPV6, AF_INET6, 16 +}; + +static const ipaddress_version * const ipaddress_versions[] = { + &ipaddress_version_4, &ipaddress_version_6 +}; + +/* + * Exception objects. + */ -/*========== Pre-definitions ==========*/ static PyObject *ErrorObject, - *SSLErrorObject, - *ZeroReturnErrorObject, - *WantReadErrorObject, - *WantWriteErrorObject, - *SSLSyscallErrorObject, - *SSLErrorSSLErrorObject, - *SSLSyscallSSLErrorObject, - *SSLUnexpectedEOFErrorObject, - *SSLOtherErrorObject; + *OpenSSLErrorObject, + *POWErrorObject, + *NotVerifiedErrorObject; + +/* + * Constructor for customized datetime class. + */ + +static PyObject *custom_datetime; + +/* + * Declarations of type objects (definitions come later). + */ static PyTypeObject - x509type, - x509_storetype, - x509_crltype, - x509_revokedtype, - asymmetrictype, - symmetrictype, - digesttype, - hmactype, - ssltype, - cmstype; -/*========== Pre-definitions ==========*/ - -/*========== C structs ==========*/ + POW_X509_Type, + POW_X509Store_Type, + POW_CRL_Type, + POW_Asymmetric_Type, + POW_Digest_Type, + POW_CMS_Type, + POW_IPAddress_Type, + POW_ROA_Type, + POW_Manifest_Type, + POW_ROA_Type, + POW_PKCS10_Type; + +/* + * Object internals. + */ + +typedef struct { + PyObject_HEAD + unsigned char address[16]; + const struct ipaddress_version *type; +} ipaddress_object; + typedef struct { PyObject_HEAD X509 *x509; @@ -249,82 +287,76 @@ typedef struct { typedef struct { PyObject_HEAD X509_CRL *crl; -} x509_crl_object; +} crl_object; typedef struct { PyObject_HEAD - X509_REVOKED *revoked; -} x509_revoked_object; - -typedef struct { - PyObject_HEAD - void *cipher; - int key_type; - int cipher_type; + EVP_PKEY *pkey; } asymmetric_object; typedef struct { PyObject_HEAD - EVP_CIPHER_CTX cipher_ctx; - int cipher_type; -} symmetric_object; - -typedef struct { - PyObject_HEAD EVP_MD_CTX digest_ctx; int digest_type; } digest_object; typedef struct { PyObject_HEAD - HMAC_CTX hmac_ctx; -} hmac_object; + CMS_ContentInfo *cms; +} cms_object; typedef struct { - PyObject_HEAD - int ctxset; - SSL *ssl; - SSL_CTX *ctx; - STACK_OF(X509) *trusted_certs; - char *x509_cb_err; -} ssl_object; + cms_object cms; /* Subclass of CMS */ + ROA *roa; +} roa_object; + +typedef struct { + cms_object cms; /* Subclass of CMS */ + Manifest *manifest; +} manifest_object; typedef struct { PyObject_HEAD - CMS_ContentInfo *cms; -} cms_object; + X509_REQ *pkcs10; + STACK_OF(X509_EXTENSION) *exts; +} pkcs10_object; -/*========== C structs ==========*/ + -/*========== helper functions ==========*/ +/* + * Utility functions. + */ /* * Minimal intervention debug-by-printf() hack, use only for good. */ #if 0 -#define KVETCH(_msg_) write(2, _msg_ "\n", sizeof(_msg_)) +#define KVETCH(_msg_) write(2, _msg_ "\n", sizeof(_msg_)) #else -#define KVETCH(_msg_) +#define KVETCH(_msg_) ((void) 0) +#endif + +#if 0 +#define ENTERING(_name_) KVETCH("Entering " #_name_ "()") +#else +#define ENTERING(_name_) ((void) 0) #endif /* - * Error handling macros. These macros make two assumptions: - * - * 1) All the macros assume that there's a cleanup label named - * "error" which these macros can use as a goto target. - * - * 2) assert_no_unhandled_openssl_errors() assumes that the return - * value is stored in a PyObject* variable named "result". - * - * These are icky assumptions, but they make it easier to provide - * uniform error handling and make the code easier to read, not to - * mention making it easier to track down obscure OpenSSL errors. + * Error handling macros. All of macros assume that there's a cleanup + * label named "error" which these macros can use as a goto target. */ #define lose(_msg_) \ do { \ - PyErr_SetString(ErrorObject, (_msg_)); \ + PyErr_SetString(POWErrorObject, (_msg_)); \ + goto error; \ + } while (0) + +#define lose_no_memory() \ + do { \ + PyErr_NoMemory(); \ goto error; \ } while (0) @@ -336,25 +368,20 @@ typedef struct { #define lose_openssl_error(_msg_) \ do { \ - set_openssl_exception(ErrorObject, (_msg_)); \ + set_openssl_exception(OpenSSLErrorObject, (_msg_)); \ goto error; \ } while (0) -#define lose_ssl_error(_self_, _code_) \ +#define lose_not_verified(_msg_) \ do { \ - set_openssl_ssl_exception(_self_, _code_); \ + PyErr_SetString(NotVerifiedErrorObject, (_msg_)); \ goto error; \ } while (0) #define assert_no_unhandled_openssl_errors() \ do { \ - if (ERR_peek_error()) { \ - if (result) { \ - Py_XDECREF(result); \ - result = NULL; \ - } \ + if (ERR_peek_error()) \ lose_openssl_error(assert_helper(__LINE__)); \ - } \ } while (0) static char * @@ -367,85 +394,21 @@ assert_helper(int line) return msg; } -static int -docset_helper_add(PyObject *set, char *v) -{ - PyObject *value = NULL; - - if ((value = PyString_FromString(v)) == NULL) - lose("could not allocate memory"); - - if (PyList_Append(set, value) != 0) - goto error; - - Py_XDECREF(value); - return 1; - - error: - - Py_XDECREF(value); - return 0; -} - /* - * Generate an encrypion envelope. Saves a lot of space having this case - * statement in one place. + * Consolidate some tedious EVP-related switch statements. */ -static const EVP_CIPHER * -evp_cipher_factory(int cipher_type) -{ - switch(cipher_type) { -#ifndef OPENSSL_NO_DES - case DES_ECB: return EVP_des_ecb(); - case DES_EDE: return EVP_des_ede(); - case DES_EDE3: return EVP_des_ede3(); - case DES_CFB: return EVP_des_cfb(); - case DES_EDE_CFB: return EVP_des_ede_cfb(); - case DES_EDE3_CFB: return EVP_des_ede3_cfb(); - case DES_OFB: return EVP_des_ofb(); - case DES_EDE_OFB: return EVP_des_ede_ofb(); - case DES_EDE3_OFB: return EVP_des_ede3_ofb(); - case DES_CBC: return EVP_des_cbc(); - case DES_EDE_CBC: return EVP_des_ede_cbc(); - case DES_EDE3_CBC: return EVP_des_ede3_cbc(); - case DESX_CBC: return EVP_desx_cbc(); -#endif -#ifndef OPENSSL_NO_RC4 - case RC4: return EVP_rc4(); - case RC4_40: return EVP_rc4_40(); -#endif -#ifndef OPENSSL_NO_IDEA - case IDEA_ECB: return EVP_idea_ecb(); - case IDEA_CFB: return EVP_idea_cfb(); - case IDEA_OFB: return EVP_idea_ofb(); - case IDEA_CBC: return EVP_idea_cbc(); -#endif -#ifndef OPENSSL_NO_RC2 - case RC2_ECB: return EVP_rc2_ecb(); - case RC2_CBC: return EVP_rc2_cbc(); - case RC2_40_CBC: return EVP_rc2_40_cbc(); - case RC2_CFB: return EVP_rc2_cfb(); - case RC2_OFB: return EVP_rc2_ofb(); -#endif -#ifndef OPENSSL_NO_BF - case BF_ECB: return EVP_bf_ecb(); - case BF_CBC: return EVP_bf_cbc(); - case BF_CFB: return EVP_bf_cfb(); - case BF_OFB: return EVP_bf_ofb(); -#endif -#ifndef OPENSSL_NO_CAST5 - case CAST5_ECB: return EVP_cast5_ecb(); - case CAST5_CBC: return EVP_cast5_cbc(); - case CAST5_CFB: return EVP_cast5_cfb(); - case CAST5_OFB: return EVP_cast5_ofb(); -#endif -#ifndef OPENSSL_NO_RC5 - case RC5_32_12_16_CBC: return EVP_rc5_32_12_16_cbc(); - case RC5_32_12_16_CFB: return EVP_rc5_32_12_16_cfb(); - case RC5_32_12_16_ECB: return EVP_rc5_32_12_16_ecb(); - case RC5_32_12_16_OFB: return EVP_rc5_32_12_16_ofb(); -#endif - default: return NULL; + +static const EVP_MD * +evp_digest_factory(int digest_type) +{ + switch (digest_type) { + case MD5_DIGEST: return EVP_md5(); + case SHA_DIGEST: return EVP_sha(); + case SHA1_DIGEST: return EVP_sha1(); + case SHA256_DIGEST: return EVP_sha256(); + case SHA384_DIGEST: return EVP_sha384(); + case SHA512_DIGEST: return EVP_sha512(); + default: return NULL; } } @@ -496,218 +459,160 @@ set_openssl_exception(PyObject *error_class, const char *msg) Py_XDECREF(errors); } -static void -set_openssl_ssl_exception(const ssl_object *self, const int ret) +static X509_NAME * +x509_object_helper_set_name(PyObject *dn_obj) { - int err = SSL_get_error(self->ssl, ret); - const char *s = NULL; - - switch(err) { - - /* - * These three get their own exceptions. - */ - - case SSL_ERROR_ZERO_RETURN: - PyErr_SetNone(ZeroReturnErrorObject); - break; - case SSL_ERROR_WANT_READ: - PyErr_SetNone(WantReadErrorObject); - break; - case SSL_ERROR_WANT_WRITE: - PyErr_SetNone(WantWriteErrorObject); - break; - - case SSL_ERROR_SYSCALL: - /* - * Horrible jumbled mess of I/O related errors. I'd ask what they - * were thinking, except that it's pretty clear that they weren't. - */ - if (ERR_peek_error()) - set_openssl_exception(SSLSyscallSSLErrorObject, NULL); - else if (ret) - PyErr_SetFromErrno(SSLSyscallErrorObject); - else - PyErr_SetNone(SSLUnexpectedEOFErrorObject); - break; - - case SSL_ERROR_SSL: - /* - * Generic OpenSSL error during an SSL call. I think. - */ - set_openssl_exception(SSLErrorSSLErrorObject, self->x509_cb_err); - break; - - /* - * All other SSL errors are returned as a (number, string) tuple. - */ - - case SSL_ERROR_NONE: - s = "SSL_ERROR_NONE"; - break; - case SSL_ERROR_WANT_X509_LOOKUP: - s = "SSL_ERROR_WANT_X509_LOOKUP"; - break; - case SSL_ERROR_WANT_CONNECT: - s = "SSL_ERROR_WANT_CONNECT"; - break; - case SSL_ERROR_WANT_ACCEPT: - s = "SSL_ERROR_WANT_ACCEPT"; - break; - default: - s = "UNKNOWN_SSL_ERROR"; - } - - if (s) - PyErr_SetObject(SSLOtherErrorObject, Py_BuildValue("(is)", err, s)); -} + PyObject *rdn_obj = NULL; + PyObject *pair_obj = NULL; + PyObject *type_obj = NULL; + PyObject *value_obj = NULL; + X509_NAME *name = NULL; + char *type_str, *value_str; + int asn1_type, i, j; -static PyObject * -X509_object_helper_set_name(X509_NAME *name, PyObject *name_sequence) -{ - PyObject *pair = NULL; PyObject *type = NULL; PyObject *value = NULL; - int no_pairs = 0, i = 0, str_type = 0, nid; - unsigned char *valueptr = NULL; - char *typeptr = NULL; + if ((name = X509_NAME_new()) == NULL) + lose_no_memory(); - no_pairs = PySequence_Size(name_sequence); - for (i = 0; i < no_pairs; i++) { - if ((pair = PySequence_GetItem(name_sequence, i)) == NULL) - return NULL; + for (i = 0; i < PySequence_Size(dn_obj); i++) { - if (!PyTuple_Check(pair) && !PyList_Check(pair)) - lose_type_error("inapropriate type"); + if ((rdn_obj = PySequence_GetItem(dn_obj, i)) == NULL) + goto error; - if (PySequence_Size(pair) != 2) - lose("each name entry must have 2 elements"); + if (!PySequence_Check(rdn_obj) || PySequence_Size(rdn_obj) == 0) + lose_type_error("each RDN must be a sequence with at least one element"); - if ((type = PySequence_GetItem(pair, 0)) == NULL) - lose_type_error("could not get type string"); + for (j = 0; j < PySequence_Size(rdn_obj); j++) { - if (!PyString_Check(type)) - lose_type_error("inapropriate type"); + if ((pair_obj = PySequence_GetItem(rdn_obj, j)) == NULL) + goto error; - if ((value = PySequence_GetItem(pair, 1)) == NULL) - lose_type_error("could not get value string"); + if (!PySequence_Check(pair_obj) || PySequence_Size(pair_obj) != 2) + lose_type_error("each name entry must be a two-element sequence"); - if (!PyString_Check(value)) - lose_type_error("inapropriate type"); + if ((type_obj = PySequence_GetItem(pair_obj, 0)) == NULL || + (type_str = PyString_AsString(type_obj)) == NULL || + (value_obj = PySequence_GetItem(pair_obj, 1)) == NULL || + (value_str = PyString_AsString(value_obj)) == NULL) + goto error; - typeptr = PyString_AsString(type); - valueptr = (unsigned char *) PyString_AsString(value); + if ((asn1_type = ASN1_PRINTABLE_type((unsigned char *) value_str, -1)) != V_ASN1_PRINTABLESTRING) + asn1_type = V_ASN1_UTF8STRING; - str_type = ASN1_PRINTABLE_type(valueptr, -1); - if ((nid = OBJ_ln2nid(typeptr)) == 0 && - (nid = OBJ_sn2nid(typeptr)) == 0) - lose("unknown ASN1 object"); + if (!X509_NAME_add_entry_by_txt(name, type_str, asn1_type, + (unsigned char *) value_str, + strlen((char *) value_str), + -1, (j ? -1 : 0))) + lose("Unable to add name entry"); - if (!X509_NAME_add_entry_by_NID(name, nid, str_type, valueptr, - strlen((char *) valueptr), -1, 0)) - lose("unable to add name entry"); + Py_XDECREF(pair_obj); + Py_XDECREF(type_obj); + Py_XDECREF(value_obj); + pair_obj = type_obj = value_obj = NULL; + } - Py_XDECREF(pair); - Py_XDECREF(type); - Py_XDECREF(value); - pair = NULL; - type = NULL; - value = NULL; + Py_XDECREF(rdn_obj); + rdn_obj = NULL; } - return name_sequence; - - error: - Py_XDECREF(pair); - Py_XDECREF(type); - Py_XDECREF(value); + return name; + error: + X509_NAME_free(name); + Py_XDECREF(rdn_obj); + Py_XDECREF(pair_obj); + Py_XDECREF(type_obj); + Py_XDECREF(value_obj); return NULL; } static PyObject * -X509_object_helper_get_name(X509_NAME *name, int format) +x509_object_helper_get_name(X509_NAME *name, int format) { - int no_entries = 0, no_pairs = 0, i = 0, j = 0, value_len = 0, nid = 0; X509_NAME_ENTRY *entry = NULL; - char *value = NULL, long_name[512]; - const char *short_name; + PyObject *result = NULL; + PyObject *rdn = NULL; + PyObject *item = NULL; + const char *oid = NULL; + char oidbuf[512]; + int i, set = -1; - PyObject *result_list = NULL; - PyObject *pair = NULL; - PyObject *py_type = NULL; - PyObject *py_value = NULL; + /* + * Overall theory here: multi-value RDNs are very rare in the wild. + * We should support them, so we don't throw an exception if handed + * one in a BPKI certificate, but with minimal effort. What we care + * about here is optimizing for the common case of single-valued RDNs. + */ - no_entries = X509_NAME_entry_count(name); + if ((result = PyTuple_New(X509_NAME_entry_count(name))) == NULL) + goto error; - if ((result_list = PyTuple_New(no_entries)) == NULL) - lose("could not allocate memory"); + for (i = 0; i < X509_NAME_entry_count(name); i++) { - for(i = 0; i < no_entries; i++) { if ((entry = X509_NAME_get_entry(name, i)) == NULL) - lose("could not get certificate name"); - - if (entry->value->length + 1 > value_len) { - if (value) - free(value); - - if ((value = malloc(entry->value->length + 1)) == NULL) - lose("could not allocate memory"); + lose("Couldn't get certificate name"); - value_len = entry->value->length + 1; - } - memcpy(value, entry->value->data, entry->value->length); - value[entry->value->length] = 0; - - if (!i2t_ASN1_OBJECT(long_name, sizeof(long_name), entry->object)) - lose("could not find object name"); + if (entry->set < 0 || entry->set < set || entry->set > set + 1) + lose("X509_NAME->set value out of expected range"); switch (format) { case SHORTNAME_FORMAT: - nid = OBJ_ln2nid(long_name); - short_name = OBJ_nid2sn(nid); - py_type = PyString_FromString(short_name); + oid = OBJ_nid2sn(OBJ_obj2nid(entry->object)); break; case LONGNAME_FORMAT: - py_type = PyString_FromString(long_name); + oid = OBJ_nid2ln(OBJ_obj2nid(entry->object)); + break; + case OIDNAME_FORMAT: + oid = NULL; break; default: - lose("unknown name format"); + lose("Unknown name format"); } - py_value = PyString_FromString(value); - - if ((pair = PyTuple_New(2)) == NULL) - lose("could not allocate memory"); + if (oid == NULL) { + if (OBJ_obj2txt(oidbuf, sizeof(oidbuf), entry->object, 1) <= 0) + lose_openssl_error("Couldn't translate OID"); + oid = oidbuf; + } - PyTuple_SetItem(pair, 0, py_type); - PyTuple_SetItem(pair, 1, py_value); - PyTuple_SetItem(result_list, i, pair); - } + if (entry->set > set) { - if (value) - free(value); + set++; + if ((item = Py_BuildValue("((ss#))", oid, + ASN1_STRING_data(entry->value), + ASN1_STRING_length(entry->value))) == NULL) + goto error; + PyTuple_SET_ITEM(result, set, item); + item = NULL; - return result_list; + } else { - error: + if ((rdn = PyTuple_GetItem(result, set)) == NULL) + goto error; + (void) _PyTuple_Resize(&rdn, PyTuple_Size(rdn) + 1); + PyTuple_SET_ITEM(result, set, rdn); + if (rdn == NULL) + goto error; + if ((item = Py_BuildValue("(ss#)", oid, + ASN1_STRING_data(entry->value), + ASN1_STRING_length(entry->value))) == NULL) + goto error; + PyTuple_SetItem(rdn, PyTuple_Size(rdn) - 1, item); + rdn = item = NULL; - if (value) - free(value); - - if (result_list) { - no_pairs = PyTuple_Size(result_list); - for (i = 0; i < no_pairs; i++) { - pair = PyTuple_GetItem(result_list, i); - no_entries = PyTuple_Size(result_list); - for (j = 0; j < no_entries; j++) { - py_value = PyTuple_GetItem(pair, i); - Py_XDECREF(py_value); - } } } - Py_XDECREF(py_type); - Py_XDECREF(py_value); - Py_XDECREF(result_list); + if (++set != PyTuple_Size(result)) { + if (set < 0 || set > PyTuple_Size(result)) + lose("Impossible set count for DN, something went horribly wrong"); + _PyTuple_Resize(&result, set); + } + + return result; + + error: + Py_XDECREF(item); + Py_XDECREF(result); return NULL; } @@ -718,11 +623,11 @@ x509_helper_sequence_to_stack(PyObject *x509_sequence) STACK_OF(X509) *x509_stack = NULL; int size = 0, i = 0; - if (x509_sequence != Py_None && !PyTuple_Check(x509_sequence) && !PyList_Check(x509_sequence)) + if (x509_sequence != Py_None && !PySequence_Check(x509_sequence)) lose_type_error("Inapropriate type"); if ((x509_stack = sk_X509_new_null()) == NULL) - lose("Couldn't create new X509 stack"); + lose_no_memory(); if (x509_sequence != Py_None) { size = PySequence_Size(x509_sequence); @@ -731,7 +636,7 @@ x509_helper_sequence_to_stack(PyObject *x509_sequence) if ((x509obj = (x509_object*) PySequence_GetItem(x509_sequence, i)) == NULL) goto error; - if (!X_X509_Check(x509obj)) + if (!POW_X509_Check(x509obj)) lose_type_error("Inapropriate type"); if (!sk_X509_push(x509_stack, x509obj->x509)) @@ -745,2809 +650,2848 @@ x509_helper_sequence_to_stack(PyObject *x509_sequence) return x509_stack; error: - - if (x509_stack) - sk_X509_free(x509_stack); - + sk_X509_free(x509_stack); Py_XDECREF(x509obj); - return NULL; } +/* + * Pull items off an OpenSSL STACK and put them into a Python tuple. + * Assumes that handler is stealing the OpenSSL references to the + * items in the STACK, so shifts consumed frames off the stack so that + * the appropriate _pop_free() destructor can clean up on failures. + * This is OK because all current uses of this function are processing + * the result of OpenSSL xxx_get1_xxx() methods which we have to free + * in any case. + */ + static PyObject * stack_to_tuple_helper(_STACK *sk, PyObject *(*handler)(void *)) { - PyObject *result_list = NULL, *result_tuple = NULL, *obj = NULL; - - if ((result_list = PyList_New(0)) == NULL) - lose("could not allocate memory"); + PyObject *result = NULL; + PyObject *obj = NULL; + int i; - while (sk_num(sk)) { + if ((result = PyTuple_New(sk_num(sk))) == NULL) + goto error; + for (i = 0; sk_num(sk); i++) { if ((obj = handler(sk_value(sk, 0))) == NULL) - lose("could not allocate memory"); - + goto error; sk_shift(sk); - - if (PyList_Append(result_list, obj) != 0) + if (PyTuple_SetItem(result, i, obj) != 0) goto error; - - Py_XDECREF(obj); obj = NULL; } - result_tuple = PyList_AsTuple(result_list); - Py_XDECREF(result_list); - - return result_tuple; + return result; error: Py_XDECREF(obj); - Py_XDECREF(result_list); return NULL; } /* - * Time conversion functions. These follow RFC 5280, but use a single - * text encoding that looks like GeneralizedTime as restricted by RFC - * 5280; conversion to and from UTCTime is handled internally - * according to the RFC 5280 rules. The intent is to hide the - * horrible short-sighted mess from Python code entirely. + * Time conversion functions. Obvious mapping into Python data types + * is datetime, or, rather, our customized rpki.sundial.datetime. + * + * Unsuprisingly, it's easiest for us to map between GeneralizedTime + * (as restricted by RFC 5280) and datetime. Conversion between + * GeneralizedTime and UTCTime is handled automatically according to + * the RFC 5280 rules for those ASN.1 types where it's required. */ static PyObject * ASN1_TIME_to_Python(ASN1_TIME *t) { - ASN1_GENERALIZEDTIME *g = ASN1_TIME_to_generalizedtime(t, NULL); + ASN1_GENERALIZEDTIME *g = NULL; PyObject *result = NULL; - if (g) { - result = Py_BuildValue("s", g->data); - ASN1_GENERALIZEDTIME_free(g); - } + int year, month, day, hour, minute, second; + + if ((g = ASN1_TIME_to_generalizedtime(t, NULL)) == NULL) + lose_openssl_error("Couldn't convert ASN.1 TIME"); + + if (sscanf((char *) g->data, "%4d%2d%2d%2d%2d%2dZ", + &year, &month, &day, &hour, &minute, &second) != 6) + lose("Couldn't scan ASN.1 TIME value"); + + if (custom_datetime != NULL && custom_datetime != Py_None) + result = PyObject_CallFunction(custom_datetime, "iiiiii", + year, month, day, hour, minute, second); + else + result = PyDateTime_FromDateAndTime(year, month, day, hour, minute, second, 0); + + error: + ASN1_GENERALIZEDTIME_free(g); return result; } -static int -python_ASN1_TIME_set_string(ASN1_TIME *t, const char *s) +static ASN1_TIME * +Python_to_ASN1_TIME(PyObject *arg, const int object_requires_utctime) { - if (t == NULL || s == NULL || strlen(s) < 10) - return 0; - if ((s[0] == '1' && s[1] == '9' && s[2] > '4') || - (s[0] == '2' && s[1] == '0' && s[2] < '5')) - return ASN1_UTCTIME_set_string(t, s + 2); - else - return ASN1_GENERALIZEDTIME_set_string(t, s); -} + char buf[sizeof("20010401123456Z") + 1]; + ASN1_TIME *result = NULL; + const char *s = NULL; + int ok; + + if (PyDateTime_Check(arg)) { + if (snprintf(buf, sizeof(buf), "%4d%02d%02d%02d%02d%02dZ", + PyDateTime_GET_YEAR(arg), + PyDateTime_GET_MONTH(arg), + PyDateTime_GET_DAY(arg), + PyDateTime_DATE_GET_HOUR(arg), + PyDateTime_DATE_GET_MINUTE(arg), + PyDateTime_DATE_GET_SECOND(arg)) >= (int) sizeof(buf)) + lose("Internal error -- GeneralizedTime buffer too small"); + s = buf; + } -/*========== helper funcitons ==========*/ + if (s == NULL && (s = PyString_AsString(arg)) == NULL) + goto error; -/*========== X509 code ==========*/ -static x509_object * -X509_object_new(void) -{ - x509_object *self; + if (strlen(s) < 10) + lose_type_error("String is too short to parse as a valid ASN.1 TIME"); - self = PyObject_New(x509_object, &x509type); - if (self == NULL) - goto error; + if ((result = ASN1_TIME_new()) == NULL) + lose_no_memory(); - self->x509 = X509_new(); - return self; + if (object_requires_utctime && + ((s[0] == '1' && s[1] == '9' && s[2] > '4') || + (s[0] == '2' && s[1] == '0' && s[2] < '5'))) + ok = ASN1_UTCTIME_set_string(result, s + 2); + else + ok = ASN1_GENERALIZEDTIME_set_string(result, s); - error: + if (ok) + return result; - Py_XDECREF(self); + error: + ASN1_TIME_free(result); return NULL; } /* - * This function is pretty dumb. Most of the work is done by the module - * function pow_module_pem_read(). + * Extract a Python string from a memory BIO. */ -static x509_object * -X509_object_pem_read(BIO *in) +static PyObject * +BIO_to_PyString_helper(BIO *bio) { - x509_object *self; - - if ((self = PyObject_New(x509_object, &x509type)) == NULL) - goto error; + char *ptr = NULL; + int len = 0; - if ((self->x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) - lose("could not load PEM encoded certificate"); + if ((len = BIO_get_mem_data(bio, &ptr)) == 0) + lose_openssl_error("Unable to get BIO data"); - return self; + return Py_BuildValue("s#", ptr, len); error: - - Py_XDECREF(self); return NULL; } -static x509_object * -X509_object_der_read(unsigned char *src, int len) +static PyObject * +read_from_string_helper(PyObject *(*object_read_helper)(PyTypeObject *, BIO *), + PyTypeObject *type, + PyObject *args) { - x509_object *self; - unsigned char *ptr = src; + PyObject *result = NULL; + char *src = NULL; + BIO *bio = NULL; + int len = 0; - if ((self = PyObject_New(x509_object, &x509type)) == NULL) + if (!PyArg_ParseTuple(args, "s#", &src, &len)) goto error; - self->x509 = X509_new(); - - if(!d2i_X509(&self->x509, (const unsigned char **) &ptr, len)) - lose("could not load PEM encoded certificate"); + if ((bio = BIO_new_mem_buf(src, len)) == NULL) + lose_no_memory(); - return self; + result = object_read_helper(type, bio); error: + BIO_free(bio); + return result; +} - Py_XDECREF(self); - return NULL; +static PyObject * +read_from_file_helper(PyObject *(*object_read_helper)(PyTypeObject *, BIO *), + PyTypeObject *type, + PyObject *args) +{ + const char *filename = NULL; + PyObject *result = NULL; + BIO *bio = NULL; + + if (!PyArg_ParseTuple(args, "s", &filename)) + goto error; + + if ((bio = BIO_new_file(filename, "rb")) == NULL) + lose_openssl_error("Could not open file"); + + result = object_read_helper(type, bio); + + error: + BIO_free(bio); + return result; } /* - * Unlike the previous function this creates the BIO itself. The BIO_s_mem - * is used as a buffer which the certificate is read into, from this buffer - * it is read into a char[] and returned as a string. + * Simplify entries in method definition tables. See the "Common + * Object Structures" section of the API manual for available flags. + */ +#define Define_Method(__python_name__, __c_name__, __flags__) \ + { #__python_name__, (PyCFunction) __c_name__, __flags__, __c_name__##__doc__ } + +#define Define_Class_Method(__python_name__, __c_name__, __flags__) \ + Define_Method(__python_name__, __c_name__, (__flags__) | METH_CLASS) + +/* + * Convert an ASN1_INTEGER into a Python integer or long. */ static PyObject * -X509_object_write_helper(x509_object *self, PyObject *args, int format) +ASN1_INTEGER_to_PyLong(ASN1_INTEGER *arg) { - int len = 0; - char *buf = NULL; - BIO *out_bio = NULL; - PyObject *cert = NULL; + PyObject *result = NULL; + PyObject *obj = NULL; - if (!PyArg_ParseTuple(args, "")) - return NULL; + if ((obj = _PyLong_FromByteArray(ASN1_STRING_data(arg), + ASN1_STRING_length(arg), + 0, 0)) != NULL) + result = PyNumber_Int(obj); - out_bio = BIO_new(BIO_s_mem()); + Py_XDECREF(obj); + return result; +} - switch (format) { +/* + * Convert a Python long to an ASN1_INTEGER. + * This is just nasty, do not read on a full stomach. + * + * Maximum size of integer to be converted here is taken from RFC 5280 + * 4.1.2.2, which sets a maximum of 20 octets for an X.509 certificate + * serial number. + * + * In theory we could use _PyLong_NumBits() to determine the length of + * the long before converting, and raise OverflowError if it's too big. + * Hmm. + */ +static ASN1_INTEGER * +PyLong_to_ASN1_INTEGER(PyObject *arg) +{ + PyObject *obj = NULL; + ASN1_INTEGER *a = NULL; + unsigned char buf[MAX_ASN1_INTEGER_LEN]; + size_t len; - case DER_FORMAT: - if (!i2d_X509_bio(out_bio, self->x509)) - lose("unable to write certificate"); - break; + memset(buf, 0, sizeof(buf)); - case PEM_FORMAT: - if (!PEM_write_bio_X509(out_bio, self->x509)) - lose("unable to write certificate"); - break; + /* + * Make sure argument is a PyLong small enough that its length (in + * bits!) doesn't overflow a size_t (which is a mis-use of size_t, + * but take that up with whoever wrote _PyLong_NumBits()...). + */ + if ((obj = PyNumber_Long(arg)) == NULL || + (len = _PyLong_NumBits(obj)) == (size_t) -1) + goto error; - default: - lose("internal error, unknown output format"); + /* + * Next make sure it's a non-negative integer small enough to fit in + * our buffer. If we really thought we needed to support larger + * integers we could allocate this dynamically, but we don't, so + * it's not worth the overhead. + * + * Paranoia: We can't convert len to bytes yet, because that + * requires rounding up and we don't know yet that we have enough + * headroom to do that arithmetic without overflowing a size_t. + */ + if (_PyLong_Sign(obj) < 0 || (len / 8) + 1 > sizeof(buf)) { + PyErr_SetObject(PyExc_OverflowError, obj); + goto error; } - if ((len = BIO_ctrl_pending(out_bio)) == 0) - lose("unable to get bytes stored in bio"); - - if ((buf = malloc(len)) == NULL) - lose("unable to allocate memory"); - - if (BIO_read(out_bio, buf, len) != len) - lose("unable to write out cert"); + /* + * Now that we know we're dealing with a sane number of bits, + * convert it to bytes. + */ + len = (len + 7) / 8; - cert = Py_BuildValue("s#", buf, len); + /* + * Extract that many bytes. + */ + if (_PyLong_AsByteArray((PyLongObject *) obj, buf, len, 0, 0) < 0) + goto error; - BIO_free(out_bio); - free(buf); - return cert; + /* + * We're done with the PyLong now. + */ + Py_XDECREF(obj); + obj = NULL; - error: + /* + * Generate the ASN1_INTEGER and return it. + */ + if ((a = ASN1_INTEGER_new()) == NULL || + (a->length < (int) len + 1 && (a->data = OPENSSL_realloc(a->data, len + 1)) == NULL)) + lose_no_memory(); - if (out_bio) - BIO_free(out_bio); + a->type = V_ASN1_INTEGER; + a->length = len; + a->data[len] = 0; + memcpy(a->data, buf, len); - if (buf) - free(buf); + return a; - Py_XDECREF(cert); + error: + Py_XDECREF(obj); + ASN1_INTEGER_free(a); return NULL; } -static char X509_object_pem_write__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>pemWrite</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a PEM encoded certificate as a\n" -" string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +/* + * Handle missing NIDs. + */ -static PyObject * -X509_object_pem_write(x509_object *self, PyObject *args) +static int +create_missing_nids(void) { - return X509_object_write_helper(self, args, PEM_FORMAT); -} + int i; -static char X509_object_der_write__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>derWrite</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a DER encoded certificate as a\n" -" string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + for (i = 0; i < (int) (sizeof(missing_nids) / sizeof(*missing_nids)); i++) + if ((*missing_nids[i].nid = OBJ_txt2nid(missing_nids[i].oid)) == NID_undef && + (*missing_nids[i].nid = OBJ_create(missing_nids[i].oid, + missing_nids[i].sn, + missing_nids[i].ln)) == NID_undef) + return 0; -static PyObject * -X509_object_der_write(x509_object *self, PyObject *args) -{ - return X509_object_write_helper(self, args, DER_FORMAT); + return 1; } -/* - * Currently this function only supports RSA keys. - */ -static char X509_object_set_public_key__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>setPublicKey</name>\n" -" <parameter>key</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method sets the public key for this certificate object. The\n" -" parameter <parameter>key</parameter> should be an instance of\n" -" <classname>Asymmetric</classname> containing a public key.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - - static PyObject * -X509_object_set_public_key(x509_object *self, PyObject *args) +ASN1_OBJECT_to_PyString(const ASN1_OBJECT *oid) { - EVP_PKEY *pkey = NULL; - asymmetric_object *asym; - - if (!PyArg_ParseTuple(args, "O!", &asymmetrictype, &asym)) - goto error; - - if ((pkey = EVP_PKEY_new()) == NULL) - lose("could not allocate memory"); + PyObject *result = NULL; + char buf[512]; - if (!EVP_PKEY_assign_RSA(pkey, asym->cipher)) - lose("EVP_PKEY assignment error"); + ENTERING(ASN1_OBJECT_to_PyString); - if (!X509_set_pubkey(self->x509,pkey)) - lose("could not set certificate's public key"); + if (OBJ_obj2txt(buf, sizeof(buf), oid, 1) <= 0) + lose_openssl_error("Couldn't translate OID"); - Py_RETURN_NONE; + result = PyString_FromString(buf); error: - - if (pkey) - EVP_PKEY_free(pkey); - - return NULL; - + return result; } -static char X509_object_sign__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>sign</name>\n" -" <parameter>key</parameter>\n" -" <optional><parameter>digest = MD5_DIGEST</parameter></optional>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method signs a certificate with a private key. See the\n" -" example for the methods which should be invoked before signing a\n" -" certificate. <parameter>key</parameter> should be an instance of\n" -" <classname>Asymmetric</classname> containing a private key.\n" -" The optional parameter <parameter>digest</parameter> indicates\n" -" which digest function should be used to compute the hash to be\n" -" signed, it should be one of the following:\n" -" </para>\n" -" <simplelist>\n" -#ifndef OPENSSL_NO_MD2 -" <member><constant>MD2_DIGEST</constant></member>\n" -#endif -" <member><constant>MD5_DIGEST</constant></member>\n" -" <member><constant>SHA_DIGEST</constant></member>\n" -" <member><constant>SHA1_DIGEST</constant></member>\n" -" <member><constant>RIPEMD160_DIGEST</constant></member>\n" -" <member><constant>SHA256_DIGEST</constant></member>\n" -" <member><constant>SHA384_DIGEST</constant></member>\n" -" <member><constant>SHA512_DIGEST</constant></member>\n" -" </simplelist>\n" -" </body>\n" -"</method>\n" -; + +/* + * IPAddress object. + */ static PyObject * -X509_object_sign(x509_object *self, PyObject *args) +ipaddress_object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - EVP_PKEY *pkey = NULL; - asymmetric_object *asym; - int digest = MD5_DIGEST; - - if (!PyArg_ParseTuple(args, "O!|i", &asymmetrictype, &asym, &digest)) - goto error; - - if ((pkey = EVP_PKEY_new()) == NULL) - lose("could not allocate memory"); - - if (asym->key_type != RSA_PRIVATE_KEY) - lose("cannot use this type of key"); + static char *kwlist[] = {"initializer", "version", NULL}; + ipaddress_object *self = NULL; + PyObject *init = NULL; + PyObject *pylong = NULL; + int version = 0; + const char *s = NULL; + int v; - if (!EVP_PKEY_assign_RSA(pkey, asym->cipher)) - lose("EVP_PKEY assignment error"); + ENTERING(ipaddress_object_new); - switch (digest) { - case MD5_DIGEST: - if (!X509_sign(self->x509, pkey, EVP_md5())) - lose("could not sign certificate"); - break; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, &init, &version) || + (self = (ipaddress_object *) type->tp_alloc(type, 0)) == NULL) + goto error; -#ifndef OPENSSL_NO_MD2 - case MD2_DIGEST: - if (!X509_sign(self->x509, pkey, EVP_md2())) - lose("could not sign certificate"); - break; -#endif + if (POW_IPAddress_Check(init)) { + ipaddress_object *src = (ipaddress_object *) init; + memcpy(self->address, src->address, sizeof(self->address)); + self->type = src->type; + return (PyObject *) self; + } - case SHA_DIGEST: - if (!X509_sign(self->x509, pkey, EVP_sha())) - lose("could not sign certificate"); - break; + if ((s = PyString_AsString(init)) == NULL) + PyErr_Clear(); + else if (version == 0) + version = strchr(s, ':') ? 6 : 4; - case SHA1_DIGEST: - if (!X509_sign(self->x509, pkey, EVP_sha1())) - lose("could not sign certificate"); - break; + self->type = NULL; - case RIPEMD160_DIGEST: - if (!X509_sign(self->x509, pkey, EVP_ripemd160())) - lose("could not sign certificate"); - break; + for (v = 0; v < (int) (sizeof(ipaddress_versions)/sizeof(*ipaddress_versions)); v++) + if ((unsigned) version == ipaddress_versions[v]->version) + self->type = ipaddress_versions[v]; - case SHA256_DIGEST: - if (!X509_sign(self->x509, pkey, EVP_sha256())) - lose("could not sign certificate"); - break; + if (self->type == NULL) + lose("Unknown IP version number"); - case SHA384_DIGEST: - if (!X509_sign(self->x509, pkey, EVP_sha384())) - lose("could not sign certificate"); - break; + if (s != NULL) { + if (inet_pton(self->type->af, s, self->address) <= 0) + lose("Couldn't parse IP address"); + return (PyObject *) self; + } - case SHA512_DIGEST: - if (!X509_sign(self->x509, pkey, EVP_sha512())) - lose("could not sign certificate"); - break; + if ((pylong = PyNumber_Long(init)) != NULL) { + if (_PyLong_AsByteArray((PyLongObject *) pylong, self->address, self->type->length, 0, 0) < 0) + goto error; + Py_XDECREF(pylong); + return (PyObject *) self; } - Py_RETURN_NONE; + lose_type_error("Couldn't convert initializer to IPAddress"); error: - - if (pkey) - EVP_PKEY_free(pkey); - + Py_XDECREF(self); + Py_XDECREF(pylong); return NULL; - } -static char X509_object_get_version__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>getVersion</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the version number from the version field of\n" -" this certificate.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - - static PyObject * -X509_object_get_version(x509_object *self, PyObject *args) +ipaddress_object_str(ipaddress_object *self) { - long version = 0; + char addrstr[sizeof("aaaa:bbbb:cccc:dddd:eeee:ffff:255.255.255.255") + 1]; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(ipaddress_object_str); - version = X509_get_version(self->x509); + if (!inet_ntop(self->type->af, self->address, addrstr, sizeof(addrstr))) + lose("Couldn't convert IP address"); - return Py_BuildValue("l", version); + return PyString_FromString(addrstr); error: - return NULL; } -static char X509_object_set_version__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>setVersion</name>\n" -" <parameter>version</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method sets the version number in the version field of\n" -" this certificate. <parameter>version</parameter> should be an\n" -" integer.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - static PyObject * -X509_object_set_version(x509_object *self, PyObject *args) +ipaddress_object_repr(ipaddress_object *self) { - long version = 0; + char addrstr[sizeof("aaaa:bbbb:cccc:dddd:eeee:ffff:255.255.255.255") + 1]; - if (!PyArg_ParseTuple(args, "l", &version)) - goto error; + ENTERING(ipaddress_object_repr); - if (!X509_set_version(self->x509, version)) - lose("could not set certificate version"); + if (!inet_ntop(self->type->af, self->address, addrstr, sizeof(addrstr))) + lose("Couldn't convert IP address"); - Py_RETURN_NONE; + return PyString_FromFormat("<%s object %s at %p>", + self->ob_type->tp_name, addrstr, self); error: - return NULL; } -static char X509_object_get_serial__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>getSerial</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method get the serial number in the serial field of\n" -" this certificate.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static int +ipaddress_object_compare(PyObject *arg1, PyObject *arg2) +{ + PyObject *obj1 = PyNumber_Long(arg1); + PyObject *obj2 = PyNumber_Long(arg2); + int cmp = -1; + + ENTERING(ipaddress_object_compare); + + if (obj1 != NULL && obj2 != NULL) + cmp = PyObject_Compare(obj1, obj2); + + Py_XDECREF(obj1); + Py_XDECREF(obj2); + return cmp; +} static PyObject * -X509_object_get_serial(x509_object *self, PyObject *args) +ipaddress_object_richcompare(PyObject *arg1, PyObject *arg2, int op) { - long serial = 0; - ASN1_INTEGER *asn1i = NULL; + PyObject *obj1 = PyNumber_Long(arg1); + PyObject *obj2 = PyNumber_Long(arg2); + PyObject *result = NULL; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(ipaddress_object_richcompare); - if ((asn1i = X509_get_serialNumber(self->x509)) == NULL) - lose("could not get serial number"); + if (obj1 != NULL && obj2 != NULL) + result = PyObject_RichCompare(obj1, obj2, op); + + Py_XDECREF(obj1); + Py_XDECREF(obj2); + return result; +} - if ((serial = ASN1_INTEGER_get(asn1i)) == -1) - lose("could not convert ASN1 Integer to long"); +static long +ipaddress_object_hash(ipaddress_object *self) +{ + unsigned long h = 0; + int i; - return Py_BuildValue("l", serial); + ENTERING(ipaddress_object_hash); - error: + for (i = 0; (unsigned) i < self->type->length; i++) + h ^= self->address[i] << ((i & 3) << 3); - return NULL; + return (long) h == -1 ? 0 : (long) h; } -static char X509_object_set_serial__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>setSerial</name>\n" -" <parameter>serial</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method sets the serial number in the serial field of\n" -" this certificate. <parameter>serial</parameter> should ba an\n" -" integer.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char ipaddress_object_from_bytes__doc__[] = + "Construct an IPAddress object from a sequence of bytes.\n" + "\n" + "Argument must be a Python string of exactly 4 or 16 bytes.\n" + ; static PyObject * -X509_object_set_serial(x509_object *self, PyObject *args) +ipaddress_object_from_bytes(PyTypeObject *type, PyObject *args) { - long serial = 0; - ASN1_INTEGER *asn1i = NULL; + ipaddress_object *result = NULL; + char *bytes = NULL; + size_t len; + int v; - if (!PyArg_ParseTuple(args, "l", &serial)) + ENTERING(ipaddress_object_from_bytes); + + if (!PyArg_ParseTuple(args, "s#", &bytes, &len)) goto error; - if ((asn1i = ASN1_INTEGER_new()) == NULL) - lose("could not allocate memory"); + if ((result = (ipaddress_object *) type->tp_alloc(type, 0)) == NULL) + goto error; - if (!ASN1_INTEGER_set(asn1i, serial)) - lose("could not set ASN1 integer"); + result->type = NULL; - if (!X509_set_serialNumber(self->x509, asn1i)) - lose("could not set certificate serial"); + for (v = 0; v < (int) (sizeof(ipaddress_versions)/sizeof(*ipaddress_versions)); v++) + if (len == ipaddress_versions[v]->length) + result->type = ipaddress_versions[v]; - ASN1_INTEGER_free(asn1i); + if (result->type == NULL) + lose("Unknown IP version number"); - Py_RETURN_NONE; + memcpy(result->address, bytes, len); error: + return (PyObject *) result; +} - if (asn1i) - ASN1_INTEGER_free(asn1i); +static char ipaddress_object_to_bytes__doc__[] = + "Return the binary value of this IPAddress as a Python string\n" + "of exactly 4 or 16 bytes.\n" + ; - return NULL; +static PyObject * +ipaddress_object_to_bytes(ipaddress_object *self) +{ + ENTERING(ipaddress_object_from_bytes); + return PyString_FromStringAndSize((char *) self->address, self->type->length); } -static char X509_object_get_issuer__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>getIssuer</name>\n" -" <parameter>format = SHORTNAME_FORMAT</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a tuple containing the issuers name. Each\n" -" element of the tuple is a tuple with 2 elements. The first tuple\n" -" is an object name and the second is it's value. Both issuer and\n" -" subject are names distinguished normally composed of a small\n" -" number of objects:\n" -" </para>\n" -" <simplelist>\n" -" <member><constant>c</constant> or <constant>countryName</constant></member>\n" -" <member><constant>st</constant> or <constant>stateOrProvinceName</constant></member>\n" -" <member><constant>o</constant> or <constant>organizationName</constant></member>\n" -" <member><constant>l</constant> or <constant>localityName</constant></member>\n" -" <member><constant>ou</constant> or <constant>organizationalUnitName</constant></member>\n" -" <member><constant>cn</constant> or <constant>commonName</constant></member>\n" -" </simplelist>\n" -" <para>\n" -" The data type varies from one object to another, however, all the\n" -" common objects are strings. It would be possible to specify any\n" -" kind of object but that would certainly adversely effect\n" -" portability and is not recommended.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - static PyObject * -X509_object_get_issuer(x509_object *self, PyObject *args) +ipaddress_object_get_bits(ipaddress_object *self, GCC_UNUSED void *closure) { - PyObject *result_list = NULL; - X509_NAME *name = NULL; - int format = SHORTNAME_FORMAT; + ENTERING(ipaddress_object_get_bits); + return PyInt_FromLong(self->type->length * 8); +} - if (!PyArg_ParseTuple(args, "|i", &format)) - goto error; +static PyObject * +ipaddress_object_get_version(ipaddress_object *self, GCC_UNUSED void *closure) +{ + ENTERING(ipaddress_object_get_version); + return PyInt_FromLong(self->type->version); +} - if ((name = X509_get_issuer_name(self->x509)) == NULL) - lose("could not get issuers name"); +static PyObject * +ipaddress_object_number_binary_helper(binaryfunc function, PyObject *arg1, PyObject *arg2) +{ + ipaddress_object *addr = NULL; + ipaddress_object *addr1 = NULL; + ipaddress_object *addr2 = NULL; + ipaddress_object *result = NULL; + PyObject *obj1 = NULL; + PyObject *obj2 = NULL; + PyObject *obj3 = NULL; + PyObject *obj4 = NULL; - if ((result_list = X509_object_helper_get_name(name, format)) == NULL) - lose("failed to produce name list"); + if (POW_IPAddress_Check(arg1)) + addr1 = (ipaddress_object *) arg1; - return result_list; + if (POW_IPAddress_Check(arg2)) + addr2 = (ipaddress_object *) arg2; - error: + if ((addr1 == NULL && addr2 == NULL) || + (addr1 != NULL && addr2 != NULL && addr1->type != addr2->type) || + (obj1 = PyNumber_Long(arg1)) == NULL || + (obj2 = PyNumber_Long(arg2)) == NULL) { + result = (ipaddress_object *) Py_NotImplemented; + goto error; + } - return NULL; -} + if ((obj3 = function(obj1, obj2)) == NULL) + goto error; -static char X509_object_get_subject__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>getSubject</name>\n" -" <parameter>format = SHORTNAME_FORMAT</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a tuple containing the subjects name. See\n" -" <function>getIssuer</function> for a description of the returned\n" -" object's format.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + if ((obj4 = PyNumber_Long(obj3)) == NULL) + lose("Couldn't convert result"); -static PyObject * -X509_object_get_subject(x509_object *self, PyObject *args) -{ - PyObject *result_list = NULL; - X509_NAME *name = NULL; - int format = SHORTNAME_FORMAT; + addr = addr1 != NULL ? addr1 : addr2; - if (!PyArg_ParseTuple(args, "|i", &format)) + if ((result = (ipaddress_object *) addr->ob_type->tp_alloc(addr->ob_type, 0)) == NULL) goto error; - if ((name = X509_get_subject_name(self->x509)) == NULL) - lose("could not get issuers name"); - - if ((result_list = X509_object_helper_get_name(name, format)) == NULL) - lose("failed to produce name list"); + result->type = addr->type; - return result_list; + if (_PyLong_AsByteArray((PyLongObject *) obj4, result->address, result->type->length, 0, 0) < 0) { + Py_XDECREF(result); + result = NULL; + } - error: + error: /* Fall through */ + Py_XDECREF(obj1); + Py_XDECREF(obj2); + Py_XDECREF(obj3); + Py_XDECREF(obj4); - return NULL; + return (PyObject *) result; } -static char X509_object_set_subject__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>setSubject</name>\n" -" <parameter>name</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to set the subjects name.\n" -" <parameter>name</parameter> can be comprised of lists or tuples in\n" -" the format described in the <function>getIssuer</function> method.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - static PyObject * -X509_object_set_subject(x509_object *self, PyObject *args) +ipaddress_object_number_long(PyObject *arg) { - PyObject *name_sequence = NULL; - X509_NAME *name = NULL; + ipaddress_object *addr = (ipaddress_object *) arg; - if (!PyArg_ParseTuple(args, "O", &name_sequence)) - goto error; - - if (!PyTuple_Check(name_sequence) && !PyList_Check(name_sequence)) - lose_type_error("Inapropriate type"); + ENTERING(ipaddress_object_number_long); - if ((name = X509_NAME_new()) == NULL) - lose("could not allocate memory"); + if (!POW_IPAddress_Check(arg)) + return Py_NotImplemented; - if (!X509_object_helper_set_name(name, name_sequence)) - lose("unable to set new name"); + return _PyLong_FromByteArray(addr->address, addr->type->length, 0, 0); +} - if (!X509_set_subject_name(self->x509, name)) - lose("unable to set name"); +static PyObject * +ipaddress_object_number_int(PyObject *arg) +{ + ENTERING(ipaddress_object_number_int); + return ipaddress_object_number_long(arg); +} - X509_NAME_free(name); +static PyObject * +ipaddress_object_number_add(PyObject *arg1, PyObject *arg2) +{ + ENTERING(ipaddress_object_number_add); + return ipaddress_object_number_binary_helper(PyNumber_Add, arg1, arg2); +} - Py_RETURN_NONE; +static PyObject * +ipaddress_object_number_subtract(PyObject *arg1, PyObject *arg2) +{ + ENTERING(ipaddress_object_number_subtract); + return ipaddress_object_number_binary_helper(PyNumber_Subtract, arg1, arg2); +} - error: +static PyObject * +ipaddress_object_number_lshift(PyObject *arg1, PyObject *arg2) +{ + ENTERING(ipaddress_object_number_lshift); + return ipaddress_object_number_binary_helper(PyNumber_Lshift, arg1, arg2); +} - return NULL; +static PyObject * +ipaddress_object_number_rshift(PyObject *arg1, PyObject *arg2) +{ + ENTERING(ipaddress_object_number_rshift); + return ipaddress_object_number_binary_helper(PyNumber_Rshift, arg1, arg2); } -static char X509_object_set_issuer__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>setIssuer</name>\n" -" <parameter>name</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to set the issuers name.\n" -" <parameter>name</parameter> can be comprised of lists or tuples in\n" -" the format described in the <function>getissuer</function> method.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static PyObject * +ipaddress_object_number_and(PyObject *arg1, PyObject *arg2) +{ + ENTERING(ipaddress_object_number_and); + return ipaddress_object_number_binary_helper(PyNumber_And, arg1, arg2); +} static PyObject * -X509_object_set_issuer(x509_object *self, PyObject *args) +ipaddress_object_number_xor(PyObject *arg1, PyObject *arg2) { - PyObject *name_sequence = NULL; - X509_NAME *name = NULL; + ENTERING(ipaddress_object_number_xor); + return ipaddress_object_number_binary_helper(PyNumber_Xor, arg1, arg2); +} - if (!PyArg_ParseTuple(args, "O", &name_sequence)) - goto error; +static PyObject * +ipaddress_object_number_or(PyObject *arg1, PyObject *arg2) +{ + ENTERING(ipaddress_object_number_or); + return ipaddress_object_number_binary_helper(PyNumber_Or, arg1, arg2); +} - if (!PyTuple_Check(name_sequence) && !PyList_Check(name_sequence)) - lose_type_error("Inapropriate type"); +static int +ipaddress_object_number_nonzero(ipaddress_object *self) +{ + int i; - if ((name = X509_NAME_new()) == NULL) - lose("could not allocate memory"); + ENTERING(ipaddress_object_number_nonzero); - if (!X509_object_helper_set_name(name, name_sequence)) - lose("unable to set new name"); + for (i = 0; (unsigned) i < self->type->length; i++) + if (self->address[i] != 0) + return 1; + return 0; +} - if (!X509_set_issuer_name(self->x509,name)) - lose("unable to set name"); +static PyObject * +ipaddress_object_number_invert(ipaddress_object *self) +{ + ipaddress_object *result = NULL; + int i; - X509_NAME_free(name); + ENTERING(ipaddress_object_number_invert); - Py_RETURN_NONE; + if ((result = (ipaddress_object *) self->ob_type->tp_alloc(self->ob_type, 0)) == NULL) + goto error; - error: + result->type = self->type; - if (name) - X509_NAME_free(name); + for (i = 0; (unsigned) i < self->type->length; i++) + result->address[i] = ~self->address[i]; - return NULL; + error: /* Fall through */ + return (PyObject *) result; } -static char X509_object_get_not_before__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>getNotBefore</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this function returns a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" - -; +static char ipaddress_object_copy__doc__[] = + "" + ; static PyObject * -X509_object_get_not_before (x509_object *self, PyObject *args) +ipaddress_object_copy(ipaddress_object *self, GCC_UNUSED PyObject *args) { - if (!PyArg_ParseTuple(args, "")) + ipaddress_object *result = NULL; + + ENTERING(ipaddress_object_copy); + + if ((result = (ipaddress_object *) self->ob_type->tp_alloc(self->ob_type, 0)) == NULL) goto error; - return ASN1_TIME_to_Python(self->x509->cert_info->validity->notBefore); + memcpy(result->address, self->address, sizeof(result->address)); + result->type = self->type; error: - - return NULL; + return (PyObject *) result; } -static char X509_object_get_not_after__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>getNotAfter</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this function returns a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static struct PyMethodDef ipaddress_object_methods[] = { + Define_Method(__copy__, ipaddress_object_copy, METH_VARARGS), + Define_Method(__deepcopy__, ipaddress_object_copy, METH_VARARGS), + Define_Method(toBytes, ipaddress_object_to_bytes, METH_NOARGS), + Define_Class_Method(fromBytes, ipaddress_object_from_bytes, METH_VARARGS), + {NULL} +}; + +static PyGetSetDef ipaddress_object_getsetters[] = { + {"bits", (getter) ipaddress_object_get_bits}, + {"version", (getter) ipaddress_object_get_version}, + {NULL} +}; + +static PyNumberMethods ipaddress_NumberMethods = { + ipaddress_object_number_add, /* nb_add */ + ipaddress_object_number_subtract, /* nb_subtract */ + 0, /* nb_multiply */ + 0, /* nb_divide */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ + (inquiry) ipaddress_object_number_nonzero, /* nb_nonzero */ + (unaryfunc) ipaddress_object_number_invert, /* nb_invert */ + ipaddress_object_number_lshift, /* nb_lshift */ + ipaddress_object_number_rshift, /* nb_rshift */ + ipaddress_object_number_and, /* nb_and */ + ipaddress_object_number_xor, /* nb_xor */ + ipaddress_object_number_or, /* nb_or */ + 0, /* nb_coerce */ + ipaddress_object_number_int, /* nb_int */ + ipaddress_object_number_long, /* nb_long */ + 0, /* nb_float */ + 0, /* nb_oct */ + 0, /* nb_hex */ + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ + 0, /* nb_inplace_divide */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + 0, /* nb_floor_divide */ + 0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ + 0, /* nb_index */ +}; + +static PyTypeObject POW_IPAddress_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "rpki.POW.IPAddress", /* tp_name */ + sizeof(ipaddress_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + ipaddress_object_compare, /* tp_compare */ + (reprfunc) ipaddress_object_repr, /* tp_repr */ + &ipaddress_NumberMethods, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc) ipaddress_object_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc) ipaddress_object_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + ipaddress_object_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ipaddress_object_methods, /* tp_methods */ + 0, /* tp_members */ + ipaddress_object_getsetters, /* 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 */ + ipaddress_object_new, /* tp_new */ +}; + + + +/* + * X509 object. + */ static PyObject * -X509_object_get_not_after (x509_object *self, PyObject *args) +x509_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds) { - if (!PyArg_ParseTuple(args, "")) - goto error; + x509_object *self; - return ASN1_TIME_to_Python(self->x509->cert_info->validity->notAfter); + ENTERING(x509_object_new); - error: + if ((self = (x509_object *) type->tp_alloc(type, 0)) != NULL && + (self->x509 = X509_new()) != NULL) + return (PyObject *) self; + Py_XDECREF(self); return NULL; } -static char X509_object_set_not_after__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>setNotAfter</name>\n" -" <parameter>time</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this accepts one parameter, a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static void +x509_object_dealloc(x509_object *self) +{ + ENTERING(x509_object_dealloc); + X509_free(self->x509); + self->ob_type->tp_free((PyObject*) self); +} static PyObject * -X509_object_set_not_after (x509_object *self, PyObject *args) +x509_object_pem_read_helper(PyTypeObject *type, BIO *bio) { - char *new_time = NULL; + x509_object *self = NULL; + + ENTERING(x509_object_pem_read_helper); - if (!PyArg_ParseTuple(args, "s", &new_time)) + if ((self = (x509_object *) x509_object_new(type, NULL, NULL)) == NULL) goto error; - if (!python_ASN1_TIME_set_string(self->x509->cert_info->validity->notAfter, new_time)) - lose("Could not set notAfter"); + if (!PEM_read_bio_X509(bio, &self->x509, NULL, NULL)) + lose_openssl_error("Couldn't load PEM encoded certificate"); - Py_RETURN_NONE; + return (PyObject *) self; error: + Py_XDECREF(self); return NULL; } -static char X509_object_set_not_before__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>setNotBefore</name>\n" -" <parameter>time</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this accepts one parameter, a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - static PyObject * -X509_object_set_not_before (x509_object *self, PyObject *args) +x509_object_der_read_helper(PyTypeObject *type, BIO *bio) { - char *new_time = NULL; + x509_object *self; - if (!PyArg_ParseTuple(args, "s", &new_time)) + ENTERING(x509_object_der_read_helper); + + if ((self = (x509_object *) x509_object_new(type, NULL, NULL)) == NULL) goto error; - if (!python_ASN1_TIME_set_string(self->x509->cert_info->validity->notBefore, new_time)) - lose("Could not set notBefore"); + if (!d2i_X509_bio(bio, &self->x509)) + lose_openssl_error("Couldn't load DER encoded certificate"); - Py_RETURN_NONE; + return (PyObject *) self; error: - + Py_XDECREF(self); return NULL; } -static char X509_object_add_extension__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>addExtension</name>\n" -" <parameter>extensionName</parameter>\n" -" <parameter>critical</parameter>\n" -" <parameter>extensionValue</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method adds an extension to this certificate.\n" -" <parameter>extensionName</parameter> should be the of the\n" -" extension. <parameter>critical</parameter> should an integer, 1\n" -" for true and 0 for false. <parameter>extensionValue</parameter>\n" -" should be a string, DER encoded value of the extension. The name\n" -" of the extension must be correct according to OpenSSL and can be\n" -" checked in the <constant>objects.h</constant> header file, part of\n" -" the OpenSSL source distribution. In the majority of cases they\n" -" are the same as those defined in <constant>POW._oids</constant>\n" -" but if you do encounter problems is may be worth checking.\n" -" </para>\n" -" <example>\n" -" <title><function>addExtension</function> method usage</title>\n" -" <programlisting>\n" -" basic = POW.pkix.BasicConstraints()\n" -" basic.set([1,5])\n" -" serverCert.addExtension('basicConstraints', 0, basic.toString())\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_pem_read__doc__[] = + "Read a PEM-encoded X.509 object from a string.\n" + ; static PyObject * -X509_object_add_extension(x509_object *self, PyObject *args) +x509_object_pem_read(PyTypeObject *type, PyObject *args) { - int critical = 0, nid = 0, len = 0; - char *name = NULL; - unsigned char *buf = NULL; - ASN1_OCTET_STRING *octetString = NULL; - X509_EXTENSION *extn = NULL; + ENTERING(x509_object_pem_read); + return read_from_string_helper(x509_object_pem_read_helper, type, args); +} - if (!PyArg_ParseTuple(args, "sis#", &name, &critical, &buf, &len)) - goto error; +static char x509_object_pem_read_file__doc__[] = + "Read a PEM-encoded X.509 object from a file.\n" + ; - if ((octetString = M_ASN1_OCTET_STRING_new()) == NULL) - lose("could not allocate memory"); +static PyObject * +x509_object_pem_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(x509_object_pem_read_file); + return read_from_file_helper(x509_object_pem_read_helper, type, args); +} - if (!ASN1_OCTET_STRING_set(octetString, buf, len)) - lose("could not set ASN1 Octect string"); +static char x509_object_der_read__doc__[] = + "Read a DER-encoded X.509 object from a string.\n" + ; - if ((nid = OBJ_txt2nid(name)) == NID_undef) - lose("extension has unknown object identifier"); +static PyObject * +x509_object_der_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(x509_object_der_read); + return read_from_string_helper(x509_object_der_read_helper, type, args); +} - if ((extn = X509_EXTENSION_create_by_NID(NULL, nid, critical, octetString)) == NULL) - lose("unable to create ASN1 X509 Extension object"); +static char x509_object_der_read_file__doc__[] = + "Read a DER-encoded X.509 object from a file.\n" + ; - if (!self->x509->cert_info->extensions && - (self->x509->cert_info->extensions = sk_X509_EXTENSION_new_null()) == NULL) - lose("unable to allocate memory"); +static PyObject * +x509_object_der_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(x509_object_der_read_file); + return read_from_file_helper(x509_object_der_read_helper, type, args); +} - if (!sk_X509_EXTENSION_push(self->x509->cert_info->extensions, extn)) - lose("unable to add extension"); +static char x509_object_pem_write__doc__[] = + "Return the PEM encoding of this certificate, as a string.\n" + ; - Py_RETURN_NONE; +static PyObject * +x509_object_pem_write(x509_object *self) +{ + PyObject *result = NULL; + BIO *bio = NULL; - error: + ENTERING(x509_object_pem_write); - if (extn) - X509_EXTENSION_free(extn); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - return NULL; + if (!PEM_write_bio_X509(bio, self->x509)) + lose_openssl_error("Unable to write certificate"); + + result = BIO_to_PyString_helper(bio); + + error: /* Fall through */ + BIO_free(bio); + return result; } -static char X509_object_clear_extensions__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>clearExtensions</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method clears the structure which holds the extension for\n" -" this certificate.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_der_write__doc__[] = + "Return the DER encoding of this certificate, as a string.\n" + ; static PyObject * -X509_object_clear_extensions(x509_object *self, PyObject *args) +x509_object_der_write(x509_object *self) { - if (!PyArg_ParseTuple(args, "")) - goto error; + PyObject *result = NULL; + BIO *bio = NULL; - if (self->x509->cert_info->extensions) { - sk_X509_EXTENSION_free(self->x509->cert_info->extensions); - self->x509->cert_info->extensions = NULL; - } + ENTERING(x509_object_der_write); - Py_RETURN_NONE; + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - error: + if (!i2d_X509_bio(bio, self->x509)) + lose_openssl_error("Unable to write certificate"); - return NULL; + result = BIO_to_PyString_helper(bio); + + error: /* Fall through */ + BIO_free(bio); + return result; } -static char X509_object_count_extensions__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>countExtensions</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the size of the structure which holds the\n" -" extension for this certificate.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_public_key__doc__[] = + "Return the public key from this certificate object,\n" + "as an Asymmetric object.\n" + ; static PyObject * -X509_object_count_extensions(x509_object *self, PyObject *args) +x509_object_get_public_key(x509_object *self) { - int num = 0; + PyTypeObject *type = &POW_Asymmetric_Type; + asymmetric_object *asym = NULL; + + ENTERING(x509_object_get_public_key); - if (!PyArg_ParseTuple(args, "")) + if ((asym = (asymmetric_object *) type->tp_alloc(type, 0)) == NULL) goto error; - if (self->x509->cert_info->extensions) - num = sk_X509_EXTENSION_num(self->x509->cert_info->extensions); + if ((asym->pkey = X509_get_pubkey(self->x509)) == NULL) + lose_openssl_error("Couldn't extract public key from certificate"); - return Py_BuildValue("i", num); + return (PyObject *) asym; error: - + Py_XDECREF(asym); return NULL; } -static char X509_object_get_extension__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>getExtension</name>\n" -" <parameter>index</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a tuple equivalent the parameters of\n" -" <function>addExtension</function>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_public_key__doc__[] = + "Set the public key of this certificate object.\n" + "\n" + "The \"key\" parameter should be an instance of the Asymmetric class,\n" + "containing a public key.\n" + ; static PyObject * -X509_object_get_extension(x509_object *self, PyObject *args) +x509_object_set_public_key(x509_object *self, PyObject *args) { - int num = 0, index = 0, ext_nid = 0; - char const *ext_ln = NULL; - char unknown_ext [] = "unknown"; - X509_EXTENSION *ext; + asymmetric_object *asym; + + ENTERING(x509_object_set_public_key); - if (!PyArg_ParseTuple(args, "i", &index)) + if (!PyArg_ParseTuple(args, "O!", &POW_Asymmetric_Type, &asym)) goto error; - if (self->x509->cert_info->extensions) - num = sk_X509_EXTENSION_num(self->x509->cert_info->extensions); + if (!X509_set_pubkey(self->x509, asym->pkey)) + lose_openssl_error("Couldn't set certificate's public key"); - if (index >= num) - lose("certificate does not have that many extensions"); + Py_RETURN_NONE; - if ((ext = sk_X509_EXTENSION_value(self->x509->cert_info->extensions, index)) == NULL) - lose("could not get extension"); + error: + return NULL; +} - if ((ext_nid = OBJ_obj2nid(ext->object)) == NID_undef) - lose("extension has unknown object identifier"); +static char x509_object_sign__doc__[] = + "Sign a certificate 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 * +x509_object_sign(x509_object *self, PyObject *args) +{ + asymmetric_object *asym; + int digest_type = SHA256_DIGEST; + const EVP_MD *digest_method = NULL; - if ((ext_ln = OBJ_nid2sn(ext_nid)) == NULL) - ext_ln = unknown_ext; + ENTERING(x509_object_sign); - return Py_BuildValue("sis#", ext_ln, ext->critical, ext->value->data, ext->value->length); + if (!PyArg_ParseTuple(args, "O!|i", &POW_Asymmetric_Type, &asym, &digest_type)) + goto error; - error: + if ((digest_method = evp_digest_factory(digest_type)) == NULL) + lose("Unsupported digest algorithm"); + + if (!X509_sign(self->x509, asym->pkey, digest_method)) + lose_openssl_error("Couldn't sign certificate"); + + Py_RETURN_NONE; + error: return NULL; } -static char x509_object_pprint__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" <name>pprint</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a formatted string showing the information\n" -" held in the certificate.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_version__doc__[] = + "Return version number of this certificate.\n" + ; static PyObject * -x509_object_pprint(x509_object *self, PyObject *args) +x509_object_get_version(x509_object *self) { - int len = 0, ret = 0; - char *buf = NULL; - BIO *out_bio = NULL; - PyObject *cert = NULL; - - if (!PyArg_ParseTuple(args, "")) - goto error; - - out_bio = BIO_new(BIO_s_mem()); + ENTERING(x509_object_get_version); + return Py_BuildValue("l", X509_get_version(self->x509)); +} - if (!X509_print(out_bio, self->x509)) - lose("unable to write crl"); +static char x509_object_set_version__doc__[] = + "Set version number of this certificate.\n" + "\n" + "The \"version\" parameter should be an integer.\n" + ; - if ((len = BIO_ctrl_pending(out_bio)) == 0) - lose("unable to get bytes stored in bio"); +static PyObject * +x509_object_set_version(x509_object *self, PyObject *args) +{ + long version = 0; - if ((buf = malloc(len)) == NULL) - lose("unable to allocate memory"); + ENTERING(x509_object_set_version); - if ((ret = BIO_read(out_bio, buf, len)) != len) - lose("unable to write out cert"); + if (!PyArg_ParseTuple(args, "l", &version)) + goto error; - cert = Py_BuildValue("s#", buf, len); + if (!X509_set_version(self->x509, version)) + lose("Couldn't set certificate version"); - BIO_free(out_bio); - free(buf); - return cert; + Py_RETURN_NONE; error: - if (out_bio) - BIO_free(out_bio); - - if (buf) - free(buf); - return NULL; - } -static struct PyMethodDef X509_object_methods[] = { - {"pemWrite", (PyCFunction)X509_object_pem_write, METH_VARARGS, NULL}, - {"derWrite", (PyCFunction)X509_object_der_write, METH_VARARGS, NULL}, - {"sign", (PyCFunction)X509_object_sign, METH_VARARGS, NULL}, - {"setPublicKey", (PyCFunction)X509_object_set_public_key, METH_VARARGS, NULL}, - {"getVersion", (PyCFunction)X509_object_get_version, METH_VARARGS, NULL}, - {"setVersion", (PyCFunction)X509_object_set_version, METH_VARARGS, NULL}, - {"getSerial", (PyCFunction)X509_object_get_serial, METH_VARARGS, NULL}, - {"setSerial", (PyCFunction)X509_object_set_serial, METH_VARARGS, NULL}, - {"getIssuer", (PyCFunction)X509_object_get_issuer, METH_VARARGS, NULL}, - {"setIssuer", (PyCFunction)X509_object_set_issuer, METH_VARARGS, NULL}, - {"getSubject", (PyCFunction)X509_object_get_subject, METH_VARARGS, NULL}, - {"setSubject", (PyCFunction)X509_object_set_subject, METH_VARARGS, NULL}, - {"getNotBefore", (PyCFunction)X509_object_get_not_before, METH_VARARGS, NULL}, - {"getNotAfter", (PyCFunction)X509_object_get_not_after, METH_VARARGS, NULL}, - {"setNotAfter", (PyCFunction)X509_object_set_not_after, METH_VARARGS, NULL}, - {"setNotBefore", (PyCFunction)X509_object_set_not_before, METH_VARARGS, NULL}, - {"addExtension", (PyCFunction)X509_object_add_extension, METH_VARARGS, NULL}, - {"clearExtensions", (PyCFunction)X509_object_clear_extensions, METH_VARARGS, NULL}, - {"countExtensions", (PyCFunction)X509_object_count_extensions, METH_VARARGS, NULL}, - {"getExtension", (PyCFunction)X509_object_get_extension, METH_VARARGS, NULL}, - {"pprint", (PyCFunction)x509_object_pprint, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ -}; +static char x509_object_get_serial__doc__[] = + "Return the serial number of this certificate.\n" + ; static PyObject * -X509_object_getattr(x509_object *self, char *name) +x509_object_get_serial(x509_object *self) { - return Py_FindMethod(X509_object_methods, (PyObject *)self, name); + ENTERING(x509_object_get_serial); + return Py_BuildValue("N", ASN1_INTEGER_to_PyLong(X509_get_serialNumber(self->x509))); } -static void -X509_object_dealloc(x509_object *self, char *name) -{ - X509_free(self->x509); - PyObject_Del(self); -} - -static char x509type__doc__[] = -"<class>\n" -" <header>\n" -" <name>X509</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides access to a significant proportion of X509\n" -" functionality of OpenSSL.\n" -" </para>\n" -"\n" -" <example>\n" -" <title><classname>x509</classname> class usage</title>\n" -" <programlisting>\n" -" privateFile = open('test/private.key', 'r')\n" -" publicFile = open('test/public.key', 'r')\n" -" certFile = open('test/cacert.pem', 'w')\n" -"\n" -" publicKey = POW.pemRead(POW.RSA_PUBLIC_KEY, publicFile.read())\n" -" privateKey = POW.pemRead(POW.RSA_PRIVATE_KEY, privateFile.read(), 'pass')\n" -"\n" -" c = POW.X509()\n" -"\n" -" name = [ ['C', 'GB'], ['ST', 'Hertfordshire'],\n" -" ['O','The House'], ['CN', 'Peter Shannon'] ]\n" -"\n" -" c.setIssuer(name)\n" -" c.setSubject(name)\n" -" c.setSerial(0)\n" -" t1 = POW.pkix.time2utc(time.time())\n" -" t2 = POW.pkix.time2utc(time.time() + 60*60*24*365)\n" -" c.setNotBefore(t1)\n" -" c.setNotAfter(t2)\n" -" c.setPublicKey(publicKey)\n" -" c.sign(privateKey)\n" -"\n" -" certFile.write(c.pemWrite())\n" -"\n" -" privateFile.close()\n" -" publicFile.close()\n" -" certFile.close()\n" -" </programlisting>\n" -" </example>\n" -"\n" -" </body>\n" -"</class>\n" -; - -static PyTypeObject x509type = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "X509", /*tp_name*/ - sizeof(x509_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)X509_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)X509_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - x509type__doc__ /* Documentation string */ -}; -/*========== X509 Code ==========*/ +static char x509_object_set_serial__doc__[] = + "Set the serial number of this certificate.\n" + "\n" + "The \"serial\" parameter should ba an integer.\n" + ; -/*========== x509 store Code ==========*/ -static x509_store_object * -x509_store_object_new(void) +static PyObject * +x509_object_set_serial(x509_object *self, PyObject *args) { - x509_store_object *self = NULL; + ASN1_INTEGER *a_serial = NULL; + PyObject *p_serial = NULL; + int ok = 0; - if ((self = PyObject_New(x509_store_object, &x509_storetype)) == NULL) + ENTERING(x509_object_set_serial); + + if (!PyArg_ParseTuple(args, "O", &p_serial) || + (a_serial = PyLong_to_ASN1_INTEGER(p_serial)) == NULL) goto error; - self->store = X509_STORE_new(); + if (!X509_set_serialNumber(self->x509, a_serial)) + lose_no_memory(); - return self; + ok = 1; error: + ASN1_INTEGER_free(a_serial); - Py_XDECREF(self); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char x509_store_object_verify__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Store</memberof>\n" -" <name>verify</name>\n" -" <parameter>certificate</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" The <classname>X509Store</classname> method\n" -" <function>verify</function> is based on the\n" -" <function>X509_verify_cert</function>. It handles certain aspects\n" -" of verification but not others. The certificate will be verified\n" -" against <constant>notBefore</constant>,\n" -" <constant>notAfter</constant> and trusted certificates.\n" -" It crucially will not handle checking the certificate against\n" -" CRLs. This functionality will probably make it into OpenSSL\n" -" 0.9.7.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - -static PyObject * -x509_store_object_verify(x509_store_object *self, PyObject *args) +static char x509_object_get_issuer__doc__[] = + "Return this certificate's issuer name, represented as a tuple.\n" + "\n" + "Each element of this tuple is another tuple representing one\n" + "\"Relative Distinguished Name\" (RDN), each element of which in turn\n" + "is yet another tuple representing one AttributeTypeAndValue pair.\n" + "\n" + "In practice, RDNs containing multiple attributes are rare, thus the RDN\n" + "tuples will usually be exactly one element long, but using the\n" + "tuple-of-tuples-of-tuples format lets us represent the general case.\n" + "\n" + "The AttributeTypeANdValue pairs are two-element tuples, the first\n" + "element of which is a string representing an Object Identifier (OID),\n" + "the second of which contains the attribute value.\n" + "\n" + "This method takes an optional \"format\" parameter which controls\n" + "the format in which OIDs are returned. Allowed values are:\n" + "\n" + " * SHORTNAME_FORMAT (the OpenSSL \"short name\" for this OID)\n" + " * LONGNAME_FORMAT (the OpenSSL \"long name\" for this OID)\n" + " * OIDNAME_FORMAT (the OID in dotted decimal numeric format)\n" + "\n" + "The default is OIDNAME_FORMAT.\n" + "\n" + "See RFC 5280 section 4.1.2.4 for details of the ASN.1 structure.\n" + ; + +static PyObject * +x509_object_get_issuer(x509_object *self, PyObject *args) { - X509_STORE_CTX csc; - x509_object *x509 = NULL; - int ok; + PyObject *result = NULL; + int format = OIDNAME_FORMAT; + + ENTERING(x509_object_get_issuer); - if (!PyArg_ParseTuple(args, "O!", &x509type, &x509)) + if (!PyArg_ParseTuple(args, "|i", &format)) goto error; - X509_STORE_CTX_init(&csc, self->store, x509->x509, NULL); - ok = X509_verify_cert(&csc) == 1; - X509_STORE_CTX_cleanup(&csc); + result = x509_object_helper_get_name(X509_get_issuer_name(self->x509), + format); - return PyBool_FromLong(ok); + error: /* Fall through */ + return result; +} - error: +static char x509_object_get_subject__doc__[] = + "Return this certificate's subject name, as a tuple.\n" + "\n" + "See the documentation for the \"getIssuer\" method for details on the\n" + "structure of the return value and use of the optional \"format\"\n" + "parameter.\n" + ; - return NULL; +static PyObject * +x509_object_get_subject(x509_object *self, PyObject *args) +{ + PyObject *result = NULL; + int format = OIDNAME_FORMAT; + + ENTERING(x509_object_get_subject); + + if (!PyArg_ParseTuple(args, "|i", &format)) + goto error; + + result = x509_object_helper_get_name(X509_get_subject_name(self->x509), + format); + + error: /* Fall through */ + return result; } -static char x509_store_object_verify_chain__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Store</memberof>\n" -" <name>verifyChain</name>\n" -" <parameter>certificate</parameter>\n" -" <parameter>chain</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" The <classname>X509Store</classname> method <function>verifyChain</function>\n" -" is based on the <function>X509_verify_cert</function> but is initialised\n" -" with a <classname>X509</classname> object to verify and list of\n" -" <classname>X509</classname> objects which form a chain to a trusted\n" -" certificate. Certain aspects of the verification are handled but not others.\n" -" The certificates will be verified against <constant>notBefore</constant>,\n" -" <constant>notAfter</constant> and trusted certificates. It crucially will\n" -" not handle checking the certificate against CRLs. This functionality will\n" -" probably make it into OpenSSL 0.9.7.\n" -" </para>\n" -" <para>\n" -" This may all sound quite straight forward but determining the\n" -" certificate associated with the signature on another certificate\n" -" can be very time consuming. The management aspects of\n" -" certificates are addressed by various V3 extensions which are not\n" -" currently supported.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_subject__doc__[] = + "Set this certificate's subject name.\n" + "\n" + "The \"name\" parameter should be in the same format as the return\n" + "value from the \"getIssuer\" method.\n" + ; static PyObject * -x509_store_object_verify_chain(x509_store_object *self, PyObject *args) +x509_object_set_subject(x509_object *self, PyObject *args) { - PyObject *x509_sequence = NULL; - X509_STORE_CTX csc; - x509_object *x509 = NULL; - STACK_OF(X509) *x509_stack = NULL; - int ok; + PyObject *name_sequence = NULL; + X509_NAME *name = NULL; - if (!PyArg_ParseTuple(args, "O!O", &x509type, &x509, &x509_sequence)) - goto error; + ENTERING(x509_object_set_subject); - if ((x509_stack = x509_helper_sequence_to_stack(x509_sequence)) == NULL) + if (!PyArg_ParseTuple(args, "O", &name_sequence)) goto error; - X509_STORE_CTX_init(&csc, self->store, x509->x509, x509_stack); - - ok = X509_verify_cert(&csc) == 1; + if (!PySequence_Check(name_sequence)) + lose_type_error("Inapropriate type"); - X509_STORE_CTX_cleanup(&csc); - sk_X509_free(x509_stack); + if ((name = x509_object_helper_set_name(name_sequence)) == NULL) + goto error; - return PyBool_FromLong(ok); + if (!X509_set_subject_name(self->x509, name)) + lose("Unable to set subject name"); - error: + X509_NAME_free(name); - if (x509_stack) - sk_X509_free(x509_stack); + Py_RETURN_NONE; + error: + X509_NAME_free(name); return NULL; } -static char x509_store_object_verify_detailed__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Store</memberof>\n" -" <name>verifyDetailed</name>\n" -" <parameter>certificate</parameter>\n" -" <optional>\n" -" <parameter>chain</parameter>\n" -" </optional>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" The <classname>X509Store</classname> method <function>verifyDetailed</function>\n" -" is based on the <function>X509_verify_cert</function> but is initialised\n" -" with a <classname>X509</classname> object to verify and list of\n" -" <classname>X509</classname> objects which form a chain to a trusted\n" -" certificate. Certain aspects of the verification are handled but not others.\n" -" The certificates will be verified against <constant>notBefore</constant>,\n" -" <constant>notAfter</constant> and trusted certificates. It crucially will\n" -" not handle checking the certificate against CRLs. This functionality will\n" -" probably make it into OpenSSL 0.9.7.\n" -" </para>\n" -" <para>\n" -" This may all sound quite straight forward but determining the\n" -" certificate associated with the signature on another certificate\n" -" can be very time consuming. The management aspects of\n" -" certificates are addressed by various V3 extensions which are not\n" -" currently supported.\n" -" </para>\n" -" <para>\n" -" Unlike the <function>verify</function> and <function>verifyChain</function>\n" -" methods, <function>verifyDetailed</function> returns some information about\n" -" what went wrong when verification fails. The return value is currently a 3-tuple:\n" -" the first value is the return value from X509_verify_cert(), the second and third\n" -" are the error and error_depth values from the X509_STORE_CTX.\n" -" Other values may added to this tuple later.\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_issuer__doc__[] = + "Set this certificate's issuer name.\n" + "\n" + "The \"name\" parameter should be in the same format as the return\n" + "value from the \"getIssuer\" method.\n" + ; static PyObject * -x509_store_object_verify_detailed(x509_store_object *self, PyObject *args) +x509_object_set_issuer(x509_object *self, PyObject *args) { - PyObject *x509_sequence = Py_None; - X509_STORE_CTX csc; - x509_object *x509 = NULL; - STACK_OF(X509) *x509_stack = NULL; - PyObject *result = NULL; - int ok; + PyObject *name_sequence = NULL; + X509_NAME *name = NULL; + + ENTERING(x509_object_set_issuer); - if (!PyArg_ParseTuple(args, "O!|O", &x509type, &x509, &x509_sequence)) + if (!PyArg_ParseTuple(args, "O", &name_sequence)) goto error; - if (x509_sequence && !(x509_stack = x509_helper_sequence_to_stack(x509_sequence))) + if (!PySequence_Check(name_sequence)) + lose_type_error("Inapropriate type"); + + if ((name = x509_object_helper_set_name(name_sequence)) == NULL) goto error; - X509_STORE_CTX_init(&csc, self->store, x509->x509, x509_stack); + if (!X509_set_issuer_name(self->x509, name)) + lose("Unable to set issuer name"); - ok = X509_verify_cert(&csc) == 1; + X509_NAME_free(name); + + Py_RETURN_NONE; - result = Py_BuildValue("(iii)", ok, csc.error, csc.error_depth); + error: + X509_NAME_free(name); + return NULL; +} - X509_STORE_CTX_cleanup(&csc); +static char x509_object_get_not_before__doc__[] = + "Return this certificate's \"notBefore\" value as a datetime.\n" + ; - error: /* fall through */ +static PyObject * +x509_object_get_not_before (x509_object *self) +{ + ENTERING(x509_object_get_not_before); + return ASN1_TIME_to_Python(X509_get_notBefore(self->x509)); +} - if (x509_stack) - sk_X509_free(x509_stack); +static char x509_object_get_not_after__doc__[] = + "Return this certificate's \"notAfter\" value as a datetime.\n" + ; - return result; +static PyObject * +x509_object_get_not_after (x509_object *self) +{ + ENTERING(x509_object_get_not_after); + return ASN1_TIME_to_Python(X509_get_notAfter(self->x509)); } -static char x509_store_object_add_trust__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Store</memberof>\n" -" <name>addTrust</name>\n" -" <parameter>cert</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method adds a new certificate to the store to be used in the\n" -" verification process. <parameter>cert</parameter> should be an\n" -" instance of <classname>X509</classname>. Using trusted certificates to manage\n" -" verification is relatively primitive, more sophisticated systems\n" -" can be constructed at an application level by by constructing\n" -" certificate chains to verify.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_not_after__doc__[] = + "Set this certificate's \"notAfter\" value.\n" + "\n" + "The \"time\" parameter should be a datetime object.\n" + ; static PyObject * -x509_store_object_add_trust(x509_store_object *self, PyObject *args) +x509_object_set_not_after (x509_object *self, PyObject *args) { - x509_object *x509 = NULL; + PyObject *o = NULL; + ASN1_TIME *t = NULL; + + ENTERING(x509_object_set_not_after); - if (!PyArg_ParseTuple(args, "O!", &x509type, &x509)) + if (!PyArg_ParseTuple(args, "O", &o)) goto error; - X509_STORE_add_cert(self->store, x509->x509); + if ((t = Python_to_ASN1_TIME(o, 1)) == NULL) + lose("Couldn't convert notAfter string"); + + if (!X509_set_notAfter(self->x509, t)) + lose("Couldn't set notAfter"); + ASN1_TIME_free(t); Py_RETURN_NONE; error: - + ASN1_TIME_free(t); return NULL; } -static char x509_store_object_add_crl__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Store</memberof>\n" -" <name>addCrl</name>\n" -" <parameter>crl</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method adds a CRL to a store to be used for verification.\n" -" <parameter>crl</parameter> should be an instance of\n" -" <classname>X509Crl</classname>.\n" -" Unfortunately, the current stable release of OpenSSL does not\n" -" support CRL checking for certificate verification.\n" -" This functionality will probably make it into OpenSSL 0.9.7, until\n" -" it does this function is useless and CRL verification must be\n" -" implemented by the application.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_not_before__doc__[] = + "Set this certificate's \"notBefore\" value.\n" + "\n" + "The \"time\" parameter should be a datetime object.\n" + ; static PyObject * -x509_store_object_add_crl(x509_store_object *self, PyObject *args) +x509_object_set_not_before (x509_object *self, PyObject *args) { - x509_crl_object *crl = NULL; + PyObject *o = NULL; + ASN1_TIME *t = NULL; + + ENTERING(x509_object_set_not_before); - if (!PyArg_ParseTuple(args, "O!", &x509_crltype, &crl)) + if (!PyArg_ParseTuple(args, "O", &o)) goto error; - X509_STORE_add_crl(self->store, crl->crl); + if ((t = Python_to_ASN1_TIME(o, 1)) == NULL) + lose("Couldn't convert notBefore string"); + + if (!X509_set_notBefore(self->x509, t)) + lose("Couldn't set notBefore"); + ASN1_TIME_free(t); Py_RETURN_NONE; error: - + ASN1_TIME_free(t); return NULL; } -static struct PyMethodDef x509_store_object_methods[] = { - {"verify", (PyCFunction)x509_store_object_verify, METH_VARARGS, NULL}, - {"verifyChain", (PyCFunction)x509_store_object_verify_chain, METH_VARARGS, NULL}, - {"verifyDetailed", (PyCFunction)x509_store_object_verify_detailed, METH_VARARGS, NULL}, - {"addTrust", (PyCFunction)x509_store_object_add_trust, METH_VARARGS, NULL}, - {"addCrl", (PyCFunction)x509_store_object_add_crl, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ -}; +static char x509_object_clear_extensions__doc__[] = + "Clear all extensions attached to this certificate.\n" + ; static PyObject * -x509_store_object_getattr(x509_store_object *self, char *name) +x509_object_clear_extensions(x509_object *self) { - return Py_FindMethod(x509_store_object_methods, (PyObject *)self, name); + X509_EXTENSION *ext; + + ENTERING(x509_object_clear_extensions); + + while ((ext = X509_delete_ext(self->x509, 0)) != NULL) + X509_EXTENSION_free(ext); + + Py_RETURN_NONE; } -static void -x509_store_object_dealloc(x509_store_object *self, char *name) +static char x509_object_get_ski__doc__[] = + "Return the Subject Key Identifier (SKI) value for this\n" + "certificate, or None if the certificate has no SKI extension.\n" + ; + +static PyObject * +x509_object_get_ski(x509_object *self) { - X509_STORE_free(self->store); - PyObject_Del(self); -} - -static char x509_storetype__doc__[] = -"<class>\n" -" <header>\n" -" <name>X509Store</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides preliminary access to OpenSSL X509 verification\n" -" facilities.\n" -" </para>\n" -"\n" -" <example>\n" -" <title><classname>x509_store</classname> class usage</title>\n" -" <programlisting>\n" -" store = POW.X509Store()\n" -"\n" -" caFile = open('test/cacert.pem', 'r')\n" -" ca = POW.pemRead(POW.X509_CERTIFICATE, caFile.read())\n" -" caFile.close()\n" -"\n" -" store.addTrust(ca)\n" -"\n" -" certFile = open('test/foocom.cert', 'r')\n" -" x509 = POW.pemRead(POW.X509_CERTIFICATE, certFile.read())\n" -" certFile.close()\n" -"\n" -" print x509.pprint()\n" -"\n" -" if store.verify(x509):\n" -" print 'Verified certificate!.'\n" -" else:\n" -" print 'Failed to verify certificate!.'\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</class>\n" -; + ENTERING(x509_object_get_ski); -static PyTypeObject x509_storetype = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "X509Store", /*tp_name*/ - sizeof(x509_store_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)x509_store_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)x509_store_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - x509_storetype__doc__ /* Documentation string */ -}; -/*========== x509 store Code ==========*/ + (void) X509_check_ca(self->x509); /* Calls x509v3_cache_extensions() */ + + if (self->x509->skid == NULL) + Py_RETURN_NONE; + else + return Py_BuildValue("s#", + ASN1_STRING_data(self->x509->skid), + ASN1_STRING_length(self->x509->skid)); +} + +static char x509_object_set_ski__doc__[] = + "Set the Subject Key Identifier (SKI) value for this certificate.\n" + ; -/*========== x509 crl Code ==========*/ -static x509_crl_object * -x509_crl_object_new(void) +static PyObject * +x509_object_set_ski(x509_object *self, PyObject *args) { - x509_crl_object *self = NULL; + ASN1_OCTET_STRING *ext = NULL; + const unsigned char *buf = NULL; + int len, ok = 0; + + ENTERING(x509_object_set_ski); - self = PyObject_New(x509_crl_object, &x509_crltype); - if (self == NULL) + if (!PyArg_ParseTuple(args, "s#", &buf, &len)) goto error; - self->crl = X509_CRL_new(); + if ((ext = ASN1_OCTET_STRING_new()) == NULL || + !ASN1_OCTET_STRING_set(ext, buf, len)) + lose_no_memory(); - return self; + /* + * RFC 5280 4.2.1.2 says this MUST be non-critical. + */ - error: + if (!X509_add1_ext_i2d(self->x509, NID_subject_key_identifier, + ext, 0, X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add SKI extension to certificate"); - Py_XDECREF(self); - return NULL; -} + ok = 1; -static x509_crl_object * -x509_crl_object_pem_read(BIO *in) -{ - x509_crl_object *self; + error: + ASN1_OCTET_STRING_free(ext); - if ((self = PyObject_New(x509_crl_object, &x509_crltype)) == NULL) - goto error; + if (ok) + Py_RETURN_NONE; + else + return NULL; +} - if ((self->crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL)) == NULL) - lose("could not load certificate"); +static char x509_object_get_aki__doc__[] = + "Return the Authority Key Identifier (AKI) keyid value for this\n" + "certificate, or None if the certificate has no AKI extension or has an\n" + "AKI extension with no keyIdentifier value.\n" + ; - return self; +static PyObject * +x509_object_get_aki(x509_object *self) +{ + ENTERING(x509_object_get_aki); - error: + (void) X509_check_ca(self->x509); /* Calls x509v3_cache_extensions() */ - Py_XDECREF(self); - return NULL; + if (self->x509->akid == NULL || self->x509->akid->keyid == NULL) + Py_RETURN_NONE; + else + return Py_BuildValue("s#", + ASN1_STRING_data(self->x509->akid->keyid), + ASN1_STRING_length(self->x509->akid->keyid)); } -static x509_crl_object * -x509_crl_object_der_read(unsigned char *src, int len) +static char x509_object_set_aki__doc__[] = + "Set the Authority Key Identifier (AKI) value for this certificate.\n" + "\n" + "We only support the keyIdentifier method, as that's the only form\n" + "which is legal for RPKI certificates.\n" + ; + +static PyObject * +x509_object_set_aki(x509_object *self, PyObject *args) { - x509_crl_object *self; - unsigned char* ptr = src; + AUTHORITY_KEYID *ext = NULL; + const unsigned char *buf = NULL; + int len, ok = 0; - if ((self = PyObject_New(x509_crl_object, &x509_crltype)) == NULL) + ENTERING(x509_object_set_aki); + + if (!PyArg_ParseTuple(args, "s#", &buf, &len)) goto error; - self->crl = X509_CRL_new(); + if ((ext = AUTHORITY_KEYID_new()) == NULL || + (ext->keyid == NULL && (ext->keyid = ASN1_OCTET_STRING_new()) == NULL) || + !ASN1_OCTET_STRING_set(ext->keyid, buf, len)) + lose_no_memory(); - if (!d2i_X509_CRL(&self->crl, (const unsigned char **) &ptr, len)) - lose("could not load PEM encoded CRL"); + /* + * RFC 5280 4.2.1.1 says this MUST be non-critical. + */ - return self; + if (!X509_add1_ext_i2d(self->x509, NID_authority_key_identifier, + ext, 0, X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add AKI extension to certificate"); + + ok = 1; error: + AUTHORITY_KEYID_free(ext); - Py_XDECREF(self); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char x509_crl_object_get_version__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>getVersion</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the version number from the version field of\n" -" this CRL.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_key_usage__doc__[] = + "Return a FrozenSet of strings representing the KeyUsage\n" + "settings for this certificate, or None if the certificate has no\n" + "KeyUsage extension. The bits have the same names as in RFC 5280.\n" + ; static PyObject * -x509_crl_object_get_version(x509_crl_object *self, PyObject *args) +x509_object_get_key_usage(x509_object *self) { - long version = 0; + extern X509V3_EXT_METHOD v3_key_usage; + BIT_STRING_BITNAME *bit_name; + ASN1_BIT_STRING *ext = NULL; + PyObject *result = NULL; + PyObject *token = NULL; + + ENTERING(x509_object_get_key_usage); - if (!PyArg_ParseTuple(args, "")) + if ((ext = X509_get_ext_d2i(self->x509, NID_key_usage, NULL, NULL)) == NULL) + Py_RETURN_NONE; + + if ((result = PyFrozenSet_New(NULL)) == NULL) goto error; - if ((version = ASN1_INTEGER_get(self->crl->crl->version)) == -1) - lose("could not get crl version"); + 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; + } - return Py_BuildValue("l", version); + ASN1_BIT_STRING_free(ext); + return result; error: - + ASN1_BIT_STRING_free(ext); + Py_XDECREF(token); + Py_XDECREF(result); return NULL; } -static char x509_crl_object_set_version__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>setVersion</name>\n" -" <parameter>version</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method sets the version number in the version field of\n" -" this CRL. <parameter>version</parameter> should be an\n" -" integer.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_key_usage__doc__[] = + "Set the KeyUsage extension for this certificate.\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 * -x509_crl_object_set_version(x509_crl_object *self, PyObject *args) +x509_object_set_key_usage(x509_object *self, PyObject *args) { - long version = 0; - ASN1_INTEGER *asn1_version = NULL; + 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 (!PyArg_ParseTuple(args, "i", &version)) + ENTERING(x509_object_set_key_usage); + + 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; - if ((asn1_version = ASN1_INTEGER_new()) == NULL) - lose("could not allocate memory"); + while ((token = PyIter_Next(iterator)) != NULL) { - if (!ASN1_INTEGER_set(asn1_version, version)) - lose("could not get set version"); + if ((t = PyString_AsString(token)) == NULL) + goto error; - self->crl->crl->version = asn1_version; + for (bit_name = v3_key_usage.usr_data; bit_name->sname != NULL; bit_name++) + if (!strcmp(t, bit_name->sname)) + break; - Py_RETURN_NONE; + if (bit_name->sname == NULL) + lose("Unrecognized KeyUsage token"); - error: + if (!ASN1_BIT_STRING_set_bit(ext, bit_name->bitnum, 1)) + lose_no_memory(); - if (asn1_version) - ASN1_INTEGER_free(asn1_version); + Py_XDECREF(token); + token = NULL; + } - return NULL; + if (!X509_add1_ext_i2d(self->x509, 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 x509_crl_object_get_issuer__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>getIssuer</name>\n" -" <parameter>format = SHORTNAME_FORMAT</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a tuple containing the issuers name. See the\n" -" <function>getIssuer</function> method of\n" -" <classname>X509</classname> for more details.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_rfc3779__doc__[] = + "Return this certificate's RFC 3779 resources.\n" + "\n" + "Return value is a three-element tuple: the first element is the ASN\n" + "resources, the second is the IPv4 resources, the third is the IPv6\n" + "resources. Each of these elements in turn is either the string\n" + "\"inherit\" or a tuple representing a set of ranges of ASNs or IP\n" + "addresses.\n" + "\n" + "Each range is a two-element tuple, respectively representing the low\n" + "and high ends of the range, inclusive. ASN ranges are represented by\n" + "pairs of integers, IP address ranges are represented by pairs of\n" + "IPAddress objects.\n" + ; static PyObject * -x509_crl_object_get_issuer(x509_crl_object *self, PyObject *args) +x509_object_get_rfc3779(x509_object *self) { - PyObject *result_list = NULL; - int format = SHORTNAME_FORMAT; + PyObject *result = NULL; + PyObject *asn_result = NULL; + PyObject *ipv4_result = NULL; + PyObject *ipv6_result = NULL; + PyObject *range = NULL; + PyObject *range_b = NULL; + PyObject *range_e = NULL; + ASIdentifiers *asid = NULL; + IPAddrBlocks *addr = NULL; + int i, j; + + ENTERING(x509_object_get_rfc3779); + + if ((asid = X509_get_ext_d2i(self->x509, NID_sbgp_autonomousSysNum, NULL, NULL)) != NULL && + asid->asnum != NULL) { + switch (asid->asnum->type) { + + case ASIdentifierChoice_inherit: + if ((asn_result = PyString_FromString("inherit")) == NULL) + goto error; + break; - if (!PyArg_ParseTuple(args, "|i", &format)) - goto error; + case ASIdentifierChoice_asIdsOrRanges: - if ((result_list = X509_object_helper_get_name(self->crl->crl->issuer, format)) == NULL) - lose("failed to produce name list"); + if ((asn_result = PyTuple_New(sk_ASIdOrRange_num(asid->asnum->u.asIdsOrRanges))) == NULL) + goto error; - return result_list; + for (i = 0; i < sk_ASIdOrRange_num(asid->asnum->u.asIdsOrRanges); i++) { + ASIdOrRange *aor = sk_ASIdOrRange_value(asid->asnum->u.asIdsOrRanges, i); + ASN1_INTEGER *b = NULL; + ASN1_INTEGER *e = NULL; - error: + switch (aor->type) { - return NULL; -} + case ASIdOrRange_id: + b = e = aor->u.id; + break; -static char x509_crl_object_set_issuer__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>setIssuer</name>\n" -" <parameter>name</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to set the issuers name.\n" -" <parameter>name</parameter> can be comprised of lists or tuples in\n" -" the format described in the <function>getIssuer</function> method\n" -" of <classname>X509</classname>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + case ASIdOrRange_range: + b = aor->u.range->min; + e = aor->u.range->max; + break; -static PyObject * -x509_crl_object_set_issuer(x509_crl_object *self, PyObject *args) -{ - PyObject *name_sequence = NULL; - X509_NAME *name = NULL; + default: + lose_type_error("Unexpected asIdsOrRanges type"); + } - if (!PyArg_ParseTuple(args, "O", &name_sequence)) - goto error; + if (ASN1_STRING_type(b) == V_ASN1_NEG_INTEGER || + ASN1_STRING_type(e) == V_ASN1_NEG_INTEGER) + lose_type_error("I don't believe in negative ASNs"); - if (!PyTuple_Check(name_sequence) && !PyList_Check(name_sequence)) - lose_type_error("Inapropriate type"); + if ((range_b = ASN1_INTEGER_to_PyLong(b)) == NULL || + (range_e = ASN1_INTEGER_to_PyLong(e)) == NULL || + (range = Py_BuildValue("(NN)", range_b, range_e)) == NULL) + goto error; - if ((name = X509_NAME_new()) == NULL) - lose("could not allocate memory"); + PyTuple_SET_ITEM(asn_result, i, range); + range = range_b = range_e = NULL; + } - if (!X509_object_helper_set_name(name, name_sequence)) - lose("unable to set new name"); + break; - if (!X509_NAME_set(&self->crl->crl->issuer, name)) - lose("unable to set name"); + default: + lose_type_error("Unexpected ASIdentifierChoice type"); + } + } - X509_NAME_free(name); + if ((addr = X509_get_ext_d2i(self->x509, NID_sbgp_ipAddrBlock, NULL, NULL)) != NULL) { + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + const struct ipaddress_version *ip_type = NULL; + const unsigned int afi = v3_addr_get_afi(f); + PyObject **result_obj = NULL; + int addr_len = 0; + + switch (afi) { + case IANA_AFI_IPV4: result_obj = &ipv4_result; ip_type = &ipaddress_version_4; break; + case IANA_AFI_IPV6: result_obj = &ipv6_result; ip_type = &ipaddress_version_6; break; + default: lose_type_error("Unknown AFI"); + } - Py_RETURN_NONE; + if (*result_obj != NULL) + lose_type_error("Duplicate IPAddressFamily"); - error: + if (f->addressFamily->length > 2) + lose_type_error("Unsupported SAFI"); - if (name) - X509_NAME_free(name); + switch (f->ipAddressChoice->type) { - return NULL; -} + case IPAddressChoice_inherit: + if ((*result_obj = PyString_FromString("inherit")) == NULL) + goto error; + continue; -static char x509_crl_object_set_this_update__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>setThisUpdate</name>\n" -" <parameter>time</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this accepts one parameter, a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + case IPAddressChoice_addressesOrRanges: + break; -static PyObject * -x509_crl_object_set_this_update (x509_crl_object *self, PyObject *args) -{ - char *new_time = NULL; + default: + lose_type_error("Unexpected IPAddressChoice type"); + } - if (!PyArg_ParseTuple(args, "s", &new_time)) - goto error; + if ((*result_obj = PyTuple_New(sk_IPAddressOrRange_num(f->ipAddressChoice->u.addressesOrRanges))) == NULL) + goto error; - if (!python_ASN1_TIME_set_string(self->crl->crl->lastUpdate, new_time)) - lose("Could not set lastUpdate"); + for (j = 0; j < sk_IPAddressOrRange_num(f->ipAddressChoice->u.addressesOrRanges); j++) { + IPAddressOrRange *aor = sk_IPAddressOrRange_value(f->ipAddressChoice->u.addressesOrRanges, j); + ipaddress_object *addr_b = NULL; + ipaddress_object *addr_e = NULL; - Py_RETURN_NONE; + if ((range_b = POW_IPAddress_Type.tp_alloc(&POW_IPAddress_Type, 0)) == NULL || + (range_e = POW_IPAddress_Type.tp_alloc(&POW_IPAddress_Type, 0)) == NULL) + goto error; - error: + addr_b = (ipaddress_object *) range_b; + addr_e = (ipaddress_object *) range_e; - return NULL; -} + if ((addr_len = v3_addr_get_range(aor, afi, addr_b->address, addr_e->address, + sizeof(addr_b->address))) == 0) + lose_type_error("Couldn't unpack IP addresses from BIT STRINGs"); -static char x509_crl_object_get_this_update__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>getThisUpdate</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this function returns a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + addr_b->type = addr_e->type = ip_type; -static PyObject * -x509_crl_object_get_this_update (x509_crl_object *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, "")) - goto error; + if ((range = Py_BuildValue("(NN)", range_b, range_e)) == NULL) + goto error; - return ASN1_TIME_to_Python(self->crl->crl->lastUpdate); + PyTuple_SET_ITEM(*result_obj, j, range); + range = range_b = range_e = NULL; + } + } + } - error: + result = Py_BuildValue("(OOO)", + (asn_result == NULL ? Py_None : asn_result), + (ipv4_result == NULL ? Py_None : ipv4_result), + (ipv6_result == NULL ? Py_None : ipv6_result)); + + error: /* Fall through */ + ASIdentifiers_free(asid); + sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free); + Py_XDECREF(range_b); + Py_XDECREF(range_e); + Py_XDECREF(range); + Py_XDECREF(asn_result); + Py_XDECREF(ipv4_result); + Py_XDECREF(ipv6_result); - return NULL; + return result; } -static char x509_crl_object_set_next_update__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>setNextUpdate</name>\n" -" <parameter>time</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this accepts one parameter, a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_rfc3779__doc__[] = + "Set this certificate's RFC 3779 resources.\n" + "\n" + "This method takes three arguments: \"asn\", \"ipv4\", and \"ipv6\".\n" + "\n" + "Each of these arguments can be:\n" + "\n" + "* None, to omit this kind of resource;\n" + "\n" + "* The string \"inherit\", to specify RFC 3779 resource inheritance; or\n" + "\n" + "* An iterable object which returns range pairs of the appropriate type.\n" + "\n" + "Range pairs are as returned by the .getRFC3779() method.\n" + ; static PyObject * -x509_crl_object_set_next_update (x509_crl_object *self, PyObject *args) +x509_object_set_rfc3779(x509_object *self, PyObject *args, PyObject *kwds) { - char *new_time = NULL; - ASN1_UTCTIME *time = NULL; + static char *kwlist[] = {"asn", "ipv4", "ipv6", NULL}; + PyObject *asn_arg = Py_None; + PyObject *ipv4_arg = Py_None; + PyObject *ipv6_arg = Py_None; + PyObject *iterator = NULL; + PyObject *item = NULL; + PyObject *fast = NULL; + ASIdentifiers *asid = NULL; + IPAddrBlocks *addr = NULL; + ASN1_INTEGER *asid_b = NULL; + ASN1_INTEGER *asid_e = NULL; + ipaddress_object *addr_b = NULL; + ipaddress_object *addr_e = NULL; + int empty = 0; - if (!PyArg_ParseTuple(args, "s", &new_time)) + ENTERING(x509_object_set_rfc3779); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &asn_arg, &ipv4_arg, &ipv6_arg)) goto error; - if (self->crl->crl->nextUpdate == NULL && (time = ASN1_UTCTIME_new()) == NULL) - lose("could not allocate memory"); + if (asn_arg != Py_None) { - self->crl->crl->nextUpdate = time; + empty = 1; - if (!python_ASN1_TIME_set_string(time, new_time)) - lose("Could not set nextUpdate"); + if ((asid = ASIdentifiers_new()) == NULL) + lose_no_memory(); - Py_RETURN_NONE; + if (PyString_Check(asn_arg)) { - error: + if (strcmp(PyString_AsString(asn_arg), "inherit")) + lose_type_error("ASID must be sequence of range pairs, or \"inherit\""); - return NULL; -} + if (!v3_asid_add_inherit(asid, V3_ASID_ASNUM)) + lose_no_memory(); -static char x509_crl_object_get_next_update__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>getNextUpdate</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this function returns a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + empty = 0; -static PyObject * -x509_crl_object_get_next_update (x509_crl_object *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, "")) - goto error; + } else { - return ASN1_TIME_to_Python(self->crl->crl->nextUpdate); + if ((iterator = PyObject_GetIter(asn_arg)) == NULL) + goto error; - error: + while ((item = PyIter_Next(iterator)) != NULL) { + + if ((fast = PySequence_Fast(item, "ASN range must be a sequence")) == NULL) + goto error; + + if (PySequence_Fast_GET_SIZE(fast) != 2) + lose_type_error("ASN range must be two-element sequence"); + + if ((asid_b = PyLong_to_ASN1_INTEGER(PySequence_Fast_GET_ITEM(fast, 0))) == NULL) + goto error; + + switch (PyObject_RichCompareBool(PySequence_Fast_GET_ITEM(fast, 0), + PySequence_Fast_GET_ITEM(fast, 1), Py_EQ)) { + case 0: + if ((asid_e = PyLong_to_ASN1_INTEGER(PySequence_Fast_GET_ITEM(fast, 1))) == NULL) + goto error; + break; + case 1: + break; + default: + goto error; + } + + if (!v3_asid_add_id_or_range(asid, V3_ASID_ASNUM, asid_b, asid_e)) + lose_openssl_error("Couldn't add range to ASID"); + + asid_b = asid_e = NULL; + Py_XDECREF(item); + Py_XDECREF(fast); + item = fast = NULL; + empty = 0; + } - return NULL; -} + if (!empty && (!v3_asid_canonize(asid) || + !X509_add1_ext_i2d(self->x509, NID_sbgp_autonomousSysNum, + asid, 1, X509V3_ADD_REPLACE))) + lose_openssl_error("Couldn't add ASID extension to certificate"); -static char x509_crl_object_set_revoked__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>setRevoked</name>\n" -" <parameter>revoked</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method sets the sequence of revoked certificates in this CRL.\n" -" <parameter>revoked</parameter> should be a list or tuple of\n" -" <classname>X509Revoked</classname>.\n" -" </para>\n" -" <example>\n" -" <title><function>setRevoked</function> function usage</title>\n" -" <programlisting>\n" -" privateFile = open('test/private.key', 'r')\n" -" publicFile = open('test/public.key', 'r')\n" -" crlFile = open('test/crl.pem', 'w')\n" -"\n" -" publicKey = POW.pemRead(POW.RSA_PUBLIC_KEY, publicFile.read())\n" -" privateKey = POW.pemRead(POW.RSA_PRIVATE_KEY, privateFile.read(), 'pass')\n" -"\n" -" crl = POW.X509Crl()\n" -"\n" -" name = [ ['C', 'GB'], ['ST', 'Hertfordshire'],\n" -" ['O','The House'], ['CN', 'Peter Shannon'] ]\n" -"\n" -" t1 = POW.pkix.time2utc(time.time())\n" -" t2 = POW.pkix.time2utc(time.time() + 60*60*24*365)\n" -" crl.setIssuer(name)\n" -" rev = [ POW.X509Revoked(3, t1),\n" -" POW.X509Revoked(4, t1),\n" -" POW.X509Revoked(5, t1) ]\n" -"\n" -" crl.setRevoked(rev)\n" -" crl.setThisUpdate(t1)\n" -" crl.setNextUpdate(t2)\n" -" crl.sign(privateKey)\n" -"\n" -" crlFile.write(crl.pemWrite())\n" -"\n" -" privateFile.close()\n" -" publicFile.close()\n" -" crlFile.close()\n" -" </programlisting>\n" -" </example>\n" -"\n" -" </body>\n" -"</method>\n" -; + Py_XDECREF(iterator); + iterator = NULL; + } + } -// added because we don't already have one! -static X509_REVOKED * -X509_REVOKED_dup(X509_REVOKED *rev) -{ - return((X509_REVOKED *)ASN1_dup((i2d_of_void *) i2d_X509_REVOKED, - (d2i_of_void *) d2i_X509_REVOKED, - (char *) rev)); -} + if (ipv4_arg != Py_None || ipv6_arg != Py_None) { + int v; -static PyObject * -x509_crl_object_set_revoked(x509_crl_object *self, PyObject *args) -{ - PyObject *revoked_sequence = NULL; - x509_revoked_object *revoked = NULL; - X509_REVOKED *tmp_revoked = NULL; - int i = 0,size = 0; + empty = 1; - if (!PyArg_ParseTuple(args, "O", &revoked_sequence)) - goto error; + if ((addr = sk_IPAddressFamily_new_null()) == NULL) + lose_no_memory(); - if (!PyTuple_Check(revoked_sequence) && !PyList_Check(revoked_sequence)) - lose_type_error("inapropriate type"); + /* + * Cheap trick to let us inline all of this instead of being + * forced to use a separate function. Refactor, some day. + */ - size = PySequence_Size(revoked_sequence); - for (i = 0; i < size; i++) { - if ((revoked = (x509_revoked_object*) PySequence_GetItem(revoked_sequence, i)) == NULL) - goto error; + for (v = 0; v < (int) (sizeof(ipaddress_versions)/sizeof(*ipaddress_versions)); v++) { + const struct ipaddress_version *ip_type = ipaddress_versions[v]; + PyObject **argp; - if (!X_X509_revoked_Check(revoked)) - lose_type_error("inapropriate type"); + switch (ip_type->version) { + case 4: argp = &ipv4_arg; break; + case 6: argp = &ipv6_arg; break; + default: continue; /* Never happens */ + } - if ((tmp_revoked = X509_REVOKED_dup(revoked->revoked)) == NULL) - lose("could not allocate memory"); + if (PyString_Check(*argp)) { - if (!X509_CRL_add0_revoked(self->crl, tmp_revoked)) - lose("could not add revokation to stack"); + if (strcmp(PyString_AsString(*argp), "inherit")) + lose_type_error("Argument must be sequence of range pairs, or \"inherit\""); - Py_XDECREF(revoked); - revoked = NULL; - } + if (!v3_addr_add_inherit(addr, ip_type->afi, NULL)) + lose_no_memory(); - Py_RETURN_NONE; + empty = 0; - error: + } else { - Py_XDECREF(revoked); + if ((iterator = PyObject_GetIter(*argp)) == NULL) + goto error; - return NULL; -} + while ((item = PyIter_Next(iterator)) != NULL) { -static PyObject * -x509_crl_object_helper_get_revoked(STACK_OF(X509_REVOKED) *revoked) -{ - int no_entries = 0, i = 0; - x509_revoked_object *revoke_obj = NULL; - PyObject *result_list = NULL, *result_tuple = NULL; + if ((fast = PySequence_Fast(item, "Address range must be a sequence")) == NULL) + goto error; - no_entries = sk_X509_REVOKED_num(revoked); + if (PySequence_Fast_GET_SIZE(fast) != 2 || + !POW_IPAddress_Check(PySequence_Fast_GET_ITEM(fast, 0)) || + !POW_IPAddress_Check(PySequence_Fast_GET_ITEM(fast, 1))) + lose_type_error("Address range must be two-element sequence of IPAddress objects"); - if ((result_list = PyList_New(0)) == NULL) - lose("could not allocate memory"); + addr_b = (ipaddress_object *) PySequence_Fast_GET_ITEM(fast, 0); + addr_e = (ipaddress_object *) PySequence_Fast_GET_ITEM(fast, 1); - for (i = 0; i < no_entries; i++) { - if ((revoke_obj = PyObject_New(x509_revoked_object, &x509_revokedtype)) == NULL) - lose("could not allocate memory"); + if (addr_b->type != ip_type || + addr_e->type != ip_type || + memcmp(addr_b->address, addr_e->address, ip_type->length) > 0) + lose("IPAddrBlock must be sequence of address pairs, or \"inherit\""); - if ((revoke_obj->revoked = X509_REVOKED_dup(sk_X509_REVOKED_value(revoked, i))) == NULL) - lose("could not get revocation"); + if (!v3_addr_add_range(addr, ip_type->afi, NULL, addr_b->address, addr_e->address)) + lose_openssl_error("Couldn't add range to IPAddrBlock"); - if (PyList_Append(result_list, (PyObject*) revoke_obj) != 0) - goto error; + Py_XDECREF(item); + Py_XDECREF(fast); + item = fast = NULL; + addr_b = addr_e = NULL; + } - Py_XDECREF(revoke_obj); - revoke_obj = NULL; - } + Py_XDECREF(iterator); + iterator = NULL; + empty = 0; + } + } - result_tuple = PyList_AsTuple(result_list); - Py_XDECREF(result_list); + if (!empty && (!v3_addr_canonize(addr) || + !X509_add1_ext_i2d(self->x509, NID_sbgp_ipAddrBlock, + addr, 1, X509V3_ADD_REPLACE))) + lose_openssl_error("Couldn't add IPAddrBlock extension to certificate"); + } - return result_tuple; + Py_RETURN_NONE; error: - - Py_XDECREF(revoke_obj); - Py_XDECREF(result_list); + ASN1_INTEGER_free(asid_b); + ASN1_INTEGER_free(asid_e); + ASIdentifiers_free(asid); + sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free); + Py_XDECREF(iterator); + Py_XDECREF(item); + Py_XDECREF(fast); return NULL; } -static char x509_crl_object_get_revoked__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>getRevoked</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a tuple of <classname>X509Revoked</classname>\n" -" objects described in the CRL.\n" -" </para>\n" -" <example>\n" -" <title><function>getRevoked</function> function usage</title>\n" -" <programlisting>\n" -" publicFile = open('test/public.key', 'r')\n" -" crlFile = open('test/crl.pem', 'r')\n" -"\n" -" publicKey = POW.pemRead(POW.RSA_PUBLIC_KEY, publicFile.read())\n" -"\n" -" crl = POW.pemRead(POW.X509_CRL, crlFile.read())\n" -"\n" -" print crl.pprint()\n" -" if crl.verify(publicKey):\n" -" print 'signature ok!'\n" -" else:\n" -" print 'signature not ok!'\n" -"\n" -" revocations = crl.getRevoked()\n" -" for revoked in revocations:\n" -" print 'serial number:', revoked.getSerial()\n" -" print 'date:', time.ctime(revoked.getDate()[0])\n" -"\n" -" publicFile.close()\n" -" crlFile.close()\n" -" </programlisting>\n" -" </example>\n" -"\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_basic_constraints__doc__[] = + "Return BasicConstraints for this certificate.\n" + "\n" + "If this certificate has no BasicConstraints extension, this method\n" + "returns None.\n" + "\n" + "Otherwise, this method returns a two-element tuple. The first element\n" + "of the tuple is a boolean representing the extension's cA value; the\n" + "second element of the tuple is either an integer representing the\n" + "pathLenConstraint value or None if there is no pathLenConstraint.\n" + ; static PyObject * -x509_crl_object_get_revoked(x509_crl_object *self, PyObject *args) +x509_object_get_basic_constraints(x509_object *self) { - if (!PyArg_ParseTuple(args, "")) - goto error; + BASIC_CONSTRAINTS *ext = NULL; + PyObject *result; - return x509_crl_object_helper_get_revoked(X509_CRL_get_REVOKED(self->crl)); + ENTERING(x509_object_get_basic_constraints); - error: + if ((ext = X509_get_ext_d2i(self->x509, NID_basic_constraints, NULL, NULL)) == NULL) + Py_RETURN_NONE; - return NULL; + 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 X509_crl_object_add_extension__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>addExtension</name>\n" -" <parameter>extensionName</parameter>\n" -" <parameter>critical</parameter>\n" -" <parameter>extensionValue</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method adds an extension to this CRL.\n" -" <parameter>extensionName</parameter> should be the of the\n" -" extension. <parameter>critical</parameter> should an integer, 1\n" -" for true and 0 for clase. <parameter>extensionValue</parameter>\n" -" should be a string, DER encoded value of the extension. The name\n" -" of the extension must be correct according to OpenSSL and can be\n" -" checkd in the <constant>objects.h</constant> header file, part of\n" -" the OpenSSL source distrobution. In the majority of cases they\n" -" are the same as those defined in <constant>POW._oids</constant>\n" -" but if you do encounter problems is may be worth checking.\n" -" </para>\n" -" <example>\n" -" <title><function>addExtension</function> method usage</title>\n" -" <programlisting>\n" -" oids = POW.pkix.OidData()\n" -" o2i = oids.obj2oid\n" -"\n" -" n1 = ('directoryName', (((o2i('countryName'), ('printableString', 'UK')),),\n" -" ((o2i('stateOrProvinceName'), ('printableString', 'Herts')),),\n" -" ((o2i('organizationName'), ('printableString', 'The House')),),\n" -" ((o2i('commonName'), ('printableString', 'Shannon Works')),)))\n" -"\n" -" n2 = ('rfc822Name', 'peter_shannon@yahoo.com')\n" -" n3 = ('uri', 'http://www.p-s.org.uk')\n" -" n4 = ('iPAddress', (192,168,100,51))\n" -"\n" -" issuer = POW.pkix.IssuerAltName()\n" -" issuer.set([n1,n2,n3,n4])\n" -" crl.addExtension('issuerAltName', 0, issuer.toString())\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_basic_constraints__doc__[] = + "Set BasicConstraints for this certificate.\n" + "\n" + "First argument \"ca\" is a boolean indicating whether the certificate\n" + "is 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 * -X509_crl_object_add_extension(x509_crl_object *self, PyObject *args) +x509_object_set_basic_constraints(x509_object *self, PyObject *args) { - int critical = 0, nid = 0, len = 0; - char *name = NULL; - unsigned char *buf = NULL; - ASN1_OCTET_STRING *octetString = NULL; - X509_EXTENSION *extn = NULL; + 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, "sis#", &name, &critical, &buf, &len)) - goto error; + ENTERING(x509_object_set_basic_constraints); - if ((octetString = M_ASN1_OCTET_STRING_new()) == NULL) - lose("could not allocate memory"); + if (!PyArg_ParseTuple(args, "O|OO", &is_ca, &pathlen_obj, &critical)) + goto error; - if (!ASN1_OCTET_STRING_set(octetString, buf, len)) - lose("could not set ASN1 Octect string"); + if (pathlen_obj != Py_None && (pathlen = PyInt_AsLong(pathlen_obj)) < 0) + lose_type_error("Bad pathLenConstraint value"); - if ((nid = OBJ_txt2nid(name)) == NID_undef) - lose("extension has unknown object identifier"); + if ((ext = BASIC_CONSTRAINTS_new()) == NULL) + lose_no_memory(); - if ((extn = X509_EXTENSION_create_by_NID(NULL, nid, critical, octetString)) == NULL) - lose("unable to create ASN1 X509 Extension object"); + ext->ca = PyObject_IsTrue(is_ca) ? 0xFF : 0; - if (!self->crl->crl->extensions && - (self->crl->crl->extensions = sk_X509_EXTENSION_new_null()) == NULL) - lose("unable to allocate memory"); + if (pathlen_obj != Py_None && + ((ext->pathlen == NULL && (ext->pathlen = ASN1_INTEGER_new()) == NULL) || + !ASN1_INTEGER_set(ext->pathlen, pathlen))) + lose_no_memory(); - if (!sk_X509_EXTENSION_push(self->crl->crl->extensions, extn)) - lose("unable to add extension"); + if (!X509_add1_ext_i2d(self->x509, NID_basic_constraints, + ext, PyObject_IsTrue(critical), X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add BasicConstraints extension to certificate"); - Py_RETURN_NONE; + ok = 1; error: + BASIC_CONSTRAINTS_free(ext); - if (extn) - X509_EXTENSION_free(extn); - - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char X509_crl_object_clear_extensions__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>clearExtensions</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method clears the structure which holds the extension for\n" -" this CRL.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_sia__doc__[] = + "Get SIA values for this certificate.\n" + "\n" + "If the certificate has no SIA extension, this method returns None.\n" + "\n" + "Otherwise, it returns a tuple containing three values:\n" + "caRepository URIs, rpkiManifest URIs, and signedObject URIs.\n" + "Each of these values is a tuple of strings, representing an ordered\n" + "sequence of URIs. Any or all of these sequences may be empty.\n" + "\n" + "Any other accessMethods are ignored, as are any non-URI\n" + "accessLocations.\n" + ; static PyObject * -X509_crl_object_clear_extensions(x509_crl_object *self, PyObject *args) +x509_object_get_sia(x509_object *self) { - if (!PyArg_ParseTuple(args, "")) + 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; + + ENTERING(x509_object_get_sia); + + if ((ext = X509_get_ext_d2i(self->x509, 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; - if (self->crl->crl->extensions) { - sk_X509_EXTENSION_free(self->crl->crl->extensions); - self->crl->crl->extensions = NULL; + 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; + } } - Py_RETURN_NONE; + result = Py_BuildValue("(OOO)", + result_caRepository, + result_rpkiManifest, + result_signedObject); error: - - return NULL; + AUTHORITY_INFO_ACCESS_free(ext); + Py_XDECREF(result_caRepository); + Py_XDECREF(result_rpkiManifest); + Py_XDECREF(result_signedObject); + return result; } -static char X509_crl_object_count_extensions__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>countExtensions</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the size of the structure which holds the\n" -" extension for this CRL.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_sia__doc__[] = + "Set SIA values for this certificate. Takes three arguments:\n" + "\"caRepository\", \"rpkiManifest\", and \"signedObject\".\n" + "Each of these should be an iterable which returns URIs.\n" + "\n" + "None is acceptable as an alternate way of specifying an empty\n" + "sequence of URIs for a particular argument.\n" + ; static PyObject * -X509_crl_object_count_extensions(x509_crl_object *self, PyObject *args) +x509_object_set_sia(x509_object *self, PyObject *args, PyObject *kwds) { - int num = 0; + static char *kwlist[] = {"caRepository", "rpkiManifest", "signedObject", NULL}; + AUTHORITY_INFO_ACCESS *ext = NULL; + PyObject *caRepository = Py_None; + PyObject *rpkiManifest = Py_None; + PyObject *signedObject = Py_None; + 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; + + ENTERING(x509_object_set_sia); - if (!PyArg_ParseTuple(args, "")) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, + &caRepository, &rpkiManifest, &signedObject)) goto error; - if (self->crl->crl->extensions) - num = sk_X509_EXTENSION_num(self->crl->crl->extensions); + 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 (!X509_add1_ext_i2d(self->x509, NID_sinfo_access, ext, 0, X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add SIA extension to certificate"); - return Py_BuildValue("i", num); + ok = 1; error: + AUTHORITY_INFO_ACCESS_free(ext); + ACCESS_DESCRIPTION_free(a); + Py_XDECREF(item); + Py_XDECREF(iterator); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char X509_crl_object_get_extension__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>getExtension</name>\n" -" <parameter>index</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a tuple equivalent the parameters of\n" -" <function>addExtension</function>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_aia__doc__[] = + "Get this certificate's AIA values.\n" + "\n" + "If the certificate has no AIA extension, this method returns None.\n" + "\n" + "Otherwise, this returns a sequence of caIssuers URIs.\n" + "\n" + "Any other accessMethods are ignored, as are any non-URI\n" + "accessLocations.\n" + ; static PyObject * -X509_crl_object_get_extension(x509_crl_object *self, PyObject *args) +x509_object_get_aia(x509_object *self) { - int num = 0, index = 0, ext_nid = 0; - char const *ext_ln = NULL; - char unknown_ext [] = "unknown"; - X509_EXTENSION *ext; - - if (!PyArg_ParseTuple(args, "i", &index)) - goto error; + AUTHORITY_INFO_ACCESS *ext = NULL; + PyObject *result = NULL; + const char *uri; + PyObject *obj; + int i, n = 0; - if (self->crl->crl->extensions) - num = sk_X509_EXTENSION_num(self->crl->crl->extensions); + ENTERING(x509_object_get_aia); + if ((ext = X509_get_ext_d2i(self->x509, NID_info_access, NULL, NULL)) == NULL) + Py_RETURN_NONE; - if (index >= num) - lose("certificate does not have that many extensions"); + 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 && + OBJ_obj2nid(a->method) == NID_ad_ca_issuers) + n++; + } - if ((ext = sk_X509_EXTENSION_value(self->crl->crl->extensions, index)) == NULL) - lose("could not get extension"); + if (((result = PyTuple_New(n)) == NULL)) + goto error; - if ((ext_nid = OBJ_obj2nid(ext->object)) == NID_undef) - lose("extension has unknown object identifier"); + n = 0; - if ((ext_ln = OBJ_nid2sn(ext_nid)) == NULL) - ext_ln = unknown_ext; + 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 && OBJ_obj2nid(a->method) == NID_ad_ca_issuers) { + uri = (char *) ASN1_STRING_data(a->location->d.uniformResourceIdentifier); + if ((obj = PyString_FromString(uri)) == NULL) + goto error; + PyTuple_SET_ITEM(result, n++, obj); + } + } - return Py_BuildValue("sis#", ext_ln, ext->critical, ext->value->data, ext->value->length); + AUTHORITY_INFO_ACCESS_free(ext); + return result; error: - + AUTHORITY_INFO_ACCESS_free(ext); + Py_XDECREF(result); return NULL; } -static char x509_crl_object_sign__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>sign</name>\n" -" <parameter>key</parameter>\n" -" <parameter>digest = MD5_DIGEST</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" <parameter>key</parameter> should be an instance of\n" -" <classname>Asymmetric</classname> and contain a private key.\n" -" <parameter>digest</parameter> indicates\n" -" which digest function should be used to compute the hash to be\n" -" signed, it should be one of the following:\n" -" </para>\n" -" <simplelist>\n" -#ifndef OPENSSL_NO_MD2 -" <member><constant>MD2_DIGEST</constant></member>\n" -#endif -" <member><constant>MD5_DIGEST</constant></member>\n" -" <member><constant>SHA_DIGEST</constant></member>\n" -" <member><constant>SHA1_DIGEST</constant></member>\n" -" <member><constant>RIPEMD160_DIGEST</constant></member>\n" -" <member><constant>SHA256_DIGEST</constant></member>\n" -" <member><constant>SHA384_DIGEST</constant></member>\n" -" <member><constant>SHA512_DIGEST</constant></member>\n" -" </simplelist>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_aia__doc__[] = + "Set AIA URIs for this certificate.\n" + "\n" + "Argument is a iterable which returns caIssuers URIs.\n" + ; static PyObject * -x509_crl_object_sign(x509_crl_object *self, PyObject *args) +x509_object_set_aia(x509_object *self, PyObject *args) { - EVP_PKEY *pkey = NULL; - asymmetric_object *asym; - int digest = MD5_DIGEST; + AUTHORITY_INFO_ACCESS *ext = NULL; + PyObject *caIssuers = NULL; + PyObject *iterator = NULL; + ASN1_OBJECT *oid = NULL; + PyObject *item = NULL; + ACCESS_DESCRIPTION *a = NULL; + int ok = 0; + Py_ssize_t urilen; + char *uri; - if (!PyArg_ParseTuple(args, "O!|i", &asymmetrictype, &asym, &digest)) - goto error; - - if ((pkey = EVP_PKEY_new()) == NULL) - lose("could not allocate memory"); + ENTERING(x509_object_set_aia); - if (asym->key_type != RSA_PRIVATE_KEY) - lose("cannot use this type of key"); + if (!PyArg_ParseTuple(args, "O", &caIssuers)) + goto error; - if (!EVP_PKEY_assign_RSA(pkey, asym->cipher)) - lose("EVP_PKEY assignment error"); + if ((ext = AUTHORITY_INFO_ACCESS_new()) == NULL) + lose_no_memory(); - switch (digest) { - case MD5_DIGEST: - if (!X509_CRL_sign(self->crl, pkey, EVP_md5())) - lose("could not sign CRL"); - break; + if ((oid = OBJ_nid2obj(NID_ad_ca_issuers)) == NULL) + lose_openssl_error("Couldn't find AIA accessMethod OID"); -#ifndef OPENSSL_NO_MD2 - case MD2_DIGEST: - if (!X509_CRL_sign(self->crl, pkey, EVP_md2())) - lose("could not sign CRL"); - break; -#endif + if ((iterator = PyObject_GetIter(caIssuers)) == NULL) + goto error; - case SHA_DIGEST: - if (!X509_CRL_sign(self->crl, pkey, EVP_sha())) - lose("could not sign CRL"); - break; + while ((item = PyIter_Next(iterator)) != NULL) { - case SHA1_DIGEST: - if (!X509_CRL_sign(self->crl, pkey, EVP_sha1())) - lose("could not sign CRL"); - break; + if (PyString_AsStringAndSize(item, &uri, &urilen) < 0) + goto error; - case RIPEMD160_DIGEST: - if (!X509_CRL_sign(self->crl, pkey, EVP_ripemd160())) - lose("could not sign CRL"); - break; + 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(); - case SHA256_DIGEST: - if (!X509_CRL_sign(self->crl, pkey, EVP_sha256())) - lose("could not sign CRL"); - break; + a->location->type = GEN_URI; - case SHA384_DIGEST: - if (!X509_CRL_sign(self->crl, pkey, EVP_sha384())) - lose("could not sign CRL"); - break; + if (!sk_ACCESS_DESCRIPTION_push(ext, a)) + lose_no_memory(); - case SHA512_DIGEST: - if (!X509_CRL_sign(self->crl, pkey, EVP_sha512())) - lose("could not sign CRL"); - break; + a = NULL; + Py_XDECREF(item); + item = NULL; } - Py_RETURN_NONE; + Py_XDECREF(iterator); + iterator = NULL; - error: + if (!X509_add1_ext_i2d(self->x509, NID_info_access, ext, 0, X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add AIA extension to certificate"); - if (pkey) - EVP_PKEY_free(pkey); + ok = 1; - return NULL; + error: + AUTHORITY_INFO_ACCESS_free(ext); + ACCESS_DESCRIPTION_free(a); + Py_XDECREF(item); + Py_XDECREF(iterator); + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char x509_crl_object_verify__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>verify</name>\n" -" <parameter>key</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" The <classname>X509Crl</classname> method\n" -" <function>verify</function> is based on the\n" -" <function>X509_CRL_verify</function> function. Unlike the\n" -" <classname>X509</classname> function of the same name, this\n" -" function simply checks the CRL was signed with the private key\n" -" which corresponds the parameter <parameter>key</parameter>.\n" -" <parameter>key</parameter> should be an instance of\n" -" <classname>Asymmetric</classname> and contain a public key.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_crldp__doc__[] = + "Get CRL Distribution Point (CRLDP) values for this certificate.\n" + "\n" + "If the certificate has no CRLDP extension, this method returns None.\n" + "\n" + "Otherwise, it returns a sequence of URIs representing distributionPoint\n" + "fullName values found in the first Distribution Point. Other CRLDP\n" + "fields are ignored, as are subsequent Distribution Points and any non-URI\n" + "fullName values.\n" + ; static PyObject * -x509_crl_object_verify(x509_crl_object *self, PyObject *args) +x509_object_get_crldp(x509_object *self) { - EVP_PKEY *pkey = NULL; - asymmetric_object *asym; - int ok; + CRL_DIST_POINTS *ext = NULL; + DIST_POINT *dp = NULL; + PyObject *result = NULL; + const char *uri; + PyObject *obj; + int i, n = 0; - if (!PyArg_ParseTuple(args, "O!", &asymmetrictype, &asym)) - goto error; + ENTERING(x509_object_get_crldp); - if ((pkey = EVP_PKEY_new()) == NULL) - lose("could not allocate memory"); + if ((ext = X509_get_ext_d2i(self->x509, NID_crl_distribution_points, NULL, NULL)) == NULL || + (dp = sk_DIST_POINT_value(ext, 0)) == NULL || + dp->distpoint == NULL || + dp->distpoint->type != 0) + Py_RETURN_NONE; - if (!EVP_PKEY_assign_RSA(pkey, asym->cipher)) - lose("EVP_PKEY assignment error"); + for (i = 0; i < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); i++) { + GENERAL_NAME *gn = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, i); + if (gn->type == GEN_URI) + n++; + } - ok = X509_CRL_verify(self->crl, pkey); + if (((result = PyTuple_New(n)) == NULL)) + goto error; - return PyBool_FromLong(ok); + n = 0; - error: + for (i = 0; i < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); i++) { + GENERAL_NAME *gn = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, i); + if (gn->type == GEN_URI) { + uri = (char *) ASN1_STRING_data(gn->d.uniformResourceIdentifier); + if ((obj = PyString_FromString(uri)) == NULL) + goto error; + PyTuple_SET_ITEM(result, n++, obj); + } + } - if (pkey) - EVP_PKEY_free(pkey); + sk_DIST_POINT_pop_free(ext, DIST_POINT_free); + return result; + error: + sk_DIST_POINT_pop_free(ext, DIST_POINT_free); + Py_XDECREF(result); return NULL; - } +static char x509_object_set_crldp__doc__[] = + "Set CRLDP values for this certificate.\n" + "\n" + "Argument is a iterable which returns distributionPoint fullName URIs.\n" + ; + static PyObject * -x509_crl_object_write_helper(x509_crl_object *self, PyObject *args, int format) +x509_object_set_crldp(x509_object *self, PyObject *args) { - int len = 0, ret = 0; - char *buf = NULL; - BIO *out_bio = NULL; - PyObject *cert = NULL; + CRL_DIST_POINTS *ext = NULL; + PyObject *fullNames = NULL; + PyObject *iterator = NULL; + PyObject *item = NULL; + DIST_POINT *dp = NULL; + GENERAL_NAME *gn = NULL; + Py_ssize_t urilen; + char *uri; + int ok = 0; - if (!PyArg_ParseTuple(args, "")) + ENTERING(x509_object_set_crldp); + + if (!PyArg_ParseTuple(args, "O", &fullNames)) goto error; - out_bio = BIO_new(BIO_s_mem()); + if ((ext = sk_DIST_POINT_new_null()) == NULL || + (dp = DIST_POINT_new()) == NULL || + (dp->distpoint = DIST_POINT_NAME_new()) == NULL || + (dp->distpoint->name.fullname = sk_GENERAL_NAME_new_null()) == NULL) + lose_no_memory(); - switch (format) { + dp->distpoint->type = 0; - case DER_FORMAT: - if (!i2d_X509_CRL_bio(out_bio, self->crl)) - lose("unable to write certificate"); - break; + if ((iterator = PyObject_GetIter(fullNames)) == NULL) + goto error; - case PEM_FORMAT: - if (!PEM_write_bio_X509_CRL(out_bio, self->crl)) - lose("unable to write certificate"); + while ((item = PyIter_Next(iterator)) != NULL) { - default: - lose("internal error, unknown output format"); - } + if (PyString_AsStringAndSize(item, &uri, &urilen) < 0) + goto error; - if ((len = BIO_ctrl_pending(out_bio)) == 0) - lose("unable to get bytes stored in bio"); + if ((gn = GENERAL_NAME_new()) == NULL || + (gn->d.uniformResourceIdentifier = ASN1_IA5STRING_new()) == NULL || + !ASN1_OCTET_STRING_set(gn->d.uniformResourceIdentifier, (unsigned char *) uri, urilen)) + lose_no_memory(); - if ((buf = malloc(len)) == NULL) - lose("unable to allocate memory"); + gn->type = GEN_URI; - if ((ret = BIO_read(out_bio, buf, len)) != len) - lose("unable to write out cert"); + if (!sk_GENERAL_NAME_push(dp->distpoint->name.fullname, gn)) + lose_no_memory(); - cert = Py_BuildValue("s#", buf, len); + gn = NULL; + Py_XDECREF(item); + item = NULL; + } - BIO_free(out_bio); - free(buf); - return cert; + Py_XDECREF(iterator); + iterator = NULL; - error: + if (!sk_DIST_POINT_push(ext, dp)) + lose_no_memory(); - if (out_bio) - BIO_free(out_bio); + dp = NULL; - if (buf) - free(buf); + if (!X509_add1_ext_i2d(self->x509, NID_crl_distribution_points, ext, 0, X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add CRLDP extension to certificate"); - return NULL; -} + ok = 1; -static char x509_crl_object_pem_write__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>pemWrite</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a PEM encoded CRL as a\n" -" string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + error: + sk_DIST_POINT_pop_free(ext, DIST_POINT_free); + DIST_POINT_free(dp); + GENERAL_NAME_free(gn); + Py_XDECREF(item); + Py_XDECREF(iterator); -static PyObject * -x509_crl_object_pem_write(x509_crl_object *self, PyObject *args) -{ - return x509_crl_object_write_helper(self, args, PEM_FORMAT); + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char x509_crl_object_der_write__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>derWrite</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a DER encoded CRL as a string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_get_certificate_policies__doc__[] = + "Get Certificate Policies values for this certificate.\n" + "\n" + "If this certificate has no Certificate Policies extension, this method\n" + "returns None.\n" + "\n" + "Otherwise, this method returns a sequence of Object Identifiers.\n" + "\n" + "Policy qualifiers, if any, are ignored.\n" + ; static PyObject * -x509_crl_object_der_write(x509_crl_object *self, PyObject *args) +x509_object_get_certificate_policies(x509_object *self) { - return x509_crl_object_write_helper(self, args, DER_FORMAT); + CERTIFICATEPOLICIES *ext = NULL; + PyObject *result = NULL; + PyObject *obj; + int i; + + ENTERING(x509_object_get_certificate_policies); + + if ((ext = X509_get_ext_d2i(self->x509, NID_certificate_policies, NULL, NULL)) == NULL) + Py_RETURN_NONE; + + if (((result = PyTuple_New(sk_POLICYINFO_num(ext))) == NULL)) + goto error; + + for (i = 0; i < sk_POLICYINFO_num(ext); i++) { + POLICYINFO *p = sk_POLICYINFO_value(ext, i); + + if ((obj = ASN1_OBJECT_to_PyString(p->policyid)) == NULL) + goto error; + + PyTuple_SET_ITEM(result, i, obj); + } + + sk_POLICYINFO_pop_free(ext, POLICYINFO_free); + return result; + + error: + sk_POLICYINFO_pop_free(ext, POLICYINFO_free); + Py_XDECREF(result); + return NULL; } -static char x509_crl_object_pprint__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Crl</memberof>\n" -" <name>pprint</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a formatted string showing the information\n" -" held in the CRL.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_object_set_certificate_policies__doc__[] = + "Set Certificate Policies for this certificate.\n" + "\n" + "Argument is a iterable which returns policy OIDs.\n" + "\n" + "Policy qualifier are not supported.\n" + "\n" + "The extension will be marked as critical, since there's not much point\n" + "in using this extension without making it critical.\n" + ; static PyObject * -x509_crl_object_pprint(x509_crl_object *self, PyObject *args) +x509_object_set_certificate_policies(x509_object *self, PyObject *args) { - int len = 0, ret = 0; - char *buf = NULL; - BIO *out_bio = NULL; - PyObject *crl = NULL; + CERTIFICATEPOLICIES *ext = NULL; + PyObject *policies = NULL; + PyObject *iterator = NULL; + POLICYINFO *pol = NULL; + PyObject *item = NULL; + const char *oid; + int ok = 0; + + ENTERING(x509_object_set_certificate_policies); - if (!PyArg_ParseTuple(args, "")) + if (!PyArg_ParseTuple(args, "O", &policies)) goto error; - out_bio = BIO_new(BIO_s_mem()); + if ((ext = sk_POLICYINFO_new_null()) == NULL) + lose_no_memory(); - if (!X509_CRL_print(out_bio, self->crl)) - lose("unable to write crl"); + if ((iterator = PyObject_GetIter(policies)) == NULL) + goto error; - if ((len = BIO_ctrl_pending(out_bio)) == 0) - lose("unable to get bytes stored in bio"); + while ((item = PyIter_Next(iterator)) != NULL) { - if ((buf = malloc(len)) == NULL) - lose("unable to allocate memory"); + if ((oid = PyString_AsString(item)) == NULL) + goto error; - if ((ret = BIO_read(out_bio, buf, len)) != len) - lose("unable to write out cert"); + if ((pol = POLICYINFO_new()) == NULL) + lose_no_memory(); - crl = Py_BuildValue("s#", buf, len); + if ((pol->policyid = OBJ_txt2obj(oid, 1)) == NULL) + lose("Couldn't parse OID"); - BIO_free(out_bio); - free(buf); - return crl; + if (!sk_POLICYINFO_push(ext, pol)) + lose_no_memory(); - error: + pol = NULL; + Py_XDECREF(item); + item = NULL; + } - if (out_bio) - BIO_free(out_bio); + Py_XDECREF(iterator); + iterator = NULL; - if (buf) - free(buf); + if (!X509_add1_ext_i2d(self->x509, NID_certificate_policies, ext, 1, X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add CERTIFICATE_POLICIES extension to certificate"); - return NULL; + ok = 1; + error: + POLICYINFO_free(pol); + sk_POLICYINFO_pop_free(ext, POLICYINFO_free); + Py_XDECREF(item); + Py_XDECREF(iterator); + + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static struct PyMethodDef x509_crl_object_methods[] = { - {"sign", (PyCFunction)x509_crl_object_sign, METH_VARARGS, NULL}, - {"verify", (PyCFunction)x509_crl_object_verify, METH_VARARGS, NULL}, - {"getVersion", (PyCFunction)x509_crl_object_get_version, METH_VARARGS, NULL}, - {"setVersion", (PyCFunction)x509_crl_object_set_version, METH_VARARGS, NULL}, - {"getIssuer", (PyCFunction)x509_crl_object_get_issuer, METH_VARARGS, NULL}, - {"setIssuer", (PyCFunction)x509_crl_object_set_issuer, METH_VARARGS, NULL}, - {"getThisUpdate", (PyCFunction)x509_crl_object_get_this_update, METH_VARARGS, NULL}, - {"setThisUpdate", (PyCFunction)x509_crl_object_set_this_update, METH_VARARGS, NULL}, - {"getNextUpdate", (PyCFunction)x509_crl_object_get_next_update, METH_VARARGS, NULL}, - {"setNextUpdate", (PyCFunction)x509_crl_object_set_next_update, METH_VARARGS, NULL}, - {"setRevoked", (PyCFunction)x509_crl_object_set_revoked, METH_VARARGS, NULL}, - {"getRevoked", (PyCFunction)x509_crl_object_get_revoked, METH_VARARGS, NULL}, - {"addExtension", (PyCFunction)X509_crl_object_add_extension, METH_VARARGS, NULL}, - {"clearExtensions", (PyCFunction)X509_crl_object_clear_extensions, METH_VARARGS, NULL}, - {"countExtensions", (PyCFunction)X509_crl_object_count_extensions, METH_VARARGS, NULL}, - {"getExtension", (PyCFunction)X509_crl_object_get_extension, METH_VARARGS, NULL}, - {"pemWrite", (PyCFunction)x509_crl_object_pem_write, METH_VARARGS, NULL}, - {"derWrite", (PyCFunction)x509_crl_object_der_write, METH_VARARGS, NULL}, - {"pprint", (PyCFunction)x509_crl_object_pprint, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ -}; +/* + * May want EKU handlers eventually, skip for now. + */ + +static char x509_object_pprint__doc__[] = + "Return a pretty-printed rendition of this certificate.\n" + ; static PyObject * -x509_crl_object_getattr(x509_crl_object *self, char *name) +x509_object_pprint(x509_object *self) { - return Py_FindMethod(x509_crl_object_methods, (PyObject *)self, name); + PyObject *result = NULL; + BIO *bio = NULL; + + ENTERING(x509_object_pprint); + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); + + if (!X509_print(bio, self->x509)) + lose_openssl_error("Unable to pretty-print certificate"); + + result = BIO_to_PyString_helper(bio); + + error: /* Fall through */ + BIO_free(bio); + return result; } -static void -x509_crl_object_dealloc(x509_crl_object *self, char *name) -{ - X509_CRL_free(self->crl); - PyObject_Del(self); -} - -static char x509_crltype__doc__[] = -"<class>\n" -" <header>\n" -" <name>X509Crl</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides access to OpenSSL X509 CRL management\n" -" facilities.\n" -" </para>\n" -" </body>\n" -"</class>\n" -; +static struct PyMethodDef x509_object_methods[] = { + Define_Method(pemWrite, x509_object_pem_write, METH_NOARGS), + Define_Method(derWrite, x509_object_der_write, METH_NOARGS), + Define_Method(sign, x509_object_sign, METH_VARARGS), + Define_Method(getPublicKey, x509_object_get_public_key, METH_NOARGS), + Define_Method(setPublicKey, x509_object_set_public_key, METH_VARARGS), + Define_Method(getVersion, x509_object_get_version, METH_NOARGS), + Define_Method(setVersion, x509_object_set_version, METH_VARARGS), + Define_Method(getSerial, x509_object_get_serial, METH_NOARGS), + Define_Method(setSerial, x509_object_set_serial, METH_VARARGS), + Define_Method(getIssuer, x509_object_get_issuer, METH_VARARGS), + Define_Method(setIssuer, x509_object_set_issuer, METH_VARARGS), + Define_Method(getSubject, x509_object_get_subject, METH_VARARGS), + Define_Method(setSubject, x509_object_set_subject, METH_VARARGS), + Define_Method(getNotBefore, x509_object_get_not_before, METH_NOARGS), + Define_Method(getNotAfter, x509_object_get_not_after, METH_NOARGS), + Define_Method(setNotAfter, x509_object_set_not_after, METH_VARARGS), + Define_Method(setNotBefore, x509_object_set_not_before, METH_VARARGS), + Define_Method(clearExtensions, x509_object_clear_extensions, METH_NOARGS), + Define_Method(pprint, x509_object_pprint, METH_NOARGS), + Define_Method(getSKI, x509_object_get_ski, METH_NOARGS), + Define_Method(setSKI, x509_object_set_ski, METH_VARARGS), + Define_Method(getAKI, x509_object_get_aki, METH_NOARGS), + Define_Method(setAKI, x509_object_set_aki, METH_VARARGS), + Define_Method(getKeyUsage, x509_object_get_key_usage, METH_NOARGS), + Define_Method(setKeyUsage, x509_object_set_key_usage, METH_VARARGS), + Define_Method(getRFC3779, x509_object_get_rfc3779, METH_NOARGS), + Define_Method(setRFC3779, x509_object_set_rfc3779, METH_KEYWORDS), + Define_Method(getBasicConstraints, x509_object_get_basic_constraints, METH_NOARGS), + Define_Method(setBasicConstraints, x509_object_set_basic_constraints, METH_VARARGS), + Define_Method(getSIA, x509_object_get_sia, METH_NOARGS), + Define_Method(setSIA, x509_object_set_sia, METH_KEYWORDS), + Define_Method(getAIA, x509_object_get_aia, METH_NOARGS), + Define_Method(setAIA, x509_object_set_aia, METH_VARARGS), + Define_Method(getCRLDP, x509_object_get_crldp, METH_NOARGS), + Define_Method(setCRLDP, x509_object_set_crldp, METH_VARARGS), + Define_Method(getCertificatePolicies, x509_object_get_certificate_policies, METH_NOARGS), + Define_Method(setCertificatePolicies, x509_object_set_certificate_policies, METH_VARARGS), + Define_Class_Method(pemRead, x509_object_pem_read, METH_VARARGS), + Define_Class_Method(pemReadFile, x509_object_pem_read_file, METH_VARARGS), + Define_Class_Method(derRead, x509_object_der_read, METH_VARARGS), + Define_Class_Method(derReadFile, x509_object_der_read_file, METH_VARARGS), + {NULL} +}; -static PyTypeObject x509_crltype = { +static char POW_X509_Type__doc__[] = + "This class represents an X.509 certificate.\n" + "\n" + LAME_DISCLAIMER_IN_ALL_CLASS_DOCUMENTATION + ; + +static PyTypeObject POW_X509_Type = { PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "X509Crl", /*tp_name*/ - sizeof(x509_crl_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)x509_crl_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)x509_crl_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - x509_crltype__doc__ /* Documentation string */ + 0, /* ob_size */ + "rpki.POW.X509", /* tp_name */ + sizeof(x509_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)x509_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_X509_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + x509_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 */ + x509_object_new, /* tp_new */ }; -/*========== x509 crl Code ==========*/ -/*========== revoked Code ==========*/ -static x509_revoked_object* x509_revoked_object_new(void) -{ - x509_revoked_object *self = NULL; + - if ((self = PyObject_New(x509_revoked_object, &x509_revokedtype)) == NULL) - goto error; +/* + * X509Store object. + */ - self->revoked = X509_REVOKED_new(); +static PyObject * +x509_store_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds) +{ + x509_store_object *self = NULL; - return self; + ENTERING(x509_store_object_new); - error: + if ((self = (x509_store_object *) type->tp_alloc(type, 0)) != NULL && + (self->store = X509_STORE_new()) != NULL) + return (PyObject *) self; Py_XDECREF(self); return NULL; } -static char x509_revoked_object_set_serial__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <name>setSerial</name>\n" -" <parameter>serial</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method sets the serial number in the serial field of\n" -" this object. <parameter>serial</parameter> should be an\n" -" integer.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static void +x509_store_object_dealloc(x509_store_object *self) +{ + ENTERING(x509_store_object_dealloc); + X509_STORE_free(self->store); + self->ob_type->tp_free((PyObject*) self); +} + +static char x509_store_object_add_trust__doc__[] = + "Add a trusted certificate to this certificate store object.\n" + "\n" + "The \"certificate\" parameter should be an instance of the X509 class.\n" + ; static PyObject * -x509_revoked_object_set_serial(x509_revoked_object *self, PyObject *args) +x509_store_object_add_trust(x509_store_object *self, PyObject *args) { - int serial = 0; + x509_object *x509 = NULL; - if (!PyArg_ParseTuple(args, "i", &serial)) + ENTERING(x509_store_object_add_trust); + + if (!PyArg_ParseTuple(args, "O!", &POW_X509_Type, &x509)) goto error; - if (!ASN1_INTEGER_set(self->revoked->serialNumber, serial)) - lose("unable to set serial number"); + X509_STORE_add_cert(self->store, x509->x509); Py_RETURN_NONE; @@ -3556,4384 +3500,4502 @@ x509_revoked_object_set_serial(x509_revoked_object *self, PyObject *args) return NULL; } -static char x509_revoked_object_get_serial__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <name>getSerial</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method gets the serial number in the serial field of\n" -" this object.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char x509_store_object_add_crl__doc__[] = + "Add a CRL to this certificate store object.\n" + "\n" + "The \"crl\" parameter should be an instance of the CRL class.\n" + ; static PyObject * -x509_revoked_object_get_serial(x509_revoked_object *self, PyObject *args) +x509_store_object_add_crl(x509_store_object *self, PyObject *args) { - int serial = 0; + crl_object *crl = NULL; - if (!PyArg_ParseTuple(args, "")) + ENTERING(x509_store_object_add_crl); + + if (!PyArg_ParseTuple(args, "O!", &POW_CRL_Type, &crl)) goto error; - if ((serial = ASN1_INTEGER_get(self->revoked->serialNumber)) == -1) - lose("unable to get serial number"); + X509_STORE_add_crl(self->store, crl->crl); - return Py_BuildValue("i", serial); + Py_RETURN_NONE; error: return NULL; } -static char x509_revoked_object_get_date__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <name>getDate</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this function returns a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static struct PyMethodDef x509_store_object_methods[] = { + Define_Method(addTrust, x509_store_object_add_trust, METH_VARARGS), + Define_Method(addCrl, x509_store_object_add_crl, METH_VARARGS), + {NULL} +}; + +static char POW_X509Store_Type__doc__[] = + "This class holds the OpenSSL certificate store objects used in CMS\n" + "verification.\n" + "\n" + LAME_DISCLAIMER_IN_ALL_CLASS_DOCUMENTATION + ; + +static PyTypeObject POW_X509Store_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + "rpki.POW.X509Store", /* tp_name */ + sizeof(x509_store_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)x509_store_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_X509Store_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + x509_store_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 */ + x509_store_object_new, /* tp_new */ +}; + + + +/* + * CRL object. + */ static PyObject * -x509_revoked_object_get_date(x509_revoked_object *self, PyObject *args) +crl_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds) { - if (!PyArg_ParseTuple(args, "")) - goto error; + crl_object *self = NULL; - return ASN1_TIME_to_Python(self->revoked->revocationDate); + ENTERING(crl_object_new); - error: + if ((self = (crl_object *) type->tp_alloc(type, 0)) != NULL && + (self->crl = X509_CRL_new()) != NULL) + return (PyObject *) self; + Py_XDECREF(self); return NULL; } -static char x509_revoked_object_set_date__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <name>setDate</name>\n" -" <parameter>time</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" In a change from previous releases, for reasons of portability\n" -" and to avoid hard to fix issues with problems in unreliable time\n" -" functions, this accepts one parameter, a UTCTime string. You\n" -" can use the function <function>time2utc</function> to convert to a\n" -" string if you like and <function>utc2time</function> to back.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static void +crl_object_dealloc(crl_object *self) +{ + ENTERING(crl_object_dealloc); + X509_CRL_free(self->crl); + self->ob_type->tp_free((PyObject*) self); +} static PyObject * -x509_revoked_object_set_date(x509_revoked_object *self, PyObject *args) +crl_object_pem_read_helper(PyTypeObject *type, BIO *bio) { - char *time = NULL; + crl_object *self; - if (!PyArg_ParseTuple(args, "s", &time)) + ENTERING(crl_object_pem_read_helper); + + if ((self = (crl_object *) crl_object_new(type, NULL, NULL)) == NULL) goto error; - if (!python_ASN1_TIME_set_string(self->revoked->revocationDate, time)) - lose_type_error("Could not set revocationDate"); + if (!PEM_read_bio_X509_CRL(bio, &self->crl, NULL, NULL)) + lose_openssl_error("Couldn't PEM encoded load CRL"); - Py_RETURN_NONE; + return (PyObject *) self; error: - + Py_XDECREF(self); return NULL; } -static char X509_revoked_object_add_extension__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <name>addExtension</name>\n" -" <parameter>extensionName</parameter>\n" -" <parameter>critical</parameter>\n" -" <parameter>extensionValue</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method adds an extension to this revocation.\n" -" <parameter>extensionName</parameter> should be the of the\n" -" extension. <parameter>critical</parameter> should an integer, 1\n" -" for true and 0 for clase. <parameter>extensionValue</parameter>\n" -" should be a string, DER encoded value of the extension. The name\n" -" of the extension must be correct according to OpenSSL and can be\n" -" checkd in the <constant>objects.h</constant> header file, part of\n" -" the OpenSSL source distrobution. In the majority of cases they\n" -" are the same as those defined in <constant>POW._oids</constant>\n" -" but if you do encounter problems is may be worth checking.\n" -" </para>\n" -" <example>\n" -" <title><function>addExtension</function> method usage</title>\n" -" <programlisting>\n" -" reason = POW.pkix.CrlReason()\n" -" reason.set(1)\n" -" revocation.addExtension('CRLReason', 0, reason.toString())\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</method>\n" -; - static PyObject * -X509_revoked_object_add_extension(x509_revoked_object *self, PyObject *args) +crl_object_der_read_helper(PyTypeObject *type, BIO *bio) { - int critical = 0, nid = 0, len = 0; - char *name = NULL; - unsigned char *buf = NULL; - ASN1_OCTET_STRING *octetString = NULL; - X509_EXTENSION *extn = NULL; + crl_object *self; - if (!PyArg_ParseTuple(args, "sis#", &name, &critical, &buf, &len)) + ENTERING(crl_object_der_read_helper); + + if ((self = (crl_object *) crl_object_new(type, NULL, NULL)) == NULL) goto error; - if ((octetString = M_ASN1_OCTET_STRING_new()) == NULL) - lose("could not allocate memory"); + if (!d2i_X509_CRL_bio(bio, &self->crl)) + lose_openssl_error("Couldn't load DER encoded CRL"); - if (!ASN1_OCTET_STRING_set(octetString, buf, strlen((char *) buf))) - lose("could not set ASN1 Octect string"); + return (PyObject *) self; - if ((nid = OBJ_txt2nid(name)) == NID_undef) - lose("extension has unknown object identifier"); + error: + Py_XDECREF(self); + return NULL; +} - if ((extn = X509_EXTENSION_create_by_NID(NULL, nid, critical, octetString)) == NULL) - lose("unable to create ASN1 X509 Extension object"); +static char crl_object_pem_read__doc__[] = + "Read a PEM-encoded CRL object from a string.\n" + ; - if (!self->revoked->extensions && (self->revoked->extensions = sk_X509_EXTENSION_new_null()) == NULL) - lose("unable to allocate memory"); +static PyObject * +crl_object_pem_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(crl_object_pem_read); + return read_from_string_helper(crl_object_pem_read_helper, type, args); +} - if (!sk_X509_EXTENSION_push(self->revoked->extensions, extn)) - lose("unable to add extension"); +static char crl_object_pem_read_file__doc__[] = + "Read a PEM-encoded CRL object from a file.\n" + ; - Py_RETURN_NONE; +static PyObject * +crl_object_pem_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(crl_object_pem_read_file); + return read_from_file_helper(crl_object_pem_read_helper, type, args); +} - error: +static char crl_object_der_read__doc__[] = + "Read a DER-encoded CRL object from a string.\n" + ; - if (extn) - X509_EXTENSION_free(extn); +static PyObject * +crl_object_der_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(crl_object_der_read); + return read_from_string_helper(crl_object_der_read_helper, type, args); +} - return NULL; +static char crl_object_der_read_file__doc__[] = + "Read a DER-encoded CRL object from a file.\n" + ; + +static PyObject * +crl_object_der_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(crl_object_der_read_file); + return read_from_file_helper(crl_object_der_read_helper, type, args); } -static char X509_revoked_object_clear_extensions__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <name>clearExtensions</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method clears the structure which holds the extension for\n" -" this revocation.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_get_version__doc__[] = + "return the version number of this CRL.\n" + ; + +static PyObject * +crl_object_get_version(crl_object *self) +{ + ENTERING(crl_object_get_version); + return Py_BuildValue("l", X509_CRL_get_version(self->crl)); +} + +static char crl_object_set_version__doc__[] = + "Set the version number of this CRL.\n" + "\n" + "The \"version\" parameter should be a positive integer.\n" + ; static PyObject * -X509_revoked_object_clear_extensions(x509_revoked_object *self, PyObject *args) +crl_object_set_version(crl_object *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) + long version = 0; + + ENTERING(crl_object_set_version); + + if (!PyArg_ParseTuple(args, "i", &version)) goto error; - if (self->revoked->extensions) { - sk_X509_EXTENSION_free(self->revoked->extensions); - self->revoked->extensions = NULL; - } + if (!X509_CRL_set_version(self->crl, version)) + lose_no_memory(); Py_RETURN_NONE; error: - return NULL; } -static char X509_revoked_object_count_extensions__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <name>countExtensions</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the size of the structure which holds the\n" -" extension for this revocation.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_get_issuer__doc__[] = + "Return issuer name of this CRL.\n" + "\n" + "See the \"getIssuer()\" method of the X509 class for more details.\n" + ; static PyObject * -X509_revoked_object_count_extensions(x509_revoked_object *self, PyObject *args) +crl_object_get_issuer(crl_object *self, PyObject *args) { - int num = 0; - - if (!PyArg_ParseTuple(args, "")) - goto error; + PyObject *result = NULL; + int format = OIDNAME_FORMAT; - if (self->revoked->extensions) - num = sk_X509_EXTENSION_num(self->revoked->extensions); + ENTERING(crl_object_get_issuer); - return Py_BuildValue("i", num); + if (!PyArg_ParseTuple(args, "|i", &format)) + goto error; - error: + result = x509_object_helper_get_name(X509_CRL_get_issuer(self->crl), format); - return NULL; + error: /* Fall through */ + return result; } -static char X509_revoked_object_get_extension__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <name>getExtension</name>\n" -" <parameter>index</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a tuple equivalent the parameters of\n" -" <function>addExtension</function>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_set_issuer__doc__[] = + "Set this CRL's issuer name.\n" + "\n" + "See the \"setIssuer()\" method of the X509 class for details.\n" + ; static PyObject * -X509_revoked_object_get_extension(x509_revoked_object *self, PyObject *args) +crl_object_set_issuer(crl_object *self, PyObject *args) { - int num = 0, index = 0, ext_nid = 0; - char const *ext_ln = NULL; - char unknown_ext [] = "unknown"; - X509_EXTENSION *ext; + PyObject *name_sequence = NULL; + X509_NAME *name = NULL; - if (!PyArg_ParseTuple(args, "i", &index)) - goto error; + ENTERING(crl_object_set_issuer); - if (self->revoked->extensions) - num = sk_X509_EXTENSION_num(self->revoked->extensions); + if (!PyArg_ParseTuple(args, "O", &name_sequence)) + goto error; - if (index >= num) - lose("certificate does not have that many extensions"); + if (!PySequence_Check(name_sequence)) + lose_type_error("Inapropriate type"); - if ((ext = sk_X509_EXTENSION_value(self->revoked->extensions, index)) == NULL) - lose("could not get extension"); + if ((name = x509_object_helper_set_name(name_sequence)) == NULL) + goto error; - if ((ext_nid = OBJ_obj2nid(ext->object)) == NID_undef) - lose("extension has unknown object identifier"); + if (!X509_CRL_set_issuer_name(self->crl, name)) + lose_openssl_error("Unable to set issuer name"); - if ((ext_ln = OBJ_nid2sn(ext_nid)) == NULL) - ext_ln = unknown_ext; + X509_NAME_free(name); - return Py_BuildValue("sis#", ext_ln, ext->critical, ext->value->data, ext->value->length); + Py_RETURN_NONE; error: - + X509_NAME_free(name); return NULL; } -static struct PyMethodDef x509_revoked_object_methods[] = { - {"getSerial", (PyCFunction)x509_revoked_object_get_serial, METH_VARARGS, NULL}, - {"setSerial", (PyCFunction)x509_revoked_object_set_serial, METH_VARARGS, NULL}, - {"getDate", (PyCFunction)x509_revoked_object_get_date, METH_VARARGS, NULL}, - {"setDate", (PyCFunction)x509_revoked_object_set_date, METH_VARARGS, NULL}, - {"addExtension", (PyCFunction)X509_revoked_object_add_extension, METH_VARARGS, NULL}, - {"clearExtensions", (PyCFunction)X509_revoked_object_clear_extensions, METH_VARARGS, NULL}, - {"countExtensions", (PyCFunction)X509_revoked_object_count_extensions, METH_VARARGS, NULL}, - {"getExtension", (PyCFunction)X509_revoked_object_get_extension, METH_VARARGS, NULL}, +/* + * NB: OpenSSL is confused about the name of this field, probably for + * backwards compatability with some ancient mistake. What RFC 5280 + * calls "thisUpdate", OpenSSL calls "lastUpdate". + */ - {NULL} /* sentinel */ -}; +static char crl_object_set_this_update__doc__[] = + "Set this CRL's \"thisUpdate\" value.\n" + "\n" + "The \"time\" parameter should be a datetime object.\n" + ; static PyObject * -x509_revoked_object_getattr(x509_revoked_object *self, char *name) +crl_object_set_this_update (crl_object *self, PyObject *args) { - return Py_FindMethod(x509_revoked_object_methods, (PyObject *) self, name); -} + PyObject *o = NULL; + ASN1_TIME *t = NULL; -static void -x509_revoked_object_dealloc(x509_revoked_object *self, char *name) -{ - X509_REVOKED_free(self->revoked); - PyObject_Del(self); -} - -static char x509_revokedtype__doc__[] = -"<class>\n" -" <header>\n" -" <name>X509Revoked</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides a container for details of a revoked\n" -" certificate. It normally would only be used in association with\n" -" a CRL, its not much use by itself. Indeed the only reason this\n" -" class exists is because in the future POW is likely to be extended\n" -" to support extensions for certificates, CRLs and revocations.\n" -" <classname>X509Revoked</classname> existing as an object in its\n" -" own right will make adding this support easier, while avoiding\n" -" backwards compatibility issues.\n" -" </para>\n" -" </body>\n" -"</class>\n" -; - -static PyTypeObject x509_revokedtype = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "X509Revoked", /*tp_name*/ - sizeof(x509_revoked_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)x509_revoked_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)x509_revoked_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - x509_revokedtype__doc__ /* Documentation string */ -}; -/*========== x509 revoked Code ==========*/ - -/*========== ssl Code ==========*/ -static char ssl_object_use_certificate__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>useCertificate</name>\n" -" <parameter>cert</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" The parameter <parameter>cert</parameter> must be an\n" -" instance of the <classname>X590</classname> class and must be\n" -" called before <function>setFd</function>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + ENTERING(crl_object_set_this_update); -static PyObject * -ssl_object_use_certificate(ssl_object *self, PyObject *args) -{ - x509_object *x509 = NULL; - - if (!PyArg_ParseTuple(args, "O!", &x509type, &x509)) + if (!PyArg_ParseTuple(args, "O", &o)) goto error; - if (self->ctxset) - lose("cannot be called after setFd()"); + if ((t = Python_to_ASN1_TIME(o, 1)) == NULL) + lose("Couldn't convert thisUpdate string"); - if (!SSL_CTX_use_certificate(self->ctx, x509->x509)) - lose("could not use certificate"); + if (!X509_CRL_set_lastUpdate(self->crl, t)) /* sic */ + lose("Couldn't set thisUpdate"); + ASN1_TIME_free(t); Py_RETURN_NONE; error: - + ASN1_TIME_free(t); return NULL; } +static char crl_object_get_this_update__doc__[] = + "Return this CRL's \"thisUpdate\" value as a datetime.\n" + ; + static PyObject * -ssl_object_add_certificate(ssl_object *self, PyObject *args) +crl_object_get_this_update (crl_object *self) { - x509_object *x509 = NULL; - X509 *x = NULL; + ENTERING(crl_object_get_this_update); + return ASN1_TIME_to_Python(X509_CRL_get_lastUpdate(self->crl)); /* sic */ +} - if (!PyArg_ParseTuple(args, "O!", &x509type, &x509)) - goto error; +static char crl_object_set_next_update__doc__[] = + "Set this CRL's \"nextUpdate\" value.\n" + "\n" + "The \"time\" parameter should be a datetime object.\n" + ; + +static PyObject * +crl_object_set_next_update (crl_object *self, PyObject *args) +{ + PyObject *o = NULL; + ASN1_TIME *t = NULL; - if (self->ctxset) - lose("cannot be called after setFd()"); + ENTERING(crl_object_set_next_update); - if ((x = X509_dup(x509->x509)) == NULL) - lose("could not duplicate X509 object"); + if (!PyArg_ParseTuple(args, "O", &o)) + goto error; - if (!SSL_CTX_add_extra_chain_cert(self->ctx, x)) - lose_openssl_error("Could not add certificate"); + if ((t = Python_to_ASN1_TIME(o, 1)) == NULL) + lose("Couldn't parse nextUpdate string"); - x = NULL; + if (!X509_CRL_set_nextUpdate(self->crl, t)) + lose("Couldn't set nextUpdate"); + ASN1_TIME_free(t); Py_RETURN_NONE; error: + ASN1_TIME_free(t); + return NULL; +} - if (x) - X509_free(x); +static char crl_object_get_next_update__doc__[] = + "Returns this CRL's \"nextUpdate\" value as a datetime.\n" + ; - return NULL; +static PyObject * +crl_object_get_next_update (crl_object *self) +{ + ENTERING(crl_object_get_next_update); + return ASN1_TIME_to_Python(X509_CRL_get_nextUpdate(self->crl)); } +static char crl_object_add_revocations__doc__[] = + "This method adds a collection of revocations to this CRL.\n" + "\n" + "The \"iterable\" parameter should be an iterable object which returns\n" + "two-element sequences. The first element of each pair should be the\n" + "revoked serial number (an integer), the second element should be the\n" + "revocation date (a datetime object).\n" + ; + static PyObject * -ssl_object_add_trust(ssl_object *self, PyObject *args) +crl_object_add_revocations(crl_object *self, PyObject *args) { - x509_object *x509 = NULL; - X509 *x = NULL; + PyObject *iterable = NULL; + PyObject *iterator = NULL; + PyObject *item = NULL; + PyObject *fast = NULL; + X509_REVOKED *revoked = NULL; + ASN1_INTEGER *serial = NULL; + ASN1_TIME *date = NULL; + int ok = 0; + + ENTERING(crl_object_add_revocations); - if (!PyArg_ParseTuple(args, "O!", &x509type, &x509)) + if (!PyArg_ParseTuple(args, "O", &iterable) || + (iterator = PyObject_GetIter(iterable)) == NULL) goto error; - if (self->ctxset) - lose("Cannot be called after setFd()"); + while ((item = PyIter_Next(iterator)) != NULL) { - if (self->trusted_certs == NULL && - (self->trusted_certs = sk_X509_new_null()) == NULL) - lose("Couldn't allocate trusted certificate stack"); + if ((fast = PySequence_Fast(item, "Revocation entry must be a sequence")) == NULL) + goto error; - if ((x = X509_dup(x509->x509)) == NULL) - lose("Couldn't duplicate X509 object"); + if (PySequence_Fast_GET_SIZE(fast) != 2) + lose_type_error("Revocation entry must be two-element sequence"); - if (!sk_X509_push(self->trusted_certs, x)) - lose("Couldn't push cert onto trusted certificate stack"); + if ((serial = PyLong_to_ASN1_INTEGER(PySequence_Fast_GET_ITEM(fast, 0))) == NULL || + (date = Python_to_ASN1_TIME(PySequence_Fast_GET_ITEM(fast, 1), 1)) == NULL) + goto error; - x = NULL; + if ((revoked = X509_REVOKED_new()) == NULL || + !X509_REVOKED_set_serialNumber(revoked, serial) || + !X509_REVOKED_set_revocationDate(revoked, date)) + lose_no_memory(); - Py_RETURN_NONE; + ASN1_INTEGER_free(serial); + serial = NULL; - error: + ASN1_TIME_free(date); + date = NULL; - if (x) - X509_free(x); + if (!X509_CRL_add0_revoked(self->crl, revoked)) + lose_no_memory(); - return NULL; + revoked = NULL; + Py_XDECREF(item); + Py_XDECREF(fast); + item = fast = NULL; + } + + if (!X509_CRL_sort(self->crl)) + lose_openssl_error("Couldn't sort CRL"); + + ok = 1; + + error: + Py_XDECREF(iterator); + Py_XDECREF(item); + Py_XDECREF(fast); + X509_REVOKED_free(revoked); + ASN1_INTEGER_free(serial); + ASN1_TIME_free(date); + + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char ssl_object_use_key__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>useKey</name>\n" -" <parameter>key</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" The parameter <parameter>key</parameter> must be an\n" -" instance of the <classname>Asymmetric</classname> class and\n" -" must contain the private key. This function cannot be called\n" -" after <function>useKey</function>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_get_revoked__doc__[] = + "Return a sequence of two-element tuples representing the sequence of\n" + "revoked certificates listed in this CRL.\n" + "\n" + "The first element of each pair is the serialNumber of the revoked\n" + "certificate, the second element is the revocationDate.\n" + ; static PyObject * -ssl_object_use_key(ssl_object *self, PyObject *args) +crl_object_get_revoked(crl_object *self) { - asymmetric_object *asym = NULL; - EVP_PKEY *pkey = NULL; + STACK_OF(X509_REVOKED) *revoked = NULL; + X509_REVOKED *r = NULL; + PyObject *result = NULL; + PyObject *item = NULL; + PyObject *serial = NULL; + PyObject *date = NULL; + int i; - if (!PyArg_ParseTuple(args, "O!", &asymmetrictype, &asym)) - goto error; + ENTERING(crl_object_get_revoked); - if (self->ctxset) - lose("cannot be called after setFd()"); + if ((revoked = X509_CRL_get_REVOKED(self->crl)) == NULL) + lose("Inexplicable NULL revocation list pointer"); - if ((pkey = EVP_PKEY_new()) == NULL) - lose("could not allocate memory"); + if ((result = PyTuple_New(sk_X509_REVOKED_num(revoked))) == NULL) + goto error; - if (asym->key_type != RSA_PRIVATE_KEY) - lose("cannot use this type of key"); + for (i = 0; i < sk_X509_REVOKED_num(revoked); i++) { + r = sk_X509_REVOKED_value(revoked, i); - if (!EVP_PKEY_set1_RSA(pkey, asym->cipher)) - lose("EVP_PKEY assignment error"); + if ((serial = ASN1_INTEGER_to_PyLong(r->serialNumber)) == NULL || + (date = ASN1_TIME_to_Python(r->revocationDate)) == NULL || + (item = Py_BuildValue("(NN)", serial, date)) == NULL) + goto error; - if (!SSL_CTX_use_PrivateKey(self->ctx, pkey)) - lose("ctx key assignment error"); + PyTuple_SET_ITEM(result, i, item); + item = serial = date = NULL; + } - Py_RETURN_NONE; + return result; error: - - if(pkey) - EVP_PKEY_free(pkey); - + Py_XDECREF(result); + Py_XDECREF(item); + Py_XDECREF(serial); + Py_XDECREF(date); return NULL; } -static char ssl_object_check_key__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>checkKey</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This simple method will return 1 if the public key, contained in\n" -" the X509 certificate this <classname>Ssl</classname> instance is using,\n" -" matches the private key this <classname>Ssl</classname> instance is using.\n" -" Otherwise it will return 0.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - -static PyObject * -ssl_object_check_key(ssl_object *self, PyObject *args) -{ - return PyBool_FromLong(SSL_CTX_check_private_key(self->ctx)); -} - -static char ssl_object_set_fd__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>setFd</name>\n" -" <parameter>descriptor</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function is used to associate a file descriptor with a\n" -" <classname>Ssl</classname> object. The file descriptor should\n" -" belong to an open TCP connection. Once this function has\n" -" been called, calling <function>useKey</function> or\n" -" <function>useCertificate</function> will, fail rasing exceptions.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_clear_extensions__doc__[] = + "Clear all extensions attached to this CRL.\n" + ; static PyObject * -ssl_object_set_fd(ssl_object *self, PyObject *args) +crl_object_clear_extensions(crl_object *self) { - int fd = 0, self_index = 0; + X509_EXTENSION *ext; - if (!PyArg_ParseTuple(args, "i", &fd)) - goto error; + ENTERING(crl_object_clear_extensions); - if ((self->ssl = SSL_new(self->ctx)) == NULL) - lose("Unable to create ssl structure"); + while ((ext = X509_CRL_delete_ext(self->crl, 0)) != NULL) + X509_EXTENSION_free(ext); - SSL_set_mode(self->ssl, (SSL_MODE_ENABLE_PARTIAL_WRITE | - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)); + Py_RETURN_NONE; +} - if (!SSL_set_fd(self->ssl, fd)) - lose("Unable to set file descriptor"); +static char crl_object_sign__doc__[] = + "Sign this CRL 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 * +crl_object_sign(crl_object *self, PyObject *args) +{ + asymmetric_object *asym; + int digest_type = SHA256_DIGEST; + const EVP_MD *digest_method = NULL; - if ((self_index = SSL_get_ex_new_index(0, "self_index", NULL, NULL, NULL)) != -1) - SSL_set_ex_data(self->ssl, self_index, self); - else - lose("Unable to create ex data index"); + ENTERING(crl_object_sign); + + 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"); - self->ctxset = 1; + if (!X509_CRL_sign(self->crl, asym->pkey, digest_method)) + lose_openssl_error("Couldn't sign CRL"); Py_RETURN_NONE; error: - return NULL; } -static char ssl_object_fileno__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>fileno</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function is used to extract the file descriptor associated\n" -" with a <classname>Ssl</classname> object.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_verify__doc__[] = + "Verifie this CRL's signature.\n" + "\n" + "The check is performed using OpenSSL's X509_CRL_verify() function.\n" + "\n" + "The \"key\" parameter should be an instance of the Asymmetric class\n" + "containing the public key of the purported signer.\n" + ; static PyObject * -ssl_object_fileno(ssl_object *self, PyObject *args) +crl_object_verify(crl_object *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) - goto error; + asymmetric_object *asym; - if (!self->ctxset) - lose("cannot be called before setFd()"); + ENTERING(crl_object_verify); - return Py_BuildValue("i", SSL_get_fd(self->ssl)); + if (!PyArg_ParseTuple(args, "O!", &POW_Asymmetric_Type, &asym)) + goto error; - error: + return PyBool_FromLong(X509_CRL_verify(self->crl, asym->pkey)); + error: return NULL; } -static char ssl_object_accept__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>accept</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function will attempt the SSL level accept with a\n" -" client. The <classname>Ssl</classname> object must have been\n" -" created using a <constant>XXXXX_SERVER_METHOD</constant> or\n" -" a <constant>XXXXX_METHOD</constant> and this function should only be\n" -" called after <function>useKey</function>,\n" -" <function>useCertificate</function> and\n" -" <function>setFd</function> functions have been called.\n" -" </para>\n" -"\n" -" <example>\n" -" <title><function>accept</function> function usage</title>\n" -" <programlisting>\n" -" keyFile = open('test/private.key', 'r')\n" -" certFile = open('test/cacert.pem', 'r')\n" -"\n" -" rsa = POW.pemRead(POW.RSA_PRIVATE_KEY, keyFile.read(), 'pass')\n" -" x509 = POW.pemRead(POW.X509_CERTIFICATE, certFile.read())\n" -"\n" -" keyFile.close()\n" -" certFile.close()\n" -"\n" -" sl = POW.Ssl(POW.SSLV23_SERVER_METHOD)\n" -" sl.useCertificate(x509)\n" -" sl.useKey(rsa)\n" -"\n" -" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n" -" s.bind(('localhost', 1111))\n" -" s.listen(5)\n" -" s2, addr = s.accept()\n" -"\n" -" s.close()\n" -"\n" -" sl.setFd(s2.fileno())\n" -" sl.accept()\n" -" print sl.read(1024)\n" -" sl.write('Message from server to client...')\n" -"\n" -" s2.close()\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_pem_write__doc__[] = + "Return the PEM encoding of this CRL, as a string.\n" + ; static PyObject * -ssl_object_accept(ssl_object *self, PyObject *args) +crl_object_pem_write(crl_object *self) { - int ret = 0; - - if (!PyArg_ParseTuple(args, "")) - goto error; - - if (!self->ctxset) - lose("cannot be called before setFd()"); + PyObject *result = NULL; + BIO *bio = NULL; - Py_BEGIN_ALLOW_THREADS; - ret = SSL_accept(self->ssl); - Py_END_ALLOW_THREADS; + ENTERING(crl_object_pem_write); - if (ret <= 0) - lose_ssl_error(self, ret); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - Py_RETURN_NONE; + if (!PEM_write_bio_X509_CRL(bio, self->crl)) + lose_openssl_error("Unable to write CRL"); - error: + result = BIO_to_PyString_helper(bio); - return NULL; + error: /* Fall through */ + BIO_free(bio); + return result; } -static char ssl_object_connect__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>connect</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function will attempt the SSL level connection with a\n" -" server. The <classname>Ssl</classname> object must have been\n" -" created using a <constant>XXXXX_CLIENT_METHOD</constant> or\n" -" a <constant>XXXXX_METHOD</constant> and this function should only be\n" -" called after <function>setFd</function> has already been\n" -" called.\n" -" </para>\n" -"\n" -" <example>\n" -" <title><function>connect</function> function usage</title>\n" -" <programlisting>\n" -" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n" -" s.connect(('localhost', 1111))\n" -"\n" -" sl = POW.Ssl(POW.SSLV23_CLIENT_METHOD)\n" -" sl.setFd(s.fileno())\n" -" sl.connect()\n" -" sl.write('Message from client to server...')\n" -" print sl.read(1024)\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_der_write__doc__[] = + "Return the DER encoding of this CRL, as a string.\n" + ; static PyObject * -ssl_object_connect(ssl_object *self, PyObject *args) +crl_object_der_write(crl_object *self) { - int ret; + PyObject *result = NULL; + BIO *bio = NULL; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(crl_object_der_write); - if (!self->ctxset) - lose("cannot be called before setFd()"); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - Py_BEGIN_ALLOW_THREADS; - ret = SSL_connect(self->ssl); - Py_END_ALLOW_THREADS; + if (!i2d_X509_CRL_bio(bio, self->crl)) + lose_openssl_error("Unable to write CRL"); - if (ret <= 0) - lose_ssl_error(self, ret); + result = BIO_to_PyString_helper(bio); - Py_RETURN_NONE; + error: /* Fall through */ + BIO_free(bio); + return result; +} - error: +static char crl_object_get_aki__doc__[] = + "Return the Authority Key Identifier (AKI) keyid value for\n" + "this CRL, or None if the CRL has no AKI extension\n" + "or has an AKI extension with no keyIdentifier value.\n" + ; - return NULL; +static PyObject * +crl_object_get_aki(crl_object *self) +{ + AUTHORITY_KEYID *ext = X509_CRL_get_ext_d2i(self->crl, NID_authority_key_identifier, NULL, NULL); + int empty = (ext == NULL || ext->keyid == NULL); + PyObject *result = NULL; + + ENTERING(crl_object_get_aki); + + if (!empty) + result = Py_BuildValue("s#", ASN1_STRING_data(ext->keyid), ASN1_STRING_length(ext->keyid)); + + AUTHORITY_KEYID_free(ext); + + if (empty) + Py_RETURN_NONE; + else + return result; } -static char ssl_object_write__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>write</name>\n" -" <parameter>string</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method writes the <parameter>string</parameter> to the\n" -" <classname>Ssl</classname> object, to be read by it's peer. This\n" -" function is analogous to the <classname>socket</classname>\n" -" classes <function>write</function> function.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_set_aki__doc__[] = + "Set the Authority Key Identifier (AKI) value for this\n" + "CRL. We only support the keyIdentifier method, as that's\n" + "the only form which is legal for RPKI certificates.\n" + ; static PyObject * -ssl_object_write(ssl_object *self, PyObject *args) +crl_object_set_aki(crl_object *self, PyObject *args) { - char *msg; - int length = 0, ret = 0; + AUTHORITY_KEYID *ext = NULL; + const unsigned char *buf = NULL; + int len, ok = 0; - if (!PyArg_ParseTuple(args, "s#", &msg, &length)) - goto error; + ENTERING(crl_object_set_aki); - if (!self->ctxset) - lose("cannot be called before setFd()"); + if (!PyArg_ParseTuple(args, "s#", &buf, &len)) + goto error; - Py_BEGIN_ALLOW_THREADS; - ret = SSL_write(self->ssl, msg, length); - Py_END_ALLOW_THREADS; + if ((ext = AUTHORITY_KEYID_new()) == NULL || + (ext->keyid = ASN1_OCTET_STRING_new()) == NULL || + !ASN1_OCTET_STRING_set(ext->keyid, buf, len)) + lose_no_memory(); - if (ret <= 0) - lose_ssl_error(self, ret); + if (!X509_CRL_add1_ext_i2d(self->crl, NID_authority_key_identifier, + ext, 0, X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add AKI extension to CRL"); - return Py_BuildValue("i", ret); + ok = 1; error: + AUTHORITY_KEYID_free(ext); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char ssl_object_read__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>read</name>\n" -" <parameter>amount = 1024</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method reads up to <parameter>amount</parameter> characters from the\n" -" <classname>Ssl</classname> object. This\n" -" function is analogous to the <classname>socket</classname>\n" -" classes <function>read</function> function.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_get_crl_number__doc__[] = + "Return the CRL Number extension value from this CRL, an integer.\n" + ; static PyObject * -ssl_object_read(ssl_object *self, PyObject *args) +crl_object_get_crl_number(crl_object *self) { - PyObject *data; - char *msg = NULL; - int len = 1024, ret = 0; + ASN1_INTEGER *ext = X509_CRL_get_ext_d2i(self->crl, NID_crl_number, NULL, NULL); + PyObject *result = NULL; - if (!PyArg_ParseTuple(args, "|i", &len)) - goto error; + ENTERING(crl_object_get_crl_number); - if (!self->ctxset) - lose("cannot be called before setFd()"); + if (ext == NULL) + Py_RETURN_NONE; - if ((msg = malloc(len)) == NULL) - lose("unable to allocate memory"); + result = Py_BuildValue("N", ASN1_INTEGER_to_PyLong(ext)); + ASN1_INTEGER_free(ext); + return result; +} - Py_BEGIN_ALLOW_THREADS; - ret = SSL_read(self->ssl, msg, len); - Py_END_ALLOW_THREADS; +static char crl_object_set_crl_number__doc__[] = + "Set the CRL Number extension value in this CRL.\n" + "\n" + "The \"number\" parameter should be an integer.\n" + ; - if (ret <= 0) - lose_ssl_error(self, ret); +static PyObject * +crl_object_set_crl_number(crl_object *self, PyObject *args) +{ + ASN1_INTEGER *ext = NULL; + PyObject *crl_number = NULL; - data = Py_BuildValue("s#", msg, ret); + ENTERING(crl_object_set_crl_number); - free(msg); - return data; + if (!PyArg_ParseTuple(args, "O", &crl_number) || + (ext = PyLong_to_ASN1_INTEGER(crl_number)) == NULL) + goto error; - error: + if (!X509_CRL_add1_ext_i2d(self->crl, NID_crl_number, ext, 0, X509V3_ADD_REPLACE)) + lose_openssl_error("Couldn't add CRL Number extension to CRL"); - if (msg) - free(msg); + ASN1_INTEGER_free(ext); + Py_RETURN_NONE; + error: + ASN1_INTEGER_free(ext); return NULL; } -static char ssl_object_peer_certificate__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>peerCertificate</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns any peer certificate presented in the initial\n" -" SSL negotiation or <constant>None</constant>. If a certificate is\n" -" returned, it will be an instance of <classname>X509</classname>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char crl_object_pprint__doc__[] = + "Return a pretty-printed rendition of this CRL.\n" + ; static PyObject * -ssl_object_peer_certificate(ssl_object *self, PyObject *args) +crl_object_pprint(crl_object *self) { - X509 *x509 = NULL; - x509_object *x509_obj = NULL; + PyObject *result = NULL; + BIO *bio = NULL; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(crl_object_pprint); - if (!self->ctxset) - lose("cannot be called before setFd()"); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - if ((x509_obj = X509_object_new()) == NULL) - lose("could not create x509 object"); + if (!X509_CRL_print(bio, self->crl)) + lose_openssl_error("Unable to pretty-print CRL"); - x509 = SSL_get_peer_certificate(self->ssl); + result = BIO_to_PyString_helper(bio); - if (x509) { - X509_free(x509_obj->x509); - x509_obj->x509 = x509; - return (PyObject *) x509_obj; - } - else { - Py_XDECREF(x509_obj); - Py_RETURN_NONE; - } + error: /* Fall through */ + BIO_free(bio); + return result; +} - error: +static struct PyMethodDef crl_object_methods[] = { + Define_Method(sign, crl_object_sign, METH_VARARGS), + Define_Method(verify, crl_object_verify, METH_VARARGS), + Define_Method(getVersion, crl_object_get_version, METH_NOARGS), + Define_Method(setVersion, crl_object_set_version, METH_VARARGS), + Define_Method(getIssuer, crl_object_get_issuer, METH_VARARGS), + Define_Method(setIssuer, crl_object_set_issuer, METH_VARARGS), + Define_Method(getThisUpdate, crl_object_get_this_update, METH_NOARGS), + Define_Method(setThisUpdate, crl_object_set_this_update, METH_VARARGS), + Define_Method(getNextUpdate, crl_object_get_next_update, METH_NOARGS), + Define_Method(setNextUpdate, crl_object_set_next_update, METH_VARARGS), + Define_Method(getRevoked, crl_object_get_revoked, METH_NOARGS), + Define_Method(addRevocations, crl_object_add_revocations, METH_VARARGS), + Define_Method(clearExtensions, crl_object_clear_extensions, METH_NOARGS), + Define_Method(pemWrite, crl_object_pem_write, METH_NOARGS), + Define_Method(derWrite, crl_object_der_write, METH_NOARGS), + Define_Method(pprint, crl_object_pprint, METH_NOARGS), + Define_Method(getAKI, crl_object_get_aki, METH_NOARGS), + Define_Method(setAKI, crl_object_set_aki, METH_VARARGS), + Define_Method(getCRLNumber, crl_object_get_crl_number, METH_NOARGS), + Define_Method(setCRLNumber, crl_object_set_crl_number, METH_VARARGS), + Define_Class_Method(pemRead, crl_object_pem_read, METH_VARARGS), + Define_Class_Method(pemReadFile, crl_object_pem_read_file, METH_VARARGS), + Define_Class_Method(derRead, crl_object_der_read, METH_VARARGS), + Define_Class_Method(derReadFile, crl_object_der_read_file, METH_VARARGS), + {NULL} +}; - if (x509) - X509_free(x509); +static char POW_CRL_Type__doc__[] = + "Container for OpenSSL's X509 CRL management facilities.\n" + ; - Py_XDECREF(x509_obj); - return NULL; -} +static PyTypeObject POW_CRL_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + "rpki.POW.CRL", /* tp_name */ + sizeof(crl_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)crl_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_CRL_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + crl_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 */ + crl_object_new, /* tp_new */ +}; -static char ssl_object_clear__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>clear</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method will clear the SSL session ready for\n" -" a new SSL connection. It will not effect the underlying socket.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + + +/* + * Asymmetric object. + */ static PyObject * -ssl_object_clear(ssl_object *self, PyObject *args) +asymmetric_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds) { - if (!PyArg_ParseTuple(args, "")) - goto error; + asymmetric_object *self = NULL; - if (!self->ctxset) - lose("cannot be called before setFd()"); + ENTERING(asymmetric_object_new); - if (!SSL_clear(self->ssl)) - lose("failed to clear ssl connection"); + if ((self = (asymmetric_object *) type->tp_alloc(type, 0)) == NULL) + goto error; - if (self->x509_cb_err) { - free(self->x509_cb_err); - self->x509_cb_err = NULL; - } + self->pkey = NULL; - Py_RETURN_NONE; + return (PyObject *) self; error: + Py_XDECREF(self); return NULL; } -static char ssl_object_shutdown__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>shutdown</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method will issue a <constant>shutdown</constant> signal to it's peer.\n" -" If this connection's peer has already initiated a shutdown this call\n" -" will succeed, otherwise it will raise and exception. In order to\n" -" check the shutdown handshake was successful,\n" -" <function>shutdown</function> must be called again. If no\n" -" exception is raised, the handshake is complete.\n" -" </para>\n" -" <para>\n" -" The odd\n" -" implementation of this function reflects the underlying OpenSSL\n" -" function, which reflects the SSL protocol. Although rasing an\n" -" exception is a bit annoying, the alternative, returning true all\n" -" false will not tell you why the call failed and the exception\n" -" will, at least that is the theory. Look up the exact meaning\n" -" of the exceptions in the OpenSSL man page SSL_get_error.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; - -static PyObject * -ssl_object_shutdown(ssl_object *self, PyObject *args) +static int +asymmetric_object_init(asymmetric_object *self, PyObject *args, PyObject *kwds) { - int ret = 0; + static char *kwlist[] = {"cipher", "key_size", NULL}; + int cipher_type = RSA_CIPHER, key_size = 2048; + EVP_PKEY_CTX *ctx = NULL; + int ok = 0; - if (!PyArg_ParseTuple(args, "")) + ENTERING(asymmetric_object_init); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwlist, &cipher_type, &key_size)) goto error; - if (!self->ctxset) - lose("cannot be called before setFd()"); + /* + * This silliness is necessary until we move this to an RSA-specific class method. + */ + if (cipher_type != RSA_CIPHER) + lose("unsupported cipher"); - ret = SSL_shutdown(self->ssl); + if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL || + EVP_PKEY_keygen_init(ctx) <= 0 || + EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, key_size) <= 0) + lose_openssl_error("Couldn't initialize EVP_PKEY_CTX"); /* - * The original POW behavior here seems nuts to me. SSL_shutdown() - * returns a tristate: - * - * 1: fully closed - * 0: close notification sent, waiting for peer - * -1: error, WANT_READ, or WANT_WRITE - * - * Doc claims the protocol allows us to bail on 0 return if we don't - * want to wait. So the "obvious" thing to do here is return boolean - * for 1 or 0 and raise an exception for -1. Original author's explanation - * for why he didn't do that makes no sense to me, so I've changed it. + * Should set RSA_F4 for drill, although I think it's the default now. + * Looks like the call is + * int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *pubexp); + * while RSA_F4 is a plain C long integer, so would need to make a bignum (sigh), + * which is probably BN_new()/BN_set_word()/BN_free(). */ - if (ret < 0) - lose_ssl_error(self, ret); + EVP_PKEY_free(self->pkey); + self->pkey = NULL; - return PyBool_FromLong(ret); + if (EVP_PKEY_keygen(ctx, &self->pkey) <= 0) + lose_openssl_error("Couldn't generate new RSA key"); + + ok = 1; error: + EVP_PKEY_CTX_free(ctx); - return NULL; + if (ok) + return 0; + else + return -1; } -static char ssl_object_get_shutdown__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>getShutdown</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function returns an integer indicating the state of the\n" -" SSL connection. <constant>SSL_RECEIVED_SHUTDOWN</constant>\n" -" will be set the if it's peer sends a <constant>shutdown</constant>\n" -" signal or the underlying socket\n" -" receives a close notify . The possible values are:\n" -" </para>\n" -" <simplelist>\n" -" <member><constant>SSL_NO_SHUTDOWN</constant></member>\n" -" <member><constant>SSL_SENT_SHUTDOWN</constant></member>\n" -" <member><constant>SSL_RECEIVED_SHUTDOWN</constant></member>\n" -" <member><constant>SSL_SENT_SHUTDOWN</constant> | <constant>SSL_RECEIVED_SHUTDOWN</constant></member>\n" -" </simplelist>\n" -" </body>\n" -"</method>\n" -; +static void +asymmetric_object_dealloc(asymmetric_object *self) +{ + ENTERING(asymmetric_object_dealloc); + EVP_PKEY_free(self->pkey); + self->ob_type->tp_free((PyObject*) self); +} static PyObject * -ssl_object_get_shutdown(ssl_object *self, PyObject *args) +asymmetric_object_pem_read_private_helper(PyTypeObject *type, BIO *bio, char *pass) { - int state = 0; + asymmetric_object *self = NULL; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(asymmetric_object_pem_read_private_helper); - if (!self->ctxset) - lose("cannot be called before setFd()"); + if ((self = (asymmetric_object *) asymmetric_object_new(type, NULL, NULL)) == NULL) + goto error; - state = SSL_get_shutdown(self->ssl); + if (!PEM_read_bio_PrivateKey(bio, &self->pkey, NULL, pass)) + lose_openssl_error("Couldn't load private key"); - return Py_BuildValue("i", state); + return (PyObject *) self; error: - + Py_XDECREF(self); return NULL; } -static char ssl_object_get_ciphers__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>getCiphers</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function returns a list of available ciphers ordered from\n" -" most favored to least. This function must be called after\n" -" <function>setFd</function>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +/* + * We can't use the generic read_from_*_helper() functions here + * because of optional the PEM password, so we just code the two PEM + * read cases for private keys directly. Other than the passphrase, + * code is pretty much the same as the generic functions. + */ + +static char asymmetric_object_pem_read_private__doc__[] = + "Read a PEM-encoded private key from a string.\n" + "\n" + "Optional second argument is a passphrase for the key.\n" + ; static PyObject * -ssl_object_get_ciphers(ssl_object *self, PyObject *args) +asymmetric_object_pem_read_private(PyTypeObject *type, PyObject *args) { - int i = 0; - const char *cipher = NULL; - PyObject *list = NULL, *name = NULL; + PyObject *result = NULL; + char *pass = NULL; + char *src = NULL; + BIO *bio = NULL; + int len = 0; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(asymmetric_object_pem_read_private); - if (!self->ctxset) - lose("cannot be called before setFd()"); + if (!PyArg_ParseTuple(args, "s#|s", &src, &len, &pass)) + goto error; - list = PyList_New(0); + if ((bio = BIO_new_mem_buf(src, len)) == NULL) + lose_no_memory(); - cipher = SSL_get_cipher_list(self->ssl, 0); - while (cipher) { - if ((name = PyString_FromString(cipher)) == NULL) - goto error; - if (PyList_Append(list, name) != 0) - goto error; - Py_XDECREF(name); - name = NULL; - cipher = SSL_get_cipher_list(self->ssl, ++i); - } - return list; + result = asymmetric_object_pem_read_private_helper(type, bio, pass); error: - - Py_XDECREF(name); - Py_XDECREF(list); - return NULL; + BIO_free(bio); + return result; } -static char ssl_object_set_ciphers__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>setCiphers</name>\n" -" <parameter>ciphers</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" <function>setCiphers</function>\n" -" can help protect against certain types of attacks which try to\n" -" coerce the server, client or both to negotiate a weak cipher.\n" -" <parameter>ciphers</parameter> should be a list of strings, as\n" -" produced by <function>getCiphers</function> and described in the\n" -" OpenSSL man page ciphers. <function>setCiphers</function> should\n" -" only be called after <function>setFd</function>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char asymmetric_object_pem_read_private_file__doc__[] = + "Read a PEM-encoded private key from a file.\n" + "\n" + "Optional second argument is a passphrase for the key.\n" + ; static PyObject * -ssl_object_set_ciphers(ssl_object *self, PyObject *args) +asymmetric_object_pem_read_private_file(PyTypeObject *type, PyObject *args) { - PyObject *ciphers = NULL; - PyObject *cipher = NULL; - int size = 0, cipherstrlen = 0, nextstrlen = 0, i = 0; - char *cipherstr = NULL; + const char *filename = NULL; + PyObject *result = NULL; + char *pass = NULL; + BIO *bio = NULL; + + ENTERING(asymmetric_object_pem_read_private_file); - if (!PyArg_ParseTuple(args, "O", &ciphers)) + if (!PyArg_ParseTuple(args, "s|s", &filename, &pass)) goto error; - if (!PyList_Check(ciphers) && !PyTuple_Check(ciphers)) - lose_type_error("inapropriate type"); + if ((bio = BIO_new_file(filename, "rb")) == NULL) + lose_openssl_error("Could not open file"); - if (!self->ctxset) - lose("cannot be called before setFd()"); + result = asymmetric_object_pem_read_private_helper(type, bio, pass); - cipherstr = malloc(8); // Very bogus, realloc() dosn't work without some - // previously allocated memory! Really should. - memset(cipherstr, 0, 8); - size = PySequence_Size(ciphers); - for (i = 0; i < size; i++) { - if ((cipher = PySequence_GetItem(ciphers, i)) == NULL) - goto error; + error: + BIO_free(bio); + return result; +} - if (!PyString_Check(cipher)) - lose_type_error("inapropriate type"); +static PyObject * +asymmetric_object_der_read_private_helper(PyTypeObject *type, BIO *bio) +{ + asymmetric_object *self = NULL; - cipherstrlen = strlen(cipherstr); - nextstrlen = strlen(PyString_AsString(cipher)); + ENTERING(asymmetric_object_der_read_private_helper); - if ((cipherstr = realloc(cipherstr, cipherstrlen + nextstrlen + 2)) == NULL) - lose_type_error("could allocate memory"); + if ((self = (asymmetric_object *) asymmetric_object_new(type, NULL, NULL)) == NULL) + goto error; - if (cipherstrlen) - strcat(cipherstr, ":\0"); + if (!d2i_PrivateKey_bio(bio, &self->pkey)) + lose_openssl_error("Couldn't load private key"); - strcat(cipherstr, PyString_AsString(cipher)); - Py_XDECREF(cipher); - cipher = NULL; - } - SSL_set_cipher_list(self->ssl, cipherstr); - free(cipherstr); - Py_RETURN_NONE; + return (PyObject *) self; error: - if (cipherstr) - free(cipherstr); + Py_XDECREF(self); + return NULL; +} - Py_XDECREF(cipher); +static char asymmetric_object_der_read_private__doc__[] = + "Read a DER-encoded private key from a string.\n" + ; - return NULL; +static PyObject * +asymmetric_object_der_read_private(PyTypeObject *type, PyObject *args) +{ + ENTERING(asymmetric_object_der_read_private); + return read_from_string_helper(asymmetric_object_der_read_private_helper, type, args); } -static char ssl_object_get_cipher__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>getCipher</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function returns the current cipher in use.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char asymmetric_object_der_read_private_file__doc__[] = + "Read a DER-encoded private key from a file.\n" + ; static PyObject * -ssl_object_get_cipher(ssl_object *self, PyObject *args) +asymmetric_object_der_read_private_file(PyTypeObject *type, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) + ENTERING(asymmetric_object_der_read_private_file); + return read_from_file_helper(asymmetric_object_der_read_private_helper, type, args); +} + +static PyObject * +asymmetric_object_pem_read_public_helper(PyTypeObject *type, BIO *bio) +{ + asymmetric_object *self = NULL; + + ENTERING(asymmetric_object_pem_read_public_helper); + + if ((self = (asymmetric_object *) asymmetric_object_new(type, NULL, NULL)) == NULL) goto error; - if (!self->ctxset) - lose("cannot be called before setFd()"); + if (!PEM_read_bio_PUBKEY(bio, &self->pkey, NULL, NULL)) + lose_openssl_error("Couldn't load public key"); - return Py_BuildValue("s", SSL_get_cipher(self->ssl)); + return (PyObject *) self; error: - + Py_XDECREF(self); return NULL; } -static int ssl_object_verify_callback(X509_STORE_CTX *ctx, void *arg) +static PyObject * +asymmetric_object_der_read_public_helper(PyTypeObject *type, BIO *bio) { - ssl_object *self = arg; - int ok; + asymmetric_object *self = NULL; - if (self->trusted_certs) - X509_STORE_CTX_trusted_stack(ctx, self->trusted_certs); + ENTERING(asymmetric_object_der_read_public_helper); - if (self->x509_cb_err) { - free(self->x509_cb_err); - self->x509_cb_err = NULL; - } + if ((self = (asymmetric_object *) asymmetric_object_new(type, NULL, NULL)) == NULL) + goto error; - ok = X509_verify_cert(ctx) == 1; + if (!d2i_PUBKEY_bio(bio, &self->pkey)) + lose_openssl_error("Couldn't load public key"); - if (!ok) { + return (PyObject *) self; - /* - * We probably should be pushing out structured Python data here - * rather than a string, but we're pretty deep in the OpenSSL call - * chain at this point and I'd rather not risk whacky interactions - * with the Python garbage collector. Try this kludge initially, - * rewrite as something better later if it looks worth the effort. - */ + error: - BIO *b = BIO_new(BIO_s_mem()); - char *buf = NULL; - int len; - - if (!b) - goto fail; - - BIO_puts(b, "TLS validation failure:\n\n"); - - if (self->trusted_certs) { - int i; - BIO_puts(b, "Trusted cert stack\n"); - for (i = 0; i < sk_X509_num(self->trusted_certs); i++) { - X509 *x = sk_X509_value(self->trusted_certs, i); - BIO_printf(b, "[%d] ", i); - if (x) - X509_print(b, x); - else - BIO_puts(b, "<NULL>!\n"); - } - } else { - BIO_puts(b, "No trusted cert stack\n"); - } + Py_XDECREF(self); + return NULL; +} - BIO_printf(b, - "\nX509_verify_cert() error: error depth %d error %d current_cert %p current_issuer %p current_crl %p: %s\n", - ctx->error_depth, - ctx->error, - ctx->current_cert, - ctx->current_issuer, - ctx->current_crl, - X509_verify_cert_error_string(ctx->error)); - if (ctx->current_cert) - X509_print(b, ctx->current_cert); - - /* This seems to be returning garbage, don't know why */ - if (ctx->current_issuer) - X509_print(b, ctx->current_issuer); - - if ((len = BIO_ctrl_pending(b)) == 0 || (buf = malloc(len + 1)) == NULL) - goto fail; - - if (BIO_read(b, buf, len) == len) { - buf[len] = '\0'; - self->x509_cb_err = buf; - } else { - free(buf); - } +static char asymmetric_object_pem_read_public__doc__[] = + "Read a PEM-encoded public key from a string.\n" + ; - fail: - BIO_free(b); - } +static PyObject * +asymmetric_object_pem_read_public(PyTypeObject *type, PyObject *args) +{ + ENTERING(asymmetric_object_pem_read_public); + return read_from_string_helper(asymmetric_object_pem_read_public_helper, type, args); +} - return ok; +static char asymmetric_object_pem_read_public_file__doc__[] = + "Read a PEM-encoded public key from a file.\n" + ; + +static PyObject * +asymmetric_object_pem_read_public_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(asymmetric_object_pem_read_public_file); + return read_from_file_helper(asymmetric_object_pem_read_public_helper, type, args); } -static char ssl_object_set_verify_mode__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <name>setVerifyMode</name>\n" -" <parameter>mode</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function sets the behavior of the SSL handshake. The\n" -" parameter <parameter>mode</parameter> should be one of the\n" -" following:\n" -" </para>\n" -" <simplelist>\n" -" <member><constant>SSL_VERIFY_NONE</constant></member>\n" -" <member><constant>SSL_VERIFY_PEER</constant></member>\n" -" <member><constant>SSL_VERIFY_PEER</constant> |\n" -" <constant>SSL_VERIFY_FAIL_IF_NO_PEER_CERT</constant></member>\n" -" </simplelist>\n" -" <para>\n" -" See the OpenSSL man page <function>SSL_CTX_set_verify</function>\n" -" for details. This function must be called after <function>setfd</function>\n" -" has been called.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char asymmetric_object_der_read_public__doc__[] = + "Read a DER-encoded public key from a string.\n" + ; + +static PyObject * +asymmetric_object_der_read_public(PyTypeObject *type, PyObject *args) +{ + ENTERING(asymmetric_object_der_read_public); + return read_from_string_helper(asymmetric_object_der_read_public_helper, type, args); +} + +static char asymmetric_object_der_read_public_file__doc__[] = + "Read a DER-encoded public key from a file.\n" + ; static PyObject * -ssl_object_set_verify_mode(ssl_object *self, PyObject *args) +asymmetric_object_der_read_public_file(PyTypeObject *type, PyObject *args) { - int mode = 0; + ENTERING(asymmetric_object_der_read_public_file); + return read_from_file_helper(asymmetric_object_der_read_public_helper, type, args); +} + +static char asymmetric_object_pem_write_private__doc__[] = + "Return the PEM encoding of an \"Asymmetric\" private key.\n" + "\n" + "This method takes an optional parameter \"passphrase\" which, if\n" + "specified, will be used to encrypt the private key with AES-256-CBC.\n" + "\n" + "If you don't specify a passphrase, the key will not be encrypted.\n" + ; - if (!PyArg_ParseTuple(args, "i", &mode)) +static PyObject * +asymmetric_object_pem_write_private(asymmetric_object *self, PyObject *args) +{ + PyObject *result = NULL; + char *passphrase = NULL; + const EVP_CIPHER *evp_method = NULL; + BIO *bio = NULL; + + ENTERING(asymmetric_object_pem_write_private); + + if (!PyArg_ParseTuple(args, "|s", &passphrase)) goto error; - if (self->ctxset) - lose("cannot be called after setfd()"); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - SSL_CTX_set_verify(self->ctx, mode, NULL); + if (passphrase) + evp_method = EVP_aes_256_cbc(); - Py_RETURN_NONE; + if (!PEM_write_bio_PrivateKey(bio, self->pkey, evp_method, NULL, 0, NULL, passphrase)) + lose_openssl_error("Unable to write key"); - error: + result = BIO_to_PyString_helper(bio); - return NULL; + error: /* Fall through */ + BIO_free(bio); + return result; } -static struct PyMethodDef ssl_object_methods[] = { - {"useCertificate", (PyCFunction)ssl_object_use_certificate, METH_VARARGS, NULL}, - {"addCertificate", (PyCFunction)ssl_object_add_certificate, METH_VARARGS, NULL}, - {"addTrust", (PyCFunction)ssl_object_add_trust, METH_VARARGS, NULL}, - {"useKey", (PyCFunction)ssl_object_use_key, METH_VARARGS, NULL}, - {"checkKey", (PyCFunction)ssl_object_check_key, METH_VARARGS, NULL}, - {"setFd", (PyCFunction)ssl_object_set_fd, METH_VARARGS, NULL}, - {"fileno", (PyCFunction)ssl_object_fileno, METH_VARARGS, NULL}, - {"connect", (PyCFunction)ssl_object_connect, METH_VARARGS, NULL}, - {"accept", (PyCFunction)ssl_object_accept, METH_VARARGS, NULL}, - {"write", (PyCFunction)ssl_object_write, METH_VARARGS, NULL}, - {"read", (PyCFunction)ssl_object_read, METH_VARARGS, NULL}, - {"peerCertificate", (PyCFunction)ssl_object_peer_certificate, METH_VARARGS, NULL}, - {"clear", (PyCFunction)ssl_object_clear, METH_VARARGS, NULL}, - {"shutdown", (PyCFunction)ssl_object_shutdown, METH_VARARGS, NULL}, - {"getShutdown", (PyCFunction)ssl_object_get_shutdown, METH_VARARGS, NULL}, - {"getCiphers", (PyCFunction)ssl_object_get_ciphers, METH_VARARGS, NULL}, - {"setCiphers", (PyCFunction)ssl_object_set_ciphers, METH_VARARGS, NULL}, - {"getCipher", (PyCFunction)ssl_object_get_cipher, METH_VARARGS, NULL}, - {"setVerifyMode", (PyCFunction)ssl_object_set_verify_mode, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ -}; +static char asymmetric_object_pem_write_public__doc__[] = + "Return the PEM encoding of an \"Asymmetric\" public key.\n" + ; -static ssl_object * -newssl_object(int type) +static PyObject * +asymmetric_object_pem_write_public(asymmetric_object *self) { - ssl_object *self; - const SSL_METHOD *method; + PyObject *result = NULL; + BIO *bio = NULL; + ENTERING(asymmetric_object_pem_write_public); - if ((self = PyObject_NEW(ssl_object, &ssltype)) == NULL) - goto error; + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - self->ctxset = 0; - self->ssl = NULL; - self->trusted_certs = NULL; - self->x509_cb_err = NULL; - - switch (type) { - case SSLV2_SERVER_METHOD: method = SSLv2_server_method(); break; - case SSLV2_CLIENT_METHOD: method = SSLv2_client_method(); break; - case SSLV2_METHOD: method = SSLv2_method(); break; - case SSLV3_SERVER_METHOD: method = SSLv3_server_method(); break; - case SSLV3_CLIENT_METHOD: method = SSLv3_client_method(); break; - case SSLV3_METHOD: method = SSLv3_method(); break; - case TLSV1_SERVER_METHOD: method = TLSv1_server_method(); break; - case TLSV1_CLIENT_METHOD: method = TLSv1_client_method(); break; - case TLSV1_METHOD: method = TLSv1_method(); break; - case SSLV23_SERVER_METHOD: method = SSLv23_server_method(); break; - case SSLV23_CLIENT_METHOD: method = SSLv23_client_method(); break; - case SSLV23_METHOD: method = SSLv23_method(); break; + if (!PEM_write_bio_PUBKEY(bio, self->pkey)) + lose_openssl_error("Unable to write key"); - default: - lose("unknown ctx method"); - } + result = BIO_to_PyString_helper(bio); - if ((self->ctx = SSL_CTX_new(method)) == NULL) - lose("unable to create new ctx"); + error: /* Fall through */ + BIO_free(bio); + return result; +} - SSL_CTX_set_cert_verify_callback(self->ctx, ssl_object_verify_callback, self); +static char asymmetric_object_der_write_private__doc__[] = + "Return the DER encoding of an \"Asymmetric\" private key.\n" + ; - return self; +static PyObject * +asymmetric_object_der_write_private(asymmetric_object *self) +{ + PyObject *result = NULL; + BIO *bio = NULL; - error: + ENTERING(asymmetric_object_der_write_private); - Py_XDECREF(self); - return NULL; + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); + + if (!i2d_PrivateKey_bio(bio, self->pkey)) + lose_openssl_error("Unable to write private key"); + + result = BIO_to_PyString_helper(bio); + + error: /* Fall through */ + BIO_free(bio); + return result; } +static char asymmetric_object_der_write_public__doc__[] = + "Return the DER encoding of an \"Asymmetric\" public key.\n" + ; + static PyObject * -ssl_object_getattr(ssl_object *self, char *name) +asymmetric_object_der_write_public(asymmetric_object *self) { - return Py_FindMethod(ssl_object_methods, (PyObject *)self, name); -} + PyObject *result = NULL; + BIO *bio = NULL; -static void -ssl_object_dealloc(ssl_object *self) -{ - SSL_free(self->ssl); - SSL_CTX_free(self->ctx); - if (self->trusted_certs) - sk_X509_pop_free(self->trusted_certs, X509_free); - if (self->x509_cb_err) - free(self->x509_cb_err); - PyObject_Del(self); -} - -static char ssltype__doc__[] = -"<class>\n" -" <header>\n" -" <name>Ssl</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides access to the Secure Socket Layer\n" -" functionality of OpenSSL. It is designed to be a simple as\n" -" possible to use and is not designed for high performance\n" -" applications which handle many simultaneous connections. The\n" -" original motivation for writing this library was to provide a\n" -" security layer for network agents written in Python, for this\n" -" application, good performance with multiple concurrent connections\n" -" is not an issue.\n" -" </para>\n" -" </body>\n" -"</class>\n" -; + ENTERING(asymmetric_object_der_write_public); -static PyTypeObject ssltype = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "Ssl", /*tp_name*/ - sizeof(ssl_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)ssl_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)ssl_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - ssltype__doc__ /* Documentation string */ -}; -/*========== ssl Object ==========*/ + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); -/*========== asymmetric Object ==========*/ -static asymmetric_object * -asymmetric_object_new(int cipher_type, int key_size) -{ - asymmetric_object *self = NULL; + if (!i2d_PUBKEY_bio(bio, self->pkey)) + lose_openssl_error("Unable to write public key"); - self = PyObject_New(asymmetric_object, &asymmetrictype); - if (self == NULL) - goto error; + result = BIO_to_PyString_helper(bio); - if (cipher_type != RSA_CIPHER) - lose("unsupported cipher"); + error: /* Fall through */ + BIO_free(bio); + return result; +} - if ((self->cipher = RSA_generate_key(key_size,RSA_F4,NULL,NULL)) == NULL) - lose("could not generate key"); +static char asymmetric_object_calculate_ski__doc__[] = + "Calculate SKI value for this key.\n" + "\n" + "The SKI is the SHA-1 hash of key's SubjectPublicKey value.\n" + ; - self->key_type = RSA_PRIVATE_KEY; - self->cipher_type = RSA_CIPHER; +static PyObject * +asymmetric_object_calculate_ski(asymmetric_object *self) +{ + PyObject *result = NULL; + X509_PUBKEY *pubkey = NULL; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned digest_length; - return self; + ENTERING(asymmetric_object_calculate_ski); - error: + if (!X509_PUBKEY_set(&pubkey, self->pkey)) + lose_openssl_error("Couldn't extract public key"); - Py_XDECREF(self); - return NULL; + if (!EVP_Digest(pubkey->public_key->data, pubkey->public_key->length, + digest, &digest_length, EVP_sha1(), NULL)) + lose_openssl_error("Couldn't calculate SHA-1 digest of public key"); + + result = PyString_FromStringAndSize((char *) digest, digest_length); + + error: + X509_PUBKEY_free(pubkey); + return result; } -static asymmetric_object * -asymmetric_object_pem_read(int key_type, BIO *in, char *pass) -{ - asymmetric_object *self = NULL; +static struct PyMethodDef asymmetric_object_methods[] = { + Define_Method(pemWritePrivate, asymmetric_object_pem_write_private, METH_VARARGS), + Define_Method(pemWritePublic, asymmetric_object_pem_write_public, METH_NOARGS), + Define_Method(derWritePrivate, asymmetric_object_der_write_private, METH_NOARGS), + Define_Method(derWritePublic, asymmetric_object_der_write_public, METH_NOARGS), + Define_Method(calculateSKI, asymmetric_object_calculate_ski, METH_NOARGS), + Define_Class_Method(pemReadPublic, asymmetric_object_pem_read_public, METH_VARARGS), + Define_Class_Method(pemReadPublicFile, asymmetric_object_pem_read_public_file, METH_VARARGS), + Define_Class_Method(derReadPublic, asymmetric_object_der_read_public, METH_VARARGS), + Define_Class_Method(derReadPublicFile, asymmetric_object_der_read_public_file, METH_VARARGS), + Define_Class_Method(pemReadPrivate, asymmetric_object_pem_read_private, METH_VARARGS), + Define_Class_Method(pemReadPrivateFile, asymmetric_object_pem_read_private_file, METH_VARARGS), + Define_Class_Method(derReadPrivate, asymmetric_object_der_read_private, METH_VARARGS), + Define_Class_Method(derReadPrivateFile, asymmetric_object_der_read_private_file, METH_VARARGS), + {NULL} +}; - self = PyObject_New(asymmetric_object, &asymmetrictype); - if (self == NULL) - goto error; +static char POW_Asymmetric_Type__doc__[] = + "Container for OpenSSL's EVP_PKEY asymmetric key classes.\n" + "\n" + "At the moment the only supported algorithm is RSA, but that will\n" + "likely change, as BGPSEC will require EC-DSA.\n" + "\n" + LAME_DISCLAIMER_IN_ALL_CLASS_DOCUMENTATION + ; - switch (key_type) { +static PyTypeObject POW_Asymmetric_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + "rpki.POW.Asymmetric", /* tp_name */ + sizeof(asymmetric_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)asymmetric_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_Asymmetric_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + asymmetric_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 */ + (initproc) asymmetric_object_init, /* tp_init */ + 0, /* tp_alloc */ + asymmetric_object_new, /* tp_new */ +}; - case RSA_PUBLIC_KEY: - if ((self->cipher = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL)) == NULL) - lose("could not load public key"); - self->key_type = RSA_PUBLIC_KEY; - self->cipher_type = RSA_CIPHER; - break; + - case RSA_PRIVATE_KEY: - if ((self->cipher = PEM_read_bio_RSAPrivateKey(in, NULL, NULL, pass)) == NULL) - lose("could not load private key"); - self->key_type = RSA_PRIVATE_KEY; - self->cipher_type = RSA_CIPHER; - break; +/* + * Digest object. + */ - default: - lose("unknown key type"); - } +static PyObject * +digest_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds) +{ + digest_object *self = NULL; - return self; + ENTERING(digest_object_new); - error: + if ((self = (digest_object *) type->tp_alloc(type, 0)) == NULL) + goto error; - Py_XDECREF(self); + self->digest_type = 0; + + return (PyObject *) self; + + error: return NULL; } -static asymmetric_object * -asymmetric_object_der_read(int key_type, unsigned char *src, int len) +static int +digest_object_init(digest_object *self, PyObject *args, PyObject *kwds) { - asymmetric_object *self = NULL; - unsigned char *ptr = src; + static char *kwlist[] = {"digest_type", NULL}; + const EVP_MD *digest_method = NULL; + int digest_type = 0; + + ENTERING(digest_object_init); - self = PyObject_New(asymmetric_object, &asymmetrictype); - if (self == NULL) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &digest_type)) goto error; - switch (key_type) { - case RSA_PUBLIC_KEY: + if ((digest_method = evp_digest_factory(digest_type)) == NULL) + lose("Unsupported digest algorithm"); - if ((self->cipher = d2i_RSA_PUBKEY(NULL, (const unsigned char **) &ptr, len)) == NULL) - lose("could not load public key"); + self->digest_type = digest_type; + if (!EVP_DigestInit(&self->digest_ctx, digest_method)) + lose_openssl_error("Couldn't initialize digest"); - self->key_type = RSA_PUBLIC_KEY; - self->cipher_type = RSA_CIPHER; - break; + return 0; - case RSA_PRIVATE_KEY: + error: + return -1; +} - if ((self->cipher = d2i_RSAPrivateKey(NULL, (const unsigned char **) &ptr, len)) == NULL) - lose("could not load private key"); +static void +digest_object_dealloc(digest_object *self) +{ + ENTERING(digest_object_dealloc); + EVP_MD_CTX_cleanup(&self->digest_ctx); + self->ob_type->tp_free((PyObject*) self); +} - self->key_type = RSA_PRIVATE_KEY; - self->cipher_type = RSA_CIPHER; - break; +static char digest_object_update__doc__[] = + "Add data to this digest.\n" + "\n" + "the \"data\" parameter should be a string containing the data to be added.\n" + ; - default: - lose("unknown key type"); - } +static PyObject * +digest_object_update(digest_object *self, PyObject *args) +{ + char *data = NULL; + int len = 0; - return self; + ENTERING(digest_object_update); - error: + if (!PyArg_ParseTuple(args, "s#", &data, &len)) + goto error; - Py_XDECREF(self); + if (!EVP_DigestUpdate(&self->digest_ctx, data, len)) + lose_openssl_error("EVP_DigestUpdate() failed"); + + Py_RETURN_NONE; + + error: return NULL; } -static char asymmetric_object_pem_write__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Asymmetric</memberof>\n" -" <name>pemWrite</name>\n" -" <parameter>keytype</parameter>\n" -" <parameter>ciphertype = None</parameter>\n" -" <parameter>passphrase = None</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to write <classname>Asymmetric</classname>\n" -" objects out as strings. The first argument should be either\n" -" <constant>RSA_PUBLIC_KEY</constant> or\n" -" <constant>RSA_PRIVATE_KEY</constant>. Private keys are often\n" -" saved in encrypted files to offer extra security above access\n" -" control mechanisms. If the <parameter>keytype</parameter> is\n" -" <constant>RSA_PRIVATE_KEY</constant> a\n" -" <parameter>ciphertype</parameter> and\n" -" <parameter>passphrase</parameter> can also be specified. The\n" -" <parameter>ciphertype</parameter> should be one of those listed in\n" -" the <classname>Symmetric</classname> class section.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char digest_object_copy__doc__[] = + "Return a copy of this Digest object.\n" + ; static PyObject * -asymmetric_object_pem_write(asymmetric_object *self, PyObject *args) +digest_object_copy(digest_object *self) { - int key_type = 0, cipher = 0, len = 0, ret = 0; - char *kstr = NULL, *buf = NULL; - BIO *out_bio = NULL; - PyObject *asymmetric = NULL; + digest_object *new = NULL; + + ENTERING(digest_object_copy); - if (!PyArg_ParseTuple(args, "|iis", &key_type, &cipher, &kstr)) + if ((new = (digest_object *) digest_object_new(&POW_Digest_Type, NULL, NULL)) == NULL) goto error; - if (key_type == 0) - key_type = self->key_type; + new->digest_type = self->digest_type; + if (!EVP_MD_CTX_copy(&new->digest_ctx, &self->digest_ctx)) + lose_openssl_error("Couldn't copy digest"); - if ((out_bio = BIO_new(BIO_s_mem())) == NULL) - lose("unable to create new BIO"); + return (PyObject*) new; - if ((kstr && !cipher) || (cipher && !kstr)) - lose("cipher type and key string must both be supplied"); + error: - switch(key_type) { + Py_XDECREF(new); + return NULL; +} - case RSA_PRIVATE_KEY: - if (kstr && cipher) { - if (!PEM_write_bio_RSAPrivateKey(out_bio, self->cipher, evp_cipher_factory(cipher), NULL, 0, NULL, kstr)) - lose("unable to write key"); - } - else { - if (!PEM_write_bio_RSAPrivateKey(out_bio, self->cipher, NULL, NULL, 0, NULL, NULL)) - lose("unable to write key"); - } - break; +static char digest_object_digest__doc__[] = + "Return the digest of all the data which this Digest object has processed.\n" + "\n" + "This method can be called at any time and will not effect the internal\n" + "state of the Digest object.\n" + ; - case RSA_PUBLIC_KEY: - if (kstr && cipher) - lose("public keys should not encrypted"); - else { - if (!PEM_write_bio_RSA_PUBKEY(out_bio, self->cipher)) - lose("unable to write key"); - } - break; +/* + * Do we really need to do this copy? Nice general operation, but does + * anything we're doing for RPKI care? + */ - default: - lose("unsupported key type"); - } +static PyObject * +digest_object_digest(digest_object *self) +{ + unsigned char digest_text[EVP_MAX_MD_SIZE]; + EVP_MD_CTX ctx; + unsigned digest_len = 0; - if ((len = BIO_ctrl_pending(out_bio)) == 0) - lose("unable to get number of bytes in bio"); + ENTERING(digest_object_digest); - if ((buf = malloc(len)) == NULL) - lose("unable to allocate memory"); + if (!EVP_MD_CTX_copy(&ctx, &self->digest_ctx)) + lose_openssl_error("Couldn't copy digest"); - if ((ret = BIO_read(out_bio, buf, len)) != len) - lose("unable to write out key"); + EVP_DigestFinal(&ctx, digest_text, &digest_len); - asymmetric = Py_BuildValue("s#", buf, len); + EVP_MD_CTX_cleanup(&ctx); - BIO_free(out_bio); - free(buf); - return asymmetric; + return Py_BuildValue("s#", digest_text, digest_len); error: + return NULL; +} - if (out_bio); - BIO_free(out_bio); +static struct PyMethodDef digest_object_methods[] = { + Define_Method(update, digest_object_update, METH_VARARGS), + Define_Method(digest, digest_object_digest, METH_NOARGS), + Define_Method(copy, digest_object_copy, METH_NOARGS), + {NULL} +}; - if (buf) - free(buf); +static char POW_Digest_Type__doc__[] = + "This class provides access to the digest functionality of OpenSSL.\n" + "It emulates the digest modules in the Python Standard Library, but\n" + "does not currently support the \"hexdigest\" method.\n" + "\n" + "The constructor takes one parameter, the kind of Digest object to create.\n" + "This 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" + ; + +static PyTypeObject POW_Digest_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + "rpki.POW.Digest", /* tp_name */ + sizeof(digest_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)digest_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_Digest_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + digest_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 */ + (initproc) digest_object_init, /* tp_init */ + 0, /* tp_alloc */ + digest_object_new, /* tp_new */ +}; + + +/* + * CMS object. + */ + +static PyObject * +cms_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds) +{ + cms_object *self; + + ENTERING(cms_object_new); + + if ((self = (cms_object *) type->tp_alloc(type, 0)) != NULL) + return (PyObject *) self; + + Py_XDECREF(self); return NULL; } -static char asymmetric_object_der_write__doc__[] = -"<method>" -" <header>" -" <memberof>Asymmetric</memberof>" -" <name>derWrite</name>" -" <parameter>keytype</parameter>" -" </header>" -" <body>" -" <para>" -" This method is used to write <classname>Asymmetric</classname>" -" objects out as strings. The first argument should be either" -" <constant>RSA_PUBLIC_KEY</constant> or " -" <constant>RSA_PRIVATE_KEY</constant>." -" </para>" -" </body>" -"</method>" -; +static void +cms_object_dealloc(cms_object *self) +{ + ENTERING(cms_object_dealloc); + CMS_ContentInfo_free(self->cms); + self->ob_type->tp_free((PyObject*) self); +} static PyObject * -asymmetric_object_der_write(asymmetric_object *self, PyObject *args) +cms_object_pem_read_helper(PyTypeObject *type, BIO *bio) { - int len = 0, key_type = 0; - unsigned char *buf = NULL, *p = NULL; - PyObject *asymmetric = NULL; + cms_object *self; + + ENTERING(cms_object_pem_read_helper); - if (!PyArg_ParseTuple(args, "|i", &key_type)) + if ((self = (cms_object *) type->tp_new(type, NULL, NULL)) == NULL) goto error; - if (key_type == 0) - key_type = self->key_type; + if (!PEM_read_bio_CMS(bio, &self->cms, NULL, NULL)) + lose_openssl_error("Couldn't load PEM encoded CMS message"); - switch(key_type) { + return (PyObject *) self; - case RSA_PRIVATE_KEY: - len = i2d_RSAPrivateKey(self->cipher, NULL); - if ((buf = malloc(len)) == NULL) - lose("could not allocate memory"); - p = buf; - if (!i2d_RSAPrivateKey(self->cipher, &buf)) - lose("unable to write key"); - break; + error: + Py_XDECREF(self); + return NULL; +} - case RSA_PUBLIC_KEY: - len = i2d_RSA_PUBKEY(self->cipher, NULL); - if ((buf = malloc(len)) == NULL) - lose("could not allocate memory"); - p = buf; - if (!i2d_RSA_PUBKEY(self->cipher, &buf)) - lose("unable to write key"); - break; +static PyObject * +cms_object_der_read_helper(PyTypeObject *type, BIO *bio) +{ + cms_object *self; - default: - lose("unsupported key type"); - } + ENTERING(cms_object_der_read_helper); - asymmetric = Py_BuildValue("s#", p, len); + if ((self = (cms_object *) type->tp_new(type, NULL, NULL)) == NULL) + goto error; + + if (!d2i_CMS_bio(bio, &self->cms)) + lose_openssl_error("Couldn't load DER encoded CMS message"); - free(p); - return asymmetric; + return (PyObject *) self; error: + Py_XDECREF(self); + return NULL; +} - if (p) - free(p); +static char cms_object_pem_read__doc__[] = + "Read a PEM-encoded CMS object from a string.\n" + ; - return NULL; +static PyObject * +cms_object_pem_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(cms_object_pem_read); + return read_from_string_helper(cms_object_pem_read_helper, type, args); } -static char asymmetric_object_public_encrypt__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Asymmetric</memberof>\n" -" <name>publicEncrypt</name>\n" -" <parameter>plaintext</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to encrypt the <parameter>plaintext</parameter>\n" -" using a public key. It should be noted; in practice this\n" -" function would be used almost exclusively to encrypt symmetric cipher\n" -" keys and not data since asymmetric cipher operations are very slow.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char cms_object_pem_read_file__doc__[] = + "Read a PEM-encoded CMS object from a file.\n" + ; static PyObject * -asymmetric_object_public_encrypt(asymmetric_object *self, PyObject *args) +cms_object_pem_read_file(PyTypeObject *type, PyObject *args) { - unsigned char *plain_text = NULL, *cipher_text = NULL; - int len = 0, size = 0; - PyObject *obj = NULL; + ENTERING(cms_object_pem_read_file); + return read_from_file_helper(cms_object_pem_read_helper, type, args); +} - if (self->cipher_type != RSA_CIPHER) - lose("unsupported cipher type"); +static char cms_object_der_read__doc__[] = + "Read a DER-encoded CMS object from a string.\n" + ; - if (!PyArg_ParseTuple(args, "s#", &plain_text, &len)) - goto error; +static PyObject * +cms_object_der_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(cms_object_der_read); + return read_from_string_helper(cms_object_der_read_helper, type, args); +} - size = RSA_size(self->cipher); - if (len > size) - lose("plain text is too long"); +static char cms_object_der_read_file__doc__[] = + "Read a DER-encoded CMS object from a file.\n" + ; - if ((cipher_text = malloc(size + 16)) == NULL) - lose("could not allocate memory"); +static PyObject * +cms_object_der_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(cms_object_der_read_file); + return read_from_file_helper(cms_object_der_read_helper, type, args); +} - if ((len = RSA_public_encrypt(len, plain_text, cipher_text, self->cipher, RSA_PKCS1_PADDING)) < 0) - lose("could not encrypt plain text"); +static char cms_object_pem_write__doc__[] = + "Return the DER encoding of this CMS message.\n" + ; - obj = Py_BuildValue("s#", cipher_text, len); - free(cipher_text); - return obj; +static PyObject * +cms_object_pem_write(cms_object *self) +{ + PyObject *result = NULL; + BIO *bio = NULL; - error: + ENTERING(cms_object_pem_write); - if (cipher_text) - free(cipher_text); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - return NULL; + if (!PEM_write_bio_CMS(bio, self->cms)) + lose_openssl_error("Unable to write CMS object"); + + result = BIO_to_PyString_helper(bio); + + error: /* Fall through */ + BIO_free(bio); + return result; } -static char asymmetric_object_private_encrypt__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Asymmetric</memberof>\n" -" <name>privateEncrypt</name>\n" -" <parameter>plaintext</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to encrypt the <parameter>plaintext</parameter>\n" -" using a private key. It should be noted; in practice this\n" -" function would be used almost exclusively to encrypt symmetric cipher\n" -" keys and not data since asymmetric cipher operations are very slow.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char cms_object_der_write__doc__[] = + "Return the DER encoding of this CMS message.\n" + ; static PyObject * -asymmetric_object_private_encrypt(asymmetric_object *self, PyObject *args) +cms_object_der_write(cms_object *self) { - unsigned char *plain_text = NULL, *cipher_text = NULL; - int len = 0, size = 0; - PyObject *obj = NULL; + PyObject *result = NULL; + BIO *bio = NULL; + + ENTERING(cms_object_der_write); + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - if (self->key_type != RSA_PRIVATE_KEY) - lose("cannot perform private encryption with this key"); + if (!i2d_CMS_bio(bio, self->cms)) + lose_openssl_error("Unable to write CMS object"); - if (!PyArg_ParseTuple(args, "s#", &plain_text, &len)) + result = BIO_to_PyString_helper(bio); + + error: /* Fall through */ + BIO_free(bio); + return result; +} + +static int +cms_object_sign_helper(cms_object *self, + BIO *bio, + x509_object *signcert, + asymmetric_object *signkey, + PyObject *x509_sequence, + PyObject *crl_sequence, + char *oid, + unsigned flags) +{ + crl_object *crlobj = NULL; + STACK_OF(X509) *x509_stack = NULL; + int i, n, ok = 0; + CMS_ContentInfo *cms = NULL; + ASN1_OBJECT *econtent_type = NULL; + + ENTERING(cms_object_sign_helper); + + assert_no_unhandled_openssl_errors(); + + flags &= CMS_NOCERTS | CMS_NOATTR; + flags |= CMS_BINARY | CMS_NOSMIMECAP | CMS_PARTIAL | CMS_USE_KEYID; + + if ((x509_stack = x509_helper_sequence_to_stack(x509_sequence)) == NULL) goto error; - size = RSA_size(self->cipher); - if (len > size) - lose("plain text is too long"); + assert_no_unhandled_openssl_errors(); - if ((cipher_text = malloc(size + 16)) == NULL) - lose("could not allocate memory"); + if (oid && (econtent_type = OBJ_txt2obj(oid, 1)) == NULL) + lose_openssl_error("Couldn't parse OID"); - if ((len = RSA_private_encrypt(len, plain_text, cipher_text, self->cipher, RSA_PKCS1_PADDING)) < 0) - lose("could not encrypt plain text"); + assert_no_unhandled_openssl_errors(); - obj = Py_BuildValue("s#", cipher_text, len); - free(cipher_text); - return obj; + if ((cms = CMS_sign(NULL, NULL, x509_stack, bio, flags)) == NULL) + lose_openssl_error("Couldn't create CMS message"); - error: + assert_no_unhandled_openssl_errors(); - if (cipher_text) - free(cipher_text); + if (econtent_type) + CMS_set1_eContentType(cms, econtent_type); - return NULL; -} + assert_no_unhandled_openssl_errors(); -static char asymmetric_object_public_decrypt__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Asymmetric</memberof>\n" -" <name>publicDecrypt</name>\n" -" <parameter>ciphertext</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to decrypt the\n" -" <parameter>ciphertext</parameter> which has been encrypted\n" -" using the corresponding private key and the\n" -" <function>privateEncrypt</function> function.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + if (!CMS_add1_signer(cms, signcert->x509, signkey->pkey, EVP_sha256(), flags)) + lose_openssl_error("Couldn't sign CMS message"); -static PyObject * -asymmetric_object_public_decrypt(asymmetric_object *self, PyObject *args) + assert_no_unhandled_openssl_errors(); + + if (crl_sequence != Py_None) { + + if (!PySequence_Check(crl_sequence)) + lose_type_error("Inapropriate type"); + + n = PySequence_Size(crl_sequence); + + for (i = 0; i < n; i++) { + + if ((crlobj = (crl_object *) PySequence_GetItem(crl_sequence, i)) == NULL) + goto error; + + if (!POW_CRL_Check(crlobj)) + lose_type_error("Inappropriate type"); + + if (!crlobj->crl) + lose("CRL object with null CRL field!"); + + if (!CMS_add1_crl(cms, crlobj->crl)) + lose_openssl_error("Couldn't add CRL to CMS"); + + assert_no_unhandled_openssl_errors(); + + Py_XDECREF(crlobj); + crlobj = NULL; + } + } + + if (!CMS_final(cms, bio, NULL, flags)) + lose_openssl_error("Couldn't finalize CMS signatures"); + + assert_no_unhandled_openssl_errors(); + + CMS_ContentInfo_free(self->cms); + self->cms = cms; + cms = NULL; + + ok = 1; + + error: /* fall through */ + CMS_ContentInfo_free(cms); + sk_X509_free(x509_stack); + ASN1_OBJECT_free(econtent_type); + Py_XDECREF(crlobj); + + return ok; +} + +static char cms_object_sign__doc__[] = + "Sign this CMS message message with a private key.\n" + "\n" + "The \"signcert\" parameter should be the certificate against which the\n" + "message will eventually be verified, an X509 object.\n" + "\n" + "The \"key\" parameter should be the private key with which to sign the\n" + "message, an Asymmetric object.\n" + "\n" + "The \"data\" parameter should be the message to be signed, a string.\n" + "\n" + "The optional \"certs\" parameter should be a sequence of X509 objects\n" + "to be included in the signed message.\n" + "\n" + "The optional \"crls\" parameter should be a sequence of CRL objects\n" + "to be included in the signed message.\n" + "\n" + "The optional \"eContentType\" parameter should be an Object Identifier\n" + "to use as the eContentType value in the signed message.\n" + "\n" + "The optional \"flags\" parameters should be an integer holding a bitmask,\n" + "and can include the following flags:\n" + "\n" + " * CMS_NOCERTS\n" + " * CMS_NOATTR\n" + ; + +static PyObject * +cms_object_sign(cms_object *self, PyObject *args) { - unsigned char *plain_text = NULL, *cipher_text = NULL; - int len = 0, size = 0; - PyObject *obj = NULL; + asymmetric_object *signkey = NULL; + x509_object *signcert = NULL; + PyObject *x509_sequence = Py_None; + PyObject *crl_sequence = Py_None; + char *buf = NULL, *oid = NULL; + int len; + unsigned flags = 0; + BIO *bio = NULL; + int ok = 0; - if (self->cipher_type != RSA_CIPHER) - lose("unsupported cipher type"); + ENTERING(cms_object_sign); - if (!PyArg_ParseTuple(args, "s#", &cipher_text, &len)) + if (!PyArg_ParseTuple(args, "O!O!s#|OOsI", + &POW_X509_Type, &signcert, + &POW_Asymmetric_Type, &signkey, + &buf, &len, + &x509_sequence, + &crl_sequence, + &oid, + &flags)) goto error; - size = RSA_size(self->cipher); - if (len > size) - lose("cipher text is too long"); + assert_no_unhandled_openssl_errors(); - if ((plain_text = malloc(size + 16)) == NULL) - lose("could not allocate memory"); + if ((bio = BIO_new_mem_buf(buf, len)) == NULL) + lose_no_memory(); - if ((len = RSA_public_decrypt(len, cipher_text, plain_text, self->cipher, RSA_PKCS1_PADDING)) < 0) - lose("could not decrypt cipher text"); + assert_no_unhandled_openssl_errors(); - obj = Py_BuildValue("s#", plain_text, len); - free(plain_text); - return obj; + if (!cms_object_sign_helper(self, bio, signcert, signkey, + x509_sequence, crl_sequence, oid, flags)) + lose_openssl_error("Couldn't sign CMS object"); - error: + assert_no_unhandled_openssl_errors(); - if (plain_text) - free(plain_text); + ok = 1; - return NULL; -} + error: + BIO_free(bio); -static char asymmetric_object_private_decrypt__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Asymmetric</memberof>\n" -" <name>privateDecrypt</name>\n" -" <parameter>ciphertext</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to decrypt ciphertext which has been encrypted\n" -" using the corresponding public key and the\n" -" <function>publicEncrypt</function> function.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + if (ok) + Py_RETURN_NONE; + else + return NULL; +} -static PyObject * -asymmetric_object_private_decrypt(asymmetric_object *self, PyObject *args) +static BIO * +cms_object_verify_helper(cms_object *self, PyObject *args, PyObject *kwds) { - unsigned char *plain_text = NULL, *cipher_text = NULL; - int len = 0, size = 0; - PyObject *obj = NULL; + static char *kwlist[] = {"store", "certs", "flags", NULL}; + x509_store_object *store = NULL; + PyObject *certs_sequence = Py_None; + STACK_OF(X509) *certs_stack = NULL; + unsigned flags = 0, ok = 0; + BIO *bio = NULL; - if (self->key_type != RSA_PRIVATE_KEY) - lose("cannot perform private decryption with this key"); + ENTERING(cms_object_verify_helper); - if (!PyArg_ParseTuple(args, "s#", &cipher_text, &len)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OI", kwlist, &POW_X509Store_Type, &store, &certs_sequence, &flags)) goto error; - size = RSA_size(self->cipher); - if (len > size) - lose("cipher text is too long"); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - if ((plain_text = malloc(size + 16)) == NULL) - lose("could not allocate memory"); + assert_no_unhandled_openssl_errors(); - if ((len = RSA_private_decrypt(len, cipher_text, plain_text, self->cipher, RSA_PKCS1_PADDING)) < 0) - lose("could not decrypt cipher text"); + flags &= (CMS_NOINTERN | CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY | + CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY); - obj = Py_BuildValue("s#", plain_text, len); - free(plain_text); - return obj; + if (certs_sequence != Py_None && + (certs_stack = x509_helper_sequence_to_stack(certs_sequence)) == NULL) + goto error; - error: + assert_no_unhandled_openssl_errors(); + + if (CMS_verify(self->cms, certs_stack, store->store, NULL, bio, flags) <= 0) + lose_openssl_error("Couldn't verify CMS message"); - if (plain_text) - free(plain_text); + assert_no_unhandled_openssl_errors(); + + ok = 1; + + error: /* fall through */ + sk_X509_free(certs_stack); + + if (ok) + return bio; + + BIO_free(bio); return NULL; } -static char asymmetric_object_sign__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Asymmetric</memberof>\n" -" <name>sign</name>\n" -" <parameter>digesttext</parameter>\n" -" <parameter>digesttype</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to produce a signed digest text.\n" -" This instance of\n" -" <classname>Asymmetric</classname> should be a private key used for\n" -" signing. The parameter\n" -" <parameter>digesttext</parameter> should be a digest of the\n" -" data to protect against alteration and\n" -" finally <parameter>digesttype</parameter> should be one of the\n" -" following:\n" -" </para>\n" -" <simplelist>\n" -#ifndef OPENSSL_NO_MD2 -" <member><constant>MD2_DIGEST</constant></member>\n" -#endif -" <member><constant>MD5_DIGEST</constant></member>\n" -" <member><constant>SHA_DIGEST</constant></member>\n" -" <member><constant>SHA1_DIGEST</constant></member>\n" -" <member><constant>RIPEMD160_DIGEST</constant></member>\n" -" <member><constant>SHA256_DIGEST</constant></member>\n" -" <member><constant>SHA384_DIGEST</constant></member>\n" -" <member><constant>SHA512_DIGEST</constant></member>\n" -" </simplelist>\n" -" <para>\n" -" If the procedure was successful, a string containing the signed\n" -" digest is returned.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char cms_object_verify__doc__[] = + "Verify this CMS message against a trusted certificate store.\n" + "\n" + "The \"store\" parameter is an X509Store object, the trusted certificate\n" + "store to use in verification.\n" + "\n" + "The optional \"certs\" parameter is a set of certificates to search\n" + "for the signer's certificate.\n" + "\n" + "The optional \"flags\" parameter is an integer of bit flags,\n" + "containing zero or more of the following:\n" + "\n" + " * CMS_NOINTERN\n" + " * CMS_NOCRL\n" + " * CMS_NO_SIGNER_CERT_VERIFY\n" + " * CMS_NO_ATTR_VERIFY\n" + " * CMS_NO_CONTENT_VERIFY\n" + ; + +static PyObject * +cms_object_verify(cms_object *self, PyObject *args, PyObject *kwds) +{ + PyObject *result = NULL; + BIO *bio = NULL; + + ENTERING(cms_object_verify); + + if ((bio = cms_object_verify_helper(self, args, kwds)) != NULL) + result = BIO_to_PyString_helper(bio); + + BIO_free(bio); + return result; +} + +static char cms_object_eContentType__doc__[] = + "Return the eContentType OID of this CMS message.\n" + ; static PyObject * -asymmetric_object_sign(asymmetric_object *self, PyObject *args) +cms_object_eContentType(cms_object *self) { - unsigned char *digest_text = NULL, *signed_text = NULL; - unsigned int digest_len = 0, digest_type = 0, digest_nid = 0, signed_len = 0; - PyObject *obj = NULL; + const ASN1_OBJECT *oid = NULL; + PyObject *result = NULL; - if (!PyArg_ParseTuple(args, "s#i", &digest_text, &digest_len, &digest_type)) - goto error; + ENTERING(cms_object_eContentType); - if (self->key_type != RSA_PRIVATE_KEY) - lose("unsupported key type"); + if ((oid = CMS_get0_eContentType(self->cms)) == NULL) + lose_openssl_error("Couldn't extract eContentType from CMS message"); - if ((signed_text = malloc(RSA_size(self->cipher))) == NULL) - lose("could not allocate memory"); + assert_no_unhandled_openssl_errors(); - switch(digest_type) { -#ifndef OPENSSL_NO_MD2 - case MD2_DIGEST: - digest_nid = NID_md2; - digest_len = MD2_DIGEST_LENGTH; - break; -#endif - case MD5_DIGEST: - digest_nid = NID_md5; - digest_len = MD5_DIGEST_LENGTH; - break; - case SHA_DIGEST: - digest_nid = NID_sha; - digest_len = SHA_DIGEST_LENGTH; - break; - case SHA1_DIGEST: - digest_nid = NID_sha1; - digest_len = SHA_DIGEST_LENGTH; - break; - case RIPEMD160_DIGEST: - digest_nid = NID_ripemd160; - digest_len = RIPEMD160_DIGEST_LENGTH; - break; - case SHA256_DIGEST: - digest_nid = NID_sha256; - digest_len = SHA256_DIGEST_LENGTH; - break; - case SHA384_DIGEST: - digest_nid = NID_sha384; - digest_len = SHA384_DIGEST_LENGTH; + result = ASN1_OBJECT_to_PyString(oid); + + error: + return result; +} + +static char cms_object_signingTime__doc__[] = + "Return the signingTime of this CMS message.\n" + ; + +static PyObject * +cms_object_signingTime(cms_object *self) +{ + PyObject *result = NULL; + STACK_OF(CMS_SignerInfo) *sis = NULL; + CMS_SignerInfo *si = NULL; + X509_ATTRIBUTE *xa = NULL; + ASN1_TYPE *so = NULL; + int i; + + ENTERING(cms_object_signingTime); + + if ((sis = CMS_get0_SignerInfos(self->cms)) == NULL) + lose_openssl_error("Couldn't extract signerInfos from CMS message[1]"); + + if (sk_CMS_SignerInfo_num(sis) != 1) + lose_openssl_error("Couldn't extract signerInfos from CMS message[2]"); + + si = sk_CMS_SignerInfo_value(sis, 0); + + if ((i = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1)) < 0) + lose_openssl_error("Couldn't extract signerInfos from CMS message[3]"); + + if ((xa = CMS_signed_get_attr(si, i)) == NULL) + lose_openssl_error("Couldn't extract signerInfos from CMS message[4]"); + + if (xa->single) + lose("Couldn't extract signerInfos from CMS message[5]"); + + if (sk_ASN1_TYPE_num(xa->value.set) != 1) + lose("Couldn't extract signerInfos from CMS message[6]"); + + if ((so = sk_ASN1_TYPE_value(xa->value.set, 0)) == NULL) + lose("Couldn't extract signerInfos from CMS message[7]"); + + switch (so->type) { + case V_ASN1_UTCTIME: + result = ASN1_TIME_to_Python(so->value.utctime); break; - case SHA512_DIGEST: - digest_nid = NID_sha512; - digest_len = SHA512_DIGEST_LENGTH; + case V_ASN1_GENERALIZEDTIME: + result = ASN1_TIME_to_Python(so->value.generalizedtime); break; default: - lose("unsupported digest"); + lose("Couldn't extract signerInfos from CMS message[8]"); } - if (!RSA_sign(digest_nid, digest_text, digest_len, signed_text, &signed_len, self->cipher)) - lose("could not sign digest"); + error: + return result; +} + +static char cms_object_pprint__doc__[] = + "Return a pretty-printed representation of this CMS message.\n" + ; + +static PyObject * +cms_object_pprint(cms_object *self) +{ + BIO *bio = NULL; + PyObject *result = NULL; + + ENTERING(cms_object_pprint); - obj = Py_BuildValue("s#", signed_text, signed_len); - free(signed_text); - return obj; + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); + + if (!CMS_ContentInfo_print_ctx(bio, self->cms, 0, NULL)) + lose_openssl_error("Unable to pretty-print CMS object"); + + result = BIO_to_PyString_helper(bio); error: + BIO_free(bio); + return result; +} - if (signed_text) - free(signed_text); +static PyObject * +cms_object_helper_get_cert(void *cert) +{ + x509_object *obj; - return NULL; + ENTERING(cms_object_helper_get_cert); + + if ((obj = (x509_object *) x509_object_new(&POW_X509_Type, NULL, NULL)) == NULL) + return NULL; + + X509_free(obj->x509); + obj->x509 = cert; + return (PyObject *) obj; } -static char asymmetric_object_verify__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Asymmetric</memberof>\n" -" <name>verify</name>\n" -" <parameter>signedtext</parameter>\n" -" <parameter>digesttext</parameter>\n" -" <parameter>digesttype</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to verify a signed digest text.\n" -" </para>\n" -" <example>\n" -" <title><function>verify</function> method usage</title>\n" -" <programlisting>\n" -" plain_text = 'Hello World!'\n" -" print '\tPlain text:', plain_text\n" -" digest = POW.Digest(POW.RIPEMD160_DIGEST)\n" -" digest.update(plain_text)\n" -" print '\tDigest text:', digest.digest()\n" -"\n" -" privateFile = open('test/private.key', 'r')\n" -" privateKey = POW.pemRead(POW.RSA_PRIVATE_KEY, privateFile.read(), 'pass')\n" -" privateFile.close()\n" -" signed_text = privateKey.sign(digest.digest(), POW.RIPEMD160_DIGEST)\n" -" print '\tSigned text:', signed_text\n" -"\n" -" digest2 = POW.Digest(POW.RIPEMD160_DIGEST)\n" -" digest2.update(plain_text)\n" -" publicFile = open('test/public.key', 'r')\n" -" publicKey = POW.pemRead(POW.RSA_PUBLIC_KEY, publicFile.read())\n" -" publicFile.close()\n" -" if publicKey.verify(signed_text, digest2.digest(), POW.RIPEMD160_DIGEST):\n" -" print 'Signing verified!'\n" -" else:\n" -" print 'Signing gone wrong!'\n" -" </programlisting>\n" -" </example>\n" -" <para>\n" -" The parameter <parameter>signedtext</parameter> should be a\n" -" signed digest text. This instance of\n" -" <classname>Asymmetric</classname> should correspond to the private\n" -" key used to sign the digest. The parameter\n" -" <parameter>digesttext</parameter> should be a digest of the same\n" -" data used to produce the <parameter>signedtext</parameter> and\n" -" finally <parameter>digesttype</parameter> should be one of the\n" -" following:\n" -" </para>\n" -" <simplelist>\n" -#ifndef OPENSSL_NO_MD2 -" <member><constant>MD2_DIGEST</constant></member>\n" -#endif -" <member><constant>MD5_DIGEST</constant></member>\n" -" <member><constant>SHA_DIGEST</constant></member>\n" -" <member><constant>SHA1_DIGEST</constant></member>\n" -" <member><constant>RIPEMD160_DIGEST</constant></member>\n" -" <member><constant>SHA256_DIGEST</constant></member>\n" -" <member><constant>SHA384_DIGEST</constant></member>\n" -" <member><constant>SHA512_DIGEST</constant></member>\n" -" </simplelist>\n" -" <para>\n" -" If the procedure was successful, 1 is returned, otherwise 0 is\n" -" returned.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char cms_object_certs__doc__[] = + "Return any certificates embedded in this CMS message, as a\n" + "tuple of X509 objects. This tuple will be empty if the message\n" + "wrapper contains no certificates.\n" + ; static PyObject * -asymmetric_object_verify(asymmetric_object *self, PyObject *args) +cms_object_certs(cms_object *self) { - unsigned char *digest_text = NULL, *signed_text = NULL; - int digest_len = 0, digest_type = 0, digest_nid = 0, signed_len = 0; + STACK_OF(X509) *certs = NULL; + PyObject *result = NULL; - if (!PyArg_ParseTuple(args, "s#s#i", &signed_text, &signed_len, &digest_text, &digest_len, &digest_type)) - goto error; + ENTERING(cms_object_certs); - switch (digest_type) { -#ifndef OPENSSL_NO_MD2 - case MD2_DIGEST: - digest_len = MD2_DIGEST_LENGTH; - digest_nid = NID_md2; - break; -#endif - case MD5_DIGEST: - digest_len = MD5_DIGEST_LENGTH; - digest_nid = NID_md5; - break; - case SHA_DIGEST: - digest_len = SHA_DIGEST_LENGTH; - digest_nid = NID_sha; - break; - case SHA1_DIGEST: - digest_len = SHA_DIGEST_LENGTH; - digest_nid = NID_sha1; - break; - case RIPEMD160_DIGEST: - digest_len = RIPEMD160_DIGEST_LENGTH; - digest_nid = NID_ripemd160; - break; - case SHA256_DIGEST: - digest_len = SHA256_DIGEST_LENGTH; - digest_nid = NID_sha256; - break; - case SHA384_DIGEST: - digest_len = SHA384_DIGEST_LENGTH; - digest_nid = NID_sha384; - break; - case SHA512_DIGEST: - digest_len = SHA512_DIGEST_LENGTH; - digest_nid = NID_sha512; - break; - default: - lose("unsupported digest"); - } + if ((certs = CMS_get1_certs(self->cms)) != NULL) + result = stack_to_tuple_helper(CHECKED_PTR_OF(STACK_OF(X509), certs), + cms_object_helper_get_cert); + else if (!ERR_peek_error()) + result = Py_BuildValue("()"); + else + lose_openssl_error("Couldn't extract certs from CMS message"); - return PyBool_FromLong(RSA_verify(digest_nid, digest_text, digest_len, signed_text, signed_len, self->cipher)); + error: /* fall through */ + sk_X509_pop_free(certs, X509_free); + return result; +} - error: +static PyObject * +cms_object_helper_get_crl(void *crl) +{ + crl_object *obj; - return NULL; + ENTERING(cms_object_helper_get_crl); + + if ((obj = (crl_object *) crl_object_new(&POW_CRL_Type, NULL, NULL)) == NULL) + return NULL; + + X509_CRL_free(obj->crl); + obj->crl = crl; + return (PyObject *) obj; } -static struct PyMethodDef asymmetric_object_methods[] = { - {"pemWrite", (PyCFunction)asymmetric_object_pem_write, METH_VARARGS, NULL}, - {"derWrite", (PyCFunction)asymmetric_object_der_write, METH_VARARGS, NULL}, - {"publicEncrypt", (PyCFunction)asymmetric_object_public_encrypt, METH_VARARGS, NULL}, - {"privateEncrypt", (PyCFunction)asymmetric_object_private_encrypt, METH_VARARGS, NULL}, - {"privateDecrypt", (PyCFunction)asymmetric_object_private_decrypt, METH_VARARGS, NULL}, - {"publicDecrypt", (PyCFunction)asymmetric_object_public_decrypt, METH_VARARGS, NULL}, - {"sign", (PyCFunction)asymmetric_object_sign, METH_VARARGS, NULL}, - {"verify", (PyCFunction)asymmetric_object_verify, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ -}; +static char cms_object_crls__doc__[] = + "Return any CRLs embedded in this CMS message, as a tuple of\n" + "CRL objects. This tuple will be empty if the message contains no CRLs.\n" + ; static PyObject * -asymmetric_object_getattr(asymmetric_object *self, char *name) +cms_object_crls(cms_object *self) { - return Py_FindMethod(asymmetric_object_methods, (PyObject *)self, name); + STACK_OF(X509_CRL) *crls = NULL; + PyObject *result = NULL; + + ENTERING(cms_object_crls); + + if ((crls = CMS_get1_crls(self->cms)) != NULL) + result = stack_to_tuple_helper(CHECKED_PTR_OF(STACK_OF(X509_CRL), crls), + cms_object_helper_get_crl); + else if (!ERR_peek_error()) + result = Py_BuildValue("()"); + else + lose_openssl_error("Couldn't extract CRLs from CMS message"); + + error: /* fall through */ + sk_X509_CRL_pop_free(crls, X509_CRL_free); + return result; } -static void -asymmetric_object_dealloc(asymmetric_object *self, char *name) -{ - switch(self->cipher_type) { - case RSA_CIPHER: - RSA_free(self->cipher); - break; - } - PyObject_Del(self); -} - -static char asymmetrictype__doc__[] = -"<class>\n" -" <header>\n" -" <name>Asymmetric</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides access to RSA asymmetric ciphers in OpenSSL.\n" -" Other ciphers will probably be supported in the future but this is\n" -" not a priority.\n" -" </para>\n" -" </body>\n" -"</class>\n" -; +static struct PyMethodDef cms_object_methods[] = { + Define_Method(pemWrite, cms_object_pem_write, METH_NOARGS), + Define_Method(derWrite, cms_object_der_write, METH_NOARGS), + Define_Method(sign, cms_object_sign, METH_VARARGS), + Define_Method(verify, cms_object_verify, METH_KEYWORDS), + Define_Method(eContentType, cms_object_eContentType, METH_NOARGS), + Define_Method(signingTime, cms_object_signingTime, METH_NOARGS), + Define_Method(pprint, cms_object_pprint, METH_NOARGS), + Define_Method(certs, cms_object_certs, METH_NOARGS), + Define_Method(crls, cms_object_crls, METH_NOARGS), + Define_Class_Method(pemRead, cms_object_pem_read, METH_VARARGS), + Define_Class_Method(pemReadFile, cms_object_pem_read_file, METH_VARARGS), + Define_Class_Method(derRead, cms_object_der_read, METH_VARARGS), + Define_Class_Method(derReadFile, cms_object_der_read_file, METH_VARARGS), + {NULL} +}; + +static char POW_CMS_Type__doc__[] = + "Wrapper for OpenSSL's CMS class. At present this only handes signed\n" + "objects, as those are the only kind of CMS objects used in RPKI.\n" + ; -static PyTypeObject asymmetrictype = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "Asymmetric", /*tp_name*/ - sizeof(asymmetric_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)asymmetric_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)asymmetric_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - asymmetrictype__doc__ /* Documentation string */ +static PyTypeObject POW_CMS_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + "rpki.POW.CMS", /* tp_name */ + sizeof(cms_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)cms_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_CMS_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + cms_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 */ + cms_object_new, /* tp_new */ }; -/*========== asymmetric Code ==========*/ -/*========== symmetric Code ==========*/ -static symmetric_object * -symmetric_object_new(int cipher_type) -{ - symmetric_object *self = NULL; + - if ((self = PyObject_New(symmetric_object, &symmetrictype)) == NULL) - goto error; +/* + * Manifest object. + */ - self->cipher_type = cipher_type; - EVP_CIPHER_CTX_init(&self->cipher_ctx); +static PyObject * +manifest_object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + manifest_object *self = NULL; - return self; + ENTERING(manifest_object_new); - error: + if ((self = (manifest_object *) cms_object_new(type, args, kwds)) != NULL && + (self->manifest = Manifest_new()) != NULL) + return (PyObject *) self; Py_XDECREF(self); return NULL; } -static char symmetric_object_encrypt_init__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Symmetric</memberof>\n" -" <name>encryptInit</name>\n" -" <parameter>key</parameter>\n" -" <parameter>initialvalue = ''</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method sets up the cipher object to start encrypting a stream\n" -" of data. The first parameter is the key used to encrypt the\n" -" data. The second, the <parameter>initialvalue</parameter> serves\n" -" a similar purpose the the salt supplied to the Unix\n" -" <function>crypt</function> function.\n" -" The <parameter>initialvalue</parameter> is normally chosen at random and\n" -" often transmitted with the encrypted data, its purpose is to prevent\n" -" two identical plain texts resulting in two identical cipher texts.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static void +manifest_object_dealloc(manifest_object *self) +{ + ENTERING(manifest_object_dealloc); + Manifest_free(self->manifest); + cms_object_dealloc(&self->cms); +} + +static char manifest_object_verify__doc__[] = + "Verify this manifest. See the CMS class's .verify() method for details.\n" + ; static PyObject * -symmetric_object_encrypt_init(symmetric_object *self, PyObject *args) +manifest_object_verify(manifest_object *self, PyObject *args, PyObject *kwds) { - unsigned char *key = NULL, *iv = NULL, nulliv [] = ""; - const EVP_CIPHER *cipher = NULL; + BIO *bio = NULL; + int ok = 0; + + ENTERING(manifest_object_verify); - if (!PyArg_ParseTuple(args, "s|s", &key, &iv)) + if ((bio = cms_object_verify_helper(&self->cms, args, kwds)) == NULL) goto error; - if (!iv) - iv = nulliv; + if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(Manifest), bio, &self->manifest)) + lose_openssl_error("Couldn't decode manifest"); - if ((cipher = evp_cipher_factory(self->cipher_type)) == NULL) - lose("unsupported cipher"); + ok = 1; - if (!EVP_EncryptInit(&self->cipher_ctx, cipher, key, iv)) - lose("could not initialise cipher"); + error: + BIO_free(bio); - Py_RETURN_NONE; + if (ok) + Py_RETURN_NONE; + else + return NULL; +} - error: +static PyObject * +manifest_object_der_read_helper(PyTypeObject *type, BIO *bio) +{ + manifest_object *self; - return NULL; + ENTERING(manifest_object_der_read_helper); + + if ((self = (manifest_object *) cms_object_der_read_helper(type, bio)) != NULL) + self->manifest = NULL; + + return (PyObject *) self; } -static char symmetric_object_decrypt_init__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Symmetric</memberof>\n" -" <name>decryptInit</name>\n" -" <parameter>key</parameter>\n" -" <parameter>initialvalue = ''</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method sets up the cipher object to start decrypting a stream\n" -" of data. The first value must be the key used to encrypt the\n" -" data. The second parameter is the <parameter>initialvalue</parameter>\n" -" used to encrypt the data.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_der_read__doc__[] = + "Read a DER-encoded manifest object from a string.\n" + ; static PyObject * -symmetric_object_decrypt_init(symmetric_object *self, PyObject *args) +manifest_object_der_read(PyTypeObject *type, PyObject *args) { - unsigned char *key = NULL, *iv = NULL, nulliv [] = ""; - const EVP_CIPHER *cipher = NULL; - - if (!PyArg_ParseTuple(args, "s|s", &key, &iv)) - goto error; + ENTERING(manifest_object_der_read); + return read_from_string_helper(manifest_object_der_read_helper, type, args); +} - if (!iv) - iv = nulliv; +static char manifest_object_der_read_file__doc__[] = + "Read a DER-encoded manifest object from a file.\n" + ; - if ((cipher = evp_cipher_factory(self->cipher_type)) == NULL) - lose("unsupported cipher"); +static PyObject * +manifest_object_der_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(manifest_object_der_read_file); + return read_from_file_helper(manifest_object_der_read_helper, type, args); +} - if (!EVP_DecryptInit(&self->cipher_ctx, cipher, key, iv)) - lose("could not initialise cipher"); +static PyObject * +manifest_object_pem_read_helper(PyTypeObject *type, BIO *bio) +{ + manifest_object *self; - Py_RETURN_NONE; + ENTERING(manifest_object_pem_read_helper); - error: + if ((self = (manifest_object *) cms_object_pem_read_helper(type, bio)) != NULL) + self->manifest = NULL; - return NULL; + return (PyObject *) self; } -static char symmetric_object_update__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Symmetric</memberof>\n" -" <name>update</name>\n" -" <parameter>data</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method is used to process the bulk of data being encrypted\n" -" or decrypted by the cipher object. <parameter>data</parameter>\n" -" should be a string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_pem_read__doc__[] = + "Read a PEM-encoded manifest object from a string.\n" + ; static PyObject * -symmetric_object_update(symmetric_object *self, PyObject *args) +manifest_object_pem_read(PyTypeObject *type, PyObject *args) { - int inl = 0, outl = 0; - unsigned char *in = NULL, *out = NULL; - PyObject *py_out = NULL; - - if (!PyArg_ParseTuple(args, "s#", &in, &inl)) - goto error; + ENTERING(manifest_object_pem_read); + return read_from_string_helper(manifest_object_pem_read_helper, type, args); +} - if ((out = malloc(inl + EVP_CIPHER_CTX_block_size(&self->cipher_ctx))) == NULL) - lose("could not allocate memory"); +static char manifest_object_pem_read_file__doc__[] = + "Read a PEM-encoded manifest object from a file.\n" + ; - if (!EVP_CipherUpdate(&self->cipher_ctx, out, &outl, in, inl)) - lose("could not update cipher"); +static PyObject * +manifest_object_pem_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(manifest_object_pem_read_file); + return read_from_file_helper(manifest_object_pem_read_helper, type, args); +} - if ((py_out = Py_BuildValue("s#", out, outl)) == NULL) - lose("could not allocate memory"); +static char manifest_object_get_version__doc__[] = + "Return the version number of this manifest.\n" + ; - free(out); - return py_out; +static PyObject * +manifest_object_get_version(manifest_object *self) +{ + ENTERING(manifest_object_get_version); - error: + if (self->manifest == NULL) + lose_not_verified("Can't report version of unverified manifest"); - if (out) - free(out); + if (self->manifest->version) + return Py_BuildValue("N", ASN1_INTEGER_to_PyLong(self->manifest->version)); + else + return PyInt_FromLong(0); + error: return NULL; } -static char symmetric_object_final__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Symmetric</memberof>\n" -" <name>final</name>\n" -" <parameter>size = 1024</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" Most ciphers are block ciphers, that is they encrypt or decrypt a block of\n" -" data at a time. Often the data being processed will not fill an\n" -" entire block, this method processes these half-empty blocks. A\n" -" string is returned of a maximum length <parameter>size</parameter>.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_set_version__doc__[] = + "Set the version number of this manifest.\n" + "\n" + "The \"version\" parameter should be a non-negative integer.\n" + "\n" + "As of this writing, zero is both the default and the only defined version.\n" + "Attempting to set any version number other than zero will fail, as we\n" + "don't understand how to write other versions, by definition.\n" + ; static PyObject * -symmetric_object_final(symmetric_object *self, PyObject *args) +manifest_object_set_version(manifest_object *self, PyObject *args) { - int outl = 0, size = 1024; - unsigned char *out = NULL; - PyObject *py_out = NULL; + int version = 0; - if (!PyArg_ParseTuple(args, "|i", &size)) + ENTERING(manifest_object_set_version); + + if (!PyArg_ParseTuple(args, "|i", &version)) goto error; - if ((out = malloc(size + EVP_CIPHER_CTX_block_size(&self->cipher_ctx))) == NULL) - lose("could not allocate memory"); + if (version != 0) + lose("RFC 6486 only defines RPKI manifest version zero"); - if (!EVP_CipherFinal(&self->cipher_ctx, out, &outl)) - lose("could not update cipher"); + if (self->manifest == NULL) + lose_not_verified("Can't set version of unverified manifest"); - if ((py_out = Py_BuildValue("s#", out, outl)) == NULL) - lose("could not allocate memory"); + ASN1_INTEGER_free(self->manifest->version); + self->manifest->version = NULL; - free(out); - return py_out; + Py_RETURN_NONE; error: - - if (out) - free(out); - return NULL; } -static struct PyMethodDef symmetric_object_methods[] = { - {"encryptInit", (PyCFunction)symmetric_object_encrypt_init, METH_VARARGS, NULL}, - {"decryptInit", (PyCFunction)symmetric_object_decrypt_init, METH_VARARGS, NULL}, - {"update", (PyCFunction)symmetric_object_update, METH_VARARGS, NULL}, - {"final", (PyCFunction)symmetric_object_final, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ -}; +static char manifest_object_get_manifest_number__doc__[] = + "Return the manifestNumber of this manifest.\n" + ; static PyObject * -symmetric_object_getattr(symmetric_object *self, char *name) +manifest_object_get_manifest_number(manifest_object *self) { - return Py_FindMethod(symmetric_object_methods, (PyObject *)self, name); -} + ENTERING(manifest_object_get_manifest_number); -static void -symmetric_object_dealloc(symmetric_object *self, char *name) -{ - PyObject_Del(self); -} - -static char symmetrictype__doc__[] = -"<class>\n" -" <header>\n" -" <name>Symmetric</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides access to all the symmetric ciphers in OpenSSL.\n" -" Initialisation of the cipher structures is performed late, only\n" -" when <function>encryptInit</function> or\n" -" <function>decryptInit</function> is called, the\n" -" constructor only records the cipher type. It is possible to reuse\n" -" the <classname>Symmetric</classname> objects by calling\n" -" <function>encryptInit</function> or <function>decryptInit</function>\n" -" again.\n" -" </para>\n" -" <example>\n" -" <title><classname>Symmetric</classname> class usage</title>\n" -" <programlisting>\n" -" passphrase = 'my silly passphrase'\n" -" md5 = POW.Digest(POW.MD5_DIGEST)\n" -" md5.update(passphrase)\n" -" password = md5.digest()[:8]\n" -"\n" -" plaintext = 'cast test message'\n" -" cast = POW.Symmetric(POW.CAST5_CFB)\n" -" cast.encryptInit(password)\n" -" ciphertext = cast.update(plaintext) + cast.final()\n" -" print 'Cipher text:', ciphertext\n" -"\n" -" cast.decryptInit(password)\n" -" out = cast.update(ciphertext) + cast.final()\n" -" print 'Deciphered text:', out\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</class>\n" -; + if (self->manifest == NULL) + lose_not_verified("Can't get manifestNumber of unverified manifest"); -static PyTypeObject symmetrictype = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "Symmetric", /*tp_name*/ - sizeof(symmetric_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)symmetric_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)symmetric_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - symmetrictype__doc__ /* Documentation string */ -}; -/*========== symmetric Code ==========*/ + return Py_BuildValue("N", ASN1_INTEGER_to_PyLong(self->manifest->manifestNumber)); -/*========== digest Code ==========*/ -static digest_object * -digest_object_new(int digest_type) + error: + return NULL; +} + +static char manifest_object_set_manifest_number__doc__[] = + "Set the manifestNumber of this manifest.\n" + "\n" + "The \"manifestNumber\" parameter should be a non-negative integer.\n" + ; + +static PyObject * +manifest_object_set_manifest_number(manifest_object *self, PyObject *args) { - digest_object *self = NULL; + PyObject *manifestNumber = NULL; + PyObject *zero = NULL; + int ok = 0; - if ((self = PyObject_New(digest_object, &digesttype)) == NULL) + ENTERING(manifest_object_set_manifest_number); + + if (!PyArg_ParseTuple(args, "O", &manifestNumber)) goto error; - switch(digest_type) { -#ifndef OPENSSL_NO_MD2 - case MD2_DIGEST: - self->digest_type = MD2_DIGEST; - EVP_DigestInit(&self->digest_ctx, EVP_md2()); - break; -#endif - case MD5_DIGEST: - self->digest_type = MD5_DIGEST; - EVP_DigestInit(&self->digest_ctx, EVP_md5()); - break; - case SHA_DIGEST: - self->digest_type = SHA_DIGEST; - EVP_DigestInit(&self->digest_ctx, EVP_sha()); - break; - case SHA1_DIGEST: - self->digest_type = SHA1_DIGEST; - EVP_DigestInit(&self->digest_ctx, EVP_sha1()); - break; - case RIPEMD160_DIGEST: - self->digest_type = RIPEMD160_DIGEST; - EVP_DigestInit(&self->digest_ctx, EVP_ripemd160()); - break; - case SHA256_DIGEST: - self->digest_type = SHA256_DIGEST; - EVP_DigestInit(&self->digest_ctx, EVP_sha256()); - break; - case SHA384_DIGEST: - self->digest_type = SHA384_DIGEST; - EVP_DigestInit(&self->digest_ctx, EVP_sha384()); - break; - case SHA512_DIGEST: - self->digest_type = SHA512_DIGEST; - EVP_DigestInit(&self->digest_ctx, EVP_sha512()); - break; - default: - lose("unsupported digest"); + if ((zero = PyInt_FromLong(0)) == NULL) + goto error; + + switch (PyObject_RichCompareBool(manifestNumber, zero, Py_GE)) { + case -1: + goto error; + case 0: + lose("Negative manifest number is not allowed"); } - return self; + if (self->manifest == NULL) + lose_not_verified("Can't set manifestNumber of unverified manifest"); + + ASN1_INTEGER_free(self->manifest->manifestNumber); + + if ((self->manifest->manifestNumber = PyLong_to_ASN1_INTEGER(manifestNumber)) == NULL) + goto error; + + ok = 1; error: + Py_XDECREF(zero); - Py_XDECREF(self); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char digest_object_update__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Digest</memberof>\n" -" <name>update</name>\n" -" <parameter>data</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method updates the internal structures of the\n" -" <classname>Digest</classname> object with <parameter>data</parameter>.\n" -" <parameter>data</parameter> should be a string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_set_this_update__doc__[] = + "Set this manifest's \"thisUpdate\" value.\n" + "\n" + "The \"time\" parameter should be a datetime object.\n" + ; static PyObject * -digest_object_update(digest_object *self, PyObject *args) +manifest_object_set_this_update (manifest_object *self, PyObject *args) { - char *data = NULL; - int len = 0; + ASN1_TIME *t = NULL; + PyObject *o = NULL; - if (!PyArg_ParseTuple(args, "s#", &data, &len)) + ENTERING(manifest_object_set_this_update); + + if (!PyArg_ParseTuple(args, "O", &o)) goto error; - EVP_DigestUpdate(&self->digest_ctx, data, len); + if (self->manifest == NULL) + lose_not_verified("Can't set thisUpdate value of unverified manifest"); + if ((t = Python_to_ASN1_TIME(o, 0)) == NULL) + lose("Couldn't convert thisUpdate string"); + + ASN1_TIME_free(self->manifest->thisUpdate); + self->manifest->thisUpdate = t; Py_RETURN_NONE; error: - + ASN1_TIME_free(t); return NULL; } -static char digest_object_copy__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Digest</memberof>\n" -" <name>copy</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a copy of the <classname>Digest</classname>\n" -" object.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_get_this_update__doc__[] = + "Return this manifest's \"thisUpdate\" value as a datetime.\n" + ; static PyObject * -digest_object_copy(digest_object *self, PyObject *args) +manifest_object_get_this_update (manifest_object *self) { - digest_object *new = NULL; + ENTERING(manifest_object_get_this_update); - if ((new = PyObject_New(digest_object, &digesttype)) == NULL) - lose("could not allocate memory"); - - new->digest_type = self->digest_type; - if (!EVP_MD_CTX_copy(&new->digest_ctx, &self->digest_ctx)) - lose("could not copy digest"); + if (self->manifest == NULL) + lose_not_verified("Can't get thisUpdate value of unverified manifest"); - return (PyObject*)new; + return ASN1_TIME_to_Python(self->manifest->thisUpdate); error: - - Py_XDECREF(new); return NULL; } -static char digest_object_digest__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Digest</memberof>\n" -" <name>digest</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the digest of all the data which has been\n" -" processed. This function can be called at any time and will not\n" -" effect the internal structure of the <classname>digest</classname>\n" -" object.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_set_next_update__doc__[] = + "Set this manifest's \"nextUpdate\" value.\n" + "\n" + "The \"time\" parameter should be a datetime object.\n" + ; static PyObject * -digest_object_digest(digest_object *self, PyObject *args) +manifest_object_set_next_update (manifest_object *self, PyObject *args) { - unsigned char digest_text[EVP_MAX_MD_SIZE]; - void *md_copy = NULL; - unsigned digest_len = 0; + ASN1_TIME *t = NULL; + PyObject *o = NULL; + + ENTERING(manifest_object_set_next_update); - if (!PyArg_ParseTuple(args, "")) + if (!PyArg_ParseTuple(args, "O", &o)) goto error; - if ((md_copy = malloc(sizeof(EVP_MD_CTX))) == NULL) - lose("could not allocate memory"); + if (self->manifest == NULL) + lose_not_verified("Can't set nextUpdate value of unverified manifest"); - if (!EVP_MD_CTX_copy(md_copy, &self->digest_ctx)) - lose("could not copy digest"); + if ((t = Python_to_ASN1_TIME(o, 0)) == NULL) + lose("Couldn't parse nextUpdate string"); - EVP_DigestFinal(md_copy, digest_text, &digest_len); + ASN1_TIME_free(self->manifest->nextUpdate); + self->manifest->nextUpdate = t; + Py_RETURN_NONE; - free(md_copy); + error: + ASN1_TIME_free(t); + return NULL; +} - return Py_BuildValue("s#", digest_text, digest_len); +static char manifest_object_get_next_update__doc__[] = + "Return this manifest's \"nextUpdate\" value as a datetime.\n" + ; - error: +static PyObject * +manifest_object_get_next_update (manifest_object *self) +{ + ENTERING(manifest_object_get_next_update); - if (md_copy) - free(md_copy); + if (self->manifest == NULL) + lose_not_verified("Can't extract nextUpdate value of unverified manifest"); + return ASN1_TIME_to_Python(self->manifest->nextUpdate); + + error: return NULL; } -static struct PyMethodDef digest_object_methods[] = { - {"update", (PyCFunction)digest_object_update, METH_VARARGS, NULL}, - {"digest", (PyCFunction)digest_object_digest, METH_VARARGS, NULL}, - {"copy", (PyCFunction)digest_object_copy, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ -}; +static char manifest_object_get_algorithm__doc__[] = + "Return this manifest's fileHashAlg OID.\n" + ; static PyObject * -digest_object_getattr(digest_object *self, char *name) +manifest_object_get_algorithm(manifest_object *self) { - return Py_FindMethod(digest_object_methods, (PyObject *)self, name); -} + PyObject *result = NULL; -static void -digest_object_dealloc(digest_object *self, char *name) -{ - EVP_MD_CTX_cleanup(&self->digest_ctx); - PyObject_Del(self); -} - -static char digesttype__doc__[] = -"<class>\n" -" <header>\n" -" <name>Digest</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides access to the digest functionality of OpenSSL.\n" -" It emulates the digest modules in the Python Standard Library but\n" -" does not currently support the <function>hexdigest</function>\n" -" function.\n" -" </para>\n" -" <example>\n" -" <title><classname>digest</classname> class usage</title>\n" -" <programlisting>\n" -" plain_text = 'Hello World!'\n" -" sha1 = POW.Digest(POW.SHA1_DIGEST)\n" -" sha1.update(plain_text)\n" -" print '\tPlain text: Hello World! =>', sha1.digest()\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</class>\n" -; + ENTERING(manifest_object_get_algorithm); -static PyTypeObject digesttype = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "Digest", /*tp_name*/ - sizeof(digest_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)digest_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)digest_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - digesttype__doc__ /* Documentation string */ -}; -/*========== digest Code ==========*/ + if (self->manifest == NULL) + lose_not_verified("Can't extract algorithm OID of unverified manifest"); + + result = ASN1_OBJECT_to_PyString(self->manifest->fileHashAlg); + + error: + return result; +} + +static char manifest_object_set_algorithm__doc__[] = + "Set this manifest's fileHashAlg OID.\n" + ; -/*========== hmac Code ==========*/ -static hmac_object * -hmac_object_new(int digest_type, char *key, int key_len) +static PyObject * +manifest_object_set_algorithm(manifest_object *self, PyObject *args) { - hmac_object *self = NULL; - const EVP_MD *md = NULL; + ASN1_OBJECT *oid = NULL; + const char *s = NULL; - if ((self = PyObject_New(hmac_object, &hmactype)) == NULL) + ENTERING(manifest_object_set_algorithm); + + if (!PyArg_ParseTuple(args, "s", &s)) goto error; - switch (digest_type) { -#ifndef OPENSSL_NO_MD2 - case MD2_DIGEST: - md = EVP_md2(); - break; -#endif - case MD5_DIGEST: - md = EVP_md5(); - break; - case SHA_DIGEST: - md = EVP_sha(); - break; - case SHA1_DIGEST: - md = EVP_sha1(); - break; - case RIPEMD160_DIGEST: - md = EVP_ripemd160(); - break; - case SHA256_DIGEST: - md = EVP_sha256(); - break; - case SHA384_DIGEST: - md = EVP_sha384(); - break; - case SHA512_DIGEST: - md = EVP_sha512(); - break; - default: - lose("unsupported digest"); - } + if (self->manifest == NULL) + lose_not_verified("Can't set algorithm OID for unverified manifest"); - HMAC_Init(&self->hmac_ctx, key, key_len, md); + if ((oid = OBJ_txt2obj(s, 1)) == NULL) + lose_no_memory(); - return self; + ASN1_OBJECT_free(self->manifest->fileHashAlg); + self->manifest->fileHashAlg = oid; + Py_RETURN_NONE; error: - - Py_XDECREF(self); + ASN1_OBJECT_free(oid); return NULL; } -static char hmac_object_update__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Hmac</memberof>\n" -" <name>update</name>\n" -" <parameter>data</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method updates the internal structures of the\n" -" <classname>Hmac</classname> object with <parameter>data</parameter>.\n" -" <parameter>data</parameter> should be a string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_add_files__doc__[] = + "Add a collection of <filename, hash> pairs to this manifest.\n" + "\n" + "The \"iterable\" parameter should be an iterable object, each element\n" + "of which is a two-element sequence; the first element of this sequence\n" + "should be the filename (a text string), the second element should be the\n" + "hash (a binary string).\n" + ; static PyObject * -hmac_object_update(hmac_object *self, PyObject *args) +manifest_object_add_files(manifest_object *self, PyObject *args) { - unsigned char *data = NULL; - int len = 0; + PyObject *iterable = NULL; + PyObject *iterator = NULL; + PyObject *item = NULL; + PyObject *fast = NULL; + FileAndHash *fah = NULL; + char *file = NULL; + char *hash = NULL; + Py_ssize_t filelen, hashlen; + int ok = 0; - if (!PyArg_ParseTuple(args, "s#", &data, &len)) + ENTERING(manifest_object_add_files); + + if (self->manifest == NULL) + lose_not_verified("Can't add files to unverified manifest"); + + if (!PyArg_ParseTuple(args, "O", &iterable) || + (iterator = PyObject_GetIter(iterable)) == NULL) goto error; - HMAC_Update(&self->hmac_ctx, data, len); + while ((item = PyIter_Next(iterator)) != NULL) { - Py_RETURN_NONE; + if ((fast = PySequence_Fast(item, "FileAndHash entry must be a sequence")) == NULL) + goto error; + + if (PySequence_Fast_GET_SIZE(fast) != 2) + lose_type_error("FileAndHash entry must be two-element sequence"); + + if (PyString_AsStringAndSize(PySequence_Fast_GET_ITEM(fast, 0), &file, &filelen) < 0 || + PyString_AsStringAndSize(PySequence_Fast_GET_ITEM(fast, 1), &hash, &hashlen) < 0) + goto error; + + if ((fah = FileAndHash_new()) == NULL || + !ASN1_OCTET_STRING_set(fah->file, (unsigned char *) file, filelen) || + !ASN1_BIT_STRING_set(fah->hash, (unsigned char *) hash, hashlen) || + !sk_FileAndHash_push(self->manifest->fileList, fah)) + lose_no_memory(); + + fah->hash->flags &= ~7; + fah->hash->flags |= ASN1_STRING_FLAG_BITS_LEFT; + + fah = NULL; + Py_XDECREF(item); + Py_XDECREF(fast); + item = fast = NULL; + } + + ok = 1; error: + Py_XDECREF(iterator); + Py_XDECREF(item); + Py_XDECREF(fast); + FileAndHash_free(fah); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char hmac_object_copy__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Hmac</memberof>\n" -" <name>copy</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a copy of the <classname>Hmac</classname>\n" -" object.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_get_files__doc__[] = + "Return a tuple of <filename, hash> pairs representing the contents of\n" + "this manifest.\n" + ; static PyObject * -hmac_object_copy(hmac_object *self, PyObject *args) +manifest_object_get_files(manifest_object *self) { - hmac_object *new = NULL; + PyObject *result = NULL; + PyObject *item = NULL; + int i; - if ((new = PyObject_New(hmac_object, &hmactype)) == NULL) - lose("could not allocate memory"); + ENTERING(manifest_object_get_files); - memcpy(&new->hmac_ctx, &self->hmac_ctx, sizeof(HMAC_CTX)); + if (self->manifest == NULL) + lose_not_verified("Can't get files from unverified manifest"); - return (PyObject*) new; + if (self->manifest->fileList == NULL) + lose("Inexplicable NULL manifest fileList pointer"); - error: + if ((result = PyTuple_New(sk_FileAndHash_num(self->manifest->fileList))) == NULL) + goto error; - Py_XDECREF(new); + for (i = 0; i < sk_FileAndHash_num(self->manifest->fileList); i++) { + FileAndHash *fah = sk_FileAndHash_value(self->manifest->fileList, i); + + if ((item = Py_BuildValue("(s#s#)", + ASN1_STRING_data(fah->file), ASN1_STRING_length(fah->file), + ASN1_STRING_data(fah->hash), ASN1_STRING_length(fah->hash))) == NULL) + goto error; + + PyTuple_SET_ITEM(result, i, item); + item = NULL; + } + + return result; + + error: + Py_XDECREF(result); + Py_XDECREF(item); return NULL; } -static char hmac_object_mac__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>Hmac</memberof>\n" -" <name>mac</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the MAC of all the data which has been\n" -" processed. This function can be called at any time and will not\n" -" effect the internal structure of the <classname>Hmac</classname>\n" -" object.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char manifest_object_sign__doc__[] = + "Sign this manifest. See the CMS class's .sign() method for details.\n" + ; static PyObject * -hmac_object_mac(hmac_object *self, PyObject *args) +manifest_object_sign(manifest_object *self, PyObject *args) { - unsigned char hmac_text[EVP_MAX_MD_SIZE]; - void *hmac_copy = NULL; - unsigned int hmac_len = 0; + asymmetric_object *signkey = NULL; + x509_object *signcert = NULL; + PyObject *x509_sequence = Py_None; + PyObject *crl_sequence = Py_None; + char *oid = NULL; + unsigned flags = 0; + BIO *bio = NULL; + int ok = 0; - if (!PyArg_ParseTuple(args, "")) + ENTERING(manifest_object_sign); + + if (!PyArg_ParseTuple(args, "O!O!|OOsI", + &POW_X509_Type, &signcert, + &POW_Asymmetric_Type, &signkey, + &x509_sequence, + &crl_sequence, + &oid, + &flags)) goto error; - if ((hmac_copy = malloc(sizeof(HMAC_CTX))) == NULL) - lose("could not allocate memory"); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); + + assert_no_unhandled_openssl_errors(); - memcpy(hmac_copy, &self->hmac_ctx, sizeof(HMAC_CTX)); - HMAC_Final(hmac_copy, hmac_text, &hmac_len); + if (!ASN1_item_i2d_bio(ASN1_ITEM_rptr(Manifest), bio, self->manifest)) + lose_openssl_error("Couldn't encode manifest"); - free(hmac_copy); - return Py_BuildValue("s#", hmac_text, hmac_len); + assert_no_unhandled_openssl_errors(); - error: + if (!cms_object_sign_helper(&self->cms, bio, signcert, signkey, + x509_sequence, crl_sequence, oid, flags)) + lose_openssl_error("Couldn't sign manifest"); - if (hmac_copy) - free(hmac_copy); + assert_no_unhandled_openssl_errors(); - return NULL; + ok = 1; + + error: + BIO_free(bio); + + if (ok) + Py_RETURN_NONE; + else + return NULL; } +static struct PyMethodDef manifest_object_methods[] = { + Define_Method(getVersion, manifest_object_get_version, METH_NOARGS), + Define_Method(setVersion, manifest_object_set_version, METH_VARARGS), + Define_Method(getManifestNumber, manifest_object_get_manifest_number, METH_NOARGS), + Define_Method(setManifestNumber, manifest_object_set_manifest_number, METH_VARARGS), + Define_Method(getThisUpdate, manifest_object_get_this_update, METH_NOARGS), + Define_Method(setThisUpdate, manifest_object_set_this_update, METH_VARARGS), + Define_Method(getNextUpdate, manifest_object_get_next_update, METH_NOARGS), + Define_Method(setNextUpdate, manifest_object_set_next_update, METH_VARARGS), + Define_Method(getAlgorithm, manifest_object_get_algorithm, METH_NOARGS), + Define_Method(setAlgorithm, manifest_object_set_algorithm, METH_VARARGS), + Define_Method(getFiles, manifest_object_get_files, METH_NOARGS), + Define_Method(addFiles, manifest_object_add_files, METH_VARARGS), + Define_Method(sign, manifest_object_sign, METH_VARARGS), + Define_Method(verify, manifest_object_verify, METH_KEYWORDS), + Define_Class_Method(pemRead, manifest_object_pem_read, METH_VARARGS), + Define_Class_Method(pemReadFile, manifest_object_pem_read_file, METH_VARARGS), + Define_Class_Method(derRead, manifest_object_der_read, METH_VARARGS), + Define_Class_Method(derReadFile, manifest_object_der_read_file, METH_VARARGS), + {NULL} +}; -static struct PyMethodDef hmac_object_methods[] = { - {"update", (PyCFunction)hmac_object_update, METH_VARARGS, NULL}, - {"mac", (PyCFunction)hmac_object_mac, METH_VARARGS, NULL}, - {"copy", (PyCFunction)hmac_object_copy, METH_VARARGS, NULL}, +static char POW_Manifest_Type__doc__[] = + "This class provides access to RPKI manifest payload.\n" + "Most methods are inherited from or share code with the CMS class.\n" + ; - {NULL} /* sentinel */ +static PyTypeObject POW_Manifest_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + "rpki.POW.Manifest", /* tp_name */ + sizeof(manifest_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)manifest_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_Manifest_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + manifest_object_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &POW_CMS_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + manifest_object_new, /* tp_new */ }; + + +/* + * ROA object. + */ + static PyObject * -hmac_object_getattr(hmac_object *self, char *name) +roa_object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - return Py_FindMethod(hmac_object_methods, (PyObject *)self, name); + roa_object *self = NULL; + + ENTERING(roa_object_new); + + if ((self = (roa_object *) cms_object_new(type, args, kwds)) != NULL && + (self->roa = ROA_new()) != NULL) + return (PyObject *) self; + + Py_XDECREF(self); + return NULL; } static void -hmac_object_dealloc(hmac_object *self, char *name) -{ - PyObject_Del(self); -} - -static char hmactype__doc__[] = -"<class>\n" -" <header>\n" -" <name>Hmac</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides access to the HMAC functionality of OpenSSL.\n" -" HMAC's are a variant on digest based MACs, which have the\n" -" interesting property of a provable level of security. HMAC is\n" -" discussed further in RFC 2104.\n" -" </para>\n" -" </body>\n" -"</class>\n" -; +roa_object_dealloc(roa_object *self) +{ + ENTERING(roa_object_dealloc); + ROA_free(self->roa); + cms_object_dealloc(&self->cms); +} -static PyTypeObject hmactype = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "Hmac", /*tp_name*/ - sizeof(hmac_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)hmac_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)hmac_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - hmactype__doc__ /* Documentation string */ -}; -/*========== hmac Code ==========*/ +static char roa_object_verify__doc__[] = + "Verify this ROA. See CMS.verify() for details.\n" + ; -/*========== CMS code ==========*/ -static cms_object * -CMS_object_new(void) +static PyObject * +roa_object_verify(roa_object *self, PyObject *args, PyObject *kwds) { - cms_object *self; + BIO *bio = NULL; + int ok = 0; + + ENTERING(roa_object_verify); - if ((self = PyObject_New(cms_object, &cmstype)) == NULL) + if ((bio = cms_object_verify_helper(&self->cms, args, kwds)) == NULL) goto error; + + if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), bio, &self->roa)) + lose_openssl_error("Couldn't decode ROA"); - self->cms = NULL; - return self; + ok = 1; error: + BIO_free(bio); - Py_XDECREF(self); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static cms_object * -CMS_object_pem_read(BIO *in) +static PyObject * +roa_object_pem_read_helper(PyTypeObject *type, BIO *bio) { - cms_object *self; - - if ((self = PyObject_New(cms_object, &cmstype)) == NULL) - goto error; + roa_object *self; - if ((self->cms = PEM_read_bio_CMS(in, NULL, NULL, NULL)) == NULL) - lose("could not load PEM encoded CMS message"); + ENTERING(roa_object_pem_read_helper); - return self; - - error: + if ((self = (roa_object *) cms_object_pem_read_helper(type, bio)) != NULL) + self->roa = NULL; - Py_XDECREF(self); - return NULL; + return (PyObject *) self; } -static cms_object * -CMS_object_der_read(char *src, int len) +static PyObject * +roa_object_der_read_helper(PyTypeObject *type, BIO *bio) { - cms_object *self; - BIO *bio = NULL; + roa_object *self; - if ((self = PyObject_New(cms_object, &cmstype)) == NULL) - goto error; + ENTERING(roa_object_der_read_helper); - self->cms = CMS_ContentInfo_new(); + if ((self = (roa_object *) cms_object_der_read_helper(type, bio)) != NULL) + self->roa = NULL; - if ((bio = BIO_new_mem_buf(src, len)) == NULL) - goto error; + return (PyObject *) self; +} - if (!d2i_CMS_bio(bio, &self->cms)) - lose("could not load DER encoded CMS message"); +static char roa_object_pem_read__doc__[] = + "Read a PEM-encoded ROA object from a string.\n" + ; - BIO_free(bio); +static PyObject * +roa_object_pem_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(roa_object_pem_read); + return read_from_string_helper(roa_object_pem_read_helper, type, args); +} - return self; +static char roa_object_pem_read_file__doc__[] = + "Read a PEM-encoded ROA object from a file.\n" + ; - error: +static PyObject * +roa_object_pem_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(roa_object_pem_read_file); + return read_from_file_helper(roa_object_pem_read_helper, type, args); +} - if (bio) - BIO_free(bio); +static char roa_object_der_read__doc__[] = + "Read a DER-encoded ROA object from a string.\n" + ; - Py_XDECREF(self); - return NULL; +static PyObject * +roa_object_der_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(roa_object_der_read); + return read_from_string_helper(roa_object_der_read_helper, type, args); } +static char roa_object_der_read_file__doc__[] = + "Read a DER-encoded ROA object from a file.\n" + ; + static PyObject * -CMS_object_write_helper(cms_object *self, PyObject *args, int format) +roa_object_der_read_file(PyTypeObject *type, PyObject *args) { - int len = 0; - char *buf = NULL; - BIO *out_bio = NULL; - PyObject *cert = NULL; - - if (!PyArg_ParseTuple(args, "")) - return NULL; + ENTERING(roa_object_der_read_file); + return read_from_file_helper(roa_object_der_read_helper, type, args); +} - out_bio = BIO_new(BIO_s_mem()); +static char roa_object_get_version__doc__[] = + "Return the version number of this ROA.\n" + ; - switch (format) { +static PyObject * +roa_object_get_version(roa_object *self) +{ + ENTERING(roa_object_get_version); - case DER_FORMAT: - if (!i2d_CMS_bio(out_bio, self->cms)) - lose("unable to write certificate"); - break; + if (self->roa == NULL) + lose_not_verified("Can't get version of unverified ROA"); - case PEM_FORMAT: - if (!PEM_write_bio_CMS(out_bio, self->cms)) - lose("unable to write certificate"); - break; + if (self->roa->version) + return Py_BuildValue("N", ASN1_INTEGER_to_PyLong(self->roa->version)); + else + return PyInt_FromLong(0); - default: - lose("internal error, unknown output format"); - } + error: + return NULL; +} - if ((len = BIO_ctrl_pending(out_bio)) == 0) - lose("unable to get bytes stored in bio"); +static char roa_object_set_version__doc__[] = + "Set the version number of this ROA.\n" + "\n" + "The \"version\" parameter should be a non-negative integer.\n" + "\n" + "As of this writing, zero is both the default and the only defined version.\n" + "Attempting to set any version number other than zero will fail, as we\n" + "don't understand how to write other versions, by definition.\n" + ; - if ((buf = malloc(len)) == NULL) - lose("unable to allocate memory"); +static PyObject * +roa_object_set_version(roa_object *self, PyObject *args) +{ + int version = 0; - if (BIO_read(out_bio, buf, len) != len) - lose("unable to write out cert"); + ENTERING(roa_object_set_version); - cert = Py_BuildValue("s#", buf, len); + if (self->roa == NULL) + lose_not_verified("Can't set version of unverified ROA"); - BIO_free(out_bio); - free(buf); - return cert; + if (!PyArg_ParseTuple(args, "|i", &version)) + goto error; - error: + if (version != 0) + lose("RFC 6482 only defines ROA version zero"); - if (out_bio) - BIO_free(out_bio); + ASN1_INTEGER_free(self->roa->version); + self->roa->version = NULL; - if (buf) - free(buf); + Py_RETURN_NONE; - Py_XDECREF(cert); + error: return NULL; } -static char CMS_object_pem_write__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>pemWrite</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a PEM encoded CMS message as a\n" -" string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char roa_object_get_asid__doc__[] = + "Return the Autonomous System ID of this ROA.\n" + ; static PyObject * -CMS_object_pem_write(cms_object *self, PyObject *args) +roa_object_get_asid(roa_object *self) { - return CMS_object_write_helper(self, args, PEM_FORMAT); -} + ENTERING(roa_object_get_asid); -static char CMS_object_der_write__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>derWrite</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a DER encoded CMS message as a\n" -" string.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + if (self->roa == NULL) + lose_not_verified("Can't get ASN of unverified ROA"); -static PyObject * -CMS_object_der_write(cms_object *self, PyObject *args) -{ - return CMS_object_write_helper(self, args, DER_FORMAT); -} - -static char CMS_object_sign__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>sign</name>\n" -" <parameter>signcert</parameter>\n" -" <parameter>key</parameter>\n" -" <parameter>data</parameter>\n" -" <optional>\n" -" <parameter>certs</parameter>\n" -" <parameter>crls</parameter>\n" -" <parameter>eContentType</parameter>\n" -" <parameter>flags</parameter>\n" -" </optional>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method signs a message with a private key.\n" -" Supported flags: CMS_NOCERTS, CMS_NOATTR.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + return Py_BuildValue("N", ASN1_INTEGER_to_PyLong(self->roa->asID)); + + error: + return NULL; +} + +static char roa_object_set_asid__doc__[] = + "Sets the Autonomous System ID of this ROA.\n" + "\n" + "The \"asID\" parameter should be a non-negative integer.\n" + ; static PyObject * -CMS_object_sign(cms_object *self, PyObject *args) +roa_object_set_asid(roa_object *self, PyObject *args) { - asymmetric_object *signkey = NULL; - x509_object *signcert = NULL; - x509_crl_object *crlobj = NULL; - PyObject *x509_sequence = Py_None, *crl_sequence = Py_None, *result = NULL; - STACK_OF(X509) *x509_stack = NULL; - EVP_PKEY *pkey = NULL; - char *buf = NULL, *oid = NULL; - int i, n, len; - unsigned flags = 0; - BIO *bio = NULL; - CMS_ContentInfo *cms = NULL; - ASN1_OBJECT *econtent_type = NULL; + PyObject *asID = NULL; + PyObject *zero = NULL; + int ok = 0; - if (!PyArg_ParseTuple(args, "O!O!s#|OOsI", - &x509type, &signcert, - &asymmetrictype, &signkey, - &buf, &len, - &x509_sequence, - &crl_sequence, - &oid, - &flags)) + ENTERING(roa_object_set_asid); + + if (self->roa == NULL) + lose_not_verified("Can't set ASN of unverified ROA"); + + if (!PyArg_ParseTuple(args, "O", &asID)) goto error; - assert_no_unhandled_openssl_errors(); + if ((zero = PyInt_FromLong(0)) == NULL) + goto error; - flags &= CMS_NOCERTS | CMS_NOATTR; - flags |= CMS_BINARY | CMS_NOSMIMECAP | CMS_PARTIAL | CMS_USE_KEYID; + switch (PyObject_RichCompareBool(asID, zero, Py_GE)) { + case -1: + goto error; + case 0: + lose("Negative asID is not allowed"); + } - if (signkey->key_type != RSA_PRIVATE_KEY) - lose("unsupported key type"); + ASN1_INTEGER_free(self->roa->asID); - if ((x509_stack = x509_helper_sequence_to_stack(x509_sequence)) == NULL) + if ((self->roa->asID = PyLong_to_ASN1_INTEGER(asID)) == NULL) goto error; - assert_no_unhandled_openssl_errors(); + ok = 1; - if ((pkey = EVP_PKEY_new()) == NULL) - lose_openssl_error("Could not allocate memory"); + error: + Py_XDECREF(zero); - assert_no_unhandled_openssl_errors(); + if (ok) + Py_RETURN_NONE; + else + return NULL; +} - if (!EVP_PKEY_assign_RSA(pkey, signkey->cipher)) - lose_openssl_error("EVP_PKEY assignment error"); +static char roa_object_get_prefixes__doc__[] = + "Return this ROA's prefix list. This is a two-element\n" + "tuple: the first element is the IPv4 prefix set, the second is the\n" + "IPv6 prefix set.\n" + "\n" + "Each prefix set is either None, if there are no prefixes for this IP\n" + "version, or a sequence of three-element tuple representing ROA prefix\n" + "entries.\n" + "\n" + "Each ROA prefix entry consists of the prefix itself (an IPAddress),\n" + "the prefix length (an integer), and the maxPrefixLen value, which is\n" + "either an integer or None depending on whether the maxPrefixLen value\n" + "is set for this prefix.\n" + ; - assert_no_unhandled_openssl_errors(); +static PyObject * +roa_object_get_prefixes(roa_object *self) +{ + PyObject *result = NULL; + PyObject *ipv4_result = NULL; + PyObject *ipv6_result = NULL; + PyObject *item = NULL; + ipaddress_object *addr = NULL; + int i, j; + + ENTERING(roa_object_get_prefixes); + + if (self->roa == NULL) + lose_not_verified("Can't get prefixes from unverified ROA"); + + for (i = 0; i < sk_ROAIPAddressFamily_num(self->roa->ipAddrBlocks); i++) { + ROAIPAddressFamily *fam = sk_ROAIPAddressFamily_value(self->roa->ipAddrBlocks, i); + const unsigned afi = (fam->addressFamily->data[0] << 8) | (fam->addressFamily->data[1]); + const ipaddress_version *ip_type = NULL; + PyObject **resultp = NULL; + + switch (afi) { + case IANA_AFI_IPV4: resultp = &ipv4_result; ip_type = &ipaddress_version_4; break; + case IANA_AFI_IPV6: resultp = &ipv6_result; ip_type = &ipaddress_version_6; break; + default: lose_type_error("Unknown AFI"); + } - if ((bio = BIO_new_mem_buf(buf, len)) == NULL) - goto error; + if (fam->addressFamily->length > 2) + lose_type_error("Unsupported SAFI"); - assert_no_unhandled_openssl_errors(); + if (*resultp != NULL) + lose_type_error("Duplicate ROAIPAddressFamily"); - if (oid && (econtent_type = OBJ_txt2obj(oid, 0)) == NULL) - lose_openssl_error("Could not parse OID"); + if ((*resultp = PyTuple_New(sk_ROAIPAddress_num(fam->addresses))) == NULL) + goto error; - assert_no_unhandled_openssl_errors(); + for (j = 0; j < sk_ROAIPAddress_num(fam->addresses); j++) { + ROAIPAddress *a = sk_ROAIPAddress_value(fam->addresses, j); + unsigned prefixlen = ((a->IPAddress)->length * 8 - ((a->IPAddress)->flags & 7)); - if ((cms = CMS_sign(NULL, NULL, x509_stack, bio, flags)) == NULL) - lose_openssl_error("Could not create CMS message"); + if ((addr = (ipaddress_object *) POW_IPAddress_Type.tp_alloc(&POW_IPAddress_Type, 0)) == NULL) + goto error; - assert_no_unhandled_openssl_errors(); + addr->type = ip_type; - if (econtent_type) - CMS_set1_eContentType(cms, econtent_type); + memset(addr->address, 0, sizeof(addr->address)); - assert_no_unhandled_openssl_errors(); + if ((unsigned) a->IPAddress->length > addr->type->length) + lose("ROAIPAddress BIT STRING too long for AFI"); - if (!CMS_add1_signer(cms, signcert->x509, pkey, EVP_sha256(), flags)) - lose_openssl_error("Could not sign CMS message"); + if (a->IPAddress->length > 0) { + memcpy(addr->address, a->IPAddress->data, a->IPAddress->length); - pkey = NULL; /* CMS_add1_signer() now owns pkey */ + if ((a->IPAddress->flags & 7) != 0) { + unsigned char mask = 0xFF >> (8 - (a->IPAddress->flags & 7)); + addr->address[a->IPAddress->length - 1] &= ~mask; + } + } - assert_no_unhandled_openssl_errors(); + if (a->maxLength == NULL) + item = Py_BuildValue("(NIO)", addr, prefixlen, Py_None); + else + item = Py_BuildValue("(NIl)", addr, prefixlen, ASN1_INTEGER_get(a->maxLength)); - if (crl_sequence != Py_None) { + if (item == NULL) + goto error; - if (!PyTuple_Check(crl_sequence) && !PyList_Check(crl_sequence)) - lose_type_error("inapropriate type"); + PyTuple_SET_ITEM(*resultp, j, item); + item = NULL; + addr = NULL; + } + } - n = PySequence_Size(crl_sequence); + result = Py_BuildValue("(OO)", + (ipv4_result == NULL ? Py_None : ipv4_result), + (ipv6_result == NULL ? Py_None : ipv6_result)); - for (i = 0; i < n; i++) { + error: /* Fall through */ + Py_XDECREF(addr); + Py_XDECREF(item); + Py_XDECREF(ipv4_result); + Py_XDECREF(ipv6_result); - if ((crlobj = (x509_crl_object *) PySequence_GetItem(crl_sequence, i)) == NULL) - goto error; + return result; +} - if (!X_X509_crl_Check(crlobj)) - lose_type_error("inappropriate type"); +static char roa_object_set_prefixes__doc__[] = + "Set this ROA's prefix list.\n" + "\n" + "This method takes two arguments, \"ipv4\" and \"ipv6\". Each of these\n" + "is either None, if no prefixes should be set for this IP version, or\n" + "an iterable object returning ROA prefix entries in the same format as\n" + "returned by the .getPrefixes() method. The maxPrefixLen value may be\n" + "omitted (that is, the ROA prefix entry tuple may be of length two\n" + "rather than of length three); this will be taken as equivalent to\n" + "specifying a maxPrefixLen value of None.\n" + ; - if (!crlobj->crl) - lose("CRL object with null crl field!"); +static PyObject * +roa_object_set_prefixes(roa_object *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ipv4", "ipv6", NULL}; + STACK_OF(ROAIPAddressFamily) *prefixes = NULL; + ROAIPAddressFamily *fam = NULL; + ROAIPAddress *a = NULL; + PyObject *ipv4_arg = Py_None; + PyObject *ipv6_arg = Py_None; + PyObject *iterator = NULL; + PyObject *item = NULL; + PyObject *fast = NULL; + int ok = 0; + int v; - if (!CMS_add1_crl(cms, crlobj->crl)) - lose_openssl_error("Could not add CRL to CMS"); + ENTERING(roa_object_set_prefixes); - assert_no_unhandled_openssl_errors(); + if (self->roa == NULL) + lose_not_verified("Can't set prefixes of unverified ROA"); - Py_XDECREF(crlobj); - crlobj = NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &ipv4_arg, &ipv6_arg)) + goto error; + + if ((prefixes = sk_ROAIPAddressFamily_new_null()) == NULL) + lose_no_memory(); + + for (v = 0; v < (int) (sizeof(ipaddress_versions)/sizeof(*ipaddress_versions)); v++) { + const struct ipaddress_version *ip_type = ipaddress_versions[v]; + unsigned char afibuf[2]; + PyObject **argp; + + switch (ip_type->version) { + case 4: argp = &ipv4_arg; break; + case 6: argp = &ipv6_arg; break; + default: continue; } - } - if (!CMS_final(cms, bio, NULL, flags)) - lose_openssl_error("Could not finalize CMS signatures"); + if (*argp == Py_None) + continue; - assert_no_unhandled_openssl_errors(); + afibuf[0] = (ip_type->afi >> 8) & 0xFF; + afibuf[1] = (ip_type->afi ) & 0xFF; - if (self->cms) - CMS_ContentInfo_free(self->cms); - self->cms = cms; - cms = NULL; + if ((iterator = PyObject_GetIter(*argp)) == NULL) + goto error; - result = Py_BuildValue(""); + while ((item = PyIter_Next(iterator)) != NULL) { + unsigned prefixlen, maxprefixlen, bitlen, bytelen; + ipaddress_object *addr = NULL; + PyObject *maxlenobj = Py_None; - error: /* fall through */ + if ((fast = PySequence_Fast(item, "ROA prefix must be a sequence")) == NULL) + goto error; - assert_no_unhandled_openssl_errors(); + switch (PySequence_Fast_GET_SIZE(fast)) { + case 3: + maxlenobj = PySequence_Fast_GET_ITEM(fast, 2); + /* Fall through */ + case 2: + if (!POW_IPAddress_Check(PySequence_Fast_GET_ITEM(fast, 0))) + lose_type_error("First element of ROA prefix must be an IPAddress object"); + addr = (ipaddress_object *) PySequence_Fast_GET_ITEM(fast, 0); + prefixlen = (unsigned) PyInt_AsLong(PySequence_Fast_GET_ITEM(fast, 1)); + if (PyErr_Occurred()) + goto error; + break; + default: + lose_type_error("ROA prefix must be a two- or three-element sequence"); + } - if (cms) - CMS_ContentInfo_free(cms); + if (maxlenobj == Py_None) { + maxprefixlen = prefixlen; + } else { + maxprefixlen = (unsigned) PyInt_AsLong(maxlenobj); + if (PyErr_Occurred()) + goto error; + } - if (bio) - BIO_free(bio); + if (addr->type != ip_type) + lose_type_error("Bad ROA prefix"); - if (x509_stack) - sk_X509_free(x509_stack); + if (prefixlen > addr->type->length * 8) + lose("Bad prefix length"); - if (pkey) - EVP_PKEY_free(pkey); + if (maxprefixlen > addr->type->length * 8 || maxprefixlen < prefixlen) + lose("Bad maxLength value"); - if (econtent_type) - ASN1_OBJECT_free(econtent_type); + bytelen = (prefixlen + 7) / 8; + bitlen = prefixlen % 8; + + if ((a = ROAIPAddress_new()) == NULL || + (a->IPAddress == NULL && (a->IPAddress = ASN1_BIT_STRING_new()) == NULL) || + !ASN1_BIT_STRING_set(a->IPAddress, addr->address, bytelen)) + lose_no_memory(); + + a->IPAddress->flags &= ~7; + a->IPAddress->flags |= ASN1_STRING_FLAG_BITS_LEFT; + if (bitlen > 0) { + a->IPAddress->data[bytelen - 1] &= ~(0xFF >> bitlen); + a->IPAddress->flags |= 8 - bitlen; + } + + if (prefixlen != maxprefixlen && + ((a->maxLength = ASN1_INTEGER_new()) == NULL || + !ASN1_INTEGER_set(a->maxLength, maxprefixlen))) + lose_no_memory(); + + if (fam == NULL && + ((fam = ROAIPAddressFamily_new()) == NULL || + !sk_ROAIPAddressFamily_push(prefixes, fam) || + !ASN1_OCTET_STRING_set(fam->addressFamily, afibuf, sizeof(afibuf)))) + lose_no_memory(); - if (crlobj) { - Py_XDECREF(crlobj); + if (!sk_ROAIPAddress_push(fam->addresses, a)) + lose_no_memory(); + + a = NULL; + Py_XDECREF(item); + Py_XDECREF(fast); + item = fast = NULL; + } + + fam = NULL; + Py_XDECREF(iterator); + iterator = NULL; } - return result; + sk_ROAIPAddressFamily_pop_free(self->roa->ipAddrBlocks, ROAIPAddressFamily_free); + self->roa->ipAddrBlocks = prefixes; + prefixes = NULL; + + ok = 1; + + error: + sk_ROAIPAddressFamily_pop_free(prefixes, ROAIPAddressFamily_free); + ROAIPAddressFamily_free(fam); + ROAIPAddress_free(a); + Py_XDECREF(iterator); + Py_XDECREF(item); + Py_XDECREF(fast); + + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char CMS_object_verify__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>verify</name>\n" -" <parameter>store</parameter>\n" -" <optional>\n" -" <parameter>certs</parameter>\n" -" <parameter>flags</parameter>\n" -" </optional>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method verifies a message against a trusted store.\n" -" The optional certs parameter is a set of certificates to search\n" -" for the signer's certificate.\n" -" Supported flags: CMS_NOINTERN, CMS_NOCRL,\n" -" CMS_NO_SIGNER_CERT_VERIFY, CMS_NO_ATTR_VERIFY,\n" -" CMS_NO_CONTENT_VERIFY.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char roa_object_sign__doc__[] = + "Sign this ROA. See CMS.sign() for details.\n" + ; static PyObject * -CMS_object_verify(cms_object *self, PyObject *args) +roa_object_sign(roa_object *self, PyObject *args) { - x509_store_object *store = NULL; - PyObject *result = NULL, *certs_sequence = Py_None; - STACK_OF(X509) *certs_stack = NULL; + asymmetric_object *signkey = NULL; + x509_object *signcert = NULL; + PyObject *x509_sequence = Py_None; + PyObject *crl_sequence = Py_None; + char *oid = NULL; unsigned flags = 0; - char *buf = NULL; BIO *bio = NULL; - int len; + int ok = 0; + + ENTERING(roa_object_sign); - if (!PyArg_ParseTuple(args, "O!|OI", &x509_storetype, &store, &certs_sequence, &flags)) + if (!PyArg_ParseTuple(args, "O!O!|OOsI", + &POW_X509_Type, &signcert, + &POW_Asymmetric_Type, &signkey, + &x509_sequence, + &crl_sequence, + &oid, + &flags)) goto error; if ((bio = BIO_new(BIO_s_mem())) == NULL) - goto error; + lose_no_memory(); assert_no_unhandled_openssl_errors(); - flags &= CMS_NOINTERN | CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY | CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY; - - if (certs_sequence != Py_None && (certs_stack = x509_helper_sequence_to_stack(certs_sequence)) == NULL) - goto error; + if (!ASN1_item_i2d_bio(ASN1_ITEM_rptr(ROA), bio, self->roa)) + lose_openssl_error("Couldn't encode ROA"); assert_no_unhandled_openssl_errors(); - if (CMS_verify(self->cms, certs_stack, store->store, NULL, bio, flags) <= 0) - lose_openssl_error("Could not verify CMS message"); + if (!cms_object_sign_helper(&self->cms, bio, signcert, signkey, + x509_sequence, crl_sequence, oid, flags)) + lose_openssl_error("Couldn't sign ROA"); assert_no_unhandled_openssl_errors(); - if ((len = BIO_ctrl_pending(bio)) == 0) - lose("unable to get bytes stored in bio"); + ok = 1; - assert_no_unhandled_openssl_errors(); - - if ((buf = malloc(len)) == NULL) - lose("unable to allocate memory"); + error: + BIO_free(bio); - assert_no_unhandled_openssl_errors(); + if (ok) + Py_RETURN_NONE; + else + return NULL; +} - if (BIO_read(bio, buf, len) != len) - lose("unable to write out CMS content"); +static struct PyMethodDef roa_object_methods[] = { + Define_Method(getVersion, roa_object_get_version, METH_NOARGS), + Define_Method(setVersion, roa_object_set_version, METH_VARARGS), + Define_Method(getASID, roa_object_get_asid, METH_NOARGS), + Define_Method(setASID, roa_object_set_asid, METH_VARARGS), + Define_Method(getPrefixes, roa_object_get_prefixes, METH_NOARGS), + Define_Method(setPrefixes, roa_object_set_prefixes, METH_KEYWORDS), + Define_Method(sign, roa_object_sign, METH_VARARGS), + Define_Method(verify, roa_object_verify, METH_KEYWORDS), + Define_Class_Method(pemRead, roa_object_pem_read, METH_VARARGS), + Define_Class_Method(pemReadFile, roa_object_pem_read_file, METH_VARARGS), + Define_Class_Method(derRead, roa_object_der_read, METH_VARARGS), + Define_Class_Method(derReadFile, roa_object_der_read_file, METH_VARARGS), + {NULL} +}; - assert_no_unhandled_openssl_errors(); +static char POW_ROA_Type__doc__[] = + "This class provides access to RPKI ROA payload.\n" + "Most methods are inherited from or share code with the CMS class.\n" + ; - result = Py_BuildValue("s#", buf, len); +static PyTypeObject POW_ROA_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + "rpki.POW.ROA", /* tp_name */ + sizeof(roa_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)roa_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_ROA_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + roa_object_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &POW_CMS_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + roa_object_new, /* tp_new */ +}; - error: /* fall through */ + - assert_no_unhandled_openssl_errors(); +/* + * PKCS10 object. + */ - if (certs_stack) - sk_X509_free(certs_stack); +static PyObject * +pkcs10_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds) +{ + pkcs10_object *self; - if (bio) - BIO_free(bio); + ENTERING(pkcs10_object_new); - if (buf) - free(buf); + 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; - return result; + Py_XDECREF(self); + return NULL; } -static char CMS_object_eContentType__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>get_eContentType</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the eContentType of a CMS message.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static void +pkcs10_object_dealloc(pkcs10_object *self) +{ + ENTERING(pkcs10_object_dealloc); + X509_REQ_free(self->pkcs10); + sk_X509_EXTENSION_pop_free(self->exts, X509_EXTENSION_free); + self->ob_type->tp_free((PyObject*) self); +} static PyObject * -CMS_object_eContentType(cms_object *self, PyObject *args) +pkcs10_object_pem_read_helper(PyTypeObject *type, BIO *bio) { - const ASN1_OBJECT *oid = NULL; - PyObject *result = NULL; - char buf[512]; + pkcs10_object *self = NULL; - if (!PyArg_ParseTuple(args, "")) - return NULL; + ENTERING(pkcs10_object_pem_read_helper); - if ((oid = CMS_get0_eContentType(self->cms)) == NULL) - lose_openssl_error("Could not extract eContentType from CMS message"); + assert_no_unhandled_openssl_errors(); - OBJ_obj2txt(buf, sizeof(buf), oid, 1); + if ((self = (pkcs10_object *) pkcs10_object_new(type, NULL, NULL)) == NULL) + goto error; - result = Py_BuildValue("s", buf); + assert_no_unhandled_openssl_errors(); - error: + if (!PEM_read_bio_X509_REQ(bio, &self->pkcs10, NULL, NULL)) + lose_openssl_error("Couldn't load PEM encoded PKCS#10 request"); + + sk_X509_EXTENSION_pop_free(self->exts, X509_EXTENSION_free); + self->exts = X509_REQ_get_extensions(self->pkcs10); assert_no_unhandled_openssl_errors(); - return result; -} + return (PyObject *) self; -static char CMS_object_signingTime__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>get_signingTime</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns the signingTime of a CMS message.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; + error: + + Py_XDECREF(self); + return NULL; +} static PyObject * -CMS_object_signingTime(cms_object *self, PyObject *args) +pkcs10_object_der_read_helper(PyTypeObject *type, BIO *bio) { - PyObject *result = NULL; - STACK_OF(CMS_SignerInfo) *sis = NULL; - CMS_SignerInfo *si = NULL; - X509_ATTRIBUTE *xa = NULL; - ASN1_TYPE *so = NULL; - int i; + pkcs10_object *self = NULL; - if (!PyArg_ParseTuple(args, "")) - return NULL; + ENTERING(pkcs10_object_der_read_helper); - if ((sis = CMS_get0_SignerInfos(self->cms)) == NULL) - lose("Could not extract signerInfos from CMS message[1]"); + assert_no_unhandled_openssl_errors(); - if (sk_CMS_SignerInfo_num(sis) != 1) - lose("Could not extract signerInfos from CMS message[2]"); + if ((self = (pkcs10_object *) pkcs10_object_new(type, NULL, NULL)) == NULL) + goto error; - si = sk_CMS_SignerInfo_value(sis, 0); + assert_no_unhandled_openssl_errors(); - if ((i = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1)) < 0) - lose("Could not extract signerInfos from CMS message[3]"); + if (!d2i_X509_REQ_bio(bio, &self->pkcs10)) + lose_openssl_error("Couldn't load DER encoded PKCS#10 request"); - if ((xa = CMS_signed_get_attr(si, i)) == NULL) - lose("Could not extract signerInfos from CMS message[4]"); + sk_X509_EXTENSION_pop_free(self->exts, X509_EXTENSION_free); + self->exts = X509_REQ_get_extensions(self->pkcs10); - if (xa->single) - lose("Could not extract signerInfos from CMS message[5]"); + assert_no_unhandled_openssl_errors(); - if (sk_ASN1_TYPE_num(xa->value.set) != 1) - lose("Could not extract signerInfos from CMS message[6]"); + return (PyObject *) self; - if ((so = sk_ASN1_TYPE_value(xa->value.set, 0)) == NULL) - lose("Could not extract signerInfos from CMS message[7]"); + error: + Py_XDECREF(self); + return NULL; +} - switch (so->type) { - case V_ASN1_UTCTIME: - result = ASN1_TIME_to_Python(so->value.utctime); - break; - case V_ASN1_GENERALIZEDTIME: - result = ASN1_TIME_to_Python(so->value.generalizedtime); - break; - default: - lose("Could not extract signerInfos from CMS message[8]"); - } +static char pkcs10_object_pem_read__doc__[] = + "Read a PEM-encoded PKCS#10 object from a string.\n" + ; - error: +static PyObject * +pkcs10_object_pem_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(pkcs10_object_pem_read); + return read_from_string_helper(pkcs10_object_pem_read_helper, type, args); +} - assert_no_unhandled_openssl_errors(); +static char pkcs10_object_pem_read_file__doc__[] = + "Read a PEM-encoded PKCS#10 object from a file.\n" + ; - return result; +static PyObject * +pkcs10_object_pem_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(pkcs10_object_pem_read_file); + return read_from_file_helper(pkcs10_object_pem_read_helper, type, args); } -static char CMS_object_pprint__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>pprint</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns a formatted string showing the information\n" -" held in the certificate.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char pkcs10_object_der_read__doc__[] = + "Read a DER-encoded PKCS#10 object from a string.\n" + ; static PyObject * -CMS_object_pprint(cms_object *self, PyObject *args) +pkcs10_object_der_read(PyTypeObject *type, PyObject *args) +{ + ENTERING(pkcs10_object_der_read); + return read_from_string_helper(pkcs10_object_der_read_helper, type, args); +} + +static char pkcs10_object_der_read_file__doc__[] = + "Read a DER-encoded PKCS#10 object from a file.\n" + ; + +static PyObject * +pkcs10_object_der_read_file(PyTypeObject *type, PyObject *args) +{ + ENTERING(pkcs10_object_der_read_file); + return read_from_file_helper(pkcs10_object_der_read_helper, type, args); +} + +static char pkcs10_object_pem_write__doc__[] = + "Returns the PEM encoding of this PKCS#10 object.\n" + ; + +static PyObject * +pkcs10_object_pem_write(pkcs10_object *self) { - int len = 0, ret = 0; - char *buf = NULL; - BIO *bio = NULL; PyObject *result = NULL; + BIO *bio = NULL; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(pkcs10_object_pem_write); - bio = BIO_new(BIO_s_mem()); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - if (!CMS_ContentInfo_print_ctx(bio, self->cms, 0, NULL)) - lose("unable to pprint CMS"); + if (!PEM_write_bio_X509_REQ(bio, self->pkcs10)) + lose_openssl_error("Unable to write PKCS#10 request"); - if ((len = BIO_ctrl_pending(bio)) == 0) - lose("unable to get bytes stored in bio"); + result = BIO_to_PyString_helper(bio); - if ((buf = malloc(len)) == NULL) - lose("unable to allocate memory"); + error: /* Fall through */ + BIO_free(bio); + return result; +} - if ((ret = BIO_read(bio, buf, len)) != len) - lose("unable to pprint CMS"); +static char pkcs10_object_der_write__doc__[] = + "Return the DER encoding of this PKCS#10 object.\n" + ; - result = Py_BuildValue("s#", buf, len); +static PyObject * +pkcs10_object_der_write(pkcs10_object *self) +{ + PyObject *result = NULL; + BIO *bio = NULL; - error: /* fall through */ + ENTERING(pkcs10_object_der_write); - assert_no_unhandled_openssl_errors(); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - if (bio) - BIO_free(bio); + if (!i2d_X509_REQ_bio(bio, self->pkcs10)) + lose_openssl_error("Unable to write PKCS#10 request"); - if (buf) - free(buf); + result = BIO_to_PyString_helper(bio); + error: /* Fall through */ + BIO_free(bio); return result; } +static char pkcs10_object_get_public_key__doc__[] = + "Return the public key from this PKCS#10 request, as an Asymmetric\n" + "object.\n" + ; static PyObject * -cms_object_helper_get_cert(void *cert) +pkcs10_object_get_public_key(pkcs10_object *self) { - x509_object *obj = PyObject_New(x509_object, &x509type); + PyTypeObject *type = &POW_Asymmetric_Type; + asymmetric_object *asym = NULL; - if (obj) - obj->x509 = cert; + ENTERING(pkcs10_object_get_public_key); - return (PyObject *) obj; + 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 CMS_object_certs__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>certs</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns any certs embedded in a CMS message.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char pkcs10_object_set_public_key__doc__[] = + "Set the public key for this PKCS#10 request.\n" + "\n" + "The \"key\" parameter should be an instance of the Asymmetric class,\n" + "containing a public key.\n" + ; static PyObject * -CMS_object_certs(cms_object *self, PyObject *args) +pkcs10_object_set_public_key(pkcs10_object *self, PyObject *args) { - STACK_OF(X509) *certs = NULL; - PyObject *result = NULL; + asymmetric_object *asym; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(pkcs10_object_set_public_key); - if ((certs = CMS_get1_certs(self->cms)) != NULL) - result = stack_to_tuple_helper(CHECKED_PTR_OF(STACK_OF(X509), certs), - cms_object_helper_get_cert); - else if (!ERR_peek_error()) - result = Py_BuildValue("()"); - else - lose_openssl_error("Could not extract certs from CMS message"); + if (!PyArg_ParseTuple(args, "O!", &POW_Asymmetric_Type, &asym)) + goto error; - error: /* fall through */ + if (!X509_REQ_set_pubkey(self->pkcs10, asym->pkey)) + lose_openssl_error("Couldn't set certificate's PKCS#10 request"); - if (certs) - sk_X509_pop_free(certs, X509_free); + Py_RETURN_NONE; - return result; + error: + return NULL; } -static PyObject * -cms_object_helper_get_crl(void *crl) +static char pkcs10_object_sign__doc__[] = + "Sign 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) { - x509_crl_object *obj = PyObject_New(x509_crl_object, &x509_crltype); + asymmetric_object *asym; + int loc, digest_type = SHA256_DIGEST; + const EVP_MD *digest_method = NULL; - if (obj) - obj->crl = crl; + ENTERING(pkcs10_object_sign); - return (PyObject *) obj; + 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"); + + while ((loc = X509_REQ_get_attr_by_NID(self->pkcs10, NID_ext_req, -1)) >= 0) + X509_ATTRIBUTE_free(X509_REQ_delete_attr(self->pkcs10, loc)); + + 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 CMS_object_crls__doc__[] = -"<method>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" <name>crls</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This method returns any CRLs embedded in a CMS message.\n" -" </para>\n" -" </body>\n" -"</method>\n" -; +static char pkcs10_object_verify__doc__[] = + "Verify a PKCS#10 request.\n" + "\n" + "This calls OpenSSL's X509_REQ_verify() method to check the request's\n" + "self-signature.\n" + ; static PyObject * -CMS_object_crls(cms_object *self, PyObject *args) +pkcs10_object_verify(pkcs10_object *self) { - STACK_OF(X509_CRL) *crls = NULL; - PyObject *result = NULL; + EVP_PKEY *pkey = NULL; + int status; - if (!PyArg_ParseTuple(args, "")) - goto error; + ENTERING(pkcs10_object_verify); - if ((crls = CMS_get1_crls(self->cms)) != NULL) - result = stack_to_tuple_helper(CHECKED_PTR_OF(STACK_OF(X509_CRL), crls), - cms_object_helper_get_crl); - else if (!ERR_peek_error()) - result = Py_BuildValue("()"); - else - lose_openssl_error("Could not extract CRLs from CMS message"); + if ((pkey = X509_REQ_get_pubkey(self->pkcs10)) == NULL) + lose_openssl_error("Couldn't extract public key from PKCS#10 for verification"); - error: /* fall through */ + if ((status = X509_REQ_verify(self->pkcs10, pkey)) < 0) + lose_openssl_error("Couldn't verify PKCS#10 signature"); - if (crls) - sk_X509_CRL_pop_free(crls, X509_CRL_free); + EVP_PKEY_free(pkey); + return PyBool_FromLong(status); - return result; + error: + EVP_PKEY_free(pkey); + return NULL; } -static struct PyMethodDef CMS_object_methods[] = { - {"pemWrite", (PyCFunction)CMS_object_pem_write, METH_VARARGS, NULL}, - {"derWrite", (PyCFunction)CMS_object_der_write, METH_VARARGS, NULL}, - {"sign", (PyCFunction)CMS_object_sign, METH_VARARGS, NULL}, - {"verify", (PyCFunction)CMS_object_verify, METH_VARARGS, NULL}, - {"eContentType", (PyCFunction)CMS_object_eContentType, METH_VARARGS, NULL}, - {"signingTime", (PyCFunction)CMS_object_signingTime, METH_VARARGS, NULL}, - {"pprint", (PyCFunction)CMS_object_pprint, METH_VARARGS, NULL}, - {"certs", (PyCFunction)CMS_object_certs, METH_VARARGS, NULL}, - {"crls", (PyCFunction)CMS_object_crls, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ -}; +static char pkcs10_object_get_version__doc__[] = + "Return the version number of this PKCS#10 request.\n" + ; static PyObject * -CMS_object_getattr(cms_object *self, char *name) +pkcs10_object_get_version(pkcs10_object *self) { - return Py_FindMethod(CMS_object_methods, (PyObject *)self, name); + ENTERING(pkcs10_object_get_version); + return Py_BuildValue("l", X509_REQ_get_version(self->pkcs10)); } -static void -CMS_object_dealloc(cms_object *self, char *name) -{ - CMS_ContentInfo_free(self->cms); - PyObject_Del(self); -} - -static char cmstype__doc__[] = -"<class>\n" -" <header>\n" -" <name>CMS</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This class provides basic access OpenSSL's CMS functionality.\n" -" </para>\n" -" </body>\n" -"</class>\n" -; - -static PyTypeObject cmstype = { - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ - "CMS", /*tp_name*/ - sizeof(cms_object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)CMS_object_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)CMS_object_getattr, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - 0, - 0, - 0, - 0, - cmstype__doc__ /* Documentation string */ -}; -/*========== CMS Code ==========*/ - -/*========== module functions ==========*/ -static char pow_module_new_ssl__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>Ssl</memberof>\n" -" <parameter>protocol = SSLV23METHOD</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor creates a new <classname>Ssl</classname> object which will behave as a client\n" -" or server, depending on the <parameter>protocol</parameter> value passed. The\n" -" <parameter>protocol</parameter> also determines the protocol type\n" -" and version and should be one of the following:\n" -" </para>\n" -"\n" -" <simplelist>\n" -" <member><constant>SSLV2_SERVER_METHOD</constant></member>\n" -" <member><constant>SSLV2_CLIENT_METHOD</constant></member>\n" -" <member><constant>SSLV2_METHOD</constant></member>\n" -" <member><constant>SSLV3_SERVER_METHOD</constant></member>\n" -" <member><constant>SSLV3_CLIENT_METHOD</constant></member>\n" -" <member><constant>SSLV3_METHOD</constant></member>\n" -" <member><constant>TLSV1_SERVER_METHOD</constant></member>\n" -" <member><constant>TLSV1_CLIENT_METHOD</constant></member>\n" -" <member><constant>TLSV1_METHOD</constant></member>\n" -" <member><constant>SSLV23_SERVER_METHOD</constant></member>\n" -" <member><constant>SSLV23_CLIENT_METHOD</constant></member>\n" -" <member><constant>SSLV23_METHOD</constant></member>\n" -" </simplelist>\n" -" </body>\n" -"</constructor>\n" +static char pkcs10_object_set_version__doc__[] = + "Set the version number of this PKCS#10 request.\n" + "\n" + "The \"version\" parameter should be an integer, but the only defined\n" + "value is zero, so this field is optional and defaults to zero.\n" ; static PyObject * -pow_module_new_ssl (PyObject *self, PyObject *args) +pkcs10_object_set_version(pkcs10_object *self, PyObject *args) { - ssl_object *ssl = NULL; - int ctxtype = SSLV23_METHOD; + long version = 0; - if (!PyArg_ParseTuple(args, "|i", &ctxtype)) - goto error; + ENTERING(pkcs10_object_set_version); - if ((ssl = newssl_object(ctxtype)) == NULL) + if (!PyArg_ParseTuple(args, "|l", &version)) goto error; - return (PyObject*) ssl; + 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 pow_module_new_x509__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>X509</memberof>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor creates a skeletal X509 certificate object.\n" -" It won't be any use at all until several structures\n" -" have been created using it's member functions.\n" -" </para>\n" -" </body>\n" -"</constructor>\n" -; +static char pkcs10_object_get_subject__doc__[] = + "Return this PKCS #10 request's subject name.\n" + "\n" + "See the X509.getIssuer() method for details of the return value and\n" + "use of the optional \"format\" parameter.\n" + ; static PyObject * -pow_module_new_x509 (PyObject *self, PyObject *args) +pkcs10_object_get_subject(pkcs10_object *self, PyObject *args) { - x509_object *x509 = NULL; - - if (!PyArg_ParseTuple(args, "")) - goto error; + PyObject *result = NULL; + int format = OIDNAME_FORMAT; - if ((x509 = X509_object_new()) == NULL) - lose("could not create new x509 object"); + ENTERING(pkcs10_object_get_subject); - return (PyObject*)x509; + if (!PyArg_ParseTuple(args, "|i", &format)) + goto error; - error: + result = x509_object_helper_get_name(X509_REQ_get_subject_name(self->pkcs10), + format); - return NULL; + error: /* Fall through */ + return result; } -static char pow_module_new_asymmetric__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>Asymmetric</memberof>\n" -" <parameter>ciphertype = RSA_CIPHER</parameter>\n" -" <parameter>keylength = 1024</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor builds a new cipher object. Only RSA ciphers\n" -" are currently support, so the first argument should always be\n" -" <constant>RSA_CIPHER</constant>. The second argument,\n" -" <parameter>keylength</parameter>,\n" -" is normally 512, 768, 1024 or 2048. Key lengths as short as 512\n" -" bits are generally considered weak, and can be cracked by\n" -" determined attackers without tremendous expense.\n" -" </para>\n" -" <example>\n" -" <title><classname>asymmetric</classname> class usage</title>\n" -" <programlisting>\n" -" privateFile = open('test/private.key', 'w')\n" -" publicFile = open('test/public.key', 'w')\n" -"\n" -" passphrase = 'my silly passphrase'\n" -" md5 = POW.Digest(POW.MD5_DIGEST)\n" -" md5.update(passphrase)\n" -" password = md5.digest()\n" -"\n" -" rsa = POW.Asymmetric(POW.RSA_CIPHER, 1024)\n" -" privateFile.write(rsa.pemWrite(\n" -" POW.RSA_PRIVATE_KEY, POW.DES_EDE3_CFB, password))\n" -" publicFile.write(rsa.pemWrite(POW.RSA_PUBLIC_KEY))\n" -"\n" -" privateFile.close()\n" -" publicFile.close()\n" -" </programlisting>\n" -" </example>\n" -" </body>\n" -"</constructor>\n" -; +static char pkcs10_object_set_subject__doc__[] = + "Set this PKCS#10 request's subject name.\n" + "\n" + "The \"name\" parameter should be in the same format as the return\n" + "value from the \"getSubject\" method.\n" + ; static PyObject * -pow_module_new_asymmetric (PyObject *self, PyObject *args) +pkcs10_object_set_subject(pkcs10_object *self, PyObject *args) { - int cipher_type = RSA_CIPHER, key_size = 1024; + PyObject *name_sequence = NULL; + X509_NAME *name = NULL; + + ENTERING(pkcs10_object_set_subject); - if (!PyArg_ParseTuple(args, "|ii", &cipher_type, &key_size)) + if (!PyArg_ParseTuple(args, "O", &name_sequence)) goto error; - return (PyObject*) asymmetric_object_new(cipher_type, key_size); + if (!PySequence_Check(name_sequence)) + lose_type_error("Inapropriate type"); - error: + 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 pow_module_new_digest__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>Digest</memberof>\n" -" <parameter>type</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor creates a new <classname>Digest</classname>\n" -" object. The parameter <parameter>type</parameter> specifies what kind\n" -" of digest to create and should be one of the following:\n" -" </para>\n" -" <simplelist>\n" -#ifndef OPENSSL_NO_MD2 -" <member><constant>MD2_DIGEST</constant></member>\n" -#endif -" <member><constant>MD5_DIGEST</constant></member>\n" -" <member><constant>SHA_DIGEST</constant></member>\n" -" <member><constant>SHA1_DIGEST</constant></member>\n" -" <member><constant>RIPEMD160_DIGEST</constant></member>\n" -" <member><constant>SHA256_DIGEST</constant></member>\n" -" <member><constant>SHA384_DIGEST</constant></member>\n" -" <member><constant>SHA512_DIGEST</constant></member>\n" -" </simplelist>\n" -" </body>\n" -"</constructor>\n" -; +static char pkcs10_object_get_key_usage__doc__[] = + "Return a FrozenSet of strings representing the KeyUsage settings for\n" + "this PKCS#10 request, or None if the request has no KeyUsage\n" + "extension. The bits have the same names as in RFC 5280.\n" + ; static PyObject * -pow_module_new_digest (PyObject *self, PyObject *args) +pkcs10_object_get_key_usage(pkcs10_object *self) { - int digest_type = 0; + extern X509V3_EXT_METHOD v3_key_usage; + BIT_STRING_BITNAME *bit_name; + ASN1_BIT_STRING *ext = NULL; + PyObject *result = NULL; + PyObject *token = NULL; + + ENTERING(pkcs10_object_get_key_usage); - if (!PyArg_ParseTuple(args, "i", &digest_type)) + if ((ext = X509V3_get_d2i(self->exts, NID_key_usage, NULL, NULL)) == NULL) + Py_RETURN_NONE; + + if ((result = PyFrozenSet_New(NULL)) == NULL) goto error; - return (PyObject*) digest_object_new(digest_type); + 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; + } - error: + ASN1_BIT_STRING_free(ext); + return result; + error: + ASN1_BIT_STRING_free(ext); + Py_XDECREF(token); + Py_XDECREF(result); return NULL; } -static char pow_module_new_hmac__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>Hmac</memberof>\n" -" <parameter>type</parameter>\n" -" <parameter>key</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor creates a new <classname>Hmac</classname>\n" -" object. The parameter <parameter>key</parameter> should be a\n" -" string and <parameter>type</parameter> should be one of the following:\n" -" </para>\n" -" <simplelist>\n" -#ifndef OPENSSL_NO_MD2 -" <member><constant>MD2_DIGEST</constant></member>\n" -#endif -" <member><constant>MD5_DIGEST</constant></member>\n" -" <member><constant>SHA_DIGEST</constant></member>\n" -" <member><constant>SHA1_DIGEST</constant></member>\n" -" <member><constant>RIPEMD160_DIGEST</constant></member>\n" -" <member><constant>SHA256_DIGEST</constant></member>\n" -" <member><constant>SHA384_DIGEST</constant></member>\n" -" <member><constant>SHA512_DIGEST</constant></member>\n" -" </simplelist>\n" -" </body>\n" -"</constructor>\n" -; +static char pkcs10_object_set_key_usage__doc__[] = + "Set 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 * -pow_module_new_hmac (PyObject *self, PyObject *args) +pkcs10_object_set_key_usage(pkcs10_object *self, PyObject *args) { - int digest_type = 0, key_len = 0; - char *key = NULL; + 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; + + ENTERING(pkcs10_object_set_key_usage); - if (!PyArg_ParseTuple(args, "is#", &digest_type, &key, &key_len)) + 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; - return (PyObject*) hmac_object_new(digest_type, key, key_len); + while ((token = PyIter_Next(iterator)) != NULL) { - error: + if ((t = PyString_AsString(token)) == NULL) + goto error; - return NULL; + 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 pow_module_new_cms__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>CMS</memberof>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor creates a skeletal CMS object.\n" -" </para>\n" -" </body>\n" -"</constructor>\n" -; +static char pkcs10_object_get_basic_constraints__doc__[] = + "Return BasicConstraints value for this PKCS#10 request.\n" + "\n" + "If this request has no BasicConstraints extension, this method returns\n" + "None.\n" + "\n" + "Otherwise, this method returns a two-element tuple. The first element\n" + "of the tuple is a boolean representing the extension's cA value; the\n" + "second element of the tuple is either an integer representing\n" + "thepathLenConstraint value or None if there is no pathLenConstraint.\n" + ; static PyObject * -pow_module_new_cms (PyObject *self, PyObject *args) +pkcs10_object_get_basic_constraints(pkcs10_object *self) { - cms_object *cms = NULL; + BASIC_CONSTRAINTS *ext = NULL; + PyObject *result; - if (!PyArg_ParseTuple(args, "")) - goto error; - - if ((cms = CMS_object_new()) == NULL) - lose("could not create new CMS object"); + ENTERING(pkcs10_object_get_basic_constraints); - return (PyObject*)cms; + if ((ext = X509V3_get_d2i(self->exts, NID_basic_constraints, NULL, NULL)) == NULL) + Py_RETURN_NONE; - error: + 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)); - return NULL; + BASIC_CONSTRAINTS_free(ext); + return result; } -static char pow_module_pem_read__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>pemRead</name>\n" -" <parameter>type</parameter>\n" -" <parameter>string</parameter>\n" -" <parameter>pass = None</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function attempts to parse the <parameter>string</parameter> according to the PEM\n" -" type passed. <parameter>type</parameter> should be one of the\n" -" following:\n" -" </para>\n" -" <simplelist>\n" -" <member><constant>RSA_PUBLIC_KEY</constant></member>\n" -" <member><constant>RSA_PRIVATE_KEY</constant></member>\n" -" <member><constant>X509_CERTIFICATE</constant></member>\n" -" <member><constant>X509_CRL</constant></member>\n" -" <member><constant>CMS_MESSAGE</constant></member>\n" -" </simplelist>\n" -" <para>\n" -" <parameter>pass</parameter> should only be provided if an encrypted\n" -" <classname>Asymmetric</classname> is being loaded. If the password\n" -" is incorrect an exception will be raised, if no password is provided\n" -" and the PEM file is encrypted the user will be prompted. If this is\n" -" not desirable, always supply a password. The object returned will be\n" -" and instance of <classname>Asymmetric</classname>,\n" -" <classname>X509</classname>, <classname>X509Crl</classname>,\n" -" or <classname>CMS</classname>.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; +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 None or a\n" + "non-negative integer specifying the pathLenConstraint value for this\n" + "certificate. Per RFC 5280, this value may only be set to an integer\n" + "value 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 * -pow_module_pem_read (PyObject *self, PyObject *args) +pkcs10_object_set_basic_constraints(pkcs10_object *self, PyObject *args) { - BIO *in = NULL; - PyObject *obj = NULL; - int object_type = 0, len = 0; - char *pass = NULL, *src = NULL; + BASIC_CONSTRAINTS *ext = NULL; + PyObject *is_ca = NULL; + PyObject *pathlen_obj = Py_None; + PyObject *critical = Py_True; + long pathlen = -1; + int ok = 0; + + ENTERING(pkcs10_object_set_basic_constraints); - if (!PyArg_ParseTuple(args, "is#|s", &object_type, &src, &len, &pass)) + if (!PyArg_ParseTuple(args, "O|OO", &is_ca, &pathlen_obj, &critical)) goto error; - if ((in = BIO_new_mem_buf(src, len)) == NULL) - lose("unable to create new BIO"); + if (pathlen_obj != Py_None && (pathlen = PyInt_AsLong(pathlen_obj)) < 0) + lose_type_error("Bad pathLenConstraint value"); - switch(object_type) { - case RSA_PRIVATE_KEY: - obj = (PyObject*)asymmetric_object_pem_read(object_type, in, pass); - break; - case RSA_PUBLIC_KEY: - obj = (PyObject*)asymmetric_object_pem_read(object_type, in, pass); - break; - case X509_CERTIFICATE: - obj = (PyObject*)X509_object_pem_read(in); - break; - case X_X509_CRL: - obj = (PyObject*)x509_crl_object_pem_read(in); - break; - case CMS_MESSAGE: - obj = (PyObject*)CMS_object_pem_read(in); - break; - default: - lose("unknown pem encoding"); - } + 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(); - BIO_free(in); + 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"); - if (obj) - return obj; + ok = 1; error: + BASIC_CONSTRAINTS_free(ext); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } - -static char pow_module_der_read__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>derRead</name>\n" -" <parameter>type</parameter>\n" -" <parameter>string</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function attempts to parse the <parameter>string</parameter> according to the PEM\n" -" type passed. <parameter>type</parameter> should be one of the\n" -" following:\n" -" </para>\n" -" <simplelist>\n" -" <member><constant>RSA_PUBLIC_KEY</constant></member>\n" -" <member><constant>RSA_PRIVATE_KEY</constant></member>\n" -" <member><constant>X509_CERTIFICATE</constant></member>\n" -" <member><constant>X509_CRL</constant></member>\n" -" <member><constant>CMS_MESSAGE</constant></member>\n" -" </simplelist>\n" -" <para>\n" -" As with the PEM operations, the object returned will be and instance\n" -" of <classname>Asymmetric</classname>, <classname>X509</classname>,\n" -" <classname>X509Crl</classname>, or <classname>CMS</classname>.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; +static char pkcs10_object_get_sia__doc__[] = + "Return the SIA values for this PKCS#10 request.\n" + "\n" + "If this request has no SIA extension, this method returns None.\n" + "\n" + "Otherwise, this 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 * -pow_module_der_read (PyObject *self, PyObject *args) +pkcs10_object_get_sia(pkcs10_object *self) { - PyObject *obj = NULL; - int object_type = 0, len = 0; - unsigned char *src = NULL; + 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; + + ENTERING(pkcs10_object_get_sia); + + 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. + */ - if (!PyArg_ParseTuple(args, "is#", &object_type, &src, &len)) + 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; - switch(object_type) { - case RSA_PRIVATE_KEY: - obj = (PyObject*) asymmetric_object_der_read(object_type, src, len); - break; - case RSA_PUBLIC_KEY: - obj = (PyObject*) asymmetric_object_der_read(object_type, src, len); - break; - case X509_CERTIFICATE: - obj = (PyObject*)X509_object_der_read(src, len); - break; - case X_X509_CRL: - obj = (PyObject*)x509_crl_object_der_read(src, len); - break; - case CMS_MESSAGE: - obj = (PyObject*)CMS_object_der_read((char *) src, len); - break; - default: - lose("unknown der encoding"); + 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; + } } - if (obj) - return obj; + result = Py_BuildValue("(OOO)", + result_caRepository, + result_rpkiManifest, + result_signedObject); error: - - return NULL; + AUTHORITY_INFO_ACCESS_free(ext); + Py_XDECREF(result_caRepository); + Py_XDECREF(result_rpkiManifest); + Py_XDECREF(result_signedObject); + return result; } -static char pow_module_new_x509_store__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>X509Store</memberof>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor takes no arguments. The\n" -" <classname>X509Store</classname> returned cannot be used for\n" -" verifying certificates until at least one trusted certificate has been\n" -" added.\n" -" </para>\n" -" </body>\n" -"</constructor>\n" -; +static char pkcs10_object_set_sia__doc__[] = + "Set SIA values for this PKCS#10 request.\n" + "\n" + "Takes three arguments: caRepository, rpkiManifest, and signedObject.\n" + "\n" + "Each of these should be an iterable which returns URIs.\n" + "\n" + "None is acceptable as an alternate way of specifying an empty\n" + "sequence of URIs for a particular argument.\n" + ; static PyObject * -pow_module_new_x509_store (PyObject *self, PyObject *args) +pkcs10_object_set_sia(pkcs10_object *self, PyObject *args) { - if (!PyArg_ParseTuple(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; + + ENTERING(pkcs10_object_set_sia); + + if (!PyArg_ParseTuple(args, "OOO", &caRepository, &rpkiManifest, &signedObject)) goto error; - return (PyObject *) x509_store_object_new(); + 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); - return NULL; + if (ok) + Py_RETURN_NONE; + else + return NULL; } -static char pow_module_new_symmetric__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>Symmetric</memberof>\n" -" <parameter>type</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor creates a new <classname>Symmetric</classname>\n" -" object. The parameter <parameter>type</parameter> specifies which kind\n" -" of cipher to create. <constant>type</constant> should be one of the following:\n" -" </para>\n" -" <simplelist columns = \"2\">\n" -" <member><constant>DES_ECB</constant></member>\n" -" <member><constant>DES_EDE</constant></member>\n" -" <member><constant>DES_EDE3</constant></member>\n" -" <member><constant>DES_CFB</constant></member>\n" -" <member><constant>DES_EDE_CFB</constant></member>\n" -" <member><constant>DES_EDE3_CFB</constant></member>\n" -" <member><constant>DES_OFB</constant></member>\n" -" <member><constant>DES_EDE_OFB</constant></member>\n" -" <member><constant>DES_EDE3_OFB</constant></member>\n" -" <member><constant>DES_CBC</constant></member>\n" -" <member><constant>DES_EDE_CBC</constant></member>\n" -" <member><constant>DES_EDE3_CBC</constant></member>\n" -" <member><constant>DESX_CBC</constant></member>\n" -" <member><constant>RC4</constant></member>\n" -" <member><constant>RC4_40</constant></member>\n" -" <member><constant>IDEA_ECB</constant></member>\n" -" <member><constant>IDEA_CFB</constant></member>\n" -" <member><constant>IDEA_OFB</constant></member>\n" -" <member><constant>IDEA_CBC</constant></member>\n" -" <member><constant>RC2_ECB</constant></member>\n" -" <member><constant>RC2_CBC</constant></member>\n" -" <member><constant>RC2_40_CBC</constant></member>\n" -" <member><constant>RC2_CFB</constant></member>\n" -" <member><constant>RC2_OFB</constant></member>\n" -" <member><constant>BF_ECB</constant></member>\n" -" <member><constant>BF_CBC</constant></member>\n" -" <member><constant>BF_CFB</constant></member>\n" -" <member><constant>BF_OFB</constant></member>\n" -" <member><constant>CAST5_ECB</constant></member>\n" -" <member><constant>CAST5_CBC</constant></member>\n" -" <member><constant>CAST5_CFB</constant></member>\n" -" <member><constant>CAST5_OFB</constant></member>\n" -" <member><constant>RC5_32_12_16_CBC</constant></member>\n" -" <member><constant>RC5_32_12_16_CFB</constant></member>\n" -" <member><constant>RC5_32_12_16_ECB</constant></member>\n" -" <member><constant>RC5_32_12_16_OFB</constant></member>\n" -" </simplelist>\n" -" <para>\n" -" Please note your version of OpenSSL might not have been compiled with\n" -" all the ciphers listed above. If that is the case, which is very\n" -" likely if you are using a stock binary, the unsuported ciphers will not even\n" -" be in the module namespace.\n" -" </para>\n" -" </body>\n" -"</constructor>\n" -; +static char pkcs10_object_get_signature_algorithm__doc__[] = + "Return this PKCS #10 reqeuest's signature algorithm OID.\n" + ; static PyObject * -pow_module_new_symmetric (PyObject *self, PyObject *args) +pkcs10_object_get_signature_algorithm(pkcs10_object *self) { - int cipher_type = 0; + ASN1_OBJECT *oid = NULL; - if (!PyArg_ParseTuple(args, "i", &cipher_type)) - goto error; + ENTERING(pkcs10_object_get_signature_algorithm); - return (PyObject *) symmetric_object_new(cipher_type); + X509_ALGOR_get0(&oid, NULL, NULL, self->pkcs10->sig_alg); - error: - - return NULL; + return ASN1_OBJECT_to_PyString(oid); } -static char pow_module_new_x509_crl__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>x509_crl</memberof>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor builds an empty CRL.\n" -" </para>\n" -" </body>\n" -"</constructor>\n" -; +static char pkcs10_object_get_extension_oids__doc__[] = + "Return the set of extension OIDs used in this request. This is mostly\n" + "useful for enforcing restrictions on what extensions are allowed to be\n" + "present, eg, to conform with the RPKI profile.\n" + ; static PyObject * -pow_module_new_x509_crl (PyObject *self, PyObject *args) +pkcs10_object_get_extension_oids(pkcs10_object *self) { - if (!PyArg_ParseTuple(args, "")) + PyObject *result = NULL; + PyObject *oid = NULL; + int i; + + ENTERING(pkcs10_object_get_extension_oids); + + if ((result = PyFrozenSet_New(NULL)) == NULL) goto error; - return (PyObject *) x509_crl_object_new(); + for (i = 0; i < sk_X509_EXTENSION_num(self->exts); i++) { + X509_EXTENSION *ext = sk_X509_EXTENSION_value(self->exts, i); + if ((oid = ASN1_OBJECT_to_PyString(ext->object)) == NULL || + PySet_Add(result, oid) < 0) + goto error; + Py_XDECREF(oid); + oid = NULL; + } - error: + return result; - return NULL; + error: + Py_XDECREF(result); + Py_XDECREF(oid); + return NULL; } -static char pow_module_new_x509_revoked__doc__[] = -"<constructor>\n" -" <header>\n" -" <memberof>X509Revoked</memberof>\n" -" <parameter>serial</parameter>\n" -" <parameter>date</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This constructor builds a X509 Revoked structure. <parameter>serial</parameter>\n" -" should be an integer and <parameter>date</parameter> should be and\n" -" UTCTime string.\n" -" </para>\n" -" </body>\n" -"</constructor>\n" -; +/* + * May want EKU handlers eventually, skip for now. + */ + +static char pkcs10_object_pprint__doc__[] = + "Return a pretty-printed rendition of this PKCS#10 request.\n" + ; static PyObject * -pow_module_new_x509_revoked (PyObject *self, PyObject *args) +pkcs10_object_pprint(pkcs10_object *self) { - int serial = -1; - char *date = NULL; - x509_revoked_object *revoke = NULL; - - if (!PyArg_ParseTuple(args, "|is", &serial, &date)) - goto error; + PyObject *result = NULL; + BIO *bio = NULL; - revoke = x509_revoked_object_new(); - if (serial != -1 && !ASN1_INTEGER_set(revoke->revoked->serialNumber, serial)) - lose("unable to set serial number"); + ENTERING(pkcs10_object_pprint); - if (date != NULL && !python_ASN1_TIME_set_string(revoke->revoked->revocationDate, date)) - lose_type_error("Could not set revocationDate"); + if ((bio = BIO_new(BIO_s_mem())) == NULL) + lose_no_memory(); - return (PyObject*) revoke; + if (!X509_REQ_print(bio, self->pkcs10)) + lose_openssl_error("Unable to pretty-print PKCS#10 request"); - error: + result = BIO_to_PyString_helper(bio); - return NULL; + 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_Method(getSignatureAlgorithm, pkcs10_object_get_signature_algorithm, METH_NOARGS), + Define_Method(getExtensionOIDs, pkcs10_object_get_extension_oids, METH_NOARGS), + 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 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 */ + "rpki.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. + */ + static char pow_module_add_object__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>addObject</name>\n" -" <parameter>oid</parameter>\n" -" <parameter>shortName</parameter>\n" -" <parameter>longName</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function can be used to dynamically add new objects to\n" -" OpenSSL. The <parameter>oid</parameter> should be a string of space separated numbers\n" -" and <parameter>shortName</parameter> and\n" -" <parameter>longName</parameter> are the names of the object, ie\n" -" 'cn' and 'commonName'.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; + "Add new a new object identifier to OpenSSL's internal database.\n" + "\n" + "The \"oid\" should be an ASN.1 object identifer, represented as a string\n" + "in dotted-decimal format.\n" + "\n" + "The \"shortName\" parameter should be the OpenSSL \"short name\" to use.\n" + "\n" + "The \"longName\" parameter should be the OpenSSL \"long name\" to use.\n" + ; static PyObject * -pow_module_add_object(PyObject *self, PyObject *args) +pow_module_add_object(GCC_UNUSED PyObject *self, PyObject *args) { char *oid = NULL, *sn = NULL, *ln = NULL; + ENTERING(pow_module_add_object); + if (!PyArg_ParseTuple(args, "sss", &oid, &sn, &ln)) goto error; if (!OBJ_create(oid, sn, ln)) - lose("unable to add object"); + lose_openssl_error("Unable to add object"); Py_RETURN_NONE; @@ -7943,104 +8005,57 @@ pow_module_add_object(PyObject *self, PyObject *args) } static char pow_module_get_error__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>getError</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" Pops an error off the global error stack and returns it as a string.\n" -" Returns None if the global error stack is empty.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; + "Pop one error off OpenSSL's global error stack and returns it as a string.\n" + "\n" + "Returns None if the error stack is empty.\n" + ; static PyObject * -pow_module_get_error(PyObject *self, PyObject *args) +pow_module_get_error(GCC_UNUSED PyObject *self) { - unsigned long error; + unsigned long error = ERR_get_error(); char buf[256]; - if (!PyArg_ParseTuple(args, "")) - goto error; - - error = ERR_get_error(); + ENTERING(pow_module_get_error); if (!error) Py_RETURN_NONE; ERR_error_string_n(error, buf, sizeof(buf)); - return Py_BuildValue("s", buf); - - error: - - return NULL; } static char pow_module_clear_error__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>clearError</name>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" Removes all errors from the global error stack.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; + "Remove all errors from OpenSSL's global error stack.\n" + ; static PyObject * -pow_module_clear_error(PyObject *self, PyObject *args) +pow_module_clear_error(GCC_UNUSED PyObject *self) { - if (!PyArg_ParseTuple(args, "")) - goto error; - + ENTERING(pow_module_clear_error); ERR_clear_error(); - Py_RETURN_NONE; - - error: - - return NULL; } static char pow_module_seed__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>seed</name>\n" -" <parameter>data</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" The <function>seed</function> function adds data to OpenSSLs PRNG\n" -" state. It is often said the hardest part of cryptography is\n" -" getting good random data, after all if you don't have good random\n" -" data, a 1024 bit key is no better than a 512 bit key and neither\n" -" would provide protection from a targeted brute force attack.\n" -" The <function>seed</function> and <function>add</function> are very\n" -" similar, except the entropy of the data is assumed to be equal to\n" -" the length for <function>seed</function>. One final point to be aware\n" -" of, only systems which support /dev/urandom are automatically seeded.\n" -" If your system does not support /dev/urandom it is your responsibility\n" -" to seed OpenSSL's PRNG.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; + "Add data to OpenSSL's pseudo-random number generator state.\n" + "\n" + "The \"data\" parameter is the seed to add. Entropy of the data is\n" + "assumed to be equal to the length of the data.\n" + ; static PyObject * -pow_module_seed(PyObject *self, PyObject *args) +pow_module_seed(GCC_UNUSED PyObject *self, PyObject *args) { - char *in = NULL; - int inl = 0; + char *data = NULL; + int datalen = 0; - if (!PyArg_ParseTuple(args, "s#", &in, &inl)) + ENTERING(pow_module_seed); + + if (!PyArg_ParseTuple(args, "s#", &data, &datalen)) goto error; - RAND_seed(in, inl); + RAND_seed(data, datalen); Py_RETURN_NONE; @@ -8050,489 +8065,213 @@ pow_module_seed(PyObject *self, PyObject *args) } static char pow_module_add__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>add</name>\n" -" <parameter>data</parameter>\n" -" <parameter>entropy</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" The <function>add</function> function adds data to OpenSSLs PRNG\n" -" state. <parameter>data</parameter> should be data obtained from a\n" -" random source and <parameter>entropy</parameter> is an estimation of the number of random\n" -" bytes in <parameter>data</parameter>.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; + "Add data to OpenSSL's pseudo-random number generator state.\n" + "\n" + "The \"data\" parameter is the data to add.\n" + "\n" + "The \"entropy\" parameter should be an estimate of the number of\n" + "random bytes in the data parameter.\n" + ; static PyObject * -pow_module_add(PyObject *self, PyObject *args) +pow_module_add(GCC_UNUSED PyObject *self, PyObject *args) { - char *in = NULL; - int inl = 0; + char *data = NULL; + int datalen = 0; double entropy = 0; - if (!PyArg_ParseTuple(args, "s#d", &in, &inl, &entropy)) + ENTERING(pow_module_add); + + if (!PyArg_ParseTuple(args, "s#d", &data, &datalen, &entropy)) goto error; - RAND_add(in, inl, entropy); + RAND_add(data, datalen, entropy); Py_RETURN_NONE; error: - return NULL; } static char pow_module_write_random_file__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>writeRandomFile</name>\n" -" <parameter>filename</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function writes the current random state to a file. Clearly\n" -" this function should be used in conjunction with\n" -" <function>readRandomFile</function>.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; + "Write the current state of OpenSSL's pseduo-random number generator to\n" + "a file.\n" + "\n" + "The \"filename\" parameter is the name of the file to write.\n" + ; static PyObject * -pow_module_write_random_file(PyObject *self, PyObject *args) +pow_module_write_random_file(GCC_UNUSED PyObject *self, PyObject *args) { - char *file = NULL; + char *filename = NULL; + + ENTERING(pow_module_write_random_file); - if (!PyArg_ParseTuple(args, "s", &file)) + if (!PyArg_ParseTuple(args, "s", &filename)) goto error; - if (RAND_write_file(file) == -1) - lose("could not write random file"); + if (RAND_write_file(filename) == -1) + lose("Couldn't write random file"); Py_RETURN_NONE; error: - return NULL; } static char pow_module_read_random_file__doc__[] = -"<modulefunction>\n" -" <header>\n" -" <name>readRandomFile</name>\n" -" <parameter>filename</parameter>\n" -" </header>\n" -" <body>\n" -" <para>\n" -" This function reads a previously saved random state. It can be very\n" -" useful to improve the quality of random data used by an application.\n" -" The random data should be added to, using the\n" -" <function>add</function> function, with data from other\n" -" suitable random sources.\n" -" </para>\n" -" </body>\n" -"</modulefunction>\n" -; + "Restore the state of OpenSSLs pseudo-random number generator from\n" + "data previously saved to a file.\n" + "\n" + "The \"filename\" parameter is the name of the file to read.\n" + ; static PyObject * -pow_module_read_random_file(PyObject *self, PyObject *args) +pow_module_read_random_file(GCC_UNUSED PyObject *self, PyObject *args) { char *file = NULL; int len = -1; + ENTERING(pow_module_read_random_file); + if (!PyArg_ParseTuple(args, "s|i", &file, &len)) goto error; if (!RAND_load_file(file, len)) - lose("could not load random file"); + lose("Couldn't load random file"); Py_RETURN_NONE; error: - return NULL; } +static char pow_module_custom_datetime__doc__[] = + "Set constructor callback for customized datetime class.\n" + ; + static PyObject * -pow_module_docset(PyObject *self, PyObject *args) +pow_module_custom_datetime(GCC_UNUSED PyObject *self, PyObject *args) { - PyObject *docset; + PyObject *cb = NULL; - if (!PyArg_ParseTuple(args, "")) + ENTERING(pow_module_custom_datetime); + + if (!PyArg_ParseTuple(args, "O", &cb)) goto error; - docset = PyList_New(0); - - // module documentation - docset_helper_add(docset, pow_module__doc__); - - // constructors - docset_helper_add(docset, pow_module_new_symmetric__doc__); - docset_helper_add(docset, pow_module_new_asymmetric__doc__); - docset_helper_add(docset, pow_module_new_digest__doc__); - docset_helper_add(docset, pow_module_new_hmac__doc__); - docset_helper_add(docset, pow_module_new_ssl__doc__); - docset_helper_add(docset, pow_module_new_x509__doc__); - docset_helper_add(docset, pow_module_new_x509_store__doc__); - docset_helper_add(docset, pow_module_new_x509_crl__doc__); - docset_helper_add(docset, pow_module_new_x509_revoked__doc__); - docset_helper_add(docset, pow_module_new_cms__doc__); - - // functions - docset_helper_add(docset, pow_module_pem_read__doc__); - docset_helper_add(docset, pow_module_der_read__doc__); - docset_helper_add(docset, pow_module_seed__doc__); - docset_helper_add(docset, pow_module_add__doc__); - docset_helper_add(docset, pow_module_read_random_file__doc__); - docset_helper_add(docset, pow_module_write_random_file__doc__); - docset_helper_add(docset, pow_module_get_error__doc__); - docset_helper_add(docset, pow_module_clear_error__doc__); - docset_helper_add(docset, pow_module_add_object__doc__); - - // ssl documentation - docset_helper_add(docset, ssltype__doc__); - docset_helper_add(docset, ssl_object_set_fd__doc__); - docset_helper_add(docset, ssl_object_fileno__doc__); - docset_helper_add(docset, ssl_object_accept__doc__); - docset_helper_add(docset, ssl_object_connect__doc__); - docset_helper_add(docset, ssl_object_write__doc__); - docset_helper_add(docset, ssl_object_read__doc__); - docset_helper_add(docset, ssl_object_peer_certificate__doc__); - docset_helper_add(docset, ssl_object_use_certificate__doc__); - docset_helper_add(docset, ssl_object_use_key__doc__); - docset_helper_add(docset, ssl_object_check_key__doc__); - docset_helper_add(docset, ssl_object_clear__doc__); - docset_helper_add(docset, ssl_object_shutdown__doc__); - docset_helper_add(docset, ssl_object_get_shutdown__doc__); - docset_helper_add(docset, ssl_object_get_ciphers__doc__); - docset_helper_add(docset, ssl_object_set_ciphers__doc__); - docset_helper_add(docset, ssl_object_get_cipher__doc__); - docset_helper_add(docset, ssl_object_set_verify_mode__doc__); - - // x509 documentation - docset_helper_add(docset, x509type__doc__); - docset_helper_add(docset, X509_object_pem_write__doc__); - docset_helper_add(docset, X509_object_der_write__doc__); - docset_helper_add(docset, X509_object_sign__doc__); - docset_helper_add(docset, X509_object_set_public_key__doc__); - docset_helper_add(docset, X509_object_get_version__doc__); - docset_helper_add(docset, X509_object_set_version__doc__); - docset_helper_add(docset, X509_object_get_serial__doc__); - docset_helper_add(docset, X509_object_set_serial__doc__); - docset_helper_add(docset, X509_object_get_issuer__doc__); - docset_helper_add(docset, X509_object_set_issuer__doc__); - docset_helper_add(docset, X509_object_get_subject__doc__); - docset_helper_add(docset, X509_object_set_subject__doc__); - docset_helper_add(docset, X509_object_get_not_before__doc__); - docset_helper_add(docset, X509_object_set_not_before__doc__); - docset_helper_add(docset, X509_object_get_not_after__doc__); - docset_helper_add(docset, X509_object_set_not_after__doc__); - docset_helper_add(docset, X509_object_add_extension__doc__); - docset_helper_add(docset, X509_object_clear_extensions__doc__); - docset_helper_add(docset, X509_object_count_extensions__doc__); - docset_helper_add(docset, X509_object_get_extension__doc__); - docset_helper_add(docset, x509_object_pprint__doc__); - - // x509_crl documentation - docset_helper_add(docset, x509_crltype__doc__); - docset_helper_add(docset, x509_crl_object_pem_write__doc__); - docset_helper_add(docset, x509_crl_object_der_write__doc__); - docset_helper_add(docset, x509_crl_object_get_version__doc__); - docset_helper_add(docset, x509_crl_object_set_version__doc__); - docset_helper_add(docset, x509_crl_object_get_issuer__doc__); - docset_helper_add(docset, x509_crl_object_set_issuer__doc__); - docset_helper_add(docset, x509_crl_object_get_this_update__doc__); - docset_helper_add(docset, x509_crl_object_set_this_update__doc__); - docset_helper_add(docset, x509_crl_object_get_next_update__doc__); - docset_helper_add(docset, x509_crl_object_set_next_update__doc__); - docset_helper_add(docset, x509_crl_object_get_revoked__doc__); - docset_helper_add(docset, x509_crl_object_set_revoked__doc__); - docset_helper_add(docset, x509_crl_object_verify__doc__); - docset_helper_add(docset, x509_crl_object_sign__doc__); - docset_helper_add(docset, X509_crl_object_add_extension__doc__); - docset_helper_add(docset, X509_crl_object_clear_extensions__doc__); - docset_helper_add(docset, X509_crl_object_count_extensions__doc__); - docset_helper_add(docset, X509_crl_object_get_extension__doc__); - docset_helper_add(docset, x509_crl_object_pprint__doc__); - - // x509_revoked documentation - docset_helper_add(docset, x509_revokedtype__doc__); - docset_helper_add(docset, x509_revoked_object_get_date__doc__); - docset_helper_add(docset, x509_revoked_object_set_date__doc__); - docset_helper_add(docset, x509_revoked_object_get_serial__doc__); - docset_helper_add(docset, x509_revoked_object_set_serial__doc__); - docset_helper_add(docset, X509_revoked_object_add_extension__doc__); - docset_helper_add(docset, X509_revoked_object_clear_extensions__doc__); - docset_helper_add(docset, X509_revoked_object_count_extensions__doc__); - docset_helper_add(docset, X509_revoked_object_get_extension__doc__); - - // x509_store documentation - docset_helper_add(docset, x509_storetype__doc__); - docset_helper_add(docset, x509_store_object_verify__doc__); - docset_helper_add(docset, x509_store_object_verify_chain__doc__); - docset_helper_add(docset, x509_store_object_verify_detailed__doc__); - docset_helper_add(docset, x509_store_object_add_trust__doc__); - docset_helper_add(docset, x509_store_object_add_crl__doc__); - - // digest documentation - docset_helper_add(docset, digesttype__doc__); - docset_helper_add(docset, digest_object_update__doc__); - docset_helper_add(docset, digest_object_copy__doc__); - docset_helper_add(docset, digest_object_digest__doc__); - - // hmac documentation - docset_helper_add(docset, hmactype__doc__); - docset_helper_add(docset, hmac_object_update__doc__); - docset_helper_add(docset, hmac_object_copy__doc__); - docset_helper_add(docset, hmac_object_mac__doc__); - - // cms documentation - docset_helper_add(docset, CMS_object_pem_write__doc__); - docset_helper_add(docset, CMS_object_der_write__doc__); - docset_helper_add(docset, CMS_object_sign__doc__); - docset_helper_add(docset, CMS_object_verify__doc__); - docset_helper_add(docset, CMS_object_eContentType__doc__); - docset_helper_add(docset, CMS_object_signingTime__doc__); - docset_helper_add(docset, CMS_object_pprint__doc__); - docset_helper_add(docset, CMS_object_certs__doc__); - docset_helper_add(docset, CMS_object_crls__doc__); - - // symmetric documentation - docset_helper_add(docset, symmetrictype__doc__); - docset_helper_add(docset, symmetric_object_encrypt_init__doc__); - docset_helper_add(docset, symmetric_object_decrypt_init__doc__); - docset_helper_add(docset, symmetric_object_update__doc__); - docset_helper_add(docset, symmetric_object_final__doc__); - - // asymmetric documentation - docset_helper_add(docset, asymmetrictype__doc__); - docset_helper_add(docset, asymmetric_object_pem_write__doc__); - docset_helper_add(docset, asymmetric_object_der_write__doc__); - docset_helper_add(docset, asymmetric_object_public_encrypt__doc__); - docset_helper_add(docset, asymmetric_object_public_decrypt__doc__); - docset_helper_add(docset, asymmetric_object_private_encrypt__doc__); - docset_helper_add(docset, asymmetric_object_private_decrypt__doc__); - docset_helper_add(docset, asymmetric_object_sign__doc__); - docset_helper_add(docset, asymmetric_object_verify__doc__); - - return docset; + Py_XINCREF(cb); + Py_XDECREF(custom_datetime); + custom_datetime = cb; - error: + Py_RETURN_NONE; + error: return NULL; } + static struct PyMethodDef pow_module_methods[] = { - {"Ssl", (PyCFunction)pow_module_new_ssl, METH_VARARGS, NULL}, - {"X509", (PyCFunction)pow_module_new_x509, METH_VARARGS, NULL}, - {"pemRead", (PyCFunction)pow_module_pem_read, METH_VARARGS, NULL}, - {"derRead", (PyCFunction)pow_module_der_read, METH_VARARGS, NULL}, - {"Digest", (PyCFunction)pow_module_new_digest, METH_VARARGS, NULL}, - {"Hmac", (PyCFunction)pow_module_new_hmac, METH_VARARGS, NULL}, - {"CMS", (PyCFunction)pow_module_new_cms, METH_VARARGS, NULL}, - {"Asymmetric", (PyCFunction)pow_module_new_asymmetric, METH_VARARGS, NULL}, - {"Symmetric", (PyCFunction)pow_module_new_symmetric, METH_VARARGS, NULL}, - {"X509Store", (PyCFunction)pow_module_new_x509_store, METH_VARARGS, NULL}, - {"X509Crl", (PyCFunction)pow_module_new_x509_crl, METH_VARARGS, NULL}, - {"X509Revoked", (PyCFunction)pow_module_new_x509_revoked, METH_VARARGS, NULL}, - {"getError", (PyCFunction)pow_module_get_error, METH_VARARGS, NULL}, - {"clearError", (PyCFunction)pow_module_clear_error, METH_VARARGS, NULL}, - {"seed", (PyCFunction)pow_module_seed, METH_VARARGS, NULL}, - {"add", (PyCFunction)pow_module_add, METH_VARARGS, NULL}, - {"readRandomFile", (PyCFunction)pow_module_read_random_file, METH_VARARGS, NULL}, - {"writeRandomFile", (PyCFunction)pow_module_write_random_file, METH_VARARGS, NULL}, - {"addObject", (PyCFunction)pow_module_add_object, METH_VARARGS, NULL}, - - {"_docset", (PyCFunction)pow_module_docset, METH_VARARGS, NULL}, - - {NULL} /* sentinel */ + Define_Method(getError, pow_module_get_error, METH_NOARGS), + Define_Method(clearError, pow_module_clear_error, METH_NOARGS), + Define_Method(seed, pow_module_seed, METH_VARARGS), + Define_Method(add, pow_module_add, METH_VARARGS), + Define_Method(readRandomFile, pow_module_read_random_file, METH_VARARGS), + Define_Method(writeRandomFile, pow_module_write_random_file, METH_VARARGS), + Define_Method(addObject, pow_module_add_object, METH_VARARGS), + Define_Method(customDatetime, pow_module_custom_datetime, METH_VARARGS), + {NULL} }; -/*========== module functions ==========*/ + + +/* + * Module initialization. + */ -/*==========================================================================*/ void init_POW(void) { - PyObject *m; - - x509type.ob_type = &PyType_Type; - x509_storetype.ob_type = &PyType_Type; - x509_crltype.ob_type = &PyType_Type; - x509_revokedtype.ob_type = &PyType_Type; - ssltype.ob_type = &PyType_Type; - asymmetrictype.ob_type = &PyType_Type; - symmetrictype.ob_type = &PyType_Type; - digesttype.ob_type = &PyType_Type; - hmactype.ob_type = &PyType_Type; - cmstype.ob_type = &PyType_Type; - - m = Py_InitModule3("_POW", pow_module_methods, pow_module__doc__); - -#define Define_Exception(__name__, __parent__) \ - PyModule_AddObject(m, #__name__, ((__name__##Object) = PyErr_NewException("POW." #__name__, __parent__, NULL))) - - Define_Exception(Error, NULL); - Define_Exception(SSLError, ErrorObject); - Define_Exception(ZeroReturnError, SSLErrorObject); - Define_Exception(WantReadError, SSLErrorObject); - Define_Exception(WantWriteError, SSLErrorObject); - Define_Exception(SSLSyscallError, SSLErrorObject); - Define_Exception(SSLErrorSSLError, SSLErrorObject); - Define_Exception(SSLSyscallSSLError, SSLErrorObject); - Define_Exception(SSLUnexpectedEOFError,SSLErrorObject); - Define_Exception(SSLOtherError, SSLErrorObject); + PyObject *m = Py_InitModule3("_POW", pow_module_methods, pow_module__doc__); + int OpenSSL_ok = 1; + + /* + * Python encourages us to use these functions instead of the ones + * in libc, and OpenSSL allows us to do this. The result seems to + * work, and, in theory, gives Python's memory allocator a better + * idea of how much memory we're really using. Not sure why it + * cares, but let's try to be nice about it. + * + * Note that this must be done BEFORE anything in OpenSSL uses + * dynamic memory, and that this will probably fail in horrible ways + * without the build-time code (-Bsymbolic, etc) which isolates our + * copy of the OpenSSL code from any system shared libraries. + * Enough other things already fail in horrible ways without that + * isolation that adding one more doesn't make much difference, but + * if you tinker with the build script and start seeing nasty + * memory-related issues, this might be the cause. + */ + CRYPTO_set_mem_functions(PyMem_Malloc, PyMem_Realloc, PyMem_Free); + + /* + * Import the DateTime API + */ + + PyDateTime_IMPORT; + +#define Define_Class(__type__) \ + do { \ + char *__name__ = strrchr(__type__.tp_name, '.'); \ + if (PyType_Ready(&__type__) == 0 && __name__ != NULL) { \ + Py_INCREF(&__type__); \ + PyModule_AddObject(m, __name__+1, (PyObject *) &__type__); \ + } \ + } while (0) + + Define_Class(POW_X509_Type); + Define_Class(POW_X509Store_Type); + Define_Class(POW_CRL_Type); + Define_Class(POW_Asymmetric_Type); + Define_Class(POW_Digest_Type); + Define_Class(POW_CMS_Type); + Define_Class(POW_IPAddress_Type); + Define_Class(POW_Manifest_Type); + Define_Class(POW_ROA_Type); + Define_Class(POW_PKCS10_Type); + +#undef Define_Class + +#define Define_Exception(__name__, __parent__) \ + PyModule_AddObject(m, #__name__, ((__name__##Object) \ + = PyErr_NewException("rpki.POW." #__name__, __parent__, NULL))) + + Define_Exception(Error, NULL); + Define_Exception(OpenSSLError, ErrorObject); + Define_Exception(POWError, ErrorObject); + Define_Exception(NotVerifiedError, ErrorObject); #undef Define_Exception #define Define_Integer_Constant(__name__) \ PyModule_AddIntConstant(m, #__name__, __name__) - // constants for SSL_get_error() - Define_Integer_Constant(SSL_ERROR_NONE); - Define_Integer_Constant(SSL_ERROR_ZERO_RETURN); - Define_Integer_Constant(SSL_ERROR_WANT_READ); - Define_Integer_Constant(SSL_ERROR_WANT_WRITE); - Define_Integer_Constant(SSL_ERROR_WANT_X509_LOOKUP); - Define_Integer_Constant(SSL_ERROR_SYSCALL); - Define_Integer_Constant(SSL_ERROR_SSL); - Define_Integer_Constant(SSL_ERROR_WANT_CONNECT); - Define_Integer_Constant(SSL_ERROR_WANT_ACCEPT); - - // constants for different types of connection methods - Define_Integer_Constant(SSLV2_SERVER_METHOD); - Define_Integer_Constant(SSLV2_CLIENT_METHOD); - Define_Integer_Constant(SSLV2_METHOD); - Define_Integer_Constant(SSLV3_SERVER_METHOD); - Define_Integer_Constant(SSLV3_CLIENT_METHOD); - Define_Integer_Constant(SSLV3_METHOD); - Define_Integer_Constant(SSLV23_SERVER_METHOD); - Define_Integer_Constant(SSLV23_CLIENT_METHOD); - Define_Integer_Constant(SSLV23_METHOD); - Define_Integer_Constant(TLSV1_SERVER_METHOD); - Define_Integer_Constant(TLSV1_CLIENT_METHOD); - Define_Integer_Constant(TLSV1_METHOD); - - Define_Integer_Constant(SSL_NO_SHUTDOWN); - Define_Integer_Constant(SSL_SENT_SHUTDOWN); - Define_Integer_Constant(SSL_RECEIVED_SHUTDOWN); - - // ssl verification mode - Define_Integer_Constant(SSL_VERIFY_NONE); - Define_Integer_Constant(SSL_VERIFY_PEER); - Define_Integer_Constant(SSL_VERIFY_FAIL_IF_NO_PEER_CERT); - Define_Integer_Constant(SSL_VERIFY_CLIENT_ONCE); - - // object format types + /* Object format types */ Define_Integer_Constant(LONGNAME_FORMAT); Define_Integer_Constant(SHORTNAME_FORMAT); + Define_Integer_Constant(OIDNAME_FORMAT); - // PEM encoded types -#ifndef OPENSSL_NO_RSA - Define_Integer_Constant(RSA_PUBLIC_KEY); - Define_Integer_Constant(RSA_PRIVATE_KEY); -#endif -#ifndef OPENSSL_NO_DSA - Define_Integer_Constant(DSA_PUBLIC_KEY); - Define_Integer_Constant(DSA_PRIVATE_KEY); -#endif -#ifndef OPENSSL_NO_DH - Define_Integer_Constant(DH_PUBLIC_KEY); - Define_Integer_Constant(DH_PRIVATE_KEY); -#endif - Define_Integer_Constant(X509_CERTIFICATE); - PyModule_AddIntConstant(m, "X509_CRL", X_X509_CRL); - Define_Integer_Constant(CMS_MESSAGE); - - // asymmetric ciphers -#ifndef OPENSSL_NO_RSA + /* Asymmetric ciphers */ Define_Integer_Constant(RSA_CIPHER); -#endif -#ifndef OPENSSL_NO_DSA - Define_Integer_Constant(DSA_CIPHER); -#endif -#ifndef OPENSSL_NO_DH - Define_Integer_Constant(DH_CIPHER); -#endif - // symmetric ciphers -#ifndef OPENSSL_NO_DES - Define_Integer_Constant(DES_ECB); - Define_Integer_Constant(DES_EDE); - Define_Integer_Constant(DES_EDE3); - Define_Integer_Constant(DES_CFB); - Define_Integer_Constant(DES_EDE_CFB); - Define_Integer_Constant(DES_EDE3_CFB); - Define_Integer_Constant(DES_OFB); - Define_Integer_Constant(DES_EDE_OFB); - Define_Integer_Constant(DES_EDE3_OFB); - Define_Integer_Constant(DES_CBC); - Define_Integer_Constant(DES_EDE_CBC); - Define_Integer_Constant(DES_EDE3_CBC); - Define_Integer_Constant(DESX_CBC); -#endif -#ifndef OPENSSL_NO_RC4 - Define_Integer_Constant(RC4); - Define_Integer_Constant(RC4_40); -#endif -#ifndef OPENSSL_NO_IDEA - Define_Integer_Constant(IDEA_ECB); - Define_Integer_Constant(IDEA_CFB); - Define_Integer_Constant(IDEA_OFB); - Define_Integer_Constant(IDEA_CBC); -#endif -#ifndef OPENSSL_NO_RC2 - Define_Integer_Constant(RC2_ECB); - Define_Integer_Constant(RC2_CBC); - Define_Integer_Constant(RC2_40_CBC); - Define_Integer_Constant(RC2_CFB); - Define_Integer_Constant(RC2_OFB); -#endif -#ifndef OPENSSL_NO_BF - Define_Integer_Constant(BF_ECB); - Define_Integer_Constant(BF_CBC); - Define_Integer_Constant(BF_CFB); - Define_Integer_Constant(BF_OFB); -#endif - Define_Integer_Constant(CAST5_ECB); - Define_Integer_Constant(CAST5_CBC); - Define_Integer_Constant(CAST5_CFB); - Define_Integer_Constant(CAST5_OFB); -#ifndef OPENSSL_NO_RC5 - Define_Integer_Constant(RC5_32_12_16_CBC); - Define_Integer_Constant(RC5_32_12_16_CFB); - Define_Integer_Constant(RC5_32_12_16_ECB); - Define_Integer_Constant(RC5_32_12_16_OFB); -#endif - - // message digests -#ifndef OPENSSL_NO_MD2 - Define_Integer_Constant(MD2_DIGEST); -#endif + /* Message digests */ Define_Integer_Constant(MD5_DIGEST); Define_Integer_Constant(SHA_DIGEST); Define_Integer_Constant(SHA1_DIGEST); - Define_Integer_Constant(RIPEMD160_DIGEST); Define_Integer_Constant(SHA256_DIGEST); Define_Integer_Constant(SHA384_DIGEST); Define_Integer_Constant(SHA512_DIGEST); - // general name - Define_Integer_Constant(GEN_OTHERNAME); - Define_Integer_Constant(GEN_EMAIL); - Define_Integer_Constant(GEN_DNS); - Define_Integer_Constant(GEN_X400); - Define_Integer_Constant(GEN_DIRNAME); - Define_Integer_Constant(GEN_EDIPARTY); - Define_Integer_Constant(GEN_URI); - Define_Integer_Constant(GEN_IPADD); - Define_Integer_Constant(GEN_RID); - - // CMS flags + /* CMS flags */ Define_Integer_Constant(CMS_NOCERTS); Define_Integer_Constant(CMS_NOATTR); Define_Integer_Constant(CMS_NOINTERN); @@ -8543,19 +8282,26 @@ init_POW(void) #undef Define_Integer_Constant - // initialise library - SSL_library_init(); + /* + * Initialise library. + * + * We shouldn't need any of the SSL code or error strings anymore. + * + * If we cared deeply about avoiding references to symmetric cipher + * algorithms and digest algorithms we're not using, we could + * replace the call to OpenSSL_add_all_algorithms() with calls to + * add just the specific algorithms we use rather than all of them. + * For now, don't worry about it. + */ + OpenSSL_add_all_algorithms(); - OpenSSL_add_all_ciphers(); - OpenSSL_add_all_digests(); + ERR_load_crypto_strings(); - // load error strings - SSL_load_error_strings(); + OpenSSL_ok &= create_missing_nids(); - if (PyErr_Occurred()) + if (PyErr_Occurred() || !OpenSSL_ok) Py_FatalError("Can't initialize module POW"); } -/*==========================================================================*/ /* * Local Variables: |