[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[minion-cvs] Overly large commit: most notably, the first pass at th...



Update of /home/minion/cvsroot/src/minion/src
In directory moria.seul.org:/tmp/cvs-serv24442/src

Modified Files:
	_minionlib.h crypt.c main.c 
Added Files:
	tls.c 
Log Message:
Overly large commit: most notably, the first pass at the Mixminion
Transfer Protocol is in.

HACKING:
	- Add notes on TLS, portability, coding style.

LICENSE:
	- Mention OpenSSL/GPL incompatibility.

Makefile:
pycheckrc:
	- Clean is even cleaner.
	- Add a pychecker target and a xxxx target to find bogus code.

TODO:
	- More specific project plan.

setup.py:
	- Be smarter about openssl, source files.

contrib/unittest.py:
	- Check in unittest.py so we build on platforms that don't have it.

lib/mixminion/.cvsignore:
	- Add .pyc and .pyo

lib/mixminion/BuildMessage.py:
	- Suppress pychecker warnings

lib/mixminion/Common.py:
	- Add division functions for python portability. 
	- Secure delete functionality
	- Logging stubs.
	- Signal handling.
	- On-exit handling.

lib/mixminion/Crypto.py
	- Doc cleanup in accordance with PEP-0257.
	- Add independent implementation of OAEP, just in case
	- Add RNG.getInt to return a number with a uniform distribution based
	  on a secure RNG.  Not a very good implementation.
	- AESCounterPRNG: deal with wraparound bug on Python<2.1

lib/mixminion/HashLog.py
	- Suppress pychecker warnings

lib/mixminion/MMTPClient.py
lib/mixminion/MMTPServer.py
	- Implementation of Mixminion Transfer Protocol.  As of now, they
	  lack: keyid checking, renegotiation, session cacheing, keygen,
	  dh parameter generation, and timeout.

	  Otherwise, they should work fine. :)

lib/mixminion/Packet.py
lib/mixminion/PacketHandler.py
	- cleanups

lib/mixminion/Queue.py
	- Implementation of a generic, pretty-secure, directory-based queue.

lib/mixminion/ServerInfo.py
	- Remove unused argument

lib/mixminion/benchmark.py
	- Timing for queues, rng.getInt, native oaep, fileops
	- Code cleanup for pychecker

lib/mixminion/test.py
	- Add test for native OAEP
	- Tests for Queues
	- Initial tests for MMTP
	- Code cleanup for pychecker

src/tls.c
	- New C-side TLS wrappers.

src/crypt.c
	- Refactored exceptions from SSLException into TLSException and 
          CryptoException.
        - Make RSA keys into objects.
	- Remove OAEP comment that doesn't apply.

src/_minionlib.h
src/main.c
	- Changes to correspond to src/*.c



--- NEW FILE: tls.c ---
/* Copyright (c) 2002 Nick Mathewson.  See LICENSE for licensing information */
/* $Id: tls.c,v 1.1 2002/06/24 20:28:19 nickm Exp $ */
#include "_minionlib.h"

#include <openssl/ssl.h>
#include <openssl/tls1.h>

char mm_TLSError__doc__[] = 
  "mixminion._minionlib.TLSError\n\n"
  "Exception raised for error in underlying TLS/SSL library.\n";
PyObject *mm_TLSError = NULL;

char mm_TLSWantRead__doc__[] = 
  "mixminion._minionlib.TLSWantRead\n\n"
"Exception raised when a non-blocking TLS operation would block on reading.\n";
PyObject *mm_TLSWantRead = NULL;

char mm_TLSWantWrite__doc__[] = 
  "mixminion._minionlib.TLSWantWrite\n\n"
"Exception raised when a non-blocking TLS operation would block on writing.\n";
PyObject *mm_TLSWantWrite = NULL;

#define TYPE_ERR(s) PyErr_SetString(PyExc_TypeError, s)
#define FAIL_IF_ARGS() if (PyTuple_Size(args)) { \
                           TYPE_ERR("No arguments expected"); \
                           return NULL; \
                       }
 
/* Return values for tls_error */
#define NO_ERROR 0
#define ERROR 1
#define ZERO_RETURN -1
/* 
 * Checks for an outstanding error on a given SSL object that has just
 * returned the value 'r'.  Returns NO_ERROR, ERROR, or ZERO_RETURN.
 * On ERROR, a Python error is set.  On ZERO_RETURN, a Python error is
 * set if 'zeroReturnIsError'.
 */
static int 
tls_error(SSL *ssl, int r, int zeroReturnIsError) 
{
	int err = SSL_get_error(ssl,r);
	switch (err) {
 	  case SSL_ERROR_NONE:
		  return NO_ERROR;
	  case SSL_ERROR_ZERO_RETURN:
		  if (zeroReturnIsError)
			  mm_SSL_ERR(0);
		  return ZERO_RETURN;
 	  case SSL_ERROR_WANT_READ:
		  PyErr_SetNone(mm_TLSWantRead);
		  return ERROR;
 	  case SSL_ERROR_WANT_WRITE:
		  PyErr_SetNone(mm_TLSWantWrite);
		  return ERROR;
 	  case SSL_ERROR_SYSCALL:
 	  default:
		  mm_SSL_ERR(0);
		  return ERROR;
	}
}

typedef struct mm_TLSContext {
	PyObject_HEAD
	SSL_CTX *ctx;
} mm_TLSContext;
#define mm_TLSContext_Check(v) ((v)->ob_type == &mm_TLSContext_Type)

typedef struct mm_TLSSock {
	PyObject_HEAD
	PyObject *context;
	SSL *ssl;
	int sock;
	PyObject *sockObj;
} mm_TLSSock;
#define mm_TLSSock_Check(v) ((v)->ob_type == &mm_TLSSock_Type)

/* XXXX Code to make new cert */
/* XXXX Code to make new dhparam */
/* XXXX IO for certs, dhparams. */

const char mm_TLSContext_new__doc__[] = 
   "TLSContext([certfile, [pkfile, [dhfile] ] ] )\n\n"
   "Allocates a new TLSContext object.  The files, if provided, are used\n"
   "contain the PEM-encoded X509 public keys, private key file, and DH\n"
   "parameters for this context.\n\n"
   "BUG:In the future, certs, pks, and dh parameters will be first-class.\n\n"
   "LIMITATION: We don\'t expose any more features than Mixminion needs.\n";

PyObject*
mm_TLSContext_new(PyObject *self, PyObject *args, PyObject *kwargs) 
{
	static char *kwlist[] = { "certfile", "pkfile", "dhfile", NULL };
	char *certfile = NULL, *pkfile=NULL, *dhfile=NULL;
	SSL_METHOD *method;
	SSL_CTX *ctx;
	DH *dh;
	mm_TLSContext *result;
	BIO *bio;
	
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sss:TLSContext_new", 
					 kwlist,
					 &certfile, &pkfile, &dhfile))
		return NULL;

	method = TLSv1_method();
	
	if (!(ctx = SSL_CTX_new(method))) {
		mm_SSL_ERR(0); return NULL;
	}

	if (!SSL_CTX_set_cipher_list(ctx, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)){
		SSL_CTX_free(ctx); mm_SSL_ERR(0); return NULL;
	}
	if (certfile && 
	    !SSL_CTX_use_certificate_file(ctx,certfile,SSL_FILETYPE_PEM)) {
		SSL_CTX_free(ctx); mm_SSL_ERR(0); return NULL;
	}
	if (pkfile && 
	    !(SSL_CTX_use_PrivateKey_file(ctx,pkfile,SSL_FILETYPE_PEM))) {
		SSL_CTX_free(ctx); mm_SSL_ERR(0); return NULL;
	} 

	if (dhfile) {
		if ( !(bio = BIO_new_file(dhfile, "r"))) {
			SSL_CTX_free(ctx); mm_SSL_ERR(0); return NULL;
		}
		dh=PEM_read_bio_DHparams(bio,NULL,NULL,NULL);
		BIO_free(bio); 
		if (!dh) {
			SSL_CTX_free(ctx); mm_SSL_ERR(0); return NULL; 
		}
		SSL_CTX_set_tmp_dh(ctx,dh); 
		DH_free(dh);
	}
	SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
	
	result = PyObject_New(mm_TLSContext, &mm_TLSContext_Type);
	if (!result) {
		SSL_CTX_free(ctx); return NULL; 
	}
	result->ctx = ctx;
	return (PyObject*)result;
}

static void 
mm_TLSContext_dealloc(mm_TLSContext *self)
{
	SSL_CTX_free(self->ctx);
	PyObject_DEL(self);
}

static char mm_TLSContext_sock__doc__[] = 
   "context.sock(socket)\n\n"
   "Creates a new TLS socket to send and receive from a given underlying\n"
   "socket.";

static PyObject *
mm_TLSContext_sock(PyObject *self, PyObject *args, PyObject *kwargs)
{
	static char *kwlist[] = { "socket", NULL };
	PyObject *sockObj;
	int sock;

	SSL_CTX *ctx;
	BIO *bio;
	SSL *ssl;
	mm_TLSSock *ret;
	
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:sock", 
					 kwlist, &sockObj))
		return NULL;
	assert(mm_TLSContext_Check(self));

	if ((sock = PyObject_AsFileDescriptor(sockObj)) < 0) {
		TYPE_ERR("TLSContext.sock requires a socket");
		return NULL;
	}

	ctx = ((mm_TLSContext*)self)->ctx;

	if (!(ssl = SSL_new(ctx))) {
		mm_SSL_ERR(0); return NULL;
	}

	if (!(bio = BIO_new_socket(sock, BIO_NOCLOSE))) {
		SSL_free(ssl); mm_SSL_ERR(0); return NULL;
	}
	SSL_set_bio(ssl,bio,bio);
	
	if (!(ret = PyObject_New(mm_TLSSock, &mm_TLSSock_Type))) {
		SSL_free(ssl); PyErr_NoMemory(); SSL_free(ssl); return NULL;
	}
	ret->ssl = ssl;
	ret->context = self;
	ret->sock = sock;
	ret->sockObj = sockObj;
	Py_INCREF(self);
	Py_INCREF(sockObj);
	return (PyObject*)ret;
}

#define METHOD(name) { #name, (PyCFunction)mm_TLSContext_##name, \
                        METH_VARARGS|METH_KEYWORDS,       \
                        (char*)mm_TLSContext_##name##__doc__ }

static PyMethodDef mm_TLSContext_methods[] = {
	METHOD(sock),
	{ NULL, NULL }
};
 
static PyObject*
mm_TLSContext_getattr(PyObject *self, char *name) 
{
	return Py_FindMethod(mm_TLSContext_methods, self, name);
}

static const char mm_TLSContext_Type__doc__[] =
   "mixminion._minionlib.TLSContext\n\n"
   "A TLSContext object represents the resources shared by a set of TLS\n"
   "sockets.  It has a single method, 'sock()', to create new sockets.\n";

PyTypeObject mm_TLSContext_Type = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,                                  /*ob_size*/
	"mixminion._minionlib.TLSContext",  /*tp_name*/
	sizeof(mm_TLSContext),              /*tp_basicsize*/
	0,                                  /*tp_itemsize*/
	/* methods */
	(destructor)mm_TLSContext_dealloc,  /*tp_dealloc*/
	(printfunc)0,                       /*tp_print*/
	(getattrfunc)mm_TLSContext_getattr, /*tp_getattr*/
	(setattrfunc)0,                     /*tp_setattr*/
	0,0,
	0,0,0,
	0,0,0,0,0,
	0,0,
	(char*)mm_TLSContext_Type__doc__
};

static void 
mm_TLSSock_dealloc(mm_TLSSock *self)
{
	Py_DECREF(self->context);
	Py_DECREF(self->sockObj);
	SSL_free(self->ssl);
	PyObject_DEL(self);
}

static char mm_TLSSock_accept__doc__[] = 
  "tlssock.accept()\n\n"
  "Perform initial server-side TLS handshaking.\n"
  "Returns None if finished.  May raise TLSWantRead or TLSWantWrite.\n";

static PyObject*
mm_TLSSock_accept(PyObject *self, PyObject *args, PyObject *kwargs)
{
	SSL *ssl;
	int r;

	assert(mm_TLSSock_check(self));
	FAIL_IF_ARGS();
	
	ssl = ((mm_TLSSock*)self)->ssl;
	Py_BEGIN_ALLOW_THREADS
	r = SSL_accept(ssl);
	Py_END_ALLOW_THREADS
	
	if (tls_error(ssl, r, 1))
		return NULL;
	
	Py_INCREF(Py_None);
	return Py_None;
} 

static char mm_TLSSock_connect__doc__[] =
  "tlssock.connect()\n\n"
  "Perform initial client-side TLS handshaking.\n"
  "returns: None for finished. May raise TLSWantRead, TLSWantWrite.\n";

static PyObject*
mm_TLSSock_connect(PyObject *self, PyObject *args, PyObject *kwargs)
{
	SSL *ssl;
	int r, err; 

	assert(mm_TLSSock_check(self));
	FAIL_IF_ARGS();
	
	ssl = ((mm_TLSSock*)self)->ssl;

	Py_BEGIN_ALLOW_THREADS
	r = SSL_connect(ssl);
	Py_END_ALLOW_THREADS
	err = SSL_get_error(ssl,r);
	if (tls_error(ssl, r, 1))
		return NULL;

	Py_INCREF(Py_None);
	return Py_None;
} 

static char mm_TLSSock_pending__doc__[] = 
   "tlssock.pending()\n\n"
   "Returns true iff there is data waiting to be read from this socket.";

static PyObject*
mm_TLSSock_pending(PyObject *self, PyObject *args, PyObject *kwargs)
{
	SSL *ssl;

	assert(mm_TLSSock_check(self));
	FAIL_IF_ARGS();
	
	ssl = ((mm_TLSSock*)self)->ssl;
	
	return PyInt_FromLong(SSL_pending(ssl));
} 

static char mm_TLSSock_read__doc__[] = 
   "tlssock.read(size)\n\n"
   "Tries to read [up to] size bytes from this socket.\n"
  "Returns a string if the read was successful.  Returns 0 if the connection\n"
   "has been closed.  Raises TLSWantRead or TLSWantWrite if the underlying\n"
   "nonblocking socket would block on one of these operations.\n";

static PyObject*
mm_TLSSock_read(PyObject *self, PyObject *args, PyObject *kwargs)
{
	static char *kwlist[] = { "size", NULL };
	int n;
	SSL *ssl;
	int r;
	PyObject *res;

	assert(mm_TLSSock_check(self));
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i:read", kwlist,
					 &n))
		return NULL;
	
	ssl = ((mm_TLSSock*)self)->ssl;
	
	if (!(res = PyString_FromStringAndSize(NULL, n))) {
		PyErr_NoMemory(); return NULL; 
	}
	
	Py_BEGIN_ALLOW_THREADS
	r = SSL_read(ssl, PyString_AS_STRING(res), n);
	Py_END_ALLOW_THREADS
	if (r > 0) {
		if (r != n && _PyString_Resize(&res,r) < 0) {
			return NULL;
		}
		return res;
	}
	Py_DECREF(res);
	switch (tls_error(ssl, r, 0)) {
	    case NO_ERROR:
		    Py_INCREF(Py_None);
		    return Py_None;
	    case ZERO_RETURN:	    
		    return PyInt_FromLong(0);
	    case ERROR:
	    default:
		    return NULL;
	}
} 

static char mm_TLSSock_write__doc__[] = 
   "tlssock.write(string)\n\n"
   "Try to write to a TLS socket.\n"
   "If the write was successful, returns the number of bytes written. If the\n"
   "connection is being shutdown, returns 0. Raises TLSWantRead or\n" 
   "TLSWantWrite if the underlying nonblocking socket would block on one of\n"
   "these operations.\n";

static PyObject*
mm_TLSSock_write(PyObject *self, PyObject *args, PyObject *kwargs)
{
	static char *kwlist[] = { "s", NULL };
	char *string;
	int stringlen;
	SSL *ssl;
	int r;

	assert(mm_TLSSock_check(self));
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#:write", kwlist,
					  &string, &stringlen))
		return NULL;
	
	ssl = ((mm_TLSSock*)self)->ssl;
	
	Py_BEGIN_ALLOW_THREADS
	r = SSL_write(ssl, string, stringlen);
	Py_END_ALLOW_THREADS
	
	switch(tls_error(ssl, r, 0)) {
	    case NO_ERROR:	    
		    return PyInt_FromLong(r);
	    case ZERO_RETURN:
		    return PyInt_FromLong(0);
	    case ERROR:
	    default:
		    return NULL;

	}
}

static char mm_TLSSock_shutdown__doc__[] = 
  "tlssock.shutdown()\n\n"
  "Initiates a shutdown.\n"
  "If 0 is returned, the shutdown is not complete.  If 1 is returned, the\n"
  "shutdown is complete. May raise TLSWantRead, TLSWantWrite.\n";

static PyObject*
mm_TLSSock_shutdown(PyObject *self, PyObject *args, PyObject *kwargs)
{
	SSL *ssl;
	int r;

	assert(mm_TLSSock_check(self));
	FAIL_IF_ARGS();
	
	ssl = ((mm_TLSSock*)self)->ssl;

	Py_BEGIN_ALLOW_THREADS
	r = SSL_shutdown(ssl);
	Py_END_ALLOW_THREADS
	if (r == 0) return PyInt_FromLong(0);
	if (r == 1) return PyInt_FromLong(1);
	if (tls_error(ssl,r,1))
		return NULL;

	Py_INCREF(Py_None);
	return Py_None;
} 

#if 0
static char mm_TLSSock_renegotiate__doc__[] = "XXXX";

static PyObject*
mm_TLSSock_renegotiate(PyObject *self, PyObject *args, PyObject *kwargs)
{
	
	
} 
#endif

static char mm_TLSSock_fileno__doc__[] = 
    "tlssock.fileno()\n\n"
    "Returns the integer filehandle underlying this TLS socket.\n";

static PyObject*
mm_TLSSock_fileno(PyObject *self, PyObject *args, PyObject *kwargs)
{

	assert(mm_TLSSock_check(self));
	FAIL_IF_ARGS();
	
	return PyInt_FromLong(((mm_TLSSock*)self)->sock);
}

static char mm_TLSSock_get_peer_cert_pk__doc__[] = 
    "tlssock.get_peer_cert_pk()\n\n"
    "Returns the public key of the certificate used by the server on the\n"
    "other side of this connection.  Returns None if no such cert exists\n";

static PyObject*
mm_TLSSock_get_peer_cert_pk(PyObject *self, PyObject *args, PyObject *kwargs)
{
	SSL *ssl;
	X509 *cert;
	EVP_PKEY *pkey;
	RSA *rsa;
	mm_RSA *result;

	assert(mm_TLSSock_check(self));
	FAIL_IF_ARGS();
	
	ssl = ((mm_TLSSock*)self)->ssl;
	if (!(cert = SSL_get_peer_certificate(ssl))) {
		mm_SSL_ERR(0); return NULL; /* ???? */
	}
	pkey = X509_get_pubkey(cert);
	/* ???? */
	if (!(rsa = EVP_PKEY_get1_RSA(pkey))) 
		return NULL; /* XXXX */
	
	if (!(result = PyObject_New(mm_RSA, &mm_RSA_Type))) {
		PyErr_NoMemory(); return NULL; 
	}
	result->rsa = rsa;

	return (PyObject*) result;
}

#undef METHOD
#define METHOD(name) { #name, (PyCFunction)mm_TLSSock_##name, \
                        METH_VARARGS|METH_KEYWORDS,       \
                        (char*)mm_TLSSock_##name##__doc__ }

static PyMethodDef mm_TLSSock_methods[] = {
	METHOD(accept),
	METHOD(connect),
	METHOD(pending),
	METHOD(read),
	METHOD(write),
	METHOD(shutdown),
	METHOD(get_peer_cert_pk),
	METHOD(fileno),
	{ NULL, NULL }
};
 
static PyObject*
mm_TLSSock_getattr(PyObject *self, char *name) 
{
	return Py_FindMethod(mm_TLSSock_methods, self, name);
}

static const char mm_TLSSock_Type__doc__[] =
   "mixminion._minionlib.TLSSock\n\n"
   "A single TLS connection.";

PyTypeObject mm_TLSSock_Type = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,                                  /*ob_size*/
	"mixminion._minionlib.TLSSock",     /*tp_name*/
	sizeof(mm_TLSSock),                 /*tp_basicsize*/
	0,                                  /*tp_itemsize*/
	/* methods */
	(destructor)mm_TLSSock_dealloc,     /*tp_dealloc*/
	(printfunc)0,                       /*tp_print*/
	(getattrfunc)mm_TLSSock_getattr,    /*tp_getattr*/
	(setattrfunc)0,                     /*tp_setattr*/
	0,0,
	0,0,0,
	0,0,0,0,0,
	0,0,
	(char*)mm_TLSSock_Type__doc__
};

/*
  Local Variables:
  mode:c
  c-basic-offset:8
  End:
*/

Index: _minionlib.h
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/_minionlib.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- _minionlib.h	31 May 2002 12:39:18 -0000	1.3
+++ _minionlib.h	24 Jun 2002 20:28:19 -0000	1.4
@@ -5,6 +5,7 @@
 
 #include <Python.h>
 #include <openssl/aes.h>
+#include <openssl/rsa.h>
 #include <openssl/opensslv.h>
 #if (OPENSSL_VERSION_NUMBER < 0x00907000L)
 #error "Mixminion requires OpenSSL 0.9.7 (which might not have been released yet, but you can get snapshots from openssl.org)."
@@ -13,6 +14,18 @@
 void mm_aes_counter128(const char *in, char *out, unsigned int len, 
 		       AES_KEY *key, unsigned long count);
 
+void mm_SSL_ERR(int crypto);
+
+extern PyTypeObject mm_RSA_Type;
+typedef struct mm_RSA {
+	PyObject_HEAD
+	RSA* rsa;
+} mm_RSA;
+#define mm_RSA_Check(v) ((v)->ob_type == &mm_RSA_Type)
+
+extern PyTypeObject mm_TLSContext_Type;
+extern PyTypeObject mm_TLSSock_Type;
+
 #define FUNC(fn) PyObject* fn(PyObject *self, PyObject *args, PyObject *kwdict)
 #define DOC(fn) extern const char fn##__doc__[]
 #define FUNC_DOC(fn) FUNC(fn); DOC(fn)
@@ -33,6 +46,25 @@
 FUNC_DOC(mm_rsa_get_modulus_bytes);
 FUNC_DOC(mm_rsa_get_public_key);
 FUNC_DOC(mm_rsa_make_public_key);
-extern PyObject *mm_SSLError;
+extern PyObject *mm_CryptoError;
+extern char mm_CryptoError__doc__[];
+
+
+/* From tls.c */
+extern PyTypeObject mm_TLSSock_Type;
+FUNC_DOC(mm_TLSContext_new);
+extern PyObject *mm_TLSError;
+extern char mm_TLSError__doc__[];
+extern PyObject *mm_TLSWantRead;
+extern char mm_TLSWantRead__doc__[];
+extern PyObject *mm_TLSWantWrite;
+extern char mm_TLSWantWrite__doc__[];
 
 #endif
+
+/*
+  Local Variables:
+  mode:c
+  c-basic-offset:8
+  End:
+*/

Index: crypt.c
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/crypt.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- crypt.c	2 Jun 2002 06:11:16 -0000	1.4
+++ crypt.c	24 Jun 2002 20:28:19 -0000	1.5
@@ -14,24 +14,29 @@
 #define TYPE_ERR(s) PyErr_SetString(PyExc_TypeError, s)
 #define KEY_IS_PRIVATE(rsa) ((rsa)->p)
 
-PyObject *mm_SSLError = NULL;
+char mm_CryptoError__doc__[] = 
+  "mixminion._minionlib.SSLError\n\n"
+  "Exception raised for error in crypto library.\n";
 
-/* Helper function: raise an SSLError with appropriate text from the
- * underlying SSL exception.  
+PyObject *mm_CryptoError = NULL;
+
+/* Helper function: raise an error with appropriate text from the
+ * underlying OpenSSL exception.  
  *
- * Requires that mm_SSLError is initialized and ERR_load_*_strings
+ * Requires that mm_*Error are initialized and ERR_load_*_strings
  * have been called.
  */
-static void 
-SSL_ERR() 
+void 
+mm_SSL_ERR(int crypto)
 {
 	int err = ERR_get_error();
 	const char *str = ERR_reason_error_string(err);
-	assert(mm_SSLError);
+	PyObject *exception = crypto ? mm_CryptoError : mm_TLSError;
+	assert(exception);
 	if (str)
-		PyErr_SetString(mm_SSLError, str);
+		PyErr_SetString(exception, str);
 	else
-		PyErr_SetString(mm_SSLError, "SSL error");
+		PyErr_SetString(exception, "Internal error");
 }
 
 const char mm_sha1__doc__[] = 
@@ -118,7 +123,7 @@
 		PyErr_NoMemory(); goto err; 
 	}
 	if (AES_set_encrypt_key(key, keylen*8, aes_key)) {
-		SSL_ERR();
+		mm_SSL_ERR(1);
 		goto err;
 	}
 	if (!(result = WRAP_AES(aes_key))) { 
@@ -247,43 +252,35 @@
 	return Py_None;
 }
 
-static char rsa_descriptor[] = "RSA objects descriptor";
 
-/* Destructor for PyCObject
- */
 static void
-rsa_destruct(void *obj, void *desc) 
+mm_RSA_dealloc(mm_RSA *self)
 {
-	assert(desc==rsa_descriptor);
-	RSA_free( (RSA*) obj);
+	RSA_free(self->rsa);
+	PyObject_DEL(self);
 }
 
-/* Converter fn for "O&" argument conversion with RSA keys. */
-static int
-rsa_arg_convert(PyObject *obj, void *adr) 
-{
-	if (PyCObject_Check(obj) && PyCObject_GetDesc(obj) == rsa_descriptor) {
-		*((RSA**) adr) = (RSA*) PyCObject_AsVoidPtr(obj);
-		return 1;
-	} else {
-		TYPE_ERR("Expected an RSA key as an argument.");
-		return 0;
-	}
+static PyObject *
+mm_RSA_new(RSA *rsa) {
+	mm_RSA *self;
+	
+	assert(rsa);
+	if (!(self=PyObject_NEW(mm_RSA, &mm_RSA_Type)))
+		return NULL;
+	self->rsa = rsa;
+	return (PyObject*)self;
 }
 
-#define WRAP_RSA(rsa) (PyCObject_FromVoidPtrAndDesc( (void*) (rsa),\
-		       (void*) rsa_descriptor, rsa_destruct))
-					     
-const char mm_rsa_crypt__doc__[]=
-  "rsa_crypt(key, string, public, encrypt) -> str\n\n"
+const char mm_RSA_crypt__doc__[]=
+  "rsa.crypt(string, public, encrypt) -> str\n\n"
   "Uses RSA to encrypt or decrypt a provided string.  If encrypt is true,\n"
   "encrypts; else, decrypts.  If public is true, performs a public-key\n"
   "operation; else, performs a private-key operation.";
 
 PyObject *
-mm_rsa_crypt(PyObject *self, PyObject *args, PyObject *kwdict) 
+mm_RSA_crypt(PyObject *self, PyObject *args, PyObject *kwdict) 
 {
-	static char *kwlist[] = { "key", "string", "public", "encrypt", NULL };
+	static char *kwlist[] = { "string", "public", "encrypt", NULL };
 
 	RSA *rsa;
 	unsigned char *string;
@@ -292,12 +289,15 @@
 	int keylen, i;
 	char *out;
 	PyObject *output;
-	
+	if (!mm_RSA_Check(self)) {
+		TYPE_ERR("Called RSA method with non-RSA object.");
+		return NULL;
+	}
 	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
-					 "O&s#ii:rsa_crypt", kwlist,
-					 rsa_arg_convert, &rsa, 
+					 "s#ii:crypt", kwlist,
 					 &string, &stringlen, &pub, &encrypt))
 		return NULL;
+	rsa = ((mm_RSA*)self)->rsa;
 	if (!pub && !KEY_IS_PRIVATE(rsa)) {
 		TYPE_ERR("Can\'t use public key for private-key operation");
 		return NULL;
@@ -325,7 +325,7 @@
 
 	if (i <= 0) {
 		Py_DECREF(output);
-		SSL_ERR();
+		mm_SSL_ERR(1);
 		return NULL;
 	}
 	if(_PyString_Resize(&output, i)) return NULL; 
@@ -352,22 +352,22 @@
 
 	rsa = RSA_generate_key(bits, e, NULL, NULL);
 	if (rsa == NULL) {
-		SSL_ERR();
+		mm_SSL_ERR(1);
 		return NULL;
 	}
 	
-	return WRAP_RSA(rsa);
+	return mm_RSA_new(rsa);
 }
 
-const char mm_rsa_encode_key__doc__[]=
-  "rsa_encode_key(rsa,public) -> str\n\n"
+const char mm_RSA_encode_key__doc__[]=
+  "rsa.encode_key(public) -> str\n\n"
   "Computes the DER encoding of a given key.  If 'public' is true, encodes\n"
   "only the public-key portions of rsa.\n";
  
 PyObject *
-mm_rsa_encode_key(PyObject *self, PyObject *args, PyObject *kwdict) 
+mm_RSA_encode_key(PyObject *self, PyObject *args, PyObject *kwdict) 
 {
-	static char *kwlist[] = { "key", "public", NULL };
+	static char *kwlist[] = { "public", NULL };
 	
 	RSA *rsa;
 	int public;
@@ -376,11 +376,15 @@
 	PyObject *output;
 	unsigned char *out, *outp;
 
+	if (!mm_RSA_Check(self)) {
+		TYPE_ERR("Called RSA method with non-RSA object.");
+		return NULL;
+	}
 	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
-					 "O&i:rsa_encode_key", kwlist,
-					 rsa_arg_convert, &rsa, &public))
+					 "i:rsa_encode_key", kwlist, &public))
 		return NULL;
-
+	rsa = ((mm_RSA*)self)->rsa;
+	
 	if (!public && !KEY_IS_PRIVATE(rsa)) {
 		TYPE_ERR("Can\'t use public key for private-key operation");
 		return NULL;
@@ -389,7 +393,7 @@
 	len = public ? i2d_RSAPublicKey(rsa,NULL) : 
 		i2d_RSAPrivateKey(rsa,NULL);
 	if (len < 0) {
-		SSL_ERR();
+		mm_SSL_ERR(1);
 		return NULL;
 	}
 	out = outp = malloc(len+1);
@@ -399,7 +403,7 @@
 		len = i2d_RSAPrivateKey(rsa, &outp);
 	if (len < 0) {
 		free(out);
-		SSL_ERR();
+		mm_SSL_ERR(1);
 		return NULL;
 	}
 
@@ -426,7 +430,6 @@
 	int stringlen, public;
 
 	RSA *rsa;
-
 	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
 					 "s#i:rsa_decode_key", kwlist,
 					 &string, &stringlen, &public))
@@ -435,10 +438,10 @@
 	rsa = public ? d2i_RSAPublicKey(NULL, &string, stringlen) : 
 		d2i_RSAPrivateKey(NULL, &string, stringlen);
 	if (!rsa) {
-		SSL_ERR();
+		mm_SSL_ERR(1);
 		return NULL;
 	}
-	return WRAP_RSA(rsa);
+	return mm_RSA_new(rsa);
 }
 
 /**
@@ -492,23 +495,27 @@
 	return result;
 }
 
-const char mm_rsa_get_public_key__doc__[]=
-   "rsa_get_public_key(rsa) -> (n,e)\n";
+const char mm_RSA_get_public_key__doc__[]=
+   "rsa.get_public_key() -> (n,e)\n";
 
 PyObject *
-mm_rsa_get_public_key(PyObject *self, PyObject *args, PyObject *kwdict) 
+mm_RSA_get_public_key(PyObject *self, PyObject *args, PyObject *kwdict) 
 {
-	static char *kwlist[] = { "key", NULL };
+	static char *kwlist[] = {  NULL };
 	
 	RSA *rsa;
 	PyObject *n, *e;
 	PyObject *output;
 
+	if (!mm_RSA_Check(self)) {
+		TYPE_ERR("Called RSA method with non-RSA object.");
+		return NULL;
+	}
 	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
-					 "O&:rsa_get_public_key", kwlist,
-					 rsa_arg_convert, &rsa))
+					 ":rsa_get_public_key", kwlist))
 		return NULL;
 	
+	rsa = ((mm_RSA*)self)->rsa;
 	if (!rsa->n) { TYPE_ERR("Key has no modulus"); return NULL;}
 	if (!rsa->e) { TYPE_ERR("Key has no e"); return NULL; }
 	if (!(n = bn2pylong(rsa->n))) { 
@@ -548,30 +555,73 @@
 		RSA_free(rsa); BN_free(rsa->n); return NULL; 
 	}
 
-	output = WRAP_RSA(rsa);
+	output = mm_RSA_new(rsa);
 	
 	return output;
 }
 
-const char mm_rsa_get_modulus_bytes__doc__[]=
-   "rsa_get_modulus_bytes(rsa) -> int\n\n"
+const char mm_RSA_get_modulus_bytes__doc__[]=
+   "rsa.get_modulus_bytes() -> int\n\n"
    "Returns the number of *bytes* (not bits) in an RSA modulus.\n";
 
-PyObject *
-mm_rsa_get_modulus_bytes(PyObject *self, PyObject *args, PyObject *kwdict) 
+static PyObject *
+mm_RSA_get_modulus_bytes(PyObject *self, PyObject *args, PyObject *kwdict) 
 {
-	static char *kwlist[] = { "key", NULL };
-	
+	static char *kwlist[] = { NULL };
 	RSA *rsa;
 
-	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
-					 "O&:rsa_get_modulus_bytes", kwlist,
-					 rsa_arg_convert, &rsa))
+	if (!mm_RSA_Check(self)) {
+		TYPE_ERR("Called RSA method with non-RSA object.");
+		return NULL;
+	}
+	rsa = ((mm_RSA*)self)->rsa;
+	if (!PyArg_ParseTupleAndKeywords(args, kwdict,
+					 ":get_modulus_bytes", kwlist))
 		return NULL;
 	
 	return PyInt_FromLong(BN_num_bytes(rsa->n));
 }
 
+ 
+#define METHOD(name) { #name, (PyCFunction)mm_RSA_##name, \
+                        METH_VARARGS|METH_KEYWORDS,       \
+                        (char*)mm_RSA_##name##__doc__ }
+
+static PyMethodDef mm_RSA_methods[] = {
+	METHOD(crypt),
+	METHOD(encode_key),
+	METHOD(get_modulus_bytes),
+	METHOD(get_public_key),
+	{ NULL, NULL }
+};
+ 
+static PyObject*
+mm_RSA_getattr(PyObject *self, char *name) 
+{
+	return Py_FindMethod(mm_RSA_methods, self, name);
+}
+
+static const char mm_RSA_Type__doc__[] = 
+  "XXXX";
+ 
+PyTypeObject mm_RSA_Type = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,                                  /*ob_size*/
+	"mixminion._minionlib.RSA",         /*tp_name*/
+	sizeof(mm_RSA),                     /*tp_basicsize*/
+	0,                                  /*tp_itemsize*/
+	/* methods */
+	(destructor)mm_RSA_dealloc,         /*tp_dealloc*/
+	(printfunc)0,                       /*tp_print*/
+	(getattrfunc)mm_RSA_getattr,        /*tp_getattr*/
+	(setattrfunc)0,                     /*tp_setattr*/
+	0,0,
+	0,0,0,
+	0,0,0,0,0,
+	0,0,
+	(char*)mm_RSA_Type__doc__
+};
+
 const char mm_add_oaep_padding__doc__[]=
    "add_oaep_padding(s, param, keylen) -> str\n\n"
    "Adds OAEP padding to a string.  Keylen is the length of the RSA key to\n"
@@ -593,8 +643,11 @@
 			      &input,&inputlen,&param,&paramlen,&keylen))
 		return NULL;
 	
+	/* Strictly speaking, this is redundant.  Nevertheless, I suspect
+	   the openssl implementation of fragility, so better safe than sorry.
+	  */
 	if (inputlen >= keylen) {
-		TYPE_ERR("String too long to pad.");
+		PyErr_SetString(mm_CryptoError, "String too long to pad.");
 		return NULL;
 	}
 	
@@ -606,7 +659,7 @@
 				       input, inputlen,
 				       param, paramlen);
 	if (r <= 0) {
-		SSL_ERR(); 
+		mm_SSL_ERR(1); 
 		Py_DECREF(output);
 		return NULL;
 	}
@@ -636,15 +689,9 @@
 				  &input,&inputlen,&param,&paramlen,&keylen))
 		return NULL;
 
-	/**
-	 * XXXX Why is this test (along with the input+1 below) necessary?
-	 * XXXX I'd be happier if I knew, and I'd take out the bit about
-	 * XXXX our assumptions being gravely mistaken. :)
-	 **/
+
 	if (inputlen == 0 || *input != '\000') {
-		PyErr_SetString(mm_SSLError,
-				"Bad padding, or our assumptions about "
-				"OAEP padding are gravely mistaken");
+		PyErr_SetString(mm_CryptoError, "Bad padding");
 		return NULL;
 	}
 	
@@ -657,7 +704,7 @@
 					 input+1, inputlen-1, keylen,
 					 param, paramlen);
 	if (r <= 0) {
-		SSL_ERR();
+		mm_SSL_ERR(1);
 		Py_DECREF(output);
 		return NULL;
 	}

Index: main.c
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/main.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- main.c	29 May 2002 17:46:24 -0000	1.2
+++ main.c	24 Jun 2002 20:28:19 -0000	1.3
@@ -2,6 +2,7 @@
 /* $Id$ */
 #include <_minionlib.h>
 
+#include <openssl/ssl.h>
 #include <openssl/err.h>
 #include <openssl/rsa.h>
 
@@ -19,16 +20,38 @@
 	ENTRY(add_oaep_padding),
 	ENTRY(check_oaep_padding),
 	ENTRY(rsa_generate),
-	ENTRY(rsa_crypt),
-	ENTRY(rsa_encode_key),
 	ENTRY(rsa_decode_key),
-	ENTRY(rsa_get_modulus_bytes),
-	ENTRY(rsa_get_public_key),
 	ENTRY(rsa_make_public_key),
 	
+	ENTRY(TLSContext_new),
 	{ NULL, NULL }
 };
 
+/* return 1 on failure. */
+static int
+exc(PyObject *module_dict, PyObject **exception, char *longName, 
+    char *itemString, char *doc)
+{
+	PyObject *s, *exc_d;
+	if (!(s = PyString_FromString(doc)))
+		return 1;
+	if (!(exc_d = PyDict_New()))
+		return 1; /* XXXX FREE */
+	if (PyDict_SetItemString(exc_d, "__doc__", s)<0) {
+		/* XXXX FREE */ 
+		return 1;
+	}
+	*exception = PyErr_NewException(longName, PyExc_Exception, exc_d);
+	if (! *exception) {
+		/* XXXX FREE */
+		return 1;
+	}
+	if (PyDict_SetItemString(module_dict,itemString,*exception) < 0)
+		return 1;
+
+	return 0;
+}
+
 DL_EXPORT(void)
 init_minionlib(void)
 {
@@ -36,13 +59,40 @@
 	m = Py_InitModule("_minionlib", _mixcryptlib_functions);
 	d = PyModule_GetDict(m);
 
+
+	SSL_library_init();
+	SSL_load_error_strings();
+
 	/* crypt */
 	ERR_load_ERR_strings();
  	ERR_load_RSA_strings();
-	mm_SSLError = PyErr_NewException("mixminion.SSLError", PyExc_Exception, NULL);
 
-	if (PyDict_SetItemString(d, "SSLError", mm_SSLError) < 0)
+	if (exc(d, &mm_CryptoError, "mixminion._minionlib.CryptoError", 
+		"CryptoError", mm_CryptoError__doc__))
 		return;
+	if (exc(d, &mm_TLSError, "mixminion._minionlib.TLSError", 
+		"TLSError", mm_TLSError__doc__))
+		return;
+	if (exc(d, &mm_TLSWantRead, "mixminion._minionlib.TLSWantRead", 
+		"TLSWantRead", mm_TLSWantRead__doc__))
+		return;
+	if (exc(d, &mm_TLSWantWrite, "mixminion._minionlib.TLSWantWrite", 
+		"TLSWantWrite", mm_TLSWantWrite__doc__))
+		return;
+
+	Py_INCREF(&mm_RSA_Type);
+	if (PyDict_SetItemString(d, "RSA", (PyObject*)&mm_RSA_Type) < 0)
+		return;
+
+	Py_INCREF(&mm_TLSContext_Type);
+	if (PyDict_SetItemString(d, "TLSContext", (PyObject*)&mm_TLSContext_Type) < 0)
+		return;
+
+	Py_INCREF(&mm_TLSSock_Type);
+	if (PyDict_SetItemString(d, "TLSSock", (PyObject*)&mm_TLSSock_Type) < 0)
+		return;
+
+
 }
 
 /*