--- old-from_zaula_new_and_improved/pycryptopp/publickey/ecdsamodule.cpp 2009-03-02 14:23:06.000000000 -0700 +++ new-from_zaula_new_and_improved/pycryptopp/publickey/ecdsamodule.cpp 2009-03-02 14:23:09.000000000 -0700 @@ -1,9 +1,12 @@ /** * ecdsamodule.cpp -- Python wrappers around Crypto++'s - * ECDSA(1363)/EMSA1(SHA-256), more precisely: ECDSA with GF(P) - * ("ECP") as the elliptic curve group parameters and SHA-256 as the hash - * function + * ECDSA(1363)/EMSA1(Tiger) -- ECDSA. + * + * The keys (192-bit) use the curve ASN1::secp192r1() and Tiger as the hash + * function. There is a custom Key Derivation Protocol to generate private + * (signing) keys from unguessable seeds -- see source code for details and + * doc string for usage. */ #include @@ -19,37 +22,105 @@ #include "osrng.h" #include "eccrypto.h" #include "oids.h" +#include "tiger.h" +#include "sha.h" +#include "pubkey.h" + +// for _dump +#include +#include "ecp.h" +#include "hex.h" + +/* The ECDSA key size that pycryptopp currently supports -- you should do your + own research, and I recommend http://keylength.com , but basically this is + probably secure for most purposes for at least the next few years, and + possibly for longer. */ +static const int SMALL_KEY_SIZE_BITS=192; USING_NAMESPACE(CryptoPP) PyDoc_STRVAR(ecdsa__doc__, -"ecdsa -- ECDSA(1363)/EMSA1(SHA-256) signatures\n\ +"ecdsa -- ECDSA(1363)/EMSA1(Tiger) signatures\n\ +\n\ +To create a new ECDSA signing key (deterministically from a 12-byte seed), construct an instance of the class, passing the seed as argument, i.e. SigningKey(seed). If you call serialize() on that instance, you'll get that seed back.\n\ \n\ -To create a new ECDSA signing key from the operating system's random number generator, call generate().\n\ -To deserialize an ECDSA signing key from a string, call create_signing_key_from_string().\n\ +To get a verifying key from a signing key, call get_verifying_key() on the signing key instance.\n\ \n\ -To get an ECDSA verifying key from an ECDSA signing key, call get_verifying_key() on the signing key.\n\ -To deserialize an ECDSA verifying key from a string, call create_verifying_key_from_string()."); +To deserialize an ECDSA verifying key from a string, call VerifyingKey(serialized_verifying_key)."); static PyObject *ecdsa_error; typedef struct { PyObject_HEAD - - /* internal */ - ECDSA::Verifier *k; + ECDSA::Verifier k; } VerifyingKey; PyDoc_STRVAR(VerifyingKey__doc__, "an ECDSA verifying key"); +struct VerifyingKey_type; +static PyObject* +VerifyingKey_alloc(PyTypeObject* typ, Py_ssize_t nitems) { + VerifyingKey* k = new VerifyingKey(); + if (!k) + return PyErr_NoMemory(); + + memset(k, 0, sizeof(PyObject)); + k->ob_refcnt = 1; + k->ob_type = typ; + + return reinterpret_cast(k); +} + static void -VerifyingKey_dealloc(VerifyingKey* self) { - if (self->k) - delete self->k; - self->ob_type->tp_free((PyObject*)self); +VerifyingKey_free(void* self) { + delete reinterpret_cast(self); +} + +static PyObject * +VerifyingKey__dump(VerifyingKey *self, PyObject *dummy) { + const CryptoMaterial& x = self->k.GetMaterial(); + std::cout << x.GetValueNames(); + Py_RETURN_NONE; +} + +static int +VerifyingKey___init__(PyObject* self, PyObject* args, PyObject* kwdict) { + static const char *kwlist[] = { "serializedverifyingkey", NULL }; + const char *serializedverifyingkey; + Py_ssize_t serializedverifyingkeysize = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "t#:VerifyingKey__init__", const_cast(kwlist), &serializedverifyingkey, &serializedverifyingkeysize)) + return NULL; + assert (serializedverifyingkeysize >= 0); + + if (serializedverifyingkeysize != 25) { + PyErr_Format(ecdsa_error, "Precondition violation: size in bits is required to be %d (for %d-bit key), but it was %d", 25, SMALL_KEY_SIZE_BITS, serializedverifyingkeysize); + return -1; + } + + VerifyingKey *mself = reinterpret_cast(self); + + StringSource ss(reinterpret_cast(serializedverifyingkey), serializedverifyingkeysize, true); + + ECP::Element element; + DL_GroupParameters_EC params(ASN1::secp192r1()); + params.SetPointCompression(true); + try { + element = params.DecodeElement(reinterpret_cast(serializedverifyingkey), true); + mself->k = ECDSA::Verifier(params, element); + } catch (InvalidDataFormat le) { + PyErr_Format(ecdsa_error, "Serialized verifying key was corrupted. Crypto++ gave this exception: %s", le.what()); + return -1; + } + + return 0; } +PyDoc_STRVAR(VerifyingKey__dump__doc__, + +"Print to stdout some descriptions of the math pieces."); + static PyObject * VerifyingKey_verify(VerifyingKey *self, PyObject *args, PyObject *kwdict) { static const char *kwlist[] = { "msg", "signature", NULL }; @@ -62,14 +133,7 @@ assert (msgsize >= 0); assert (signaturesize >= 0); - Py_ssize_t sigsize = self->k->SignatureLength(); - if (sigsize != signaturesize) - return PyErr_Format(ecdsa_error, "Precondition violation: signatures are required to be of size %zu, but it was %zu", sigsize, signaturesize); - assert (sigsize >= 0); - - assert (signaturesize == sigsize); - - if (self->k->VerifyMessage(reinterpret_cast(msg), msgsize, reinterpret_cast(signature), signaturesize)) + if (self->k.VerifyMessage(reinterpret_cast(msg), msgsize, reinterpret_cast(signature), signaturesize)) Py_RETURN_TRUE; else Py_RETURN_FALSE; @@ -80,14 +144,17 @@ static PyObject * VerifyingKey_serialize(VerifyingKey *self, PyObject *dummy) { - std::string outstr; - StringSink ss(outstr); - self->k->DEREncode(ss); - PyStringObject* result = reinterpret_cast(PyString_FromStringAndSize(outstr.c_str(), outstr.size())); - if (!result) - return NULL; + const DL_PublicKey_EC* pubkey; + pubkey = dynamic_cast*>(&(self->k.GetPublicKey())); + const DL_GroupParameters_EC& params = pubkey->GetGroupParameters(); - return reinterpret_cast(result); + Py_ssize_t len = params.GetEncodedElementSize(true); +// params.SetPointCompression(true); + PyObject* result = PyString_FromStringAndSize(NULL, len); + + params.EncodeElement(true, pubkey->GetPublicElement(), reinterpret_cast(PyString_AS_STRING(result))); + + return result; } PyDoc_STRVAR(VerifyingKey_serialize__doc__, @@ -97,6 +164,7 @@ static PyMethodDef VerifyingKey_methods[] = { {"verify", reinterpret_cast(VerifyingKey_verify), METH_KEYWORDS, VerifyingKey_verify__doc__}, {"serialize", reinterpret_cast(VerifyingKey_serialize), METH_NOARGS, VerifyingKey_serialize__doc__}, + {"_dump", reinterpret_cast(VerifyingKey__dump), METH_NOARGS, VerifyingKey__dump__doc__}, {NULL}, }; @@ -106,7 +174,7 @@ "ecdsa.VerifyingKey", /*tp_name*/ sizeof(VerifyingKey), /*tp_basicsize*/ 0, /*tp_itemsize*/ - reinterpret_cast(VerifyingKey_dealloc), /*tp_dealloc*/ + 0, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ @@ -123,42 +191,179 @@ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ VerifyingKey__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ VerifyingKey_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + VerifyingKey___init__, /* tp_init */ + VerifyingKey_alloc, /* tp_alloc */ + 0, /* tp_new */ + VerifyingKey_free /* tp_free */ }; -/** This function is only for internal use by ecdsamodule.cpp. */ -static VerifyingKey* -VerifyingKey_construct() { - VerifyingKey *self = reinterpret_cast(VerifyingKey_type.tp_alloc(&VerifyingKey_type, 0)); - if (!self) - return NULL; - self->k = NULL; - return self; -} - PyDoc_STRVAR(SigningKey__doc__, "an ECDSA signing key"); typedef struct { PyObject_HEAD - - /* internal */ - ECDSA::Signer *k; + ECDSA::Signer k; } SigningKey; +struct SigningKey_type; +static PyObject* +SigningKey_alloc(PyTypeObject* typ, Py_ssize_t nitems) { + SigningKey* k = new SigningKey(); + if (!k) + return PyErr_NoMemory(); + + memset(k, 0, sizeof(PyObject)); + k->ob_refcnt = 1; + k->ob_type = typ; + + return reinterpret_cast(k); +} + static void -SigningKey_dealloc(SigningKey* self) { - if (self->k) - delete self->k; - self->ob_type->tp_free((PyObject*)self); +SigningKey_free(void* self) { + delete reinterpret_cast(self); } +static const char* TAG_AND_SALT = "102:pycryptopp v0.5.3 key derivation algorithm using Tiger hash to generate ECDSA 192-bit secret exponents," \ + "16:H1yGNvUONoc0FD1d,"; +static const size_t TAG_AND_SALT_len = 127; + +static int +SigningKey___init__(PyObject* self, PyObject* args, PyObject* kwdict) { + static const char *kwlist[] = { "seed", NULL }; + const char* seed; + int seedlen; + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "t#:SigningKey___init__", const_cast(kwlist), &seed, &seedlen)) { + return -1; + } + + if (seedlen != 12) { + PyErr_Format(ecdsa_error, "Precondition violation: seed is required to be of length 12, but it was %d", seedlen); + return -1; + } + + OID curve; + Integer grouporderm1; + byte privexpbytes[24] = {0}; + Integer privexponentm1; + privexponentm1.Decode(privexpbytes, sizeof(privexpbytes)); assert (priveexponentm1 == 0); // just checking.. + + curve = ASN1::secp192r1(); + grouporderm1 = DL_GroupParameters_EC(curve).GetGroupOrder() - 1; + Tiger t; + + t.Update(reinterpret_cast(TAG_AND_SALT), TAG_AND_SALT_len); + t.Update(reinterpret_cast(seed), seedlen); + t.TruncatedFinal(privexpbytes, Tiger::DIGESTSIZE); + privexponentm1.Decode(privexpbytes, sizeof(privexpbytes)); + + while (privexponentm1 >= grouporderm1) { + Tiger t2; + t2.Update(reinterpret_cast(TAG_AND_SALT), TAG_AND_SALT_len); + std::cerr << "WHEE " << sizeof(privexpbytes) << "\n";std::cerr.flush(); + t2.Update(privexpbytes, sizeof(privexpbytes)); + t2.TruncatedFinal(privexpbytes, Tiger::DIGESTSIZE); + privexponentm1.Decode(privexpbytes, sizeof(privexpbytes)); + } + + SigningKey* mself = reinterpret_cast(self); + mself->k.AccessKey().Initialize(curve, privexponentm1+1); + + return 0; +} + +PyDoc_STRVAR(SigningKey__init____doc__, +"Create a signing key (192 bits) deterministically from the given seed.\n\ +\n\ +This implies that if someone can guess the seed then they can learn the signing key. A good way to get an unguessable seed is os.urandom(12).\n\ +\n\ +@param seed seed\n\ +\n\ +@precondition len(seed) >= ceil(sizeinbits/16.0)"); + +static PyObject * +SigningKey__dump(SigningKey *self, PyObject *dummy) { + const DL_GroupParameters_EC& gp = self->k.GetKey().GetGroupParameters(); + std::cout << "whee " << gp.GetEncodedElementSize(true) << "\a"; + std::cout << "booo " << gp.GetEncodedElementSize(false) << "\n"; + + ECPPoint p = gp.GetSubgroupGenerator(); + std::cout << "generator " << p.x << ", " << p.y << "\n"; + + std::cout << "GroupOrder: "; + std::cout << gp.GetGroupOrder(); + std::cout << "\n"; + + std::string s; + StringSink* ss = new StringSink(s); + HexEncoder he(ss); + std::cout << "AlgorithmID: "; + gp.GetAlgorithmID().DEREncode(he); + std::cout << s << "\n"; + + const ECP& ec = gp.GetCurve(); + Integer fieldsize = ec.FieldSize(); + std::cout << "field size " << fieldsize.BitCount() << " " << fieldsize.ByteCount() << " " << ec.FieldSize() << "\n"; + std::cout << "Curve: "; + std::cout << "curve field max element bit length: " << ec.GetField().MaxElementBitLength() << "\n"; + std::cout << "curve field modulus: " << ec.GetField().GetModulus() << "\n"; + std::cout << "curve A: " << ec.GetA() << ", curve B: " << ec.GetB(); + + const ECP::Field& f = ec.GetField(); + std::cout << "curve field modulus: " << f.GetModulus() << "\n"; + std::cout << "curve field identity: " << f.Identity() << "\n"; + + std::string cfs; + StringSink* cfss = new StringSink(cfs); + HexEncoder cfhe(cfss); + f.DEREncode(cfhe); + std::cout << "curve field derencoding: " << cfs << "\n"; + + const CryptoMaterial& cm = self->k.GetMaterial(); + Integer i; + cm.GetValue("SubgroupOrder", i); + std::cout << "\n"; + std::cout << "SubgroupOrder: "; + std::cout << i; + std::cout << "\n"; + ECP::Element e; + cm.GetValue("SubgroupGenerator", e); + std::cout << "SubgroupGenerator: "; + std::cout << e.x << ", " << e.y; + std::cout << "\n"; + + std::cout << "private key: "; + + const PrivateKey& privkey = self->k.GetPrivateKey(); + + std::cout << privkey.GetValueNames() << "\n"; + + Integer privi; + privkey.GetValue("PrivateExponent", privi); + std::cout << privi << "\n"; + std::cout << "numbits: " << privi.BitCount() << "\n"; + std::cout << "numbytes: " << privi.ByteCount() << "\n"; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(SigningKey__dump__doc__, +"Print to stdout some descriptions of the math pieces."); + static PyObject * SigningKey_sign(SigningKey *self, PyObject *msgobj) { const char *msg; @@ -166,22 +371,32 @@ PyString_AsStringAndSize(msgobj, const_cast(&msg), reinterpret_cast(&msgsize)); assert (msgsize >= 0); - Py_ssize_t sigsize = self->k->SignatureLength(); + Py_ssize_t sigsize; + sigsize = self->k.SignatureLength(); + PyStringObject* result = reinterpret_cast(PyString_FromStringAndSize(NULL, sigsize)); if (!result) return NULL; assert (sigsize >= 0); AutoSeededRandomPool randpool(false); - Py_ssize_t siglengthwritten = self->k->SignMessage( - randpool, - reinterpret_cast(msg), - msgsize, - reinterpret_cast(PyString_AS_STRING(result))); + + Py_ssize_t siglengthwritten; + try { + siglengthwritten = self->k.SignMessage( + randpool, + reinterpret_cast(msg), + msgsize, + reinterpret_cast(PyString_AS_STRING(result))); + } catch (InvalidDataFormat le) { + Py_DECREF(result); + return PyErr_Format(ecdsa_error, "Signing key was corrupted. Crypto++ gave this exception: %s", le.what()); + } + if (siglengthwritten < sigsize) fprintf(stderr, "%s: %d: %s: %s", __FILE__, __LINE__, "SigningKey_sign", "INTERNAL ERROR: signature was shorter than expected."); else if (siglengthwritten > sigsize) { - fprintf(stderr, "%s: %d: %s: %s", __FILE__, __LINE__, "SigningKey_sign", "INTERNAL ERROR: signature was longer than expected, so invalid memory was overwritten."); + fprintf(stderr, "%s: %d: %s: %s", __FILE__, __LINE__, "SigningKey_sign", "INTERNAL ERROR: signature was longer than expected, so memory was invalidly overwritten."); abort(); } assert (siglengthwritten >= 0); @@ -190,43 +405,30 @@ } PyDoc_STRVAR(SigningKey_sign__doc__, -"Return a signature on the argument."); + "Return a signature on the argument."); //XXX If randseed is not None then it is required to be an "); // XXX randseed! static PyObject * SigningKey_get_verifying_key(SigningKey *self, PyObject *dummy) { - VerifyingKey *verifier = reinterpret_cast(VerifyingKey_construct()); + VerifyingKey *verifier = PyObject_New(VerifyingKey, &VerifyingKey_type); + if (!verifier) return NULL; - verifier->k = new ECDSA::Verifier(*(self->k)); - if (!verifier->k) - return PyErr_NoMemory(); + ECDSA::Verifier* kp; + kp = new ECDSA::Verifier(self->k); + + verifier->k = (*kp); + return reinterpret_cast(verifier); } PyDoc_STRVAR(SigningKey_get_verifying_key__doc__, "Return the corresponding verifying key."); -static PyObject * -SigningKey_serialize(SigningKey *self, PyObject *dummy) { - Py_ssize_t len = self->k->GetKey().GetGroupParameters().GetSubgroupOrder().ByteCount(); - PyObject* result = PyString_FromStringAndSize(NULL, len); - - const DL_PrivateKey_EC& privkey = dynamic_cast&>(self->k->GetPrivateKey()); - - privkey.GetPrivateExponent().Encode(reinterpret_cast(PyString_AS_STRING(result)), len); - - return result; -} - -PyDoc_STRVAR(SigningKey_serialize__doc__, -"Return a string containing the key material. The string can be passed to \n\ -create_signing_key_from_string() to instantiate a new copy of this key."); - static PyMethodDef SigningKey_methods[] = { {"sign", reinterpret_cast(SigningKey_sign), METH_O, SigningKey_sign__doc__}, + {"_dump", reinterpret_cast(SigningKey__dump), METH_NOARGS, SigningKey__dump__doc__}, {"get_verifying_key", reinterpret_cast(SigningKey_get_verifying_key), METH_NOARGS, SigningKey_get_verifying_key__doc__}, - {"serialize", reinterpret_cast(SigningKey_serialize), METH_NOARGS, SigningKey_serialize__doc__}, {NULL}, }; @@ -236,7 +438,7 @@ "ecdsa.SigningKey", /*tp_name*/ sizeof(SigningKey), /*tp_basicsize*/ 0, /*tp_itemsize*/ - (destructor)SigningKey_dealloc, /*tp_dealloc*/ + 0, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ @@ -251,143 +453,34 @@ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - SigningKey__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - SigningKey_methods /* tp_methods */ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + SigningKey__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + SigningKey_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + SigningKey___init__, /* tp_init */ + SigningKey_alloc, /* tp_alloc */ + 0, /* tp_new */ + SigningKey_free /* tp_free */ }; -/** This function is only for internal use by ecdsamodule.cpp. */ -static SigningKey* -SigningKey_construct() { - SigningKey *self = reinterpret_cast(SigningKey_type.tp_alloc(&SigningKey_type, 0)); - if (!self) - return NULL; - self->k = NULL; - return self; -} - -/* The smaller ECDSA key size that pycryptopp supports -- you should do your - own research, and I recommend http://keylength.com , but basically this is - probably secure for most purposes for at least the next few years, and - possibly for longer. */ -static const int SMALL_KEY_SIZE_BITS=192; - -/* The larger ECDSA key size that pycryptopp supports -- you should do your - own research, and I recommend http://keylength.com , but basically this is - probably secure for many years, unless there is a surprising breakthrough in - the theory of elliptic curve cryptography. */ -static const int LARGE_KEY_SIZE_BITS=521; - -static PyObject * -generate(PyObject *dummy, PyObject *args, PyObject *kwdict) { - static const char *kwlist[] = { - "sizeinbits", - NULL - }; - int sizeinbits; - - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "i:generate", const_cast(kwlist), &sizeinbits)) - return NULL; - - if (sizeinbits != SMALL_KEY_SIZE_BITS && sizeinbits != LARGE_KEY_SIZE_BITS) - return PyErr_Format(ecdsa_error, "Precondition violation: size in bits is required to be either %d or %d, but it was %d", SMALL_KEY_SIZE_BITS, LARGE_KEY_SIZE_BITS, sizeinbits); - - AutoSeededRandomPool osrng(false); - SigningKey *signer = SigningKey_construct(); - if (!signer) - return NULL; - - OID curve; - if (sizeinbits == 192) - curve = ASN1::secp192r1(); - else - curve = ASN1::secp521r1(); - - signer->k = new ECDSA::Signer(osrng, curve); - if (!signer->k) - return PyErr_NoMemory(); - return reinterpret_cast(signer); -} - -PyDoc_STRVAR(generate__doc__, -"Create a signing key using the operating system's random number generator.\n\ -\n\ -@param sizeinbits size of the key in bits\n\ -\n\ -@precondition sizeinbits in (192, 521)"); - -static PyObject * -create_verifying_key_from_string(PyObject *dummy, PyObject *args, PyObject *kwdict) { - static const char *kwlist[] = { - "serializedverifyingkey", - NULL - }; - const char *serializedverifyingkey; - Py_ssize_t serializedverifyingkeysize = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "t#:create_verifying_key_from_string", const_cast(kwlist), &serializedverifyingkey, &serializedverifyingkeysize)) - return NULL; - assert (serializedverifyingkeysize >= 0); - - VerifyingKey *verifier = reinterpret_cast(VerifyingKey_construct()); - if (!verifier) - return NULL; - StringSource ss(reinterpret_cast(serializedverifyingkey), serializedverifyingkeysize, true); - - verifier->k = new ECDSA::Verifier(ss); - if (!verifier->k) - return PyErr_NoMemory(); - return reinterpret_cast(verifier); -} - PyDoc_STRVAR(create_verifying_key_from_string__doc__, -"Create a verifying key from its serialized state."); - -static PyObject * -create_signing_key_from_string(PyObject *dummy, PyObject *args, PyObject *kwdict) { - static const char *kwlist[] = { - "serializedsigningkey", - NULL - }; - const char *serializedsigningkey; - Py_ssize_t serializedsigningkeysize = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "t#:create_signing_key_from_string", const_cast(kwlist), &serializedsigningkey, &serializedsigningkeysize)) - return NULL; - if (serializedsigningkeysize != 24 && serializedsigningkeysize != 66) - return PyErr_Format(ecdsa_error, "Precondition violation: size in bytes of the serialized signing key is required to be either %d (for %d-bit keys) or %d (for %d-bit keys), but it was %d", 24, SMALL_KEY_SIZE_BITS, 66, LARGE_KEY_SIZE_BITS, serializedsigningkeysize); - - - SigningKey *verifier = SigningKey_construct(); - if (!verifier) - return NULL; - - OID curve; - if (serializedsigningkeysize == 24) - curve = ASN1::secp192r1(); - else - curve = ASN1::secp521r1(); - Integer privexponent(reinterpret_cast(serializedsigningkey), serializedsigningkeysize); - - verifier->k = new ECDSA::Signer(curve, privexponent); - if (!verifier->k) - return PyErr_NoMemory(); - return reinterpret_cast(verifier); -} - -PyDoc_STRVAR(create_signing_key_from_string__doc__, -"Create a signing key from its serialized state."); +"Create a verifying key from its serialized state.\n\ +\n\ +@precondition Length of serialized key is required to be 24 (for 192-bit key)."); //XXX actually 25 length static PyMethodDef ecdsa_functions[] = { - {"generate", reinterpret_cast(generate), METH_KEYWORDS, generate__doc__}, - {"create_verifying_key_from_string", reinterpret_cast(create_verifying_key_from_string), METH_KEYWORDS, create_verifying_key_from_string__doc__}, - {"create_signing_key_from_string", reinterpret_cast(create_signing_key_from_string), METH_KEYWORDS, create_signing_key_from_string__doc__}, {NULL, NULL, 0, NULL} /* sentinel */ }; @@ -399,8 +492,10 @@ PyObject *module; PyObject *module_dict; + VerifyingKey_type.tp_new = PyType_GenericNew; if (PyType_Ready(&VerifyingKey_type) < 0) return; + SigningKey_type.tp_new = PyType_GenericNew; if (PyType_Ready(&SigningKey_type) < 0) return; diff -rN -u old-from_zaula_new_and_improved/pycryptopp/test/test_ecdsa.py new-from_zaula_new_and_improved/pycryptopp/test/test_ecdsa.py --- old-from_zaula_new_and_improved/pycryptopp/test/test_ecdsa.py 2009-03-02 14:23:06.000000000 -0700 +++ new-from_zaula_new_and_improved/pycryptopp/test/test_ecdsa.py 2009-03-02 14:23:09.000000000 -0700 @@ -2,10 +2,39 @@ import random +import os +SEED = os.environ.get('REPEATABLE_RANDOMNESS_SEED', None) + +if SEED is None: + # Generate a seed which is fairly short (to ease cut-and-paste, writing it + # down, etc.). Note that Python's random module's seed() function is going + # to take the hash() of this seed, which is a 32-bit value (currently) so + # there is no point in making this seed larger than 32 bits. Make it 30 + # bits, which conveniently fits into six base-32 chars. Include a separator + # because chunking facilitates memory (including working and short-term + # memory) in humans. + chars = "ybndrfg8ejkmcpqxot1uwisza345h769" # Zooko's choice, rationale in "DESIGN" doc in z-base-32 project + SEED = ''.join([random.choice(chars) for x in range(3)] + ['-'] + [random.choice(chars) for x in range(3)]) + +import logging +logging.info("REPEATABLE_RANDOMNESS_SEED: %s\n" % SEED) +logging.info("In order to reproduce this run of the code, set the environment variable \"REPEATABLE_RANDOMNESS_SEED\" to %s before executing.\n" % SEED) +random.seed(SEED) + +def seed_which_refuses(a): + logging.warn("I refuse to reseed to %s -- I already seeded with %s.\n" % (a, SEED,)) + return +random.seed = seed_which_refuses + +from random import randrange + import unittest from pycryptopp.publickey import ecdsa +def randstr(n, rr=randrange): + return ''.join([chr(rr(0, 256)) for x in xrange(n)]) + from base64 import b32encode def ab(x): # debuggery if len(x) >= 3: @@ -17,102 +46,186 @@ elif len(x) == 0: return "%s:%s" % (len(x), "--empty--",) -def randstr(n): - return ''.join(map(chr, map(random.randrange, [0]*n, [256]*n))) +def div_ceil(n, d): + """ + The smallest integer k such that k*d >= n. + """ + return (n/d) + (n%d != 0) + +KEYBITS=192 + +# The number of bytes required for a seed to have the same security level as a +# key in this elliptic curve: 2 bits of public key per bit of security. +SEEDBITS=div_ceil(192, 2) +SEEDBYTES=div_ceil(SEEDBITS, 8) + +# The number of bytes required to encode a public key in this elliptic curve. +PUBKEYBYTES=div_ceil(KEYBITS, 8)+1 # 1 byte for the sign of the y component + +# The number of bytes requires to encode a signature in this elliptic curve. +SIGBITS=KEYBITS*2 +SIGBYTES=div_ceil(SIGBITS, 8) -KEYSIZE=192 # The choices are 192 or 521 -- they are both secure, and 192 makes for faster unit tests. class Signer(unittest.TestCase): - def test_generate_bad_size(self): + def test_construct_from_same_seed_is_reproducible(self): + seed = randstr(SEEDBYTES) + signer1 = ecdsa.SigningKey(seed) + self.failUnlessEqual(signer1.serialize(), seed) + signer2 = ecdsa.SigningKey(seed) + self.failUnlessEqual(signer1.serialize(), signer2.serialize()) + self.failUnlessEqual(signer1.get_verifying_key().serialize(), signer2.get_verifying_key().serialize()) + + # ... and using different seeds constructs a different private key. + seed3 = randstr(SEEDBYTES) + assert seed3 != seed, "Internal error in Python random module's PRNG (or in pycryptopp's hacks to it to facilitate testing) -- got two identical strings from randstr(%s)" % SEEDBYTES + signer3 = ecdsa.SigningKey(seed3) + self.failUnlessEqual(signer3.serialize(), seed3) + self.failUnlessEqual(signer1.serialize(), signer3.serialize()) + self.failIfEqual(signer1.get_verifying_key().serialize(), signer3.get_verifying_key().serialize()) + + # Also try the all-zeroes string just because bugs sometimes are + # data-dependent on zero or cause bogus zeroes. + seed4 = '\x00'*SEEDBYTES + assert seed4 != seed, "Internal error in Python random module's PRNG (or in pycryptopp's hacks to it to facilitate testing) -- got the all-zeroes string from randstr(%s)" % SEEDBYTES + signer4 = ecdsa.SigningKey(seed4) + self.failUnlessEqual(signer4.serialize(), seed4) + self.failUnlessEqual(signer4.serialize(), signer1.serialize()) + self.failIfEqual(signer4.get_verifying_key().serialize(), signer1.get_verifying_key().serialize()) + + signer5 = ecdsa.SigningKey(seed4) + self.failUnlessEqual(signer5.serialize(), seed4) + self.failUnlessEqual(signer5.serialize(), signer4.serialize()) + self.failUnlessEqual(signer5.get_verifying_key().serialize(), signer4.get_verifying_key().serialize()) + + def test_construct_short_seed(self): try: - signer = ecdsa.generate(KEYSIZE-1) + signer = ecdsa.SigningKey("\x00\x00\x00") except ecdsa.Error, le: - self.failUnless("size in bits is required to be " in str(le), le) + self.failUnless("seed is required to be of length >=" in str(le), le) else: - self.fail("Should have raised error from size being too small.") + self.fail("Should have raised error from seed being too short.") + + def test_construct_bad_arg_type(self): try: - signer = ecdsa.generate(sizeinbits=KEYSIZE-1) - except ecdsa.Error, le: - self.failUnless("size in bits is required to be " in str(le), le) + signer = ecdsa.SigningKey(1) + except TypeError, le: + self.failUnless("must be string" in str(le), le) else: - self.fail("Should have raised error from size being too small.") + self.fail("Should have raised error from seed being of the wrong type.") - def test_generate(self): - signer = ecdsa.generate(KEYSIZE) - # Hooray! It didn't raise an exception! We win! - signer = ecdsa.generate(sizeinbits=KEYSIZE) - # Hooray! It didn't raise an exception! We win! - - def test_sign(self): - signer = ecdsa.generate(KEYSIZE) - result = signer.sign("abc") - self.failUnlessEqual(len(result), 2*((KEYSIZE+7)/8)) - # TODO: test against someone's official test vectors. +class Verifier(unittest.TestCase): + def test_from_signer_and_serialize_and_deserialize(self): + seed = randstr(SEEDBYTES) + signer = ecdsa.SigningKey(seed) + + verifier = signer.get_verifying_key() + s1 = verifier.serialize() + self.failUnlessEqual(len(s1), PUBKEYBYTES) + verifier2 = ecdsa.create_verifying_key_from_string(s1) + s2 = verifier.serialize() + self.failUnlessEqual(s1, s2) + +def flip_one_bit(s): + i = randrange(0, len(s)) + result = s[:i] + chr(ord(s[i])^(0x01<