/**
* Things to do:
* Make it work and pass tests.
* compressed pub keys -- check out Wei Dai's example code on mailinglist as linked to from pycryptopp trac by Brian
* Make new KDF (standard, Crypto++-compatible).
* in C++
* in Python
* use Crypto++ Randomize()'s
* provide RNG class which is P1363-SHA-256
* Profit!
* Migrate pair-programming to Bespin.
* Put a Tahoe backend under Bespin.
*/
/**
* ecdsamodule.cpp -- Python wrappers around Crypto++'s
* ECDSA(1363)/EMSA1(SHA-256) -- ECDSA.
*
* The keys (192-bit) use the curve ASN1::secp192r1() and SHA-256 as the
* hash function. The Key Derivation Protocol is P1363_KDF2
* http://www.users.zetnet.co.uk/hopwood/crypto/scan/prf.html#KDF2
* to generate private (signing) keys from unguessable seeds -- see
* source code for details and doc string for usage.
*/
#define PY_SSIZE_T_CLEAN
#include
#if (PY_VERSION_HEX < 0x02050000)
typedef int Py_ssize_t;
#endif
#include
#include "ecdsamodule.hpp"
/* from Crypto++ */
#ifdef DISABLE_EMBEDDED_CRYPTOPP
#include
#include
#include
#include
#include
#include
#include
// only needed for debugging -- the _dump() function
#include
#include
#include
#else
#include
#include
#include
#include
#include
#include
#include
// only needed for debugging -- the _dump() function
#include
#include
#include
#endif
static const int KEY_SIZE_BITS=192;
USING_NAMESPACE(CryptoPP)
static const char*const ecdsa___doc__ = "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).\n\
\n\
To get a verifying key from a signing key, call get_verifying_key() on the signing key instance.\n\
\n\
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;
} VerifyingKey;
PyDoc_STRVAR(VerifyingKey__doc__,
"an ECDSA verifying key");
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 %Zd", 25, 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 = new ECDSA::Verifier(params, element);
if (!mself->k) {
PyErr_NoMemory();
return -1;
}
} catch (InvalidDataFormat le) {
PyErr_Format(ecdsa_error, "Serialized verifying key was corrupted. Crypto++ gave this exception: %s", le.what());
return -1;
}
return 0;
}
static void
VerifyingKey_dealloc(VerifyingKey* self) {
if (self->k)
delete self->k;
self->ob_type->tp_free((PyObject*)self);
}
static PyObject *
VerifyingKey_verify(VerifyingKey *self, PyObject *args, PyObject *kwdict) {
static const char *kwlist[] = { "msg", "signature", NULL };
const char *msg;
Py_ssize_t msgsize;
const char *signature;
Py_ssize_t signaturesize = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwdict, "t#t#:verify", const_cast(kwlist), &msg, &msgsize, &signature, &signaturesize))
return NULL;
assert (msgsize >= 0);
assert (signaturesize >= 0);
if (self->k->VerifyMessage(reinterpret_cast(msg), msgsize, reinterpret_cast(signature), signaturesize))
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
PyDoc_STRVAR(VerifyingKey_verify__doc__,
"Return whether the signature is a valid signature on the msg.");
static PyObject *
VerifyingKey_serialize(VerifyingKey *self, PyObject *dummy) {
ECDSA::Verifier *pubkey;
pubkey = new ECDSA::Verifier(*(self->k));
const DL_GroupParameters_EC& params = pubkey->GetKey().GetGroupParameters();
Py_ssize_t len = params.GetEncodedElementSize(true);
PyObject* result = PyString_FromStringAndSize(NULL, len);
if (!result)
return NULL;
params.EncodeElement(true, pubkey->GetKey().GetPublicElement(),
reinterpret_cast(PyString_AS_STRING(result)));
return result;
}
PyDoc_STRVAR(VerifyingKey_serialize__doc__,
"Return a string containing the key material. The string can be passed to \n\
the constructor of VerifyingKey to instantiate a new copy of this key.");
static PyMethodDef VerifyingKey_methods[] = {
{"verify", reinterpret_cast(VerifyingKey_verify), METH_KEYWORDS, VerifyingKey_verify__doc__},
{"serialize", reinterpret_cast(VerifyingKey_serialize), METH_NOARGS, VerifyingKey_serialize__doc__},
{NULL},
};
static PyTypeObject VerifyingKey_type = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"ecdsa.VerifyingKey", /*tp_name*/
sizeof(VerifyingKey), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)VerifyingKey_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
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 */
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 */
};
typedef struct {
PyObject_HEAD
/* internal */
ECDSA::Signer *k;
} SigningKey;
static void
SigningKey_dealloc(SigningKey* self) {
if (self->k)
delete self->k;
self->ob_type->tp_free((PyObject*)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;
/** copied from Crypto++'s integer.cpp */
/** The following is in Crypto++'s integer.cpp and we use them:
* void Integer::Randomize(RandomNumberGenerator &rng, size_t nbits)
* {
* const size_t nbytes = nbits/8 + 1;
* SecByteBlock buf(nbytes);
* rng.GenerateBlock(buf, nbytes);
* if (nbytes)
* buf[0] = (byte)Crop(buf[0], nbits % 8);
* Decode(buf, nbytes, UNSIGNED);
* }
* void Integer::Randomize(RandomNumberGenerator &rng, const Integer &min, const Integer &max)
* {
* if (min > max)
* throw InvalidArgument("Integer: Min must be no greater than Max");
*
* Integer range = max - min;
* const unsigned int nbits = range.BitCount();
*
* do
* {
* Randomize(rng, nbits);
* }
* while (*this > range);
*
* *this += min;
* }
*
*/
static int
SigningKey___init__(PyObject* self, PyObject* args, PyObject* kwdict) {
static const char *kwlist[] = { "seed", NULL };
const char* seed;
Py_ssize_t 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 %zd", seedlen);
return -1;
}
OID curve;
Integer grouporderm1;
byte privexpbytes[24] = {0};
Integer privexponentm1;
privexponentm1.Decode(privexpbytes, sizeof(privexpbytes)); assert (privexponentm1 == 0); // just checking..
DL_GroupParameters_EC params(ASN1::secp192r1());
params.SetPointCompression(true);
grouporderm1 = params.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 = new ECDSA::Signer(params, privexponentm1+1);
if (!mself->k) {
PyErr_NoMemory();
return -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;
Py_ssize_t msgsize;
PyString_AsStringAndSize(msgobj, const_cast(&msg), reinterpret_cast(&msgsize));
assert (msgsize >= 0);
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); //XXX
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 memory was invalidly overwritten.");
abort();
}
assert (siglengthwritten >= 0);
return reinterpret_cast(result);
}
PyDoc_STRVAR(SigningKey_sign__doc__,
"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 = PyObject_New(VerifyingKey, &VerifyingKey_type);
if (!verifier)
return NULL;
verifier->k = new ECDSA::Verifier(*(self->k));
if (!verifier->k)
return PyErr_NoMemory();
verifier->k->AccessKey().AccessGroupParameters().SetPointCompression(true);
return reinterpret_cast(verifier);
}
PyDoc_STRVAR(SigningKey_get_verifying_key__doc__,
"Return the corresponding verifying 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__},
{NULL},
};
static PyTypeObject SigningKey_type = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"ecdsa.SigningKey", /*tp_name*/
sizeof(SigningKey), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)SigningKey_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
SigningKey__init____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 */
};
void
init_ecdsa(PyObject*const module) {
VerifyingKey_type.tp_new = PyType_GenericNew;
if (PyType_Ready(&VerifyingKey_type) < 0)
return;
Py_INCREF(&VerifyingKey_type);
PyModule_AddObject(module, "ecdsa_VerifyingKey", (PyObject *)&VerifyingKey_type);
SigningKey_type.tp_new = PyType_GenericNew;
if (PyType_Ready(&SigningKey_type) < 0)
return;
Py_INCREF(&SigningKey_type);
PyModule_AddObject(module, "ecdsa_SigningKey", (PyObject *)&SigningKey_type);
ecdsa_error = PyErr_NewException(const_cast("_ecdsa.Error"), NULL, NULL);
PyModule_AddObject(module, "ecdsa_Error", ecdsa_error);
PyModule_AddStringConstant(module, "ecdsa___doc__", const_cast(ecdsa___doc__));
}