pynacl

Python wrapper for http://nacl.cace-project.eu/
git clone https://code.literati.org/pynacl.git
Log | Files | Refs | README

commit 6ce9a293b13e89ccd0107ed4ca7ff45de886243a
parent acc356f697111265b984d5b9d282aaf0f558a251
Author: Sean Lynch <seanl@literati.org>
Date:   Sun, 12 Jun 2011 18:51:32 -0700

Add randombytes and authenticated encryption.

Diffstat:
Mnacl.i | 209+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mtest.py | 50++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 181 insertions(+), 78 deletions(-)

diff --git a/nacl.i b/nacl.i @@ -37,10 +37,10 @@ void ge25519_scalarmult_base(ge25519 *r, const sc25519 *s); void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]); - void crypto_sign_keypair_fromseed(unsigned char *pk, - unsigned char *sk, - unsigned char *seed, - int seedlen) { + int crypto_sign_keypair_fromseed(unsigned char *pk, + unsigned char *sk, + unsigned char *seed, + unsigned long long seedlen) { sc25519 scsk; ge25519 gepk; @@ -54,6 +54,7 @@ ge25519_scalarmult_base(&gepk, &scsk); ge25519_pack(pk, &gepk); + return 0; } %} @@ -69,86 +70,46 @@ $2 = PyString_Size($input); } -%typemap(in, numinputs=0) unsigned char *sha256hash (unsigned char temp[32]) { - $1 = temp; -} - -%typemap(argout) unsigned char *sha256hash { - $result = PyString_FromStringAndSize((char *)$1, 32); -} - -int crypto_hash_sha256(unsigned char *sha256hash, const unsigned char *m, - unsigned long long mlen); - -%typemap(in, numinputs=0) unsigned char *sha512hash (unsigned char temp[64]) { - $1 = temp; -} - -%typemap(argout) unsigned char *sha512hash { - $result = PyString_FromStringAndSize((char *)$1, 64); -} - -int crypto_hash_sha512(unsigned char *sha512hash, const unsigned char *m, - unsigned long long mlen); - - -%constant int crypto_sign_PUBLICKEYBYTES; -%constant int crypto_sign_SECRETKEYBYTES; - -%typemap(in, numinputs=0) (unsigned char *pk, unsigned char *sk) - (unsigned char temp1[crypto_sign_PUBLICKEYBYTES], - unsigned char temp2[crypto_sign_SECRETKEYBYTES]) { - $1 = temp1; - $2 = temp2; +%typemap(in, numinputs=0) unsigned char hash[ANY], unsigned char k[ANY] { + $result = PyString_FromStringAndSize(NULL, $1_dim0); + $1 = (unsigned char *)PyString_AsString($result); } -%typemap(argout) (unsigned char *pk, unsigned char *sk) { - $result = PyList_New(2); - PyList_SetItem($result, 0, - PyString_FromStringAndSize((char *)$1, - crypto_sign_PUBLICKEYBYTES)); - PyList_SetItem($result, 1, - PyString_FromStringAndSize((char *)$2, - crypto_sign_SECRETKEYBYTES)); +// For some reason [ANY] doesn't work for multi-argument typemaps. +%typemap(in, numinputs=0) (unsigned char pk[crypto_sign_PUBLICKEYBYTES], + unsigned char sk[crypto_sign_SECRETKEYBYTES]) + (PyObject *temp1, PyObject *temp2), + (unsigned char pk[crypto_box_PUBLICKEYBYTES], + unsigned char sk[crypto_box_SECRETKEYBYTES]) + (PyObject *temp1, PyObject *temp2) { + temp1 = PyString_FromStringAndSize(NULL, $1_dim0); + $1 = (unsigned char *)PyString_AS_STRING(temp1); + temp2 = PyString_FromStringAndSize(NULL, $2_dim0); + $2 = (unsigned char *)PyString_AS_STRING(temp2); + $result = PyTuple_Pack(2, temp1, temp2); + Py_XDECREF(temp1); + Py_XDECREF(temp2); } -%typemap(in) (unsigned char *seed, int seedlen) { - if (!PyString_Check($input)) { - PyErr_SetString(PyExc_ValueError, "Expecting a string"); - return NULL; - } - $1 = (unsigned char *)PyString_AsString($input); - $2 = PyString_Size($input); -} - -void crypto_sign_keypair_fromseed(unsigned char *pk, unsigned char *sk, - unsigned char *seed, int seedlen); -int crypto_sign_keypair(unsigned char *pk, unsigned char *sk); - -%typemap(in) unsigned char *pk { +%typemap(in) (unsigned char *seed, unsigned long long seedlen) { if (!PyString_Check($input)) { PyErr_SetString(PyExc_ValueError, "Expecting a string"); return NULL; } - if (PyString_Size($input) != crypto_sign_PUBLICKEYBYTES) { - PyErr_Format(PyExc_ValueError, "Expecting a string of length %d", - crypto_sign_PUBLICKEYBYTES); - return NULL; - } - $1 = (unsigned char *)PyString_AsString($input); + $1 = (unsigned char *)PyString_AS_STRING($input); + $2 = (unsigned long long)PyString_GET_SIZE($input); } -%typemap(in) unsigned char *sk { +%typemap(in) const unsigned char [ANY] { if (!PyString_Check($input)) { PyErr_SetString(PyExc_ValueError, "Expecting a string"); return NULL; } - if (PyString_Size($input) != crypto_sign_SECRETKEYBYTES) { - PyErr_Format(PyExc_ValueError, "Expecting a string of length %d", - crypto_sign_SECRETKEYBYTES); + if (PyString_GET_SIZE($input) != $1_dim0) { + PyErr_Format(PyExc_ValueError, "Expecting a string of length %d", $1_dim0); return NULL; } - $1 = (unsigned char *)PyString_AsString($input); + $1 = (unsigned char *)PyString_AS_STRING($input); } %typemap(in) @@ -186,8 +147,13 @@ int crypto_sign_keypair(unsigned char *pk, unsigned char *sk); _PyString_Resize(&$result, *$2); } -%typemap(freearg) unsigned char *sm, unsigned char *m { - free($1); +%typemap(in) (unsigned char *buffer, unsigned long long bytes) { + $2 = PyInt_AsUnsignedLongLongMask($input); + if ($2 == -1 && PyErr_Occurred() != NULL) { + return NULL; + } + $result = PyString_FromStringAndSize(NULL, $2); + $1 = (unsigned char *)PyString_AS_STRING($result); } %typemap(out) int { @@ -197,11 +163,106 @@ int crypto_sign_keypair(unsigned char *pk, unsigned char *sk); } } +%typemap(out) void randombytes {} + +/** + * crypto_box typemaps. The dimensions on the arrays indicate padding. + */ +%typemap(in) (unsigned char out[crypto_box_BOXZEROBYTES], + const unsigned char in[crypto_box_ZEROBYTES], + unsigned long long mlen), + (unsigned char out[crypto_box_ZEROBYTES], + const unsigned char in[crypto_box_BOXZEROBYTES], + unsigned long long mlen) { + if (!PyString_Check($input)) { + PyErr_SetString(PyExc_ValueError, "Expecting a string"); + return NULL; + } + $3 = PyString_GET_SIZE($input) + $2_dim0; + // Need to pad the beginning + $1 = (unsigned char *)calloc($3 + $1_dim0, sizeof(unsigned char)); + $2 = (unsigned char *)calloc($3 + $2_dim0, sizeof(unsigned char)); + memcpy(&$2[$2_dim0], PyString_AS_STRING($input), $3); +} + +%typemap(argout) (unsigned char out[crypto_box_BOXZEROBYTES], + const unsigned char in [crypto_box_ZEROBYTES], + unsigned long long mlen), + (unsigned char out[crypto_box_ZEROBYTES], + const unsigned char in[crypto_box_BOXZEROBYTES], + unsigned long long mlen) { + $result = PyString_FromStringAndSize((char *)&$1[$1_dim0], $3 - $1_dim0); + free($1); + free($2); +} + +/** + * Utilities + */ +void randombytes(unsigned char *buffer, unsigned long long bytes); + +/** + * Hash stuff + */ +int crypto_hash_sha256(unsigned char hash[32], const unsigned char *m, + unsigned long long mlen); +int crypto_hash_sha512(unsigned char hash[64], const unsigned char *m, + unsigned long long mlen); + + +/** + * Authenticated public-key encryption + */ + +%constant int crypto_box_PUBLICKEYBYTES; +%constant int crypto_box_SECRETKEYBYTES; +%constant int crypto_box_ZEROBYTES; +%constant int crypto_box_BOXZEROBYTES; +%constant int crypto_box_NONCEBYTES; + +int crypto_box(unsigned char out[crypto_box_BOXZEROBYTES], + const unsigned char in[crypto_box_ZEROBYTES], + unsigned long long mlen, + const unsigned char n[crypto_box_NONCEBYTES], + const unsigned char pk[crypto_box_PUBLICKEYBYTES], + const unsigned char sk[crypto_box_SECRETKEYBYTES]); +int crypto_box_open(unsigned char out[crypto_box_ZEROBYTES], + const unsigned char in[crypto_box_BOXZEROBYTES], + unsigned long long mlen, + const unsigned char n[crypto_box_NONCEBYTES], + const unsigned char pk[crypto_box_PUBLICKEYBYTES], + const unsigned char sk[crypto_box_SECRETKEYBYTES]); +int crypto_box_keypair(unsigned char pk[crypto_box_PUBLICKEYBYTES], + unsigned char sk[crypto_box_SECRETKEYBYTES]); +int crypto_box_beforenm(unsigned char k[crypto_box_BEFORENMBYTES], + const unsigned char pk[crypto_box_PUBLICKEYBYTES], + const unsigned char sk[crypto_box_PUBLICKEYBYTES]); +int crypto_box_afternm(unsigned char out[crypto_box_BOXZEROBYTES], + const unsigned char in[crypto_box_ZEROBYTES], + unsigned long long mlen, + const unsigned char n[crypto_box_NONCEBYTES], + const unsigned char k[crypto_box_BEFORENMBYTES]); +int crypto_box_open_afternm(unsigned char out[crypto_box_ZEROBYTES], + const unsigned char in[crypto_box_BOXZEROBYTES], + unsigned long long mlen, + const unsigned char n[crypto_box_NONCEBYTES], + const unsigned char k[crypto_box_BEFORENMBYTES]); + +/** + * Signature stuff + */ +%constant int crypto_sign_PUBLICKEYBYTES; +%constant int crypto_sign_SECRETKEYBYTES; + +int crypto_sign_keypair_fromseed(unsigned char pk[crypto_sign_PUBLICKEYBYTES], + unsigned char sk[crypto_sign_SECRETKEYBYTES], + unsigned char *seed, + unsigned long long seedlen); // Custom +int crypto_sign_keypair(unsigned char pk[crypto_sign_PUBLICKEYBYTES], + unsigned char sk[crypto_sign_SECRETKEYBYTES]); int crypto_sign(unsigned char *sm, unsigned long long *smlen, const unsigned char *m, unsigned long long mlen, - unsigned char *sk); + const unsigned char sk[crypto_sign_SECRETKEYBYTES]); int crypto_sign_open(unsigned char *m, unsigned long long *mlen, const unsigned char *sm, unsigned long long smlen, - unsigned char *pk); - - + const unsigned char pk[crypto_sign_PUBLICKEYBYTES]); diff --git a/test.py b/test.py @@ -4,6 +4,16 @@ import unittest import nacl +class RandomTestCase(unittest.TestCase): + def test_random_10(self): + r = nacl.randombytes(10) + self.assertEqual(len(r), 10) + s = nacl.randombytes(10) + self.assertNotEqual(r, s) + + def test_random_1000(self): + r = nacl.randombytes(1000) + self.assertEqual(len(r), 1000) class HashTestCase(unittest.TestCase): @@ -35,24 +45,56 @@ class HashTestCase(unittest.TestCase): "90cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3" "c463d481c7e586c39ac1ed") +class BoxTestCase(unittest.TestCase): + msg = "The quick brown fox jumps over the lazy dog." + def setUp(self): + self.pk1, self.sk1 = nacl.crypto_box_keypair() + self.pk2, self.sk2 = nacl.crypto_box_keypair() + + def test_key_sizes(self): + self.assertEqual(len(self.pk1), nacl.crypto_box_PUBLICKEYBYTES) + self.assertEqual(len(self.sk1), nacl.crypto_box_SECRETKEYBYTES) + + def test_box(self): + nonce = nacl.randombytes(nacl.crypto_box_NONCEBYTES) + c = nacl.crypto_box(self.msg, nonce, self.pk2, self.sk1) + m = nacl.crypto_box_open(c, nonce, self.pk1, self.sk2) + self.assertEqual(m, self.msg) + + def test_box_badsig(self): + nonce = nacl.randombytes(nacl.crypto_box_NONCEBYTES) + c = nacl.crypto_box(self.msg, nonce, self.pk1, self.sk2) + c1 = c[:-1] + chr((ord(c[-1]) + 1) % 256) + self.assertRaises(ValueError, nacl.crypto_box_open, c1, nonce, self.pk2, + self.sk1) + class SignTestCase(unittest.TestCase): msg = "hello world" def setUp(self): self.pk, self.sk = nacl.crypto_sign_keypair() - self.pk1, self.sk1 = nacl.crypto_sign_keypair() + self.pk1, self.sk1 = nacl.crypto_sign_keypair_fromseed("hello world") + + def test_keys_different(self): self.assertNotEqual(self.pk, self.pk1) self.assertNotEqual(self.sk, self.sk1) self.assertNotEqual(self.pk, self.sk) self.assertNotEqual(self.pk1, self.sk1) - def test_pk_length(self): + def test_key_length(self): self.assertEqual(len(self.pk), nacl.crypto_sign_PUBLICKEYBYTES) - - def test_sk_length(self): self.assertEqual(len(self.sk), nacl.crypto_sign_SECRETKEYBYTES) + def test_seed(self): + self.assertEqual(binascii.b2a_hex(self.pk1), + "683d8d0458ef6ec4cfef25157f5d88ce7a0bba334fd102fafc7e" + "2751410d5718") + self.assertEqual(binascii.b2a_hex(self.sk1), + "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd" + "3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f" + "605dcf7dc5542e93ae9cd76f") + def test_signature(self): sm = nacl.crypto_sign(self.msg, self.sk) r = nacl.crypto_sign_open(sm, self.pk)