| /* |
| * crypto.c |
| * |
| * Copyright (C) AB Strakt 2001, All rights reserved |
| * Copyright (C) Jean-Paul Calderone 2008-2009, All rights reserved |
| * |
| * Main file of crypto sub module. |
| * See the file RATIONALE for a short explanation of why this module was written. |
| * |
| * Reviewed 2001-07-23 |
| */ |
| #include <Python.h> |
| #define crypto_MODULE |
| #include "crypto.h" |
| |
| static char crypto_doc[] = "\n\ |
| Main file of crypto sub module.\n\ |
| See the file RATIONALE for a short explanation of why this module was written.\n\ |
| "; |
| |
| void **ssl_API; |
| |
| PyObject *crypto_Error; |
| |
| static int |
| global_passphrase_callback(char *buf, int len, int rwflag, void *cb_arg) |
| { |
| PyObject *func, *argv, *ret; |
| int nchars; |
| |
| func = (PyObject *)cb_arg; |
| argv = Py_BuildValue("(i)", rwflag); |
| ret = PyEval_CallObject(func, argv); |
| Py_DECREF(argv); |
| if (ret == NULL) |
| return 0; |
| if (!PyString_Check(ret)) |
| { |
| PyErr_SetString(PyExc_ValueError, "String expected"); |
| return 0; |
| } |
| nchars = PyString_Size(ret); |
| if (nchars > len) |
| nchars = len; |
| strncpy(buf, PyString_AsString(ret), nchars); |
| return nchars; |
| } |
| |
| static char crypto_load_privatekey_doc[] = "\n\ |
| Load a private key from a buffer\n\ |
| \n\ |
| @param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ |
| @param buffer: The buffer the key is stored in\n\ |
| @param passphrase: (optional) if encrypted PEM format, this can be\n\ |
| either the passphrase to use, or a callback for\n\ |
| providing the passphrase.\n\ |
| \n\ |
| @return: The PKey object\n\ |
| "; |
| |
| static PyObject * |
| crypto_load_privatekey(PyObject *spam, PyObject *args) |
| { |
| crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int); |
| int type, len; |
| char *buffer; |
| PyObject *pw = NULL; |
| pem_password_cb *cb = NULL; |
| void *cb_arg = NULL; |
| BIO *bio; |
| EVP_PKEY *pkey; |
| |
| if (!PyArg_ParseTuple(args, "is#|O:load_privatekey", &type, &buffer, &len, &pw)) |
| return NULL; |
| |
| if (pw != NULL) |
| { |
| if (PyString_Check(pw)) |
| { |
| cb = NULL; |
| cb_arg = PyString_AsString(pw); |
| } |
| else if (PyCallable_Check(pw)) |
| { |
| cb = global_passphrase_callback; |
| cb_arg = pw; |
| } |
| else |
| { |
| PyErr_SetString(PyExc_TypeError, "Last argument must be string or callable"); |
| return NULL; |
| } |
| } |
| |
| bio = BIO_new_mem_buf(buffer, len); |
| switch (type) |
| { |
| case X509_FILETYPE_PEM: |
| pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, cb_arg); |
| break; |
| |
| case X509_FILETYPE_ASN1: |
| pkey = d2i_PrivateKey_bio(bio, NULL); |
| break; |
| |
| default: |
| PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); |
| BIO_free(bio); |
| return NULL; |
| } |
| BIO_free(bio); |
| |
| if (pkey == NULL) |
| { |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| |
| return (PyObject *)crypto_PKey_New(pkey, 1); |
| } |
| |
| static char crypto_dump_privatekey_doc[] = "\n\ |
| Dump a private key to a buffer\n\ |
| \n\ |
| @param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ |
| @param pkey: The PKey to dump\n\ |
| @param cipher: (optional) if encrypted PEM format, the cipher to\n\ |
| use\n\ |
| @param passphrase - (optional) if encrypted PEM format, this can be either\n\ |
| the passphrase to use, or a callback for providing the\n\ |
| passphrase.\n\ |
| @return: The buffer with the dumped key in\n\ |
| @rtype: C{str}\n\ |
| "; |
| |
| static PyObject * |
| crypto_dump_privatekey(PyObject *spam, PyObject *args) |
| { |
| int type, ret, buf_len; |
| char *temp; |
| PyObject *buffer; |
| char *cipher_name = NULL; |
| const EVP_CIPHER *cipher = NULL; |
| PyObject *pw = NULL; |
| pem_password_cb *cb = NULL; |
| void *cb_arg = NULL; |
| BIO *bio; |
| RSA *rsa; |
| crypto_PKeyObj *pkey; |
| |
| if (!PyArg_ParseTuple(args, "iO!|sO:dump_privatekey", &type, |
| &crypto_PKey_Type, &pkey, &cipher_name, &pw)) |
| return NULL; |
| |
| if (cipher_name != NULL && pw == NULL) |
| { |
| PyErr_SetString(PyExc_ValueError, "Illegal number of arguments"); |
| return NULL; |
| } |
| if (cipher_name != NULL) |
| { |
| cipher = EVP_get_cipherbyname(cipher_name); |
| if (cipher == NULL) |
| { |
| PyErr_SetString(PyExc_ValueError, "Invalid cipher name"); |
| return NULL; |
| } |
| if (PyString_Check(pw)) |
| { |
| cb = NULL; |
| cb_arg = PyString_AsString(pw); |
| } |
| else if (PyCallable_Check(pw)) |
| { |
| cb = global_passphrase_callback; |
| cb_arg = pw; |
| } |
| else |
| { |
| PyErr_SetString(PyExc_TypeError, "Last argument must be string or callable"); |
| return NULL; |
| } |
| } |
| |
| bio = BIO_new(BIO_s_mem()); |
| switch (type) |
| { |
| case X509_FILETYPE_PEM: |
| ret = PEM_write_bio_PrivateKey(bio, pkey->pkey, cipher, NULL, 0, cb, cb_arg); |
| if (PyErr_Occurred()) |
| { |
| BIO_free(bio); |
| return NULL; |
| } |
| break; |
| |
| case X509_FILETYPE_ASN1: |
| ret = i2d_PrivateKey_bio(bio, pkey->pkey); |
| break; |
| |
| case X509_FILETYPE_TEXT: |
| rsa = EVP_PKEY_get1_RSA(pkey->pkey); |
| ret = RSA_print(bio, rsa, 0); |
| RSA_free(rsa); |
| break; |
| |
| default: |
| PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT"); |
| BIO_free(bio); |
| return NULL; |
| } |
| |
| if (ret == 0) |
| { |
| BIO_free(bio); |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| |
| buf_len = BIO_get_mem_data(bio, &temp); |
| buffer = PyString_FromStringAndSize(temp, buf_len); |
| BIO_free(bio); |
| |
| return buffer; |
| } |
| |
| static char crypto_load_certificate_doc[] = "\n\ |
| Load a certificate from a buffer\n\ |
| \n\ |
| @param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ |
| buffer - The buffer the certificate is stored in\n\ |
| @return: The X509 object\n\ |
| "; |
| |
| static PyObject * |
| crypto_load_certificate(PyObject *spam, PyObject *args) |
| { |
| crypto_X509Obj *crypto_X509_New(X509 *, int); |
| int type, len; |
| char *buffer; |
| BIO *bio; |
| X509 *cert; |
| |
| if (!PyArg_ParseTuple(args, "is#:load_certificate", &type, &buffer, &len)) |
| return NULL; |
| |
| bio = BIO_new_mem_buf(buffer, len); |
| switch (type) |
| { |
| case X509_FILETYPE_PEM: |
| cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); |
| break; |
| |
| case X509_FILETYPE_ASN1: |
| cert = d2i_X509_bio(bio, NULL); |
| break; |
| |
| default: |
| PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); |
| BIO_free(bio); |
| return NULL; |
| } |
| BIO_free(bio); |
| |
| if (cert == NULL) |
| { |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| |
| return (PyObject *)crypto_X509_New(cert, 1); |
| } |
| |
| static char crypto_dump_certificate_doc[] = "\n\ |
| Dump a certificate to a buffer\n\ |
| \n\ |
| @param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ |
| @param cert: The certificate to dump\n\ |
| @return: The buffer with the dumped certificate in\n\ |
| "; |
| |
| static PyObject * |
| crypto_dump_certificate(PyObject *spam, PyObject *args) |
| { |
| int type, ret, buf_len; |
| char *temp; |
| PyObject *buffer; |
| BIO *bio; |
| crypto_X509Obj *cert; |
| |
| if (!PyArg_ParseTuple(args, "iO!:dump_certificate", &type, |
| &crypto_X509_Type, &cert)) |
| return NULL; |
| |
| bio = BIO_new(BIO_s_mem()); |
| switch (type) |
| { |
| case X509_FILETYPE_PEM: |
| ret = PEM_write_bio_X509(bio, cert->x509); |
| break; |
| |
| case X509_FILETYPE_ASN1: |
| ret = i2d_X509_bio(bio, cert->x509); |
| break; |
| |
| case X509_FILETYPE_TEXT: |
| ret = X509_print_ex(bio, cert->x509, 0, 0); |
| break; |
| |
| default: |
| PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT"); |
| BIO_free(bio); |
| return NULL; |
| } |
| |
| if (ret == 0) |
| { |
| BIO_free(bio); |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| |
| buf_len = BIO_get_mem_data(bio, &temp); |
| buffer = PyString_FromStringAndSize(temp, buf_len); |
| BIO_free(bio); |
| |
| return buffer; |
| } |
| |
| static char crypto_load_certificate_request_doc[] = "\n\ |
| Load a certificate request from a buffer\n\ |
| \n\ |
| @param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ |
| buffer - The buffer the certificate request is stored in\n\ |
| @return: The X509Req object\n\ |
| "; |
| |
| static PyObject * |
| crypto_load_certificate_request(PyObject *spam, PyObject *args) |
| { |
| crypto_X509ReqObj *crypto_X509Req_New(X509_REQ *, int); |
| int type, len; |
| char *buffer; |
| BIO *bio; |
| X509_REQ *req; |
| |
| if (!PyArg_ParseTuple(args, "is#:load_certificate_request", &type, &buffer, &len)) |
| return NULL; |
| |
| bio = BIO_new_mem_buf(buffer, len); |
| switch (type) |
| { |
| case X509_FILETYPE_PEM: |
| req = PEM_read_bio_X509_REQ(bio, NULL, NULL, NULL); |
| break; |
| |
| case X509_FILETYPE_ASN1: |
| req = d2i_X509_REQ_bio(bio, NULL); |
| break; |
| |
| default: |
| PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); |
| BIO_free(bio); |
| return NULL; |
| } |
| BIO_free(bio); |
| |
| if (req == NULL) |
| { |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| |
| return (PyObject *)crypto_X509Req_New(req, 1); |
| } |
| |
| static char crypto_dump_certificate_request_doc[] = "\n\ |
| Dump a certificate request to a buffer\n\ |
| \n\ |
| @param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ |
| req - The certificate request to dump\n\ |
| @return: The buffer with the dumped certificate request in\n\ |
| "; |
| |
| static PyObject * |
| crypto_dump_certificate_request(PyObject *spam, PyObject *args) |
| { |
| int type, ret, buf_len; |
| char *temp; |
| PyObject *buffer; |
| BIO *bio; |
| crypto_X509ReqObj *req; |
| |
| if (!PyArg_ParseTuple(args, "iO!:dump_certificate_request", &type, |
| &crypto_X509Req_Type, &req)) |
| return NULL; |
| |
| bio = BIO_new(BIO_s_mem()); |
| switch (type) |
| { |
| case X509_FILETYPE_PEM: |
| ret = PEM_write_bio_X509_REQ(bio, req->x509_req); |
| break; |
| |
| case X509_FILETYPE_ASN1: |
| ret = i2d_X509_REQ_bio(bio, req->x509_req); |
| break; |
| |
| case X509_FILETYPE_TEXT: |
| ret = X509_REQ_print_ex(bio, req->x509_req, 0, 0); |
| break; |
| |
| default: |
| PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT"); |
| BIO_free(bio); |
| return NULL; |
| } |
| |
| if (ret == 0) |
| { |
| BIO_free(bio); |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| |
| buf_len = BIO_get_mem_data(bio, &temp); |
| buffer = PyString_FromStringAndSize(temp, buf_len); |
| BIO_free(bio); |
| |
| return buffer; |
| } |
| |
| static char crypto_load_pkcs7_data_doc[] = "\n\ |
| Load pkcs7 data from a buffer\n\ |
| \n\ |
| @param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)\n\ |
| buffer - The buffer with the pkcs7 data.\n\ |
| @return: The PKCS7 object\n\ |
| "; |
| |
| static PyObject * |
| crypto_load_pkcs7_data(PyObject *spam, PyObject *args) |
| { |
| int type, len; |
| char *buffer; |
| BIO *bio; |
| PKCS7 *pkcs7 = NULL; |
| |
| if (!PyArg_ParseTuple(args, "is#:load_pkcs7_data", &type, &buffer, &len)) |
| return NULL; |
| |
| /* |
| * Try to read the pkcs7 data from the bio |
| */ |
| bio = BIO_new_mem_buf(buffer, len); |
| switch (type) |
| { |
| case X509_FILETYPE_PEM: |
| pkcs7 = PEM_read_bio_PKCS7(bio, NULL, NULL, NULL); |
| break; |
| |
| case X509_FILETYPE_ASN1: |
| pkcs7 = d2i_PKCS7_bio(bio, NULL); |
| break; |
| |
| default: |
| PyErr_SetString(PyExc_ValueError, |
| "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); |
| return NULL; |
| } |
| BIO_free(bio); |
| |
| /* |
| * Check if we got a PKCS7 structure |
| */ |
| if (pkcs7 == NULL) |
| { |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| |
| return (PyObject *)crypto_PKCS7_New(pkcs7, 1); |
| } |
| |
| static char crypto_load_pkcs12_doc[] = "\n\ |
| Load a PKCS12 object from a buffer\n\ |
| \n\ |
| @param buffer: The buffer the certificate is stored in\n\ |
| passphrase (Optional) - The password to decrypt the PKCS12 lump\n\ |
| @returns: The PKCS12 object\n\ |
| "; |
| |
| static PyObject * |
| crypto_load_pkcs12(PyObject *spam, PyObject *args) |
| { |
| crypto_PKCS12Obj *crypto_PKCS12_New(PKCS12 *, char *); |
| int len; |
| char *buffer, *passphrase = NULL; |
| BIO *bio; |
| PKCS12 *p12; |
| |
| if (!PyArg_ParseTuple(args, "s#|s:load_pkcs12", &buffer, &len, &passphrase)) |
| return NULL; |
| |
| bio = BIO_new_mem_buf(buffer, len); |
| if ((p12 = d2i_PKCS12_bio(bio, NULL)) == NULL) |
| { |
| BIO_free(bio); |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| BIO_free(bio); |
| |
| return (PyObject *)crypto_PKCS12_New(p12, passphrase); |
| } |
| |
| |
| static char crypto_X509_verify_cert_error_string_doc[] = "\n\ |
| Get X509 verify certificate error string.\n\ |
| \n\ |
| @param errnum: The error number.\n\ |
| @return: Error string as a Python string\n\ |
| "; |
| |
| static PyObject * |
| crypto_X509_verify_cert_error_string(PyObject *spam, PyObject *args) |
| { |
| int errnum; |
| const char *str; |
| |
| if (!PyArg_ParseTuple(args, "i", &errnum)) |
| return NULL; |
| |
| str = X509_verify_cert_error_string(errnum); |
| return PyString_FromString(str); |
| } |
| |
| static char crypto_exception_from_error_queue_doc[] = "\n\ |
| Raise an exception from the current OpenSSL error queue.\n\ |
| "; |
| |
| static PyObject * |
| crypto_exception_from_error_queue(PyObject *spam, PyObject *eggs) { |
| exception_from_error_queue(crypto_Error); |
| return NULL; |
| } |
| |
| |
| /* Methods in the OpenSSL.crypto module (i.e. none) */ |
| static PyMethodDef crypto_methods[] = { |
| /* Module functions */ |
| { "load_privatekey", (PyCFunction)crypto_load_privatekey, METH_VARARGS, crypto_load_privatekey_doc }, |
| { "dump_privatekey", (PyCFunction)crypto_dump_privatekey, METH_VARARGS, crypto_dump_privatekey_doc }, |
| { "load_certificate", (PyCFunction)crypto_load_certificate, METH_VARARGS, crypto_load_certificate_doc }, |
| { "dump_certificate", (PyCFunction)crypto_dump_certificate, METH_VARARGS, crypto_dump_certificate_doc }, |
| { "load_certificate_request", (PyCFunction)crypto_load_certificate_request, METH_VARARGS, crypto_load_certificate_request_doc }, |
| { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc }, |
| { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc }, |
| { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc }, |
| { "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc }, |
| { "_exception_from_error_queue", (PyCFunction)crypto_exception_from_error_queue, METH_NOARGS, crypto_exception_from_error_queue_doc }, |
| { NULL, NULL } |
| }; |
| |
| |
| #ifdef WITH_THREAD |
| |
| #include <pythread.h> |
| |
| /** |
| * This array will store all of the mutexes available to OpenSSL. |
| */ |
| static PyThread_type_lock *mutex_buf = NULL; |
| |
| |
| /** |
| * Callback function supplied to OpenSSL to acquire or release a lock. |
| * |
| */ |
| static void locking_function(int mode, int n, const char * file, int line) { |
| if (mode & CRYPTO_LOCK) { |
| PyThread_acquire_lock(mutex_buf[n], WAIT_LOCK); |
| } else { |
| PyThread_release_lock(mutex_buf[n]); |
| } |
| } |
| |
| |
| /** |
| * Initialize OpenSSL for use from multiple threads. |
| * |
| * Returns: 0 if initialization fails, 1 otherwise. |
| */ |
| static int init_openssl_threads(void) { |
| int i; |
| |
| mutex_buf = (PyThread_type_lock *)malloc( |
| CRYPTO_num_locks() * sizeof(PyThread_type_lock)); |
| if (!mutex_buf) { |
| return 0; |
| } |
| for (i = 0; i < CRYPTO_num_locks(); ++i) { |
| mutex_buf[i] = PyThread_allocate_lock(); |
| } |
| CRYPTO_set_id_callback((unsigned long (*)(void))PyThread_get_thread_ident); |
| CRYPTO_set_locking_callback(locking_function); |
| return 1; |
| } |
| |
| /* /\** */ |
| /* * Clean up after OpenSSL thread initialization. */ |
| /* *\/ */ |
| /* static int deinit_openssl_threads() { */ |
| /* int i; */ |
| |
| /* if (!mutex_buf) { */ |
| /* return 0; */ |
| /* } */ |
| /* CRYPTO_set_id_callback(NULL); */ |
| /* CRYPTO_set_locking_callback(NULL); */ |
| /* for (i = 0; i < CRYPTO_num_locks(); i++) { */ |
| /* PyThread_free_lock(mutex_buf[i]); */ |
| /* } */ |
| /* free(mutex_buf); */ |
| /* mutex_buf = NULL; */ |
| /* return 1; */ |
| /* } */ |
| |
| #endif |
| |
| |
| /* |
| * Initialize crypto sub module |
| * |
| * Arguments: None |
| * Returns: None |
| */ |
| void |
| initcrypto(void) |
| { |
| static void *crypto_API[crypto_API_pointers]; |
| PyObject *c_api_object; |
| PyObject *module; |
| |
| ERR_load_crypto_strings(); |
| OpenSSL_add_all_algorithms(); |
| |
| if ((module = Py_InitModule3("crypto", crypto_methods, crypto_doc)) == NULL) |
| return; |
| |
| /* Initialize the C API pointer array */ |
| crypto_API[crypto_X509_New_NUM] = (void *)crypto_X509_New; |
| crypto_API[crypto_X509Name_New_NUM] = (void *)crypto_X509Name_New; |
| crypto_API[crypto_X509Req_New_NUM] = (void *)crypto_X509Req_New; |
| crypto_API[crypto_X509Store_New_NUM] = (void *)crypto_X509Store_New; |
| crypto_API[crypto_PKey_New_NUM] = (void *)crypto_PKey_New; |
| crypto_API[crypto_X509Extension_New_NUM] = (void *)crypto_X509Extension_New; |
| crypto_API[crypto_PKCS7_New_NUM] = (void *)crypto_PKCS7_New; |
| crypto_API[crypto_NetscapeSPKI_New_NUM] = (void *)crypto_NetscapeSPKI_New; |
| c_api_object = PyCObject_FromVoidPtr((void *)crypto_API, NULL); |
| if (c_api_object != NULL) |
| PyModule_AddObject(module, "_C_API", c_api_object); |
| |
| crypto_Error = PyErr_NewException("OpenSSL.crypto.Error", NULL, NULL); |
| if (crypto_Error == NULL) |
| goto error; |
| if (PyModule_AddObject(module, "Error", crypto_Error) != 0) |
| goto error; |
| |
| PyModule_AddIntConstant(module, "FILETYPE_PEM", X509_FILETYPE_PEM); |
| PyModule_AddIntConstant(module, "FILETYPE_ASN1", X509_FILETYPE_ASN1); |
| PyModule_AddIntConstant(module, "FILETYPE_TEXT", X509_FILETYPE_TEXT); |
| |
| PyModule_AddIntConstant(module, "TYPE_RSA", crypto_TYPE_RSA); |
| PyModule_AddIntConstant(module, "TYPE_DSA", crypto_TYPE_DSA); |
| |
| #ifdef WITH_THREAD |
| if (!init_openssl_threads()) |
| goto error; |
| #endif |
| if (!init_crypto_x509(module)) |
| goto error; |
| if (!init_crypto_x509name(module)) |
| goto error; |
| if (!init_crypto_x509store(module)) |
| goto error; |
| if (!init_crypto_x509req(module)) |
| goto error; |
| if (!init_crypto_pkey(module)) |
| goto error; |
| if (!init_crypto_x509extension(module)) |
| goto error; |
| if (!init_crypto_pkcs7(module)) |
| goto error; |
| if (!init_crypto_pkcs12(module)) |
| goto error; |
| if (!init_crypto_netscape_spki(module)) |
| goto error; |
| |
| error: |
| ; |
| } |