X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba6e6b64033b1f9de49feccb5c9cd438354481f7..0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a:/pub/gkcdsa.c diff --git a/pub/gkcdsa.c b/pub/gkcdsa.c new file mode 100644 index 0000000..eb5a3de --- /dev/null +++ b/pub/gkcdsa.c @@ -0,0 +1,309 @@ +/* -*-c-*- + * + * Generalized version of KCDSA + * + * (c) 2004 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "gkcdsa.h" +#include "group.h" +#include "ghash.h" +#include "mpbarrett.h" +#include "mprand.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @gkcdsa_beginhash@ --- * + * + * Arguments: @const gkcdsa *c@ = pointer to the context structure + * + * Returns: A hashing context for you to hash the message. + * + * Use: Initializes a hash function correctly for you to hash a + * message. Requires @h@, @g@ and @p@. + */ + +ghash *gkcdsa_beginhash(const gkcdsa *c) +{ + ghash *h = GH_INIT(c->h); + mp *v = G_TOINT(c->g, MP_NEW, c->p); + size_t sz = c->h->bufsz; + void *p = xmalloc(sz); + if (/*ouch*/ !v) memset(p, 0, sz); + else mp_storeb(v, p, sz); + GH_HASH(h, p, sz); + mp_drop(v); xfree(p); + return (h); +} + +/* --- @gkcdsa_endhash@ --- * + * + * Arguments: @const gkcdsa *c@ = pointer to the context structure + * @ghash *h@ = the hashing context + * + * Returns: --- + * + * Use: Does any final thing that KCDSA wants to do when hashing a + * message. (Actually, there's nothing.) The hashing context + * isn't finalized. + */ + +void gkcdsa_endhash(const gkcdsa *c, ghash *h) { ; } + +/* --- @hashge@ --- * + * + * Arguments: @group *g@ = abstract group + * @const gchash *hc@ = hash class + * @ge *w@ = a group element + * + * Returns: A hash context, with the hash of @w@ in it. + */ + +static ghash *hashge(group *g, const gchash *hc, ge *w) +{ + ghash *h; + size_t sz; + void *p; + buf b; + int rc; + + sz = hc->hashsz; + if (sz < g->noctets) + sz = g->noctets; + p = xmalloc(sz); + buf_init(&b, p, sz); + rc = G_TORAW(g, &b, w); + assert(rc == 0); + h = GH_INIT(hc); + GH_HASH(h, BBASE(&b), BLEN(&b)); + xfree(p); + return (h); +} + +/* --- @gkcdsa_sign@ --- * + * + * Arguments: @const gkcdsa *c@ = my context structure + * @gkcdsa_sig *s@ = where to put the signature (initialized) + * @const void *m@ = pointer to message hash + * @mp *k@ = random exponent for this message or null + * + * Returns: --- + * + * Use: Signs a message. Requires @g@, @u@, @h@, and @r@ if @k@ is + * null. This is a better idea than inventing @k@ yourself. + */ + +void gkcdsa_sign(const gkcdsa *c, gkcdsa_sig *s, const void *m, mp *k) +{ + group *g = c->g; + mp *x, *y; + ge *z = G_CREATE(g); + size_t hsz = c->h->hashsz; + ghash *h; + + if (k) { MP_COPY(k); goto have_k; } +new_k: + k = mprand_range(k, g->r, c->r, 0); +have_k: + if (MP_ZEROP(k)) goto new_k; + G_EXP(g, z, g->g, k); + if (!s->r) s->r = xmalloc(hsz); + h = hashge(g, c->h, z); GH_DONE(h, s->r); + + x = mp_loadb(s->s, m, hsz); + y = mp_loadb(MP_NEW, s->r, hsz); + x = mp_xor(x, x, y); + mp_div(0, &x, x, g->r); + x = mp_sub(x, g->r, x); + x = mp_add(x, x, k); + if (MP_CMP(x, >=, g->r)) x = mp_sub(x, x, g->r); + x = mp_mul(x, x, c->u); mp_div(0, &x, x, g->r); + s->s = x; + mp_drop(k); mp_drop(y); GH_DESTROY(h); G_DESTROY(g, z); +} + +/* --- @gkcdsa_verify@ --- * + * + * Arguments: @const gkcdsa *c@ = my context structure + * @const gkcdsa_sig *s@ = the signature to verify + * @const void *m@ = pointer to message hash + * + * Returns: Zero if OK, negative on failure. + * + * Use: Checks a signature on a message, Requires @g@, @p@, @h@. + */ + +int gkcdsa_verify(const gkcdsa *c, const gkcdsa_sig *s, const void *m) +{ + group *g = c->g; + size_t hsz = c->h->hashsz; + group_expfactor e[2]; + mp *x, *y; + ghash *h; + ge *z; + octet *p; + int rc = -1; + + if (MP_CMP(s->s, <, MP_ONE) || MP_CMP(s->s, >=, g->r)) + return (-1); + x = mp_loadb(MP_NEW, m, hsz); y = mp_loadb(MP_NEW, s->r, hsz); + x = mp_xor(x, x, y); mp_div(0, &x, x, g->r); + e[0].base = c->p; e[0].exp = s->s; + e[1].base = g->g; e[1].exp = x; + z = G_CREATE(g); G_MEXP(g, z, e, 2); + h = hashge(g, c->h, z); p = GH_DONE(h, 0); + if (memcmp(p, s->r, hsz) == 0) rc = 0; + mp_drop(x); mp_drop(y); G_DESTROY(g, z); GH_DESTROY(h); + return (rc); +} + +/*----- Test rig ----------------------------------------------------------*/ + +#ifdef TEST_RIG + +static group *getgroup(const char *p) { + group *g; qd_parse qd; + qd.p = p; qd.e = 0; g = group_parse(&qd); + if (g && !qd_eofp(&qd)) { G_DESTROYGROUP(g); g = 0; qd.e = "junk at eof"; } + if (!g) { fprintf(stderr, "bad group string `%.*s|%s': %s\n", qd.p - p, + p, qd.p, qd.e); exit(1); } + return (g); +} + +static ge *getge(group *g, const char *p) { + ge *x = G_CREATE(g); + if (group_readstring(g, x, p, 0)) { + fprintf(stderr, "bad group element `%s'\n", p); + exit(1); + } + return (x); +} + +static void showge(group *g, const char *p, ge *x) { + fprintf(stderr, "*** %s = ", p); group_writefile(g, x, stderr); + putc('\n', stderr); +} + +static void showmp(const char *p, mp *x, int r) { + fprintf(stderr, "*** %s = ", p); mp_writefile(x, stderr, r); + putc('\n', stderr); +} + +static int tsign(dstr *v) +{ + gdsa c; + gkcdsa_sig s, ss = GKCDSA_SIG_INIT; + ghash *h; + mp *k; + dstr d = DSTR_INIT; + mp *x; + int ok = 1; + + c.g = getgroup(v[0].buf); c.h = ghash_byname(v[1].buf); + c.u = *(mp **)v[2].buf; k = *(mp **)v[4].buf; + s.r = (octet *)v[5].buf; s.s = *(mp **)v[6].buf; + DENSURE(&d, c.h->hashsz); d.len = c.h->hashsz; memset(d.buf, 0, d.len); + ss.r = (octet *)d.buf; + + x = mp_modinv(MP_NEW, c.u, c.g->r); + c.p = G_CREATE(c.g); G_EXP(c.g, c.p, c.g->g, x); + h = gkcdsa_beginhash(&c); + GH_HASH(h, v[3].buf, v[3].len); + gkcdsa_endhash(&c, h); + gkcdsa_sign(&c, &ss, GH_DONE(h, 0), k); + if (memcmp(s.r, ss.r, c.h->hashsz) || !MP_EQ(s.s, ss.s)) { + ok = 0; + fprintf(stderr, "*** sign failed!\n"); + fprintf(stderr, "*** group: %s\n", v[0].buf); + fprintf(stderr, "*** hash: %s\n", c.h->name); + showmp("private key", c.u, 16); + showge(c.g, "public key", c.p); + fprintf(stderr, "*** message: `%s'\n", v[3].buf); + fprintf(stderr, "*** computed r = "); + type_hex.dump(&d, stderr); putc('\n', stderr); + showmp("computed s", ss.s, 16); + fprintf(stderr, "*** computed r = "); + type_hex.dump(&v[5], stderr); putc('\n', stderr); + showmp("expected s", s.s, 16); + } + mp_drop(s.s); dstr_destroy(&d); mp_drop(ss.s); mp_drop(x); mp_drop(k); + mp_drop(c.u); G_DESTROY(c.g, c.p); G_DESTROYGROUP(c.g); GH_DESTROY(h); + assert(mparena_count(MPARENA_GLOBAL) == 0); + return (ok); +} + +static int tverify(dstr *v) +{ + gkcdsa c; + gkcdsa_sig s; + ghash *h; + int rc, erc; + int ok = 1; + + c.g = getgroup(v[0].buf); c.h = ghash_byname(v[1].buf); + c.p = getge(c.g, v[2].buf); + s.r = (octet *)v[4].buf; s.s = *(mp **)v[5].buf; + erc = *(int *)v[6].buf; + + h = gkcdsa_beginhash(&c); + GH_HASH(h, v[3].buf, v[3].len); + gkcdsa_endhash(&c, h); + rc = gkcdsa_verify(&c, &s, GH_DONE(h, 0)); + if (!rc != !erc) { + ok = 0; + fprintf(stderr, "*** verify failed!\n"); + fprintf(stderr, "*** group: %s\n", v[0].buf); + fprintf(stderr, "*** hash: %s\n", c.h->name); + showge(c.g, "public key", c.p); + fprintf(stderr, "*** message: `%s'\n", v[3].buf); + fprintf(stderr, "*** sig r = "); + type_hex.dump(&v[4], stderr); putc('\n', stderr); + showmp("sig s", s.s, 16); + fprintf(stderr, "*** expected %s\n", !erc ? "pass" : "fail"); + } + mp_drop(s.s); G_DESTROY(c.g, c.p); G_DESTROYGROUP(c.g); + GH_DESTROY(h); + assert(mparena_count(MPARENA_GLOBAL) == 0); + return (ok); +} + +static const test_chunk tests[] = { + { "sign", tsign, { &type_string, &type_string, &type_mp, &type_string, + &type_mp, &type_hex, &type_mp } }, + { "verify", tverify, { &type_string, &type_string, &type_string, + &type_string, &type_hex, &type_mp, &type_int } }, + { 0 } +}; + +int main(int argc, char *argv[]) +{ + sub_init(); + test_run(argc, argv, tests, SRCDIR "/t/gkcdsa"); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/