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

[minion-cvs] Additions to C-level code: Added ability to generate X5...



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

Modified Files:
	_minionlib.h crypt.c main.c tls.c 
Log Message:
Additions to C-level code: Added ability to generate X509 certs, and
to read and write RSA keys from PEM format.

This isn't used yet beyond a verified-to-work-once level, but I'm
checkpointing it before my next batch of hacks so I can't break
anything.


Index: _minionlib.h
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/_minionlib.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- _minionlib.h	1 Jul 2002 18:03:05 -0000	1.5
+++ _minionlib.h	5 Jul 2002 23:34:33 -0000	1.6
@@ -57,11 +57,12 @@
 FUNC_DOC(mm_check_oaep_padding);
 FUNC_DOC(mm_rsa_generate);
 FUNC_DOC(mm_rsa_crypt);
-FUNC_DOC(mm_rsa_encode_key);
 FUNC_DOC(mm_rsa_decode_key);
-FUNC_DOC(mm_rsa_get_modulus_bytes);
+FUNC_DOC(mm_rsa_PEM_read_key);
 FUNC_DOC(mm_rsa_get_public_key);
 FUNC_DOC(mm_rsa_make_public_key);
+FUNC_DOC(mm_generate_dh_parameters);
+FUNC_DOC(mm_generate_cert);
 extern PyObject *mm_CryptoError;
 extern char mm_CryptoError__doc__[];
 

Index: crypt.c
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/crypt.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- crypt.c	1 Jul 2002 18:03:05 -0000	1.6
+++ crypt.c	5 Jul 2002 23:34:33 -0000	1.7
@@ -8,6 +8,8 @@
 #include <openssl/sha.h>
 #include <openssl/rand.h>
 #include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
 #include <_minionlib.h>
 #include <assert.h>
 
@@ -437,6 +439,128 @@
 	return mm_RSA_new(rsa);
 }
 
+const char mm_RSA_PEM_write_key__doc__[]=
+  "rsa.PEM_write_key(file, public, [password])\n\n"
+  "Writes an RSA key to a file in PEM format with PKCS#8 encryption.\n" 
+  "If public is true, writes only the public key, and ignores the password.\n"
+  "Otherwise, writes the full private key, optionally encrypted by a\n"
+  "password.\n";
+
+PyObject *
+mm_RSA_PEM_write_key(PyObject *self, PyObject *args, PyObject *kwdict)
+{
+	static char* kwlist[] = { "file", "public", "password", NULL };
+	PyObject *pyfile;
+	int public, passwordlen=0;
+	char *password=NULL;
+
+	RSA *rsa = NULL;
+	EVP_PKEY *pkey = NULL;
+	FILE *file;
+	
+	assert(mm_RSA_Check(self));
+	if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O!i|s#:PEM_write_key",
+					 kwlist, &PyFile_Type, &pyfile,
+					 &public,
+					 &password, &passwordlen))
+		return NULL;
+	if (!(file = PyFile_AsFile(pyfile))) {
+		TYPE_ERR("Invalid file object"); 
+		return NULL;
+	}
+
+	if (public) {
+		rsa = ((mm_RSA*)self)->rsa;
+		if (!PEM_write_RSAPublicKey(file, rsa))
+			goto error;
+	} else {
+		if (!(rsa = RSAPrivateKey_dup(((mm_RSA*)self)->rsa)))
+			goto error;
+		if (!(pkey = EVP_PKEY_new()))
+			goto error;
+		if (!EVP_PKEY_assign_RSA(pkey,rsa))
+			goto error;
+		rsa = NULL;
+
+		if (password) {
+			printf("Got here 1\n");
+			if (!PEM_write_PKCS8PrivateKey(file, pkey,
+						       EVP_des_ede3_cbc(),
+						       NULL, 0,
+						       NULL, password))
+				goto error;
+		} else {
+			printf("Got here 2\n");
+			if (!PEM_write_PKCS8PrivateKey(file, pkey,
+						       NULL, 
+						       NULL, 0,
+						       NULL, NULL))
+				goto error;
+		}
+		printf("got here 3\n");
+	}
+	Py_INCREF(Py_None);
+	return Py_None;
+
+ error:
+	if (rsa && !public)
+		RSA_free(rsa);
+	if (pkey)
+		EVP_PKEY_free(pkey);
+
+	mm_SSL_ERR(1);
+	return NULL;
+}
+
+const char mm_rsa_PEM_read_key__doc__[]=
+  "rsa_PEM_read_key(file, public, [password]) -> rsa\n\n"
+  "Writes an RSA key to a file in PEM format with PKCS#8 encryption.\n" 
+  "If public is true, reads only the public key, and ignores the password.\n"
+  "Otherwise, writes the full private key, optionally encrypted by a\n"
+  "password.\n";
+
+PyObject *
+mm_rsa_PEM_read_key(PyObject *self, PyObject *args, PyObject *kwdict)
+{
+	static char *kwlist[] = { "file", "public", "password", NULL };
+	PyObject *pyfile;
+	int public, passwordlen=0;
+	char *password=NULL;
+
+	RSA *rsa;
+	FILE *file;
+	
+	if (!PyArg_ParseTupleAndKeywords(args, kwdict,
+					 "O!i|s#:rsa_PEM_read_key",
+					 kwlist, &PyFile_Type, &pyfile,
+					 &public,
+					 &password, &passwordlen))
+		return NULL;
+	if (!(file = PyFile_AsFile(pyfile))) {
+		TYPE_ERR("Invalid file object"); 
+		return NULL;
+	}
+	if (!passwordlen)
+		password = "";
+
+	if (public) {
+		rsa = PEM_read_RSAPublicKey(file, NULL, NULL, NULL);
+	} else {
+		rsa = PEM_read_RSAPrivateKey(file, NULL,
+					     NULL, password);
+	}
+	if (!rsa) {
+		mm_SSL_ERR(1);
+		return NULL;
+	}
+
+	return mm_RSA_new(rsa);
+}
+
+
+
+
+
 /**
  * Converts a BIGNUM into a newly allocated PyLongObject.  
  **/
@@ -555,14 +679,14 @@
    "Returns the number of *bytes* (not bits) in an RSA modulus.\n";
 
 static PyObject *
-mm_RSA_get_modulus_bytes(PyObject *self, PyObject *args, PyObject *kwdict) 
+mm_RSA_get_modulus_bytes(PyObject *self, PyObject *args, PyObject *kwargs) 
 {
 	static char *kwlist[] = { NULL };
 	RSA *rsa;
 
 	assert(mm_RSA_Check(self));
 	rsa = ((mm_RSA*)self)->rsa;
-	if (!PyArg_ParseTupleAndKeywords(args, kwdict,
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs,
 					 ":get_modulus_bytes", kwlist))
 		return NULL;
 	
@@ -574,6 +698,7 @@
 	METHOD(mm_RSA, encode_key),
 	METHOD(mm_RSA, get_modulus_bytes),
 	METHOD(mm_RSA, get_public_key),
+	METHOD(mm_RSA, PEM_write_key),
 	{ NULL, NULL }
 };
  
@@ -610,7 +735,7 @@
    "be used, in bytes;  Param is the security parameter string.\n";
 
 PyObject *
-mm_add_oaep_padding(PyObject *self, PyObject *args, PyObject *kwdict) 
+mm_add_oaep_padding(PyObject *self, PyObject *args, PyObject *kwargs)
 {
 	static char *kwlist[] = { "s", "param", "keylen", NULL };
 
@@ -620,7 +745,7 @@
 
 	PyObject *output;
 	
-	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, 
 					 "s#s#i:add_oaep_padding", kwlist,
 			      &input,&inputlen,&param,&paramlen,&keylen))
 		return NULL;
@@ -656,7 +781,7 @@
    "If the padding is in tact, the original string is returned.\n";
 
 PyObject *
-mm_check_oaep_padding(PyObject *self, PyObject *args, PyObject *kwdict) 
+mm_check_oaep_padding(PyObject *self, PyObject *args, PyObject *kwargs) 
 {
 	static char *kwlist[] = { "s", "param", "keylen", NULL };
 
@@ -666,7 +791,7 @@
 
 	PyObject *output;
 	
-	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, 
 					 "s#s#i:check_oaep_padding", kwlist,
 				  &input,&inputlen,&param,&paramlen,&keylen))
 		return NULL;
@@ -692,6 +817,148 @@
 	if(_PyString_Resize(&output, r)) return NULL;
 
 	return output;
+}
+
+static void
+gen_dh_callback(int p, int n, void *arg) 
+{
+	if (p == 0) fputs(".", stderr);
+	if (p == 1) fputs("+", stderr);
+	if (p == 2) fputs("*", stderr);
+	if (p == 3) fputs("\n", stderr);
+}
+
+const char mm_generate_dh_parameters__doc__[] = 
+   "generate_dh_parameters(filename, [bits, [verbose]])\n\n"
+   "XXXX";
+
+PyObject *
+mm_generate_dh_parameters(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+	static char *kwlist[] = { "filename", "verbose", NULL };
+	char *filename;
+	int bits=512, verbose=0;
+	
+	BIO *out = NULL;
+	DH *dh = NULL;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, 
+					 "s|i:generate_dh_parameters", 
+					 kwlist,
+					 &filename, &verbose))
+		return NULL;
+	
+	if (!(out = BIO_new_file(filename, "w")))
+		goto error;
+	if (!(dh = DH_generate_parameters(bits, 2, 
+					  verbose?gen_dh_callback:NULL, 
+					  NULL)))
+		goto error;
+	if (!PEM_write_bio_DHparams(out, dh))
+		goto error;
+	BIO_free(out);
+	DH_free(dh);
+	Py_INCREF(Py_None);
+	return Py_None;
+
+ error:
+	if (out)
+		BIO_free(out);
+	if (dh)
+		DH_free(dh);
+	mm_SSL_ERR(0);
+	return NULL;
+}
+
+const char mm_generate_cert__doc__[] = 
+   "generate_cert(filename, rsa, days, cn)\n\n"
+   "XXXX";
+
+PyObject *
+mm_generate_cert(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+	static char *kwlist[] = { "filename", "rsa", "days", "cn", NULL };
+	char *filename, *cn;
+	PyObject *_rsa;
+	int days;
+	
+	RSA *rsa = NULL;
+	EVP_PKEY *pkey = NULL;
+	BIO *out = NULL;
+	X509 *x509 = NULL;
+	X509_NAME *name = NULL;
+	int nid;
+	PyObject *retval;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!is:PEM_write_key",
+					 kwlist, &filename,
+					 &mm_RSA_Type, &_rsa, &days, &cn))
+		return NULL;
+
+	if (!(rsa = RSAPrivateKey_dup(((mm_RSA*)_rsa)->rsa)))
+		goto error;
+	if (!(pkey = EVP_PKEY_new()))
+		goto error;
+	if (!(EVP_PKEY_assign_RSA(pkey, rsa)))
+		goto error;
+	rsa = NULL;
+
+	if (!(x509 = X509_new()))
+		goto error;
+	if (!(X509_set_version(x509, 2)))
+		goto error;
+	if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509),0L)))
+		goto error;
+	if (!(name = X509_NAME_new()))
+		goto error;
+	
+#define SET_PART(part, val)                                     \
+	if ((nid = OBJ_txt2nid(part)) == NID_undef) goto error; \
+        if (!X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,\
+                                        val, -1, -1, 0)) goto error;
+       
+	SET_PART("countryName", "US");
+	SET_PART("organizationName", "Mixminion network");
+	SET_PART("commonName", cn);
+
+	if (!(X509_set_issuer_name(x509, name)))
+		goto error;
+	if (!X509_gmtime_adj(X509_get_notBefore(x509),0)) 
+		goto error;
+	/* XXXX */
+	if (!X509_gmtime_adj(X509_get_notAfter(x509), 60L*60L*24L*days)) 
+		goto error;
+	if (!(X509_set_pubkey(x509, pkey)))
+		goto error;
+	if (!(X509_sign(x509, pkey, EVP_md5())))
+		goto error;
+
+	if (!(out = BIO_new_file(filename, "w")))
+		goto error;
+	if (!(PEM_write_bio_X509(out, x509)))
+		goto error;
+
+	retval = Py_None;
+	Py_INCREF(Py_None);
+	goto done;
+
+error:
+	P(error);
+	retval = NULL;
+	mm_SSL_ERR(1);
+ done:
+	if (out)
+		BIO_free(out);
+	if (name)
+		X509_NAME_free(name);
+	if (x509)
+		X509_free(x509);
+	if (rsa)
+		RSA_free(rsa);
+	if (pkey)
+		EVP_PKEY_free(pkey);
+
+	return retval;
 }
 
 /*

Index: main.c
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/main.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- main.c	1 Jul 2002 18:03:05 -0000	1.4
+++ main.c	5 Jul 2002 23:34:33 -0000	1.5
@@ -29,8 +29,11 @@
 	ENTRY(check_oaep_padding),
 	ENTRY(rsa_generate),
 	ENTRY(rsa_decode_key),
+	ENTRY(rsa_PEM_read_key),
 	ENTRY(rsa_make_public_key),
-	
+	ENTRY(generate_dh_parameters),
+	ENTRY(generate_cert),
+
 	ENTRY(TLSContext_new),
 	{ NULL, NULL }
 };
@@ -85,6 +88,8 @@
 	/* crypt */
 	ERR_load_ERR_strings();
  	ERR_load_RSA_strings();
+
+	OpenSSL_add_all_algorithms();
 
 	if (exc(d, &mm_CryptoError, "mixminion._minionlib.CryptoError", 
 		"CryptoError", mm_CryptoError__doc__))

Index: tls.c
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/tls.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- tls.c	1 Jul 2002 18:03:05 -0000	1.3
+++ tls.c	5 Jul 2002 23:34:33 -0000	1.4
@@ -85,13 +85,11 @@
 #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"
+   "TLSContext([certfile, [rsa, [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"
+   "contain the PEM-encoded X509 public keys, private key, 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";
@@ -100,16 +98,22 @@
 mm_TLSContext_new(PyObject *self, PyObject *args, PyObject *kwargs) 
 {
 	static char *kwlist[] = { "certfile", "pkfile", "dhfile", NULL };
-	char *certfile = NULL, *pkfile=NULL, *dhfile=NULL;
+	char *certfile = NULL, *dhfile=NULL;
+	mm_RSA *rsa = NULL;
+
 	SSL_METHOD *method;
 	SSL_CTX *ctx;
 	DH *dh;
 	mm_TLSContext *result;
 	BIO *bio;
+	RSA *_rsa = NULL;
+	EVP_PKEY *pkey = NULL; /* Leaked? ???? */
 	
-	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sss:TLSContext_new", 
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sO!s:TLSContext_new", 
 					 kwlist,
-					 &certfile, &pkfile, &dhfile))
+					 &certfile, 
+					 &mm_RSA_Type, &rsa,
+					 &dhfile))
 		return NULL;
 
 	method = TLSv1_method();
@@ -117,7 +121,6 @@
 	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;
 	}
@@ -125,9 +128,18 @@
 	    !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 (rsa) {
+		if (!(_rsa = RSAPrivateKey_dup(rsa->rsa)) || 
+		    !(pkey = EVP_PKEY_new()) ||
+		    !EVP_PKEY_assign_RSA(pkey, _rsa)) {
+			if (!pkey && _rsa) RSA_free(_rsa);
+			if (pkey) EVP_PKEY_free(pkey);
+			SSL_CTX_free(ctx); mm_SSL_ERR(0); return NULL;
+		}
+		if (!(SSL_CTX_use_PrivateKey(ctx, pkey))) {
+			EVP_PKEY_free(pkey);
+			SSL_CTX_free(ctx); mm_SSL_ERR(0); return NULL;
+		}
 	} 
 
 	if (dhfile) {
@@ -487,7 +499,7 @@
 		mm_SSL_ERR(0); return NULL; /* ???? */
 	}
 	pkey = X509_get_pubkey(cert);
-	/* ???? */
+	/* ???? free? leak? */
 	if (!(rsa = EVP_PKEY_get1_RSA(pkey))) 
 		return NULL; /* XXXX */