Merge lp:~morgan-s-reed/pyopenssl/mr-RSAadditions into lp:~exarkun/pyopenssl/trunk

Proposed by Morgan
Status: Work in progress
Proposed branch: lp:~morgan-s-reed/pyopenssl/mr-RSAadditions
Merge into: lp:~exarkun/pyopenssl/trunk
Diff against target: 967 lines (+736/-18)
8 files modified
PKG-INFO (+3/-3)
doc/pyOpenSSL.tex (+60/-3)
setup.py (+2/-2)
src/crypto/crypto.c (+71/-6)
src/crypto/crypto.h (+10/-1)
src/crypto/rsa.c (+400/-0)
src/crypto/rsa.h (+47/-0)
test/test_crypto.py (+143/-3)
To merge this branch: bzr merge lp:~morgan-s-reed/pyopenssl/mr-RSAadditions
Reviewer Review Type Date Requested Status
Jean-Paul Calderone Needs Fixing
Review via email: mp+15119@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Morgan (morgan-s-reed) wrote :

This branch is now ready for merging, all changes from trunk have been back-merged in, all tests succeed.

Revision history for this message
Jean-Paul Calderone (exarkun) wrote :

The first thing that strikes me about this change is the apparent redundancy that it introduces into the key APIs exposed by pyOpenSSL. It seems like adding sign, verify, encrypt, and decrypt methods to the `PKey` type would be the right way to expose this functionality. As I understand it, the EVP_Sign*, EVP_Verify*, EVP_Encrypt*, and EVP_Decrypt* functions will do this with the existing data structure wrapped by the `PKey` type.

review: Needs Fixing
Revision history for this message
Morgan (morgan-s-reed) wrote :

On Mon, Nov 23, 2009 at 03:04, Jean-Paul Calderone
<email address hidden> wrote:
> Review: Needs Fixing
> The first thing that strikes me about this change is the apparent redundancy that it introduces into the key APIs exposed by pyOpenSSL.  It seems like adding sign, verify, encrypt, and decrypt methods to the `PKey` type would be the right way to expose this functionality.  As I understand it, the EVP_Sign*, EVP_Verify*, EVP_Encrypt*, and EVP_Decrypt* functions will do this with the existing data structure wrapped by the `PKey` type.

Reviewing the documentation for the EVP module, you are correct that
the RSA_sign and RSA_verify methods are redundant. However it is not
(from what I can tell) possible to do direct RSA encryption/decryption
with the EVP module (this is what I implemented the RSA bindings for,
I was working with a licensing scheme that used a license key
encrypted directly by RSA), the API only allows you to "envelope"
symmetrically encrypted data in an EVP context (generate symmetric
keys/IVs encrypt "enveloped" data with those, encrypt those with
RSA/DSA). EVP_Decrypt* and EVP_Encrypt* methods are (as far as I can
tell) only for symmetric encryption.

That said the person who requested the RSA/AES stuff on the list
probably needs the EVP methods rather than the direct low-level
methods.

I will have a look into implementing the relevant EVP methods on PKey
but I'm not sure if I will have time to complete the implementation.

Revision history for this message
Morgan (morgan-s-reed) wrote :

On Mon, Nov 23, 2009 at 08:45, Morgan Reed <email address hidden> wrote:
> I will have a look into implementing the relevant EVP methods on PKey
> but I'm not sure if I will have time to complete the implementation.

Apologies for the double message, Gmail was having kittens for some
unknown reason.

Have we heard anything from Edward Tait recently? I note he was
working on a "branch with-EVP_sign+verify", though I've no idea what
the status of it is.

Revision history for this message
Morgan (morgan-s-reed) wrote :

On Mon, Nov 23, 2009 at 08:50, Morgan Reed <email address hidden> wrote:
> Apologies for the double message, Gmail was having kittens for some
> unknown reason.

Ahh, I see now, scratch that, launchpad is sending me copies of my responses...

Unmerged revisions

91. By intrinsic <intrinsic@athena>

Fixed RSA test case

90. By intrinsic <intrinsic@athena>

Cleaned the garbage out of crypto.c

89. By intrinsic <intrinsic@athena>

Fixed breakage from the merging trunk

88. By intrinsic <intrinsic@athena>

Trunk Merge Completed

87. By intrinsic <intrinsic@athena>

Incremental merge commit 5

86. By intrinsic <intrinsic@athena>

Incremental merge commit 4

85. By intrinsic <intrinsic@athena>

Incremental merge commit 3

84. By intrinsic <intrinsic@athena>

Incremental merge commit 2

83. By intrinsic <intrinsic@athena>

Incremental merge commit 1

82. By intrinsic <intrinsic@athena>

Committing local changes prior to back-merging trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'PKG-INFO'
--- PKG-INFO 2008-02-19 01:50:23 +0000
+++ PKG-INFO 2009-11-21 12:40:29 +0000
@@ -1,10 +1,10 @@
1Metadata-Version: 1.01Metadata-Version: 1.0
2Name: pyOpenSSL2Name: pyOpenSSL
3Version: 0.63Version: 0.8a1
4Summary: Python wrapper module around the OpenSSL library4Summary: Python wrapper module around the OpenSSL library
5Home-page: http://pyopenssl.sourceforge.net/5Home-page: http://pyopenssl.sourceforge.net/
6Author: Martin Sjögren, AB Strakt6Author: Jean-Paul Calderone
7Author-email: msjogren@gmail.com7Author-email: exarkun@twistedmatrix.com
8License: LGPL8License: LGPL
9Description: High-level wrapper around a subset of the OpenSSL library, includes9Description: High-level wrapper around a subset of the OpenSSL library, includes
10 * SSL.Connection objects, wrapping the methods of Python's portable10 * SSL.Connection objects, wrapping the methods of Python's portable
1111
=== modified file 'doc/pyOpenSSL.tex'
--- doc/pyOpenSSL.tex 2009-11-13 14:16:32 +0000
+++ doc/pyOpenSSL.tex 2009-11-21 12:40:29 +0000
@@ -207,6 +207,14 @@
207method.207method.
208\end{classdesc}208\end{classdesc}
209209
210\begin{datadesc}{RSAType}
211A Python type object representing the RSA object type.
212\end{datadesc}
213
214\begin{funcdesc}{RSA}{}
215Factory function that creates a RSA object.
216\end{funcdesc}
217
210\begin{datadesc}{FILETYPE_PEM}218\begin{datadesc}{FILETYPE_PEM}
211\dataline{FILETYPE_ASN1}219\dataline{FILETYPE_ASN1}
212File type constants.220File type constants.
@@ -612,10 +620,61 @@
612Verify the NetscapeSPKI object using the given \var{key}.620Verify the NetscapeSPKI object using the given \var{key}.
613\end{methoddesc}621\end{methoddesc}
614622
623\subsubsection{RSA objects \label{openssl-rsa}}
624
625RSA objects have the following methods:
626
627\begin{methoddesc}[RSA]{generate_key}{bits}
628Generate a new RSA keypair of the given size (\var{bits}) and store within the
629object.
630\end{methoddesc}
631
632\begin{methoddesc}[RSA]{key_bits}{}
633Returns the RSA_size of the contained keys
634\end{methoddesc}
635
636\begin{methoddesc}[RSA]{public_encrypt}{message\optional{, padding}}
637Returns the given \var{message} encrypted with the public key stored in the
638object with the given \var{padding}, (defaults to RSA_PKCS1_PADDING, other valid
639options are; RSA_PKCS1_OAEP_PADDING, RSA_SSLV23_PADDING and RSA_NO_PADDING),
640throws an error if the input data is larger than the key stored in the object.
641\end{methoddesc}
642
643\begin{methoddesc}[RSA]{private_encrypt}{message\optional{, padding}}
644Returns the given \var{message} encrypted with the private key stored in the
645object with the given \var{padding}, (defaults to RSA_PKCS1_PADDING, the only
646other valid option at this time is RSA_NO_PADDING (these are the only supported
647paddings in OpenSSL 0.9.8g)), throws an error if the input data is larger than
648the key stored in the object.
649\end{methoddesc}
650
651\begin{methoddesc}[RSA]{public_decrypt}{message\optional{, padding}}
652Returns the plaintext decrypted from the given \var{message} using the public
653key stored in the object, throws an error if the type of the padding does not
654match the padding used to encrypt the message.
655\end{methoddesc}
656
657\begin{methoddesc}[RSA]{private_decrypt}{message\optional{, padding}}
658Returns the plaintext decrypted from the given \var{message} using the private
659key stored in the object.
660\end{methoddesc}
661
662\begin{methoddesc}[RSA]{sign}{algorithm, digest}
663Signs the message whose digest as generated by \var{algorithm} (valid options
664are NID_sha1, NID_ripemd160, NID_md5 and NID_md5_sha1) is \var{digest} with the
665contained private key.
666\end{methoddesc}
667
668\begin{methoddesc}[RSA]{verify}{algorithm, digest, signature}
669Verifies the \var{signature} for the message whose digest as generated by
670\var{algorithm} (valid options are NID_sha1, NID_ripemd160, NID_md5 and
671NID_md5_sha1) is \var{digest} using the contained public key.
672\end{methoddesc}
615673
616% % % rand module674% % % rand module
617675
618\subsection{\module{rand} --- An interface to the OpenSSL pseudo random number generator \label{openssl-rand}}676\subsection{\module{rand} --- An interface to the OpenSSL pseudo random number
677generator \label{openssl-rand}}
619678
620\declaremodule{extension}{rand}679\declaremodule{extension}{rand}
621\modulesynopsis{An interface to the OpenSSL pseudo random number generator}680\modulesynopsis{An interface to the OpenSSL pseudo random number generator}
@@ -1191,8 +1250,6 @@
1191operation.1250operation.
1192\end{methoddesc}1251\end{methoddesc}
11931252
1194
1195
1196\section{Internals \label{internals}}1253\section{Internals \label{internals}}
11971254
1198We ran into three main problems developing this: Exceptions, callbacks and1255We ran into three main problems developing this: Exceptions, callbacks and
11991256
=== modified file 'setup.py'
--- setup.py 2009-11-11 15:51:56 +0000
+++ setup.py 2009-11-21 12:40:29 +0000
@@ -26,13 +26,13 @@
26 'src/crypto/x509store.c', 'src/crypto/x509req.c',26 'src/crypto/x509store.c', 'src/crypto/x509req.c',
27 'src/crypto/x509ext.c', 'src/crypto/pkcs7.c',27 'src/crypto/x509ext.c', 'src/crypto/pkcs7.c',
28 'src/crypto/pkcs12.c', 'src/crypto/netscape_spki.c',28 'src/crypto/pkcs12.c', 'src/crypto/netscape_spki.c',
29 'src/util.c']29 'src/crypto/rsa.c', 'src/util.c']
30crypto_dep = ['src/crypto/crypto.h', 'src/crypto/x509.h',30crypto_dep = ['src/crypto/crypto.h', 'src/crypto/x509.h',
31 'src/crypto/x509name.h', 'src/crypto/pkey.h',31 'src/crypto/x509name.h', 'src/crypto/pkey.h',
32 'src/crypto/x509store.h', 'src/crypto/x509req.h',32 'src/crypto/x509store.h', 'src/crypto/x509req.h',
33 'src/crypto/x509ext.h', 'src/crypto/pkcs7.h',33 'src/crypto/x509ext.h', 'src/crypto/pkcs7.h',
34 'src/crypto/pkcs12.h', 'src/crypto/netscape_spki.h',34 'src/crypto/pkcs12.h', 'src/crypto/netscape_spki.h',
35 'src/util.h']35 'src/crypto/rsa.h', 'src/util.h']
36rand_src = ['src/rand/rand.c', 'src/util.c']36rand_src = ['src/rand/rand.c', 'src/util.c']
37rand_dep = ['src/util.h']37rand_dep = ['src/util.h']
38ssl_src = ['src/ssl/connection.c', 'src/ssl/context.c', 'src/ssl/ssl.c',38ssl_src = ['src/ssl/connection.c', 'src/ssl/context.c', 'src/ssl/ssl.c',
3939
=== modified file 'src/crypto/crypto.c'
--- src/crypto/crypto.c 2009-07-17 17:50:12 +0000
+++ src/crypto/crypto.c 2009-11-21 12:40:29 +0000
@@ -150,7 +150,7 @@
150 crypto_PKeyObj *pkey;150 crypto_PKeyObj *pkey;
151151
152 if (!PyArg_ParseTuple(args, "iO!|sO:dump_privatekey", &type,152 if (!PyArg_ParseTuple(args, "iO!|sO:dump_privatekey", &type,
153 &crypto_PKey_Type, &pkey, &cipher_name, &pw))153 &crypto_PKey_Type, &pkey, &cipher_name, &pw))
154 return NULL;154 return NULL;
155155
156 if (cipher_name != NULL && pw == NULL)156 if (cipher_name != NULL && pw == NULL)
@@ -290,7 +290,7 @@
290 crypto_X509Obj *cert;290 crypto_X509Obj *cert;
291291
292 if (!PyArg_ParseTuple(args, "iO!:dump_certificate", &type,292 if (!PyArg_ParseTuple(args, "iO!:dump_certificate", &type,
293 &crypto_X509_Type, &cert))293 &crypto_X509_Type, &cert))
294 return NULL;294 return NULL;
295295
296 bio = BIO_new(BIO_s_mem());296 bio = BIO_new(BIO_s_mem());
@@ -393,7 +393,7 @@
393 crypto_X509ReqObj *req;393 crypto_X509ReqObj *req;
394394
395 if (!PyArg_ParseTuple(args, "iO!:dump_certificate_request", &type,395 if (!PyArg_ParseTuple(args, "iO!:dump_certificate_request", &type,
396 &crypto_X509Req_Type, &req))396 &crypto_X509Req_Type, &req))
397 return NULL;397 return NULL;
398398
399 bio = BIO_new(BIO_s_mem());399 bio = BIO_new(BIO_s_mem());
@@ -514,6 +514,54 @@
514 return (PyObject *)crypto_PKCS12_New(p12, passphrase);514 return (PyObject *)crypto_PKCS12_New(p12, passphrase);
515}515}
516516
517static char crypto_load_rsa_doc[] = "\n\
518Load an RSA object from a buffer\n\
519\n\
520@param spam: Always NULL\n\
521 args - The Python argument tuple, should be empty.\n\
522@returns: The RSA object\n\
523";
524
525static PyObject *
526crypto_load_rsa(PyObject *spam, PyObject *args)
527{
528 crypto_RSAObj *crypto_RSA_New(RSA *, int);
529 RSA *rsa;
530
531 if (!PyArg_ParseTuple(args, "s#|s:load_rsa"))
532 return NULL;
533
534 if ((rsa = RSA_new()) == NULL) {
535 exception_from_error_queue(crypto_Error);
536 return NULL;
537 }
538
539 return (PyObject *)crypto_RSA_New(rsa, 1);
540}
541
542static char crypto_rsa_doc[] = "\n\
543The factory function inserted in the module dictionary to create RSA\n\
544objects\n\
545\n\
546Arguments: spam - Always NULL\n\
547 args - The Python argument tuple, should be empty\n\
548Returns: The RSA object\n\
549";
550
551static PyObject *
552crypto_rsa(PyObject *spam, PyObject *args)
553{
554 crypto_RSAObj *py_rsa;
555
556 if (!PyArg_ParseTuple(args, ":RSA"))
557 return NULL;
558
559 py_rsa = crypto_RSA_New(RSA_new(), 1);
560 if (py_rsa) {
561 py_rsa->initialized = 0;
562 }
563 return (PyObject *)py_rsa;
564}
517565
518static char crypto_X509_verify_cert_error_string_doc[] = "\n\566static char crypto_X509_verify_cert_error_string_doc[] = "\n\
519Get X509 verify certificate error string.\n\567Get X509 verify certificate error string.\n\
@@ -557,6 +605,9 @@
557 { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc },605 { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc },
558 { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },606 { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },
559 { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },607 { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },
608 { "load_rsa", (PyCFunction)crypto_load_rsa, METH_VARARGS, crypto_load_rsa_doc },
609 /* Factory functions */
610 { "RSA", (PyCFunction)crypto_rsa, METH_VARARGS, crypto_rsa_doc },
560 { "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc },611 { "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc },
561 { "_exception_from_error_queue", (PyCFunction)crypto_exception_from_error_queue, METH_NOARGS, crypto_exception_from_error_queue_doc },612 { "_exception_from_error_queue", (PyCFunction)crypto_exception_from_error_queue, METH_NOARGS, crypto_exception_from_error_queue_doc },
562 { NULL, NULL }613 { NULL, NULL }
@@ -654,9 +705,10 @@
654 crypto_API[crypto_X509Req_New_NUM] = (void *)crypto_X509Req_New;705 crypto_API[crypto_X509Req_New_NUM] = (void *)crypto_X509Req_New;
655 crypto_API[crypto_X509Store_New_NUM] = (void *)crypto_X509Store_New;706 crypto_API[crypto_X509Store_New_NUM] = (void *)crypto_X509Store_New;
656 crypto_API[crypto_PKey_New_NUM] = (void *)crypto_PKey_New;707 crypto_API[crypto_PKey_New_NUM] = (void *)crypto_PKey_New;
657 crypto_API[crypto_X509Extension_New_NUM] = (void *)crypto_X509Extension_New;708 crypto_API[crypto_X509Extension_New_NUM]= (void *)crypto_X509Extension_New;
658 crypto_API[crypto_PKCS7_New_NUM] = (void *)crypto_PKCS7_New;709 crypto_API[crypto_PKCS7_New_NUM] = (void *)crypto_PKCS7_New;
659 crypto_API[crypto_NetscapeSPKI_New_NUM] = (void *)crypto_NetscapeSPKI_New;710 crypto_API[crypto_NetscapeSPKI_New_NUM] = (void *)crypto_NetscapeSPKI_New;
711 crypto_API[crypto_RSA_New_NUM] = (void *)crypto_RSA_New;
660 c_api_object = PyCObject_FromVoidPtr((void *)crypto_API, NULL);712 c_api_object = PyCObject_FromVoidPtr((void *)crypto_API, NULL);
661 if (c_api_object != NULL)713 if (c_api_object != NULL)
662 PyModule_AddObject(module, "_C_API", c_api_object);714 PyModule_AddObject(module, "_C_API", c_api_object);
@@ -674,6 +726,17 @@
674 PyModule_AddIntConstant(module, "TYPE_RSA", crypto_TYPE_RSA);726 PyModule_AddIntConstant(module, "TYPE_RSA", crypto_TYPE_RSA);
675 PyModule_AddIntConstant(module, "TYPE_DSA", crypto_TYPE_DSA);727 PyModule_AddIntConstant(module, "TYPE_DSA", crypto_TYPE_DSA);
676728
729 PyModule_AddIntConstant(module, "RSA_PKCS1_PADDING", crypto_RSA_PKCS1_PADDING);
730 PyModule_AddIntConstant(module, "RSA_PKCS1_OAEP_PADDING", crypto_RSA_PKCS1_OAEP_PADDING);
731 PyModule_AddIntConstant(module, "RSA_SSLV23_PADDING", crypto_RSA_SSLV23_PADDING);
732 PyModule_AddIntConstant(module, "RSA_NO_PADDING", crypto_RSA_NO_PADDING);
733
734 PyModule_AddIntConstant(module, "NID_sha1", crypto_NID_sha1);
735 PyModule_AddIntConstant(module, "NID_ripemd160", crypto_NID_ripemd160);
736 PyModule_AddIntConstant(module, "NID_md5", crypto_NID_md5);
737 PyModule_AddIntConstant(module, "NID_md5_sha1", crypto_NID_md5_sha1);
738
739
677#ifdef WITH_THREAD740#ifdef WITH_THREAD
678 if (!init_openssl_threads())741 if (!init_openssl_threads())
679 goto error;742 goto error;
@@ -696,6 +759,8 @@
696 goto error;759 goto error;
697 if (!init_crypto_netscape_spki(module))760 if (!init_crypto_netscape_spki(module))
698 goto error;761 goto error;
762 if (!init_crypto_rsa(module))
763 goto error;
699764
700error:765error:
701 ;766 ;
702767
=== modified file 'src/crypto/crypto.h'
--- src/crypto/crypto.h 2009-07-17 20:06:12 +0000
+++ src/crypto/crypto.h 2009-11-21 12:40:29 +0000
@@ -2,6 +2,7 @@
2 * crypto.h2 * crypto.h
3 *3 *
4 * Copyright (C) AB Strakt 2001, All rights reserved4 * Copyright (C) AB Strakt 2001, All rights reserved
5 * Copyright (C) Morgan Reed 2008, All rights reserved
5 *6 *
6 * Exports from crypto.c.7 * Exports from crypto.c.
7 * See the file RATIONALE for a short explanation of why this module was written.8 * See the file RATIONALE for a short explanation of why this module was written.
@@ -23,6 +24,7 @@
23#include "x509ext.h"24#include "x509ext.h"
24#include "pkcs7.h"25#include "pkcs7.h"
25#include "pkcs12.h"26#include "pkcs12.h"
27#include "rsa.h"
26#include "../util.h"28#include "../util.h"
2729
28extern PyObject *crypto_Error;30extern PyObject *crypto_Error;
@@ -59,7 +61,11 @@
59#define crypto_NetscapeSPKI_New_RETURN crypto_NetscapeSPKIObj *61#define crypto_NetscapeSPKI_New_RETURN crypto_NetscapeSPKIObj *
60#define crypto_NetscapeSPKI_New_PROTO (NETSCAPE_SPKI *, int)62#define crypto_NetscapeSPKI_New_PROTO (NETSCAPE_SPKI *, int)
6163
62#define crypto_API_pointers 864#define crypto_RSA_New_NUM 8
65#define crypto_RSA_New_RETURN crypto_RSAObj *
66#define crypto_RSA_New_PROTO (RSA *, int)
67
68#define crypto_API_pointers 9
6369
64#ifdef crypto_MODULE70#ifdef crypto_MODULE
6571
@@ -71,6 +77,7 @@
71extern crypto_X509Extension_New_RETURN crypto_X509Extension_New crypto_X509Extension_New_PROTO;77extern crypto_X509Extension_New_RETURN crypto_X509Extension_New crypto_X509Extension_New_PROTO;
72extern crypto_PKCS7_New_RETURN crypto_PKCS7_New crypto_PKCS7_New_PROTO;78extern crypto_PKCS7_New_RETURN crypto_PKCS7_New crypto_PKCS7_New_PROTO;
73extern crypto_NetscapeSPKI_New_RETURN crypto_NetscapeSPKI_New crypto_NetscapeSPKI_New_PROTO;79extern crypto_NetscapeSPKI_New_RETURN crypto_NetscapeSPKI_New crypto_NetscapeSPKI_New_PROTO;
80extern crypto_RSA_New_RETURN crypto_RSA_New crypto_RSA_New_PROTO;
7481
75#else /* crypto_MODULE */82#else /* crypto_MODULE */
7683
@@ -92,6 +99,8 @@
92 (*(crypto_PKCS7_New_RETURN (*)crypto_PKCS7_New_PROTO) crypto_API[crypto_PKCS7_New_NUM])99 (*(crypto_PKCS7_New_RETURN (*)crypto_PKCS7_New_PROTO) crypto_API[crypto_PKCS7_New_NUM])
93#define crypto_NetscapeSPKI_New \100#define crypto_NetscapeSPKI_New \
94 (*(crypto_NetscapeSPKI_New_RETURN (*)crypto_NetscapeSPKI_New_PROTO) crypto_API[crypto_NetscapeSPKI_New_NUM])101 (*(crypto_NetscapeSPKI_New_RETURN (*)crypto_NetscapeSPKI_New_PROTO) crypto_API[crypto_NetscapeSPKI_New_NUM])
102#define crypto_RSA_New \
103 (*(crypto_RSA_New_RETURN (*)crypto_RSA_New_PROTO) crypto_API[crypto_RSA_New_NUM])
95104
96#define import_crypto() \105#define import_crypto() \
97{ \106{ \
98107
=== added file 'src/crypto/rsa.c'
--- src/crypto/rsa.c 1970-01-01 00:00:00 +0000
+++ src/crypto/rsa.c 2009-11-21 12:40:29 +0000
@@ -0,0 +1,400 @@
1/*
2 * rsa.c
3 *
4 * Copyright (C) Morgan Reed 2008, All rights reserved
5 *
6 * RSA encryption/decryption routines.
7 *
8 */
9#include <Python.h>
10#define crypto_MODULE
11#include "crypto.h"
12
13#define DEBUG
14
15#define DEFAULT_PADDING RSA_PKCS1_PADDING
16
17/*
18 * This is done every time something fails, so turning it into a macro is
19 * really nice.
20 *
21 * Arguments: None
22 * Returns: Doesn't return
23 */
24#define FAIL() \
25do { \
26 exception_from_error_queue(crypto_Error); \
27 return NULL; \
28} while (0)
29
30static char crypto_RSA_sign_doc[] = "\n\
31Returns the passed message digest signed with the contained private key\n\
32\n\
33Arguments: self - The RSA object\n\
34 args - The python argument tuple, should be:\n\
35 algo - The algorithm used to generate the digest\n\
36 digest - The message digest\n\
37Returns: String containing the signature\n\
38";
39
40static PyObject *
41crypto_RSA_sign(crypto_RSAObj *self, PyObject *args)
42{
43 int bufsz = RSA_size(self->rsa);
44 unsigned int algo, digestlen, siglen;
45 unsigned char *digest;
46 unsigned char *sig = (char *)malloc(bufsz);
47
48
49 if(!PyArg_ParseTuple(args, "is#:sign", &algo, &digest, &digestlen))
50 return NULL;
51
52 if(!RSA_sign(algo, digest, digestlen, sig, &siglen, self->rsa)) {
53 ERR_print_errors_fp(stdout);
54 FAIL();
55 }
56
57 return PyString_FromStringAndSize(sig, siglen);
58}
59
60static char crypto_RSA_verify_doc[] = "\n\
61Tests the given signature against the contained public key\n\
62\n\
63Arguments: self - The RSA object\n\
64 args - The python argument tuple, should be:\n\
65 algo - The algorithm used to generate the digest\n\
66 digest - The message digest\n\
67 sig - The signature\n\
68Returns: Boolean result of comparison\n\
69";
70
71static PyObject *
72crypto_RSA_verify(crypto_RSAObj *self, PyObject *args)
73{
74 unsigned long ret;
75 unsigned int algo, digestlen, siglen;
76 unsigned char *digest;
77 unsigned char *sig;
78
79 if(!PyArg_ParseTuple(args, "is#s#:verify", &algo, &digest, &digestlen,
80 &sig, &siglen))
81 return NULL;
82
83 if(!(ret =RSA_verify(algo, digest, digestlen, sig, siglen, self->rsa)))
84 ERR_print_errors_fp(stdout);
85
86 return PyLong_FromUnsignedLong(ret);
87
88}
89
90static char crypto_RSA_key_bits_doc[] = "\n\
91Returns the length of the contained RSA key\n\
92\n\
93Arguments: self - The RSA object\n\
94 args - The python argument tuple, should be empty\n\
95Returns: Integer length of key\n\
96";
97
98static PyObject *
99crypto_RSA_key_bits(crypto_RSAObj *self, PyObject *args)
100{
101 unsigned long bits = (8 * RSA_size(self->rsa));
102 return PyLong_FromUnsignedLong(bits);
103}
104
105static char crypto_RSA_generate_key_doc[] = "\n\
106Generates a new RSA keypair\n\
107\n\
108Arguments: self - The RSA object\n\
109 args - The Python argument tuple, should be:\n\
110 bits - Key bits\n\
111Returns: None\n\
112";
113
114static PyObject *
115crypto_RSA_generate_key(crypto_RSAObj *self, PyObject *args)
116{
117 int bits;
118 RSA *rsa;
119
120 if (!PyArg_ParseTuple(args, "i:generate_key", &bits))
121 return NULL;
122
123 if (bits <= 0) {
124 PyErr_SetString(PyExc_ValueError, "Invalid number of bits");
125 return NULL;
126 }
127 if ((rsa = RSA_generate_key(bits, 0x10001, NULL, NULL)) == NULL)
128 FAIL();
129
130 self->rsa = rsa;
131
132 self->initialized = 1;
133 Py_INCREF(Py_None);
134 return Py_None;
135}
136
137
138static char crypto_RSA_public_encrypt_doc[] = "\n\
139Encrypts the input buffer with the associated public key\n\
140\n\
141Arguments: self - The RSA object\n\
142 args - The Python argument tuple, should be:\n\
143 ibuf - Cleartext input\n\
144Returns: String containing encrypted data\n\
145";
146
147static PyObject *
148crypto_RSA_public_encrypt(crypto_RSAObj *self, PyObject *args)
149{
150 int bufsz = RSA_size(self->rsa);
151 int sz, rsz;
152 int padding = DEFAULT_PADDING;
153 unsigned char *buf = (char*)malloc(bufsz);
154 unsigned char *ibuf;
155
156 if (!self->rsa) {
157 FAIL();
158 }
159
160 if (!PyArg_ParseTuple(args, "s#|i:public_encrypt", &ibuf, &sz, &padding)) {
161 FAIL();
162 }
163
164 rsz = RSA_public_encrypt(sz, ibuf, buf, self->rsa, padding);
165 if (rsz == -1) {
166 ERR_print_errors_fp(stdout);
167 FAIL();
168 }
169
170 return PyString_FromStringAndSize(buf, rsz);
171}
172
173static char crypto_RSA_public_decrypt_doc[] = "\n\
174Decrypts the input buffer with the associated public key\n\
175\n\
176Arguments: self - The RSA object\n\
177 args - The Python argument tuple, should be:\n\
178 ibuf - Cyphertext input\n\
179Returns: String containing encrypted data\n\
180";
181
182static PyObject *
183crypto_RSA_public_decrypt(crypto_RSAObj *self, PyObject *args)
184{
185 int bufsz = RSA_size(self->rsa);
186 int sz, rsz;
187 int padding = DEFAULT_PADDING;
188 unsigned char *buf = (char*)malloc(bufsz);
189 unsigned char *ibuf;
190
191 if (!self->rsa) {
192 FAIL();
193 }
194
195 if (!PyArg_ParseTuple(args, "s#|i:public_decrypt", &ibuf, &sz, &padding)) {
196 FAIL();
197 }
198
199 rsz = RSA_public_decrypt(sz, ibuf, buf, self->rsa, padding);
200 if (rsz == -1) {
201 ERR_print_errors_fp(stdout);
202 FAIL();
203 }
204
205 return PyString_FromStringAndSize(buf, rsz);
206}
207
208static char crypto_RSA_private_encrypt_doc[] = "\n\
209Encrypts the input buffer with the associated private key\n\
210\n\
211Arguments: self - The RSA object\n\
212 args - The Python argument tuple, should be:\n\
213 ibuf - Cleartext input\n\
214Returns: String containing encrypted data\n\
215";
216
217static PyObject *
218crypto_RSA_private_encrypt(crypto_RSAObj *self, PyObject *args)
219{
220 int bufsz = RSA_size(self->rsa);
221 int sz, rsz;
222 int padding = DEFAULT_PADDING;
223 unsigned char *buf = (char*)malloc(bufsz);
224 unsigned char *ibuf;
225
226 if (!self->rsa) {
227 FAIL();
228 }
229
230 if (!PyArg_ParseTuple(args, "s#|i:private_decrypt", &ibuf, &sz, &padding)) {
231 FAIL();
232 }
233
234 rsz = RSA_private_encrypt(sz, ibuf, buf, self->rsa, padding);
235 if (rsz == -1) {
236 ERR_print_errors_fp(stdout);
237 FAIL();
238 }
239
240 return PyString_FromStringAndSize(buf, rsz);
241}
242
243
244static char crypto_RSA_private_decrypt_doc[] = "\n\
245Decrypts the input buffer with the associated private key\n\
246\n\
247Arguments: self - The RSA object\n\
248 args - The Python argument tuple, should be:\n\
249 ibuf - Cyphertext input\n\
250Returns: String containing encrypted data\n\
251";
252
253static PyObject *
254crypto_RSA_private_decrypt(crypto_RSAObj *self, PyObject *args)
255{
256 int bufsz = RSA_size(self->rsa);
257 int sz, rsz;
258 int padding = DEFAULT_PADDING;
259 unsigned char *buf = (char*)malloc(bufsz);
260 unsigned char *ibuf;
261
262 if (!self->rsa) {
263 FAIL();
264 }
265
266 if (!PyArg_ParseTuple(args, "s#|i:private_decrypt", &ibuf, &sz, &padding)){
267 FAIL();
268 }
269
270 rsz = RSA_private_decrypt(sz, ibuf, buf, self->rsa, padding);
271 if (rsz == -1) {
272 ERR_print_errors_fp(stdout);
273 FAIL();
274 }
275
276 return PyString_FromStringAndSize(buf, rsz);
277}
278
279/*
280 * ADD_METHOD(name) expands to a correct PyMethodDef declaration
281 * { 'name', (PyCFunction)crypto_RSA_name, METH_VARARGS }
282 * for convenience
283 */
284#define ADD_METHOD(name) \
285 { #name, (PyCFunction)crypto_RSA_##name, METH_VARARGS, crypto_RSA_##name##_doc }
286static PyMethodDef crypto_RSA_methods[] =
287{
288 ADD_METHOD(sign),
289 ADD_METHOD(verify),
290 ADD_METHOD(key_bits),
291 ADD_METHOD(generate_key),
292 ADD_METHOD(public_encrypt),
293 ADD_METHOD(public_decrypt),
294 ADD_METHOD(private_encrypt),
295 ADD_METHOD(private_decrypt),
296 { NULL, NULL }
297};
298#undef ADD_METHOD
299
300
301/*
302 * Constructor for RSA objects, never called by Python code directly
303 *
304 * Arguments: rsa - A "real" RSA object
305 * dealloc - Boolean value to specify whether the destructor should
306 * free the "real" RSA object
307 * Returns: The newly created RSA object
308 */
309crypto_RSAObj *
310crypto_RSA_New(RSA *rsa, int dealloc)
311{
312 crypto_RSAObj *self;
313
314 self = PyObject_New(crypto_RSAObj, &crypto_RSA_Type);
315
316 if (self == NULL)
317 return NULL;
318
319 self->rsa = rsa;
320 self->dealloc = dealloc;
321
322 /*
323 * Heuristic. Most call-sites pass an initialized EVP_PKEY. Not
324 * necessarily the case that they will, though. That's part of why this is
325 * a hack. -exarkun
326 */
327 self->initialized = 1;
328
329 return self;
330}
331
332/*
333 * Deallocate the memory used by the RSA object
334 *
335 * Arguments: self - The RSA object
336 * Returns: None
337 */
338static void
339crypto_RSA_dealloc(crypto_RSAObj *self)
340{
341 /* Sometimes we don't have to dealloc the "real" RSA pointer ourselves */
342 if (self->dealloc)
343 RSA_free(self->rsa);
344
345 PyObject_Del(self);
346}
347
348/*
349 * Find attribute
350 *
351 * Arguments: self - The RSA object
352 * name - The attribute name
353 * Returns: A Python object for the attribute, or NULL if something went
354 * wrong
355 */
356static PyObject *
357crypto_RSA_getattr(crypto_RSAObj *self, char *name)
358{
359 return Py_FindMethod(crypto_RSA_methods, (PyObject *)self, name);
360}
361
362PyTypeObject crypto_RSA_Type = {
363 PyObject_HEAD_INIT(NULL)
364 0,
365 "RSA",
366 sizeof(crypto_RSAObj),
367 0,
368 (destructor)crypto_RSA_dealloc,
369 NULL, /* print */
370 (getattrfunc)crypto_RSA_getattr,
371 NULL, /* setattr */
372 NULL, /* compare */
373 NULL, /* repr */
374 NULL, /* as_number */
375 NULL, /* as_sequence */
376 NULL, /* as_mapping */
377 NULL, /* hash */
378};
379
380
381/*
382 * Initialize the RSA part of the crypto sub module
383 *
384 * Arguments: dict - The crypto module dictionary
385 * Returns: None
386 */
387int
388init_crypto_rsa(PyObject *module)
389{
390 if (PyType_Ready(&crypto_RSA_Type) < 0) {
391 return 0;
392 }
393
394 if(PyModule_AddObject(module, "RSAType", (PyObject *)&crypto_RSA_Type) != 0) {
395 return 0;
396 }
397
398 return 1;
399}
400
0401
=== added file 'src/crypto/rsa.h'
--- src/crypto/rsa.h 1970-01-01 00:00:00 +0000
+++ src/crypto/rsa.h 2009-11-21 12:40:29 +0000
@@ -0,0 +1,47 @@
1/*
2 * rsa.h
3 *
4 * Copyright (C) Morgan Reed 2008, All rights reserved
5 *
6 * Interface to RSA encryption/decryption routines
7 *
8 */
9#ifndef PyOpenSSL_crypto_RSA_H_
10#define PyOpenSSL_crypto_RSA_H_
11
12#include <Python.h>
13#include <openssl/rsa.h>
14#include <openssl/objects.h>
15
16extern int init_crypto_rsa (PyObject *);
17
18extern PyTypeObject crypto_RSA_Type;
19
20#define crypto_RSA_Check(v) ((v)->ob_type == &crypto_RSA_Type)
21
22typedef struct {
23 PyObject_HEAD
24
25 /*
26 * A pointer to the underlying OpenSSL structure.
27 */
28 RSA *rsa;
29
30 int initialized;
31 /*
32 * A flag indicating whether pkey will be freed when this object is freed.
33 */
34 int dealloc;
35} crypto_RSAObj;
36
37#define crypto_RSA_PKCS1_PADDING RSA_PKCS1_PADDING
38#define crypto_RSA_PKCS1_OAEP_PADDING RSA_PKCS1_OAEP_PADDING
39#define crypto_RSA_SSLV23_PADDING RSA_SSLV23_PADDING
40#define crypto_RSA_NO_PADDING RSA_NO_PADDING
41
42#define crypto_NID_sha1 NID_sha1
43#define crypto_NID_ripemd160 NID_ripemd160
44#define crypto_NID_md5 NID_md5
45#define crypto_NID_md5_sha1 NID_md5_sha1
46
47#endif
048
=== modified file 'test/test_crypto.py'
--- test/test_crypto.py 2009-09-01 14:35:50 +0000
+++ test/test_crypto.py 2009-11-21 12:40:29 +0000
@@ -10,10 +10,15 @@
10from os import popen210from os import popen2
11from datetime import datetime, timedelta11from datetime import datetime, timedelta
1212
13from hashlib import sha1
14
13from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType15from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
14from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType16from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
15from OpenSSL.crypto import X509Req, X509ReqType17from OpenSSL.crypto import X509Req, X509ReqType
16from OpenSSL.crypto import X509Extension, X509ExtensionType18from OpenSSL.crypto import X509Extension, X509ExtensionType
19from OpenSSL.crypto import RSA, RSAType, NID_sha1, NID_md5
20from OpenSSL.crypto import RSA_PKCS1_OAEP_PADDING, RSA_PKCS1_PADDING
21from OpenSSL.crypto import RSA_NO_PADDING, RSA_SSLV23_PADDING
17from OpenSSL.crypto import load_certificate, load_privatekey22from OpenSSL.crypto import load_certificate, load_privatekey
18from OpenSSL.crypto import FILETYPE_PEM, FILETYPE_ASN1, FILETYPE_TEXT23from OpenSSL.crypto import FILETYPE_PEM, FILETYPE_ASN1, FILETYPE_TEXT
19from OpenSSL.crypto import dump_certificate, load_certificate_request24from OpenSSL.crypto import dump_certificate, load_certificate_request
@@ -197,6 +202,8 @@
197-----END RSA PRIVATE KEY-----202-----END RSA PRIVATE KEY-----
198"""203"""
199encryptedPrivateKeyPEMPassphrase = "foobar"204encryptedPrivateKeyPEMPassphrase = "foobar"
205# testPlaintext MUST be 128-bits long for the RSA no_padding tests
206testPlaintext = "testing testing "
200207
201# Some PKCS#7 stuff. Generated with the openssl command line:208# Some PKCS#7 stuff. Generated with the openssl command line:
202#209#
@@ -735,7 +742,8 @@
735 """742 """
736 def signable(self):743 def signable(self):
737 """744 """
738 Return something with a C{set_pubkey}, C{set_pubkey}, and C{sign} method.745 Return something with a C{set_pubkey}, C{set_pubkey}, and C{sign}
746 method.
739 """747 """
740 raise NotImplementedError()748 raise NotImplementedError()
741749
@@ -790,7 +798,9 @@
790 request = X509Req()798 request = X509Req()
791 self.assertTrue(799 self.assertTrue(
792 isinstance(request, X509ReqType),800 isinstance(request, X509ReqType),
793 "%r is of type %r, should be %r" % (request, type(request), X509ReqType))801 "%r is of type %r, should be %r" % (request,
802 type(request),
803 X509ReqType))
794804
795805
796 def test_version(self):806 def test_version(self):
@@ -817,7 +827,9 @@
817 subject = request.get_subject()827 subject = request.get_subject()
818 self.assertTrue(828 self.assertTrue(
819 isinstance(subject, X509NameType),829 isinstance(subject, X509NameType),
820 "%r is of type %r, should be %r" % (subject, type(subject), X509NameType))830 "%r is of type %r, should be %r" % (subject,
831 type(subject),
832 X509NameType))
821 subject.commonName = "foo"833 subject.commonName = "foo"
822 self.assertEqual(request.get_subject().commonName, "foo")834 self.assertEqual(request.get_subject().commonName, "foo")
823 del request835 del request
@@ -1533,5 +1545,133 @@
15331545
15341546
15351547
1548class RSATests(TestCase):
1549
1550 def test_construction(self):
1551 """
1552 L{RSA} takes no arguments and returns an instance of L{RSAType}.
1553 """
1554 rsaobj = RSA()
1555 self.assertTrue(
1556 isinstance(rsaobj, RSAType),
1557 "%r is of type %r, should be %r" % (rsaobj,
1558 type(rsaobj),
1559 RSAType))
1560
1561 def test_generate_key(self):
1562 """
1563 L{generate_key} generates a new RSA key the given size and stores it in
1564 the internal structure
1565 """
1566 bits = 1024
1567 rsaobj = RSA()
1568 rsaobj.generate_key(bits)
1569 self.assertEqual(rsaobj.key_bits(), bits)
1570
1571 def test_regenerate_key(self):
1572 """
1573 L{generate_key} should generates a new key of the given size everytime
1574 it is called
1575 """
1576 bits = 512
1577 rsaobj = RSA()
1578 rsaobj.generate_key(bits)
1579 self.assertEqual(rsaobj.key_bits(), bits)
1580 rsaobj.generate_key(bits * 2)
1581 self.assertEqual(rsaobj.key_bits(), bits * 2)
1582
1583 def test_public_encrypt(self):
1584 """
1585 L{public_encrypt} generates a new key and encrypts a static value with
1586 the public key, then decrypts it with the private and compare the
1587 result
1588 """
1589 bits = 512
1590 rsaobj = RSA()
1591 rsaobj.generate_key(bits)
1592 crypt = rsaobj.public_encrypt(testPlaintext)
1593 decrypt = rsaobj.private_decrypt(crypt)
1594 self.assertEqual(decrypt, testPlaintext)
1595
1596 def test_private_encrypt(self):
1597 """
1598 L{private_encrypt} generates a new key and encrypts a static value with
1599 the private key, then decrypts it with the public and compares the
1600 result
1601 """
1602 bits = 512
1603 rsaobj = RSA()
1604 rsaobj.generate_key(bits)
1605 crypt = rsaobj.private_encrypt(testPlaintext)
1606 decrypt = rsaobj.public_decrypt(crypt)
1607 self.assertEqual(decrypt, testPlaintext)
1608
1609 def test_public_encrypt_oaep_padding(self):
1610 """
1611 L{public_encrypt} generates a new key and encrypts a static value with
1612 the public key, then decrypts it with the private and compare the
1613 result
1614 """
1615 bits = 512
1616 rsaobj = RSA()
1617 rsaobj.generate_key(bits)
1618 crypt = rsaobj.public_encrypt(testPlaintext, RSA_PKCS1_OAEP_PADDING)
1619 decrypt = rsaobj.private_decrypt(crypt, RSA_PKCS1_OAEP_PADDING)
1620 self.assertEqual(decrypt, testPlaintext)
1621
1622 def test_public_encrypt_no_padding(self):
1623 """
1624 L{private_encrypt} generates a new key and encrypts a static value with
1625 the private key, then decrypts it with the public and compares the
1626 result
1627 """
1628 bits = 128
1629 rsaobj = RSA()
1630 rsaobj.generate_key(bits)
1631 crypt = rsaobj.public_encrypt(testPlaintext, RSA_NO_PADDING)
1632 decrypt = rsaobj.private_decrypt(crypt, RSA_NO_PADDING)
1633 self.assertEqual(decrypt, testPlaintext)
1634
1635 def test_private_encrypt_no_padding(self):
1636 """
1637 L{private_encrypt} generates a new key and encrypts a static value with
1638 the private key, then decrypts it with the public and compares the
1639 result
1640 """
1641 bits = 128
1642 rsaobj = RSA()
1643 rsaobj.generate_key(bits)
1644 crypt = rsaobj.private_encrypt(testPlaintext, RSA_NO_PADDING)
1645 decrypt = rsaobj.public_decrypt(crypt, RSA_NO_PADDING)
1646 self.assertEqual(decrypt, testPlaintext)
1647
1648 def test_sign(self):
1649 """
1650 L{sign} generates a new key, signs a static value with it then verifies
1651 the generated signature
1652 """
1653 bits = 512
1654 rsaobj = RSA()
1655 rsaobj.generate_key(bits)
1656 digest = sha1()
1657 digest.update(testPlaintext)
1658 sig = rsaobj.sign(NID_sha1, digest.digest())
1659 self.assertTrue(rsaobj.verify(NID_sha1, digest.digest(), sig))
1660
1661 def test_verify(self):
1662 """
1663 L{verify} generates a new key, signs a static value with it then
1664 mangles the message and verifies, verify should fail
1665 """
1666 bits = 512
1667 rsaobj = RSA()
1668 rsaobj.generate_key(bits)
1669 digest = sha1()
1670 digest.update(testPlaintext)
1671 sig = rsaobj.sign(NID_sha1, digest.digest())
1672 digest.update(testPlaintext[::-1])
1673 self.assertFalse(rsaobj.verify(NID_sha1, digest.digest(), sig))
1674
1675
1536if __name__ == '__main__':1676if __name__ == '__main__':
1537 main()1677 main()

Subscribers

People subscribed via source and target branches

to status/vote changes: