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

[minion-cvs] Backport; tests; hacking: Core code is clean, commented...



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

Modified Files:
	aes_ctr.c crypt.c 
Log Message:
Backport; tests; hacking: Core code is clean, commented, documented, and tested

- Backported to older versions of Python.  We now support 2.0 through 
  2.2 inclusive.  This included:
      - Removing uses of // operator.
      - Checking version properly in setup.py
      - Removing use of nested scopes.
      - Replace type(foo) == str with type(foo) == type("")
      - Bundling Python's unittest.py module so we can run tests on systems
        (Python 2.0) that don't include it.
      - In crypt.c, we can no longer go from openssl BIGNUMs to Python Longs
        using _PyLong_(To|From)ByteArray -- the latter method didn't exist
        until 2.2.  We have to to/from a hex representation instead.

- Better message on insane-endianness case.

- Rename 'Formats' module to 'Packet'.
- Rename 'ServerProcess' module to 'PacketHandler'.
- Document and comment all remaining code.
- Changes to Packet: (Formerly Formats)
     - Add ReplyBlock object
     - Fix bugs in header slicing
     - Freak out on overlong subheaders.

- Changes to BuildMessage:
     - Make BuildStatelessReplyBlock a bit more paranoid about missing userKey.
     - Rejoin _buildFoo and _buildFoo_impl
     - Grouse early if header will be too long.
     - Misc cleanups
  
     - New tests: Long intermediate info,  fail on overlong routing info

- Changes to Crypto: 
     - Use symbolic constants throughout
     - Add cache for TRNG

- Changes to Hashlog:
     - Make 'sync' method conditional on underlying sync: this allows us
       to support dumbdbm.

- Changes to PacketHandler: (formerly ServerProcess)
     - Remove processMessage intermediary; rename _processMessage.
     - Add support for multiple keys
     - Make setup more sane
     - Add note on timing attacks
     - New tests: Most failing cases 

- Changes to benchMark:
     - Restructure handling of primitives, loop overhead
     - Make cleanup process handle dumbdbm.
     - Add actual vs ideal comparisons for Lioness and PacketHandler

- Changes to crypt.c:
     - Write more idiomatic and paranoid C. 
     - Make failing SHA cases fail faster
     - Backport to py2.0->py2.2; see above



Index: aes_ctr.c
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/aes_ctr.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- aes_ctr.c	31 May 2002 12:39:18 -0000	1.2
+++ aes_ctr.c	2 Jun 2002 06:11:16 -0000	1.3
@@ -35,8 +35,8 @@
 
 #if 0  /* On my Athlon, bswap_32 is actually slower.  Surprisingly,
 	 the code in glib/gtypes.h _is_ faster; but shaves only 1%
-         off encryption.  We seem to have reached the point of diminishing
-         returns here.*/
+         off encryption.  We seem to be near the point of diminishing
+         returns here. */
 #ifdef MM_L_ENDIAN
 #ifdef MM_HAVE_BYTESWAP_H
 #include <byteswap.h>
@@ -60,7 +60,6 @@
 #define SET_U32(ptr,i) SET_U32_cp(((u8*)(ptr)), i)
 #endif
 
-
 static inline void
 mm_incr(u32 const* ctr32)
 {
@@ -114,4 +113,3 @@
   c-basic-offset:8
   End:
 */
-

Index: crypt.c
===================================================================
RCS file: /home/minion/cvsroot/src/minion/src/crypt.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- crypt.c	31 May 2002 12:39:18 -0000	1.3
+++ crypt.c	2 Jun 2002 06:11:16 -0000	1.4
@@ -9,17 +9,25 @@
 #include <openssl/rand.h>
 #include <openssl/err.h>
 #include <_minionlib.h>
+#include <assert.h>
 
 #define TYPE_ERR(s) PyErr_SetString(PyExc_TypeError, s)
 #define KEY_IS_PRIVATE(rsa) ((rsa)->p)
 
 PyObject *mm_SSLError = NULL;
 
+/* Helper function: raise an SSLError with appropriate text from the
+ * underlying SSL exception.  
+ *
+ * Requires that mm_SSLError is initialized and ERR_load_*_strings
+ * have been called.
+ */
 static void 
 SSL_ERR() 
 {
 	int err = ERR_get_error();
 	const char *str = ERR_reason_error_string(err);
+	assert(mm_SSLError);
 	if (str)
 		PyErr_SetString(mm_SSLError, str);
 	else
@@ -41,20 +49,22 @@
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwdict, "s#:sha1", kwlist,
 					 &cp, &len))
-		return NULL;
-	SHA1_Init(&ctx);
-	SHA1_Update(&ctx,cp,len); 
-	output = PyString_FromStringAndSize(NULL, SHA_DIGEST_LENGTH);
-	if (!output) {
+		return NULL;	
+	if (!(output = PyString_FromStringAndSize(NULL, SHA_DIGEST_LENGTH))) {
 		PyErr_NoMemory();
 		return NULL;
 	}
+	
+	SHA1_Init(&ctx);
+	SHA1_Update(&ctx,cp,len); 
 	SHA1_Final(PyString_AS_STRING(output),&ctx);
 	memset(&ctx,0,sizeof(ctx));
-
+	
 	return output;
 }
 
+static char aes_descriptor[] = "AES key objects descriptor";
+
 /* Destructor of PyCObject
  */
 static void
@@ -65,8 +75,7 @@
 	free(obj);
 }
 
-static char aes_descriptor[] = "AES key objects descriptor";
-
+/* Converter fn for "O&" argument conversion with AES keys. */
 static int
 aes_arg_convert(PyObject *obj, void *adr)
 {
@@ -79,6 +88,9 @@
 	}
 }
 
+#define WRAP_AES(aes) (PyCObject_FromVoidPtrAndDesc( (void*) (aes),\
+		       (void*) aes_descriptor, aes_destruct))
+
 const char mm_aes_key__doc__[] = 
     "aes_key(str) -> key\n\n"
     "Converts a 16-byte string to an AES key for use with aes_ctr128_crypt.\n"
@@ -101,15 +113,17 @@
 		TYPE_ERR("aes_key() requires a 128-bit (16 byte) string");
 		return NULL;
 	}
-	aes_key = malloc(sizeof(AES_KEY));
-	if (!aes_key) { PyErr_NoMemory(); goto err; }
+	
+	if (!(aes_key = malloc(sizeof(AES_KEY)))) {
+		PyErr_NoMemory(); goto err; 
+	}
 	if (AES_set_encrypt_key(key, keylen*8, aes_key)) {
 		SSL_ERR();
 		goto err;
 	}
-	result = PyCObject_FromVoidPtrAndDesc( (void*) aes_key,
-				(void*) aes_descriptor, aes_destruct );
-	if (!result) { PyErr_NoMemory(); goto err; }
+	if (!(result = WRAP_AES(aes_key))) { 
+		PyErr_NoMemory(); goto err; 
+	}
 	return result;
 
  err:
@@ -126,7 +140,7 @@
   "Encrypts a string in counter mode.  If idx is nonzero, the counter begins\n"
   "at idx.  If prng is nonzero, ignores string and just produces a stream of\n"
   "length prng.\n\n"
-  "BUG: only the 32 least significant bits of idx are used.\n\n"
+  "WART: only the 32 least significant bits of idx are used.\n\n"
   "Performance notes:  PRNG mode is much faster (36% @ 32K) than generating\n"
   "a string of NULs in Python and encrypting it.  Encryption, on the other\n"
   "hand, is only slightly faster (15% @ 32K) than XORing the prng output\n"
@@ -155,12 +169,11 @@
 
 	if (prng) { 
 		inputlen = prng;
-		input = malloc(prng);
-		if (!input) { PyErr_NoMemory(); return NULL; }
+		if (!(input = malloc(prng))) { PyErr_NoMemory(); return NULL; }
 		memset(input, 0, inputlen);
 	} 
-	output = PyString_FromStringAndSize(NULL, inputlen);
-	if (!output) {
+	
+	if (!(output = PyString_FromStringAndSize(NULL, inputlen))) {
 		PyErr_NoMemory(); 
 		if (prng) free(input);
 		return NULL;
@@ -196,8 +209,11 @@
 		return NULL;
 	}
 
-	output = PyString_FromStringAndSize(NULL,s1len);
-	if (! output) { PyErr_NoMemory(); return NULL; }
+	
+	if (!(output = PyString_FromStringAndSize(NULL,s1len))) { 
+		PyErr_NoMemory(); 
+		return NULL; 
+	}
 
 	outp = PyString_AS_STRING(output);
 	while (s1len--) {
@@ -231,6 +247,8 @@
 	return Py_None;
 }
 
+static char rsa_descriptor[] = "RSA objects descriptor";
+
 /* Destructor for PyCObject
  */
 static void
@@ -240,8 +258,7 @@
 	RSA_free( (RSA*) obj);
 }
 
-static char rsa_descriptor[] = "RSA objects descriptor";
-
+/* Converter fn for "O&" argument conversion with RSA keys. */
 static int
 rsa_arg_convert(PyObject *obj, void *adr) 
 {
@@ -254,7 +271,6 @@
 	}
 }
 
-
 #define WRAP_RSA(rsa) (PyCObject_FromVoidPtrAndDesc( (void*) (rsa),\
 		       (void*) rsa_descriptor, rsa_destruct))
 					     
@@ -333,7 +349,7 @@
 					 kwlist,
 					 &bits, &e))
 		return NULL;
-	
+
 	rsa = RSA_generate_key(bits, e, NULL, NULL);
 	if (rsa == NULL) {
 		SSL_ERR();
@@ -425,52 +441,54 @@
 	return WRAP_RSA(rsa);
 }
 
+/**
+ * Converts a BIGNUM into a newly allocated PyLongObject.  
+ **/
 static PyObject*
 bn2pylong(const BIGNUM *bn) 
 {
-	int len, len2;
-	unsigned char *buf;
 	PyObject *output;
 
-	len = BN_num_bytes(bn);
-	buf = malloc(len);
-	if (!buf) { PyErr_NoMemory(); return NULL; }
-        len2 = BN_bn2bin(bn, buf);
-	assert(len == len2);
-
-	/* read big-endian. */
-	output = _PyLong_FromByteArray(buf, len, 0, 0);
-
-	free(buf);
-	return output;
+	/**
+	 * We could get better performance with _PyLong_FromByteArray,
+	 * but that wasn't introduced until Python 2.2.  We go with
+	 * only a single implementation here, since this isn't in the
+	 * critical path.  See CVS version 1.3 of this file for such
+	 * an implementation.
+	 **/
+	char *hex = BN_bn2hex(bn);
+	output = PyLong_FromString(hex, NULL, 16); 
+	OPENSSL_free(hex);
+	return output; /* pass along errors */
 }
 
-/*
- * It's hard to actually get Python to tell you the order-of-magnitude
- * of a long.  Instead, we give an overflow error if you're over 2**4096.
- */
-#define MAX_LONG_BYTES 4096/8
-
+/**
+ * Converts a PyLongObject into a freshly allocated BIGNUM.
+ **/
 static BIGNUM*
 pylong2bn(PyObject *pylong)
 {
+	PyObject *str;
+	char *buf;
+	BIGNUM *result = NULL;
 	int r;
-	unsigned char *buf;
-	BIGNUM *result;
-
-	buf = malloc(MAX_LONG_BYTES);
-	if (!buf) { PyErr_NoMemory(); return NULL; }
-
-	r = _PyLong_AsByteArray((PyLongObject*)pylong, 
-				buf, MAX_LONG_BYTES, 0, 0);
-	if (r<0) {
-		free(buf);
+	assert(PyLong_Check(pylong));
+	assert(pylong && pylong->ob_type 
+	       && pylong->ob_type->tp_as_number
+	       && pylong->ob_type->tp_as_number->nb_hex);
+	
+	if (!(str = pylong->ob_type->tp_as_number->nb_hex(pylong)))
 		return NULL;
-	}
-	result = BN_bin2bn(buf,MAX_LONG_BYTES,NULL);
-	if (result == NULL) { free(buf); PyErr_NoMemory(); return NULL; }
 	
-	free(buf);
+	buf = PyString_AsString(str);
+	if (!buf || buf[0]!='0' || buf[1]!='x') {
+		Py_DECREF(str); return NULL;
+	}
+	r = BN_hex2bn(&result, &buf[2]);
+	if (r<0 || result == NULL) {
+		Py_DECREF(str); return NULL;
+	}
+	Py_DECREF(str);
 	return result;
 }
 
@@ -493,11 +511,12 @@
 	
 	if (!rsa->n) { TYPE_ERR("Key has no modulus"); return NULL;}
 	if (!rsa->e) { TYPE_ERR("Key has no e"); return NULL; }
-	n = bn2pylong(rsa->n);
-	if (n == NULL) { PyErr_NoMemory(); return NULL; }
-	e = bn2pylong(rsa->e);
-	if (e == NULL) { PyErr_NoMemory(); Py_DECREF(n); return NULL; }
-
+	if (!(n = bn2pylong(rsa->n))) { 
+		PyErr_NoMemory(); return NULL; 
+	}
+	if (!(e = bn2pylong(rsa->e))) { 
+		PyErr_NoMemory(); Py_DECREF(n); return NULL; 
+	}
 	output = Py_BuildValue("OO", n, e);
 	Py_DECREF(n);
 	Py_DECREF(e);
@@ -523,12 +542,11 @@
 		return NULL;
 	
 	rsa = RSA_new();
-	if (!rsa) { PyErr_NoMemory(); return NULL; }
-
-	rsa->n = pylong2bn(n);
-	if (!rsa->n) { RSA_free(rsa); return NULL; }
-	rsa->e = pylong2bn(e);
-	if (!rsa->e) { RSA_free(rsa); BN_free(rsa->n); return NULL; }
+	if (!(rsa = RSA_new())) { PyErr_NoMemory(); return NULL; }
+	if (!(rsa->n = pylong2bn(n))) { RSA_free(rsa); return NULL; }
+	if (!(rsa->e = pylong2bn(e))) { 
+		RSA_free(rsa); BN_free(rsa->n); return NULL; 
+	}
 
 	output = WRAP_RSA(rsa);
 	
@@ -537,7 +555,7 @@
 
 const char mm_rsa_get_modulus_bytes__doc__[]=
    "rsa_get_modulus_bytes(rsa) -> int\n\n"
-   "Returns the numbe of *bytes* (not bits) in an RSA modulus.\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) 
@@ -579,8 +597,10 @@
 		TYPE_ERR("String too long to pad.");
 		return NULL;
 	}
-	output = PyString_FromStringAndSize(NULL,keylen);
-	if (!output) { PyErr_NoMemory(); return NULL; }
+	
+	if (!(output = PyString_FromStringAndSize(NULL,keylen))) { 
+		PyErr_NoMemory(); return NULL; 
+	}
 	
 	r = RSA_padding_add_PKCS1_OAEP(PyString_AS_STRING(output), keylen,
 				       input, inputlen,
@@ -628,8 +648,10 @@
 		return NULL;
 	}
 	
-	output = PyString_FromStringAndSize(NULL,keylen);
-	if (!output) { PyErr_NoMemory(); return NULL; }
+	
+	if (!(output = PyString_FromStringAndSize(NULL,keylen))) { 
+		PyErr_NoMemory(); return NULL; 
+	}
 	
 	r = RSA_padding_check_PKCS1_OAEP(PyString_AS_STRING(output), keylen,
 					 input+1, inputlen-1, keylen,