From b115b0c0883c2af8170b0e733bb1c47b40f2647e Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 13 Nov 2019 00:59:07 +0000 Subject: [PATCH] *.c: Publish algorithm and crypto-group tables as `mapping' objects. Previously, the algorithm tables were published as plain Python dictionaries, and the group tables were published in a complicated way through a read-only mapping object implemented in Python. With the richer Python 3 mapping protocol, maintaining a separate custom mapping object in Python seems much less attractive. Also, the group tables were read-only while the algorithm tables were mutable, which was a rather unfortunate inconsistency. Fix this mess by implementing a new simple mapping object, based on Pyke's generic mapping machinery, and populating instances of it with the necessary data. --- algorithms.c | 31 ++++++---- catacomb-python.h | 26 +++----- catacomb.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++ catacomb/__init__.py | 42 ++----------- ec.c | 60 ++++++------------ group.c | 82 +++++++++--------------- rand.c | 14 +++-- 7 files changed, 261 insertions(+), 165 deletions(-) diff --git a/algorithms.c b/algorithms.c index f792087..1b1545c 100644 --- a/algorithms.c +++ b/algorithms.c @@ -3612,12 +3612,14 @@ void algorithms_pyinit(void) addmethods(methods); } -GEN(gcciphers, cipher) -GEN(gcaeads, aead) -GEN(gchashes, hash) -GEN(gcmacs, mac) #define gcprp prpinfo -GEN(gcprps, prp) +#define CLASS_TABLES(_) _(cipher) _(aead) _(hash) _(mac) _(prp) +#define TABLE_FNS(pre) \ + static const char *pre##_namefn(const void *p) \ + { const gc##pre *const *cls = p; return (*cls ? (*cls)->name : 0); } \ + static PyObject *pre##_valfn(const void *p) \ + { gc##pre *const*cls = p; return (gc##pre##_pywrap(*cls)); } +CLASS_TABLES(TABLE_FNS) void algorithms_pyinsert(PyObject *mod) { @@ -3628,7 +3630,8 @@ void algorithms_pyinsert(PyObject *mod) INSERT("KeySZSet", keyszset_pytype); INSERT("GCCipher", gccipher_pytype); INSERT("GCipher", gcipher_pytype); - INSERT("gcciphers", gcciphers()); + INSERT("gcciphers", make_algtab(gciphertab, sizeof(gccipher *), + cipher_namefn, cipher_valfn)); INSERT("GCAEAD", gcaead_pytype); INSERT("GAEKey", gaeadkey_pytype); INSERT("GAEAADClass", gcaeadaad_pytype); @@ -3637,16 +3640,19 @@ void algorithms_pyinsert(PyObject *mod) INSERT("GAEEnc", gaeadenc_pytype); INSERT("GAEDecClass", gcaeaddec_pytype); INSERT("GAEDec", gaeaddec_pytype); - INSERT("gcaeads", gcaeads()); + INSERT("gcaeads", make_algtab(gaeadtab, sizeof(gcaead *), + aead_namefn, aead_valfn)); INSERT("GCHash", gchash_pytype); INSERT("GHash", ghash_pytype); - INSERT("gchashes", d = gchashes()); - sha_pyobj = PyDict_GetItemString(d, "sha"); Py_INCREF(sha_pyobj); - has160_pyobj = PyDict_GetItemString(d, "has160"); Py_INCREF(has160_pyobj); + d = make_algtab(ghashtab, sizeof(gchash *), hash_namefn, hash_valfn); + INSERT("gchashes", d); + sha_pyobj = PyMapping_GetItemString(d, "sha"); Py_INCREF(sha_pyobj); + has160_pyobj = PyMapping_GetItemString(d, "has160"); Py_INCREF(has160_pyobj); INSERT("GCMAC", gcmac_pytype); INSERT("GMAC", gmac_pytype); INSERT("GMACHash", gmhash_pytype); - INSERT("gcmacs", gcmacs()); + INSERT("gcmacs", make_algtab(gmactab, sizeof(gcmac *), + mac_namefn, mac_valfn)); INSERT("Poly1305Class", poly1305cls_pytype); INSERT("poly1305", poly1305key_pytype); INSERT("Poly1305Hash", poly1305hash_pytype); @@ -3656,7 +3662,8 @@ void algorithms_pyinsert(PyObject *mod) INSERT("Shake256", shake256_pytype); INSERT("GCPRP", gcprp_pytype); INSERT("GPRP", gprp_pytype); - INSERT("gcprps", gcprps()); + INSERT("gcprps", make_algtab(gprptab, sizeof(gcprp *), + prp_namefn, prp_valfn)); setconstants(mod, consts); } diff --git a/catacomb-python.h b/catacomb-python.h index 4c7ced4..da6a8fe 100644 --- a/catacomb-python.h +++ b/catacomb-python.h @@ -136,24 +136,14 @@ MODULES(DECLARE_MODINIT) /* Conversions. */ extern int convmpw(PyObject *, void *); -/* Make a dictionary of generic-crypto classes. */ -#define GEN(func, base) \ - static PyObject *func(void) \ - { \ - PyObject *d = PyDict_New(); \ - PyObject *o; \ - int i; \ - \ - for (i = 0; g##base##tab[i]; i++) { \ - o = gc##base##_pywrap(CONVERT_CAREFULLY(gc##base *, const gc##base *, \ - g##base##tab[i])); \ - PyDict_SetItemString(d, \ - (/*unconst*/ char *)g##base##tab[i]->name, \ - o); \ - Py_DECREF(o); \ - } \ - return (d); \ - } +/* Building tables of things. */ +extern PyObject *make_algtab(const void *tab, size_t esz, + const char *(*namefn)(const void *), + PyObject *(*valfn)(const void *)); +extern PyObject *make_grouptab(const void *tab, size_t esz, + const char *(*namefn)(const void *), + int (*ixfn)(const void *), + PyObject *(*valfn)(int)); /* Common handling for simultaneous exponentiation. */ extern PyObject *mexp_common(PyObject *, PyObject *, size_t, diff --git a/catacomb.c b/catacomb.c index 60dae45..a914bff 100644 --- a/catacomb.c +++ b/catacomb.c @@ -110,6 +110,175 @@ end: return (0); } +static PyTypeObject *thingtab_pytype; + +typedef struct thingentry { + sym_base _b; + PyObject *val; +} thingentry; +#define THING_VAL(x) (((thingentry *)(x))->val) + +typedef struct thingtab_pyobj { + GMAP_PYOBJ_HEAD + sym_table t; +} thingtab_pyobj; +#define THINGTAB_T(x) (&((thingtab_pyobj *)(x))->t) + +static void *thingtab_gmlookup(PyObject *me, PyObject *key, unsigned *f) +{ + const char *p; + + p = PyString_AsString(key); if (!p) return (0); + return (sym_find(THINGTAB_T(me), p, -1, 0, f)); +} + +static void thingtab_gmiterinit(PyObject *me, void *i) + { sym_mkiter(i, THINGTAB_T(me)); } + +static void *thingtab_gmiternext(PyObject *me, void *i) + { sym_iter *it = i; void *e; SYM_NEXT(it, e); return (e); } + +static PyObject *thingtab_gmentrykey(PyObject *me, void *e) + { return (PyString_FromString(SYM_NAME(e))); } + +static PyObject *thingtab_gmentryvalue(PyObject *me, void *e) + { PyObject *rc = THING_VAL(e); RETURN_OBJ(rc); } + +static const gmap_ops thingtab_gmops = { + sizeof(sym_iter), + thingtab_gmlookup, + thingtab_gmiterinit, + thingtab_gmiternext, + thingtab_gmentrykey, + thingtab_gmentryvalue +}; + +static Py_ssize_t thing_pysize(PyObject *me) + { return gmap_pysize_from_sym(THINGTAB_T(me)); } + +static const PyMappingMethods thingtab_pymapping = { + thing_pysize, + gmap_pylookup, + 0 +}; + +static thingtab_pyobj *make_thingtab(void) +{ + thingtab_pyobj *map = PyObject_NEW(thingtab_pyobj, thingtab_pytype); + + map->gmops = &thingtab_gmops; + sym_create(&map->t); + return (map); +} + +PyObject *make_algtab(const void *tab, size_t esz, + const char *(*namefn)(const void *), + PyObject *(*valfn)(const void *)) +{ + thingtab_pyobj *map = make_thingtab(); + const char *p = tab; + const char *name; + thingentry *e; + unsigned f; + + for (;;) { + name = namefn(p); if (!name) break; + e = sym_find(&map->t, name, -1, sizeof(*e), &f); assert(!f); + e->val = valfn(p); + p += esz; + } + return ((PyObject *)map); +} + +PyObject *make_grouptab(const void *tab, size_t esz, + const char *(*namefn)(const void *), + int (*ixfn)(const void *), PyObject *(*valfn)(int)) +{ + thingtab_pyobj *map = make_thingtab(); + struct { const char *name; int ix; } *ixtab = 0; + PyObject **valtab, **vv; + size_t i = 0, n = 0; + const char *p = tab; + const char *name; + thingentry *e; + unsigned f; + + for (;;) { + name = namefn(p); if (!name) break; + if (i >= n) { + if (!n) n = 16; + else n *= 2; + ixtab = xrealloc(ixtab, n*sizeof(*ixtab), i*sizeof(*ixtab)); + } + ixtab[i].name = name; ixtab[i].ix = ixfn(p); assert(ixtab[i].ix >= 0); + p += esz; i++; + } + n = i; + + valtab = xmalloc(n*sizeof(*valtab)); + for (i = 0; i < n; i++) valtab[i] = 0; + + for (i = 0; i < n; i++) { + e = sym_find(&map->t, ixtab[i].name, -1, sizeof(*e), &f); assert(!f); + vv = &valtab[ixtab[i].ix]; + if (*vv) Py_INCREF(*vv); + else *vv = valfn(ixtab[i].ix); + e->val = *vv; + } + + xfree(ixtab); xfree(valtab); + return ((PyObject *)map); +} + +static const PyTypeObject thingtab_pytype_skel = { + PyObject_HEAD_INIT(0) 0, /* Header */ + "_MiscTable", /* @tp_name@ */ + sizeof(thingtab_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + 0, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + 0, /* @tp_as_number@ */ + PYSEQUENCE(gmap), /* @tp_as_sequence@ */ + PYMAPPING(thingtab), /* @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@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ + "Class for tables of algorithms and abstract-group data.\n" + " Not instantiable by users.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + gmap_pyiter, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + PYMETHODS(gmapro), /* @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@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + static PyObject *smallprimes(void) { PyObject *v = PyList_New(NPRIME); @@ -159,9 +328,11 @@ EXPORT void init_base(void) modname = PyString_FromString("catacomb"); addmethods(methods); INIT_MODULES; + INITTYPE(thingtab, root); init_random(); mod = Py_InitModule("catacomb._base", donemethods()); INSERT_MODULES; + INSERT("_MiscTable", thingtab_pytype); INSERT("smallprimes", smallprimes()); } diff --git a/catacomb/__init__.py b/catacomb/__init__.py index 0996c5c..c810c42 100644 --- a/catacomb/__init__.py +++ b/catacomb/__init__.py @@ -1002,14 +1002,9 @@ class Ed448Priv (_EdDSAPriv, Ed448Pub): return ed448_sign(me.priv, msg, pub = me.pub, **kw) ###-------------------------------------------------------------------------- -### Built-in named curves and prime groups. - -class _groupmap (object): - def __init__(me, map, nth): - me.map = map - me.nth = nth - me._n = max(map.values()) + 1 - me.i = me._n*[None] +### Built-in algorithm and group tables. + +class _tmp: def __repr__(me): return '{%s}' % ', '.join(['%r: %r' % kv for kv in me.iteritems()]) def _repr_pretty_(me, pp, cyclep): @@ -1017,36 +1012,7 @@ class _groupmap (object): if cyclep: pp.text('...') else: _pp_dict(pp, me.iteritems()) pp.end_group(ind, ' }') - def __len__(me): - return me._n - def __contains__(me, k): - return k in me.map - def __getitem__(me, k): - i = me.map[k] - if me.i[i] is None: - me.i[i] = me.nth(i) - return me.i[i] - def __setitem__(me, k, v): - raise TypeError, "immutable object" - def __iter__(me): - return iter(me.map) - def iterkeys(me): - return iter(me.map) - def itervalues(me): - for k in me: - yield me[k] - def iteritems(me): - for k in me: - yield k, me[k] - def keys(me): - return [k for k in me] - def values(me): - return [me[k] for k in me] - def items(me): - return [(k, me[k]) for k in me] -eccurves = _groupmap(_base._eccurves, ECInfo._curven) -primegroups = _groupmap(_base._pgroups, DHInfo._groupn) -bingroups = _groupmap(_base._bingroups, BinDHInfo._groupn) +_augment(_base._MiscTable, _tmp) ###-------------------------------------------------------------------------- ### Prime number generation. diff --git a/ec.c b/ec.c index 759ac57..606b235 100644 --- a/ec.c +++ b/ec.c @@ -1339,8 +1339,6 @@ static const PyTypeObject ecbinprojcurve_pytype_skel = { /*----- Curve info --------------------------------------------------------*/ -static int ncurves = -1; - void ecinfo_copy(ec_info *eic, const ec_info *ei) { eic->c = eccurve_copy(ei->c); @@ -1414,20 +1412,6 @@ end: return (rc); } -static PyObject *eimeth__curven(PyObject *me, PyObject *arg) -{ - int i; - ec_info ei; - PyObject *rc = 0; - - if (!PyArg_ParseTuple(arg, "i:_curven", &i)) goto end; - if (i < 0 || i >= ncurves) VALERR("curve index out of range"); - ec_infofromdata(&ei, ectab[i].data); - rc = ecinfo_pywrap(&ei); -end: - return (rc); -} - static PyObject *ecinfo_pyrichcompare(PyObject *x, PyObject *y, int op) { int b = ec_sameinfop(ECINFO_EI(x), ECINFO_EI(y)); @@ -1488,7 +1472,6 @@ static const PyMethodDef ecinfo_pymethods[] = { #define METHNAME(name) eimeth_##name KWMETH(check, "I.check([rng = rand]) -> None") SMTH (parse, "parse(STR) -> (I, REST)") - SMTH (_curven, "_curven(N) -> I") #undef METHNAME { 0 } }; @@ -1561,29 +1544,25 @@ void ec_pyinit(void) INITTYPE(ecinfo, root); } -static PyObject *namedcurves(void) +static const char *ec_namefn(const void *p) + { const ecentry *ec = p; return (ec->name); } + +static int ec_ixfn(const void *p) { - int i, j; - const char *p; - PyObject *d, *c; - - d = PyDict_New(); - for (i = 0; ectab[i].name; i++) { - p = ectab[i].name; - for (j = 0; j < i; j++) { - if (ectab[i].data == ectab[j].data) { - c = PyDict_GetItemString(d, (/*unconst*/ char *)ectab[j].name); - Py_INCREF(c); - goto found; - } - } - c = PyInt_FromLong(i); - found: - PyDict_SetItemString(d, (/*unconst*/ char *)p, c); - Py_DECREF(c); - } - ncurves = i; - return (d); + const ecentry *ec = p; + int i; + + for (i = 0; ectab[i].name; i++) + if (ectab[i].data == ec->data) return (i); + return (-1); +} + +static PyObject *ec_valfn(int i) +{ + ec_info ei; + + ec_infofromdata(&ei, ectab[i].data); + return (ecinfo_pywrap(&ei)); } void ec_pyinsert(PyObject *mod) @@ -1596,7 +1575,8 @@ void ec_pyinsert(PyObject *mod) INSERT("ECBinCurve", ecbincurve_pytype); INSERT("ECBinProjCurve", ecbinprojcurve_pytype); INSERT("ECInfo", ecinfo_pytype); - INSERT("_eccurves", namedcurves()); + INSERT("eccurves", make_grouptab(ectab, sizeof(*ectab), + ec_namefn, ec_ixfn, ec_valfn)); setconstants(mod, consts); } diff --git a/group.c b/group.c index d02f356..3ff0ad3 100644 --- a/group.c +++ b/group.c @@ -220,54 +220,6 @@ end: return (rc); } -static int npgroups = -1, nbingroups = -1; - -static PyObject *namedgroups(const pentry *pp, int *ne) -{ - int i, j; - const char *p; - PyObject *d, *c; - - d = PyDict_New(); - for (i = 0; pp[i].name; i++) { - p = pp[i].name; - for (j = 0; j < i; j++) { - if (pp[i].data == pp[j].data) { - c = PyDict_GetItemString(d, (/*unconst*/ char *)pp[j].name); - Py_INCREF(c); - goto found; - } - } - c = PyInt_FromLong(i); - found: - PyDict_SetItemString(d, (/*unconst*/ char *)p, c); - Py_DECREF(c); - } - *ne = i; - return (d); -} - -static PyObject *meth__groupn(PyObject *me, PyObject *arg, - PyTypeObject *ty, const pentry *pp, int ne) -{ - int i; - gprime_param gp; - PyObject *rc = 0; - - if (!PyArg_ParseTuple(arg, "i:_groupn", &i)) goto end; - if (i < 0 || i >= ne) VALERR("group index out of range"); - dh_infofromdata(&gp, pp[i].data); - rc = fginfo_pywrap(&gp, ty); -end: - return (rc); -} - -static PyObject *dimeth__groupn(PyObject *me, PyObject *arg) - { return (meth__groupn(me, arg, dhinfo_pytype, ptab, npgroups)); } - -static PyObject *bimeth__groupn(PyObject *me, PyObject *arg) - { return (meth__groupn(me, arg, bindhinfo_pytype, bintab, nbingroups)); } - static PyObject *meth__parse(PyObject *me, PyObject *arg, PyTypeObject *ty, int (*parse)(qd_parse *, gprime_param *)) { @@ -308,7 +260,6 @@ static const PyGetSetDef dhinfo_pygetset[] = { static const PyMethodDef dhinfo_pymethods[] = { #define METHNAME(name) dimeth_##name SMTH (parse, "parse(STR) -> D, REST") - SMTH (_groupn, 0) KWSMTH(generate, "generate(PBITS, [qbits = 0], [event = pgen_nullev],\n" " [rng = rand], [nsteps = 0]) -> D") @@ -340,7 +291,6 @@ static const PyGetSetDef bindhinfo_pygetset[] = { static const PyMethodDef bindhinfo_pymethods[] = { #define METHNAME(name) bimeth_##name SMTH (parse, "parse(STR) -> D, REST") - SMTH (_groupn, 0) #undef METHNAME { 0 } }; @@ -1413,6 +1363,32 @@ void group_pyinit(void) INITTYPE(ecgroup, group); } +static const char *grp_namefn(const void *p) + { const pentry *pt = p; return (pt->name); } + +static int grp_ixfn(const pentry *tab, const pentry *pt) +{ + int i; + + for (i = 0; tab[i].name; i++) + if (tab[i].data == pt->data) return (i); + return (-1); +} +static int pgrp_ixfn(const void *p) { return (grp_ixfn(ptab, p)); } +static int bgrp_ixfn(const void *p) { return (grp_ixfn(bintab, p)); } + +static PyObject *grp_valfn(const pentry *tab, PyTypeObject *ty, int i) +{ + gprime_param gp; + + dh_infofromdata(&gp, tab[i].data); + return (fginfo_pywrap(&gp, ty)); +} +static PyObject *pgrp_valfn(int i) + { return (grp_valfn(ptab, dhinfo_pytype, i)); } +static PyObject *bgrp_valfn(int i) + { return (grp_valfn(bintab, bindhinfo_pytype, i)); } + void group_pyinsert(PyObject *mod) { INSERT("FGInfo", fginfo_pytype); @@ -1423,8 +1399,10 @@ void group_pyinsert(PyObject *mod) INSERT("PrimeGroup", primegroup_pytype); INSERT("BinGroup", bingroup_pytype); INSERT("ECGroup", ecgroup_pytype); - INSERT("_pgroups", namedgroups(ptab, &npgroups)); - INSERT("_bingroups", namedgroups(bintab, &nbingroups)); + INSERT("primegroups", make_grouptab(ptab, sizeof(*ptab), + grp_namefn, pgrp_ixfn, pgrp_valfn)); + INSERT("bingroups", make_grouptab(bintab, sizeof(*bintab), + grp_namefn, bgrp_ixfn, bgrp_valfn)); } /*----- That's all, folks -------------------------------------------------*/ diff --git a/rand.c b/rand.c index 2f69336..36d32d4 100644 --- a/rand.c +++ b/rand.c @@ -65,7 +65,8 @@ PyObject *grand_pywrap(grand *r, unsigned f) else if (STRCMP(r->ops->name, ==, "sslprf")) ty = sslprf_pytype; else if (STRCMP(r->ops->name, ==, "tlsdx")) ty = tlsdx_pytype; else if (STRCMP(r->ops->name, ==, "tlsprf")) ty = tlsprf_pytype; - else if ((ob = PyDict_GetItemString(gccrands_dict, r->ops->name)) != 0) + else if ((ob = PyMapping_GetItemString + (gccrands_dict, (/*unconst*/ char *)r->ops->name)) != 0) ty = (PyTypeObject *)ob; return (grand_dopywrap(ty, r, f)); } @@ -1513,8 +1514,10 @@ void rand_pyinit(void) rand_seed(RAND_GLOBAL, 160); } -#define gccrand gccrand_info -GEN(gccrands, crand) +static const char *crand_namefn(const void *p) + { const gccrand_info *const *cls = p; return (*cls ? (*cls)->name : 0); } +static PyObject *crand_valfn(const void *p) + { const gccrand_info *const *cls = p; return (gccrand_pywrap(*cls)); } void rand_pyinsert(PyObject *mod) { @@ -1532,8 +1535,9 @@ void rand_pyinsert(PyObject *mod) INSERT("GCRand", gcrand_pytype); INSERT("GCLatinRand", gclatinrand_pytype); rand_pyobj = grand_pywrap(&rand_global, 0); Py_INCREF(rand_pyobj); - gccrands_dict = gccrands(); Py_INCREF(gccrands_dict); - INSERT("gccrands", gccrands_dict); + gccrands_dict = make_algtab(gcrandtab, sizeof(gccrand_info *), + crand_namefn, crand_valfn); + INSERT("gccrands", gccrands_dict); Py_INCREF(gccrands_dict); INSERT("rand", rand_pyobj); setconstants(mod, consts); } -- 2.11.0