X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba6e6b64033b1f9de49feccb5c9cd438354481f7..0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a:/rand/tlsprf.c diff --git a/rand/tlsprf.c b/rand/tlsprf.c new file mode 100644 index 0000000..3f055b0 --- /dev/null +++ b/rand/tlsprf.c @@ -0,0 +1,540 @@ +/* -*-c-*- + * + * The TLS pseudo-random function + * + * (c) 2001 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 +#include +#include + +#include "arena.h" +#include "gmac.h" +#include "grand.h" +#include "paranoia.h" +#include "tlsprf.h" + +/*----- The data expansion function ---------------------------------------*/ + +/* --- @tlsdx_init@ --- * + * + * Arguments: @tlsdx_ctx *c@ = pointer to a context + * @gmac *m@ = pointer to a generic MAC instance + * @const void *sd@ = pointer to the seed block + * @size_t sdsz@ = size of the seed block + * + * Returns: --- + * + * Use: Initializes a context for the TLS data expansion function. + * This doesn't take ownership of the MAC instance or the seed + * memory, nor does it allocate copies. + */ + +void tlsdx_init(tlsdx_ctx *c, gmac *m, const void *sd, size_t sdsz) +{ + c->k = m; + c->hashsz = GM_CLASS(c->k)->hashsz; + c->sd = sd; c->sdsz = sdsz; + + c->i = GM_INIT(c->k); + GH_HASH(c->i, sd, sdsz); + c->ai = GH_DONE(c->i, 0); + c->o = GM_INIT(c->k); + GH_HASH(c->o, c->ai, c->hashsz); + GH_HASH(c->o, sd, sdsz); + c->p = GH_DONE(c->o, 0); + c->sz = c->hashsz; +} + +/* --- @tlsdx_encrypt@ --- * + * + * Arguments: @tlsdx_ctx *c@ = pointer to a context + * @const void *src@ = pointer to source data + * @void *dest@ = pointer to destination buffer + * @size_t sz@ = size of buffer + * + * Returns: --- + * + * Use: Encrypts data using the TLS data expansion function. If the + * destination pointer is null, the generator is spun and no + * output is produced; if the source pointer is null, raw output + * from the generator is written; otherwise, the source data is + * XORed with the generator output. + */ + +void tlsdx_encrypt(tlsdx_ctx *c, const void *src, void *dest, size_t sz) +{ + const octet *s = src; + octet *d = dest; + ghash *h; + size_t i; + size_t n; + + while (sz) { + if (c->sz) + n = c->sz; + else { + h = GM_INIT(c->k); + GH_HASH(h, c->ai, c->hashsz); + c->ai = GH_DONE(h, 0); + GH_DESTROY(c->i); + c->i = h; + GH_DESTROY(c->o); + h = c->o = GM_INIT(c->k); + GH_HASH(h, c->ai, c->hashsz); + GH_HASH(h, c->sd, c->sdsz); + c->p = GH_DONE(h, 0); + c->sz = n = c->hashsz; + } + if (n > sz) + n = sz; + if (d) { + if (!s) + memcpy(d, c->p, n); + else { + for (i = 0; i < n; i++) d[i] = s[i] ^ c->p[i]; + s += n; + } + d += n; + } + c->p += n; + c->sz -= n; + sz -= n; + } +} + +/* --- @tlsdx_free@ --- * + * + * Arguments: @tlsdx_ctx *c@ = pointer to the context block + * + * Returns: --- + * + * Use: Frees a context for the TLS data expansion function + */ + +void tlsdx_free(tlsdx_ctx *c) +{ + GH_DESTROY(c->i); + GH_DESTROY(c->o); +} + +/* --- Generic random number generator --- */ + +typedef struct dx_grctx { + grand r; + grand_ops ops; + tlsdx_ctx dx; +} dx_grctx; + +static void dx_grdestroy(grand *r) +{ + dx_grctx *g = (dx_grctx *)r; + xfree((char *)g->ops.name); + xfree((octet *)g->dx.sd); + g->dx.k->ops->destroy(g->dx.k); + tlsdx_free(&g->dx); + BURN(*g); + S_DESTROY(g); +} + +static void dx_seed(dx_grctx *g, const void *p, size_t sz) +{ + octet *q; + xfree((octet *)g->dx.sd); + g->dx.sd = q = xmalloc(sz); + memcpy(q, p, sz); + g->dx.sdsz = sz; +} + +static int dx_grmisc(grand *r, unsigned op, ...) +{ + dx_grctx *g = (dx_grctx *)r; + va_list ap; + int rc = 0; + uint32 i; + octet buf[4]; + va_start(ap, op); + + switch (op) { + case GRAND_CHECK: + switch (va_arg(ap, unsigned)) { + case GRAND_CHECK: + case GRAND_SEEDINT: + case GRAND_SEEDUINT32: + case GRAND_SEEDBLOCK: + case GRAND_SEEDRAND: + rc = 1; + break; + default: + rc = 0; + break; + } + break; + case GRAND_SEEDINT: + i = va_arg(ap, unsigned); + STORE32(buf, i); + dx_seed(g, buf, sizeof(buf)); + break; + case GRAND_SEEDUINT32: + i = va_arg(ap, uint32); + STORE32(buf, i); + dx_seed(g, buf, sizeof(buf)); + break; + case GRAND_SEEDBLOCK: { + const void *p = va_arg(ap, const void *); + size_t sz = va_arg(ap, size_t); + dx_seed(g, p, sz); + } break; + case GRAND_SEEDRAND: { + grand *rr = va_arg(ap, grand *); + octet buf[16]; + rr->ops->fill(rr, buf, sizeof(buf)); + dx_seed(g, buf, sizeof(buf)); + } break; + default: + GRAND_BADOP; + break; + } + + va_end(ap); + return (rc); +} + +static octet dx_grbyte(grand *r) +{ + dx_grctx *g = (dx_grctx *)r; + octet o; + tlsdx_encrypt(&g->dx, 0, &o, 1); + return (o); +} + +static uint32 dx_grword(grand *r) +{ + dx_grctx *g = (dx_grctx *)r; + octet b[4]; + tlsdx_encrypt(&g->dx, 0, &b, sizeof(b)); + return (LOAD32(b)); +} + +static void dx_grfill(grand *r, void *p, size_t sz) +{ + dx_grctx *g = (dx_grctx *)r; + tlsdx_encrypt(&g->dx, 0, p, sz); +} + +static const grand_ops dx_grops = { + "", + GRAND_CRYPTO, 0, + dx_grmisc, dx_grdestroy, + dx_grword, dx_grbyte, dx_grword, grand_range, dx_grfill +}; + +/* ---@tlsdx_rand@ --- * + * + * Arguments: @const gcmac *mc@ = MAC function to use + * @const void *k@ = pointer to the key material + * @size_t ksz@ = size of the key material + * @const void *sd@ = pointer to the seed material + * @size_t sdsz@ = size of the seed material + * + * Returns: Pointer to generic random number generator interface. + * + * Use: Creates a generic generator which does TLS data expansion. + */ + +grand *tlsdx_rand(const gcmac *mc, const void *k, size_t ksz, + const void *sd, size_t sdsz) +{ + dx_grctx *g = S_CREATE(dx_grctx); + dstr d = DSTR_INIT; + gmac *m = GM_KEY(mc, k, ksz); + octet *q = xmalloc(sdsz); + memcpy(q, sd, sdsz); + dstr_putf(&d, "tlsdx(%s)", mc->name); + g->ops = dx_grops; + g->ops.name = xstrdup(d.buf); + g->r.ops = &g->ops; + dstr_destroy(&d); + tlsdx_init(&g->dx, m, q, sdsz); + return (&g->r); +} + +/* --- The actual very paranoid PRF ---------------------------------------*/ + +/* --- @tlsprf_init@ --- * + * + * Arguments: @tlsprf_ctx *c@ = pointer to context block + * @const gcmac *mcx, *mcy@ = left and right MAC functions + * @const void *k@ = pointer to the key material + * @size_t ksz@ = size of the key material + * @const void *sd@ = pointer to the seed material + * @size_t sdsz@ = size of the seed material + * + * Returns: --- + * + * Use: Initializes a TLS PRF context. + */ + +void tlsprf_init(tlsprf_ctx *c, const gcmac *mcx, const gcmac *mcy, + const void *k, size_t ksz, const void *sd, size_t sdsz) +{ + size_t n = (ksz + 1)/2; + const octet *kk = k; + tlsdx_init(&c->px, mcx->key(kk, n), sd, sdsz); + tlsdx_init(&c->py, mcy->key(kk + ksz - n, n), sd, sdsz); +} + +/* --- @tlsprf_encrypt@ --- * + * + * Arguments: @tlsprf_ctx *c@ = pointer to a context + * @const void *src@ = pointer to source data + * @void *dest@ = pointer to destination buffer + * @size_t sz@ = size of buffer + * + * Returns: --- + * + * Use: Encrypts data using the TLS pseudo-random function. If the + * destination pointer is null, the generator is spun and no + * output is produced; if the source pointer is null, raw output + * from the generator is written; otherwise, the source data is + * XORed with the generator output. + */ + +void tlsprf_encrypt(tlsprf_ctx *c, const void *src, void *dest, size_t sz) +{ + tlsdx_encrypt(&c->px, src, dest, sz); + tlsdx_encrypt(&c->py, dest, dest, sz); +} + +/* --- @tlsprf_free@ --- * + * + * Arguments: @tlsprf_ctx *c@ = pointer to a context + * + * Returns: --- + * + * Use: Frees a TLS PRF context. + */ + +void tlsprf_free(tlsprf_ctx *c) +{ + c->px.k->ops->destroy(c->px.k); + c->py.k->ops->destroy(c->py.k); + tlsdx_free(&c->px); + tlsdx_free(&c->py); +} + +/* --- Generic random number generator --- */ + +typedef struct prf_grctx { + grand r; + grand_ops ops; + tlsprf_ctx prf; +} prf_grctx; + +static void prf_grdestroy(grand *r) +{ + prf_grctx *g = (prf_grctx *)r; + xfree((char *)g->ops.name); + xfree((octet *)g->prf.px.sd); + tlsprf_free(&g->prf); + BURN(*g); + S_DESTROY(g); +} + +static void prf_seed(prf_grctx *g, const void *p, size_t sz) +{ + octet *q; + + xfree((octet *)g->prf.px.sz); + g->prf.px.sd = g->prf.py.sd = q = xmalloc(sz); + memcpy(q, p, sz); + g->prf.px.sdsz = g->prf.py.sdsz = sz; +} + +static int prf_grmisc(grand *r, unsigned op, ...) +{ + prf_grctx *g = (prf_grctx *)r; + va_list ap; + int rc = 0; + uint32 i; + octet buf[4]; + va_start(ap, op); + + switch (op) { + case GRAND_CHECK: + switch (va_arg(ap, unsigned)) { + case GRAND_CHECK: + case GRAND_SEEDINT: + case GRAND_SEEDUINT32: + case GRAND_SEEDBLOCK: + case GRAND_SEEDRAND: + rc = 1; + break; + default: + rc = 0; + break; + } + break; + case GRAND_SEEDINT: + i = va_arg(ap, unsigned); + STORE32(buf, i); + prf_seed(g, buf, sizeof(buf)); + break; + case GRAND_SEEDUINT32: + i = va_arg(ap, uint32); + STORE32(buf, i); + prf_seed(g, buf, sizeof(buf)); + break; + case GRAND_SEEDBLOCK: { + const void *p = va_arg(ap, const void *); + size_t sz = va_arg(ap, size_t); + prf_seed(g, p, sz); + } break; + case GRAND_SEEDRAND: { + grand *rr = va_arg(ap, grand *); + octet buf[16]; + rr->ops->fill(rr, buf, sizeof(buf)); + prf_seed(g, buf, sizeof(buf)); + } break; + default: + GRAND_BADOP; + break; + } + + va_end(ap); + return (rc); +} + +static octet prf_grbyte(grand *r) +{ + prf_grctx *g = (prf_grctx *)r; + octet o; + tlsprf_encrypt(&g->prf, 0, &o, 1); + return (o); +} + +static uint32 prf_grword(grand *r) +{ + prf_grctx *g = (prf_grctx *)r; + octet b[4]; + tlsprf_encrypt(&g->prf, 0, &b, sizeof(b)); + return (LOAD32(b)); +} + +static void prf_grfill(grand *r, void *p, size_t sz) +{ + prf_grctx *g = (prf_grctx *)r; + tlsprf_encrypt(&g->prf, 0, p, sz); +} + +static const grand_ops prf_grops = { + "", + GRAND_CRYPTO, 0, + prf_grmisc, prf_grdestroy, + prf_grword, prf_grbyte, prf_grword, grand_range, prf_grfill +}; + +/* ---@tlsprf_rand@ --- * + * + * Arguments: @const gcmac *mcx, *mcy@ = MAC function to use + * @const void *k@ = pointer to the key material + * @size_t ksz@ = size of the key material + * @const void *sd@ = pointer to the seed material + * @size_t sdsz@ = size of the seed material + * + * Returns: Pointer to generic random number generator interface. + * + * Use: Creates a generic generator which does TLS data expansion. + */ + +grand *tlsprf_rand(const gcmac *mcx, const gcmac *mcy, + const void *k, size_t ksz, const void *sd, size_t sdsz) +{ + prf_grctx *g = S_CREATE(prf_grctx); + dstr d = DSTR_INIT; + octet *q = xmalloc(sdsz); + memcpy(q, sd, sdsz); + dstr_putf(&d, "tlsprf(%s,%s)", mcx->name, mcy->name); + g->ops = prf_grops; + g->ops.name = xstrdup(d.buf); + g->r.ops = &g->ops; + dstr_destroy(&d); + tlsprf_init(&g->prf, mcx, mcy, k, ksz, q, sdsz); + return (&g->r); +} + +/*----- Test rig ----------------------------------------------------------*/ + +#ifdef TEST_RIG + +#include +#include + +#include +#include + +#include "sha-hmac.h" +#include "md5-hmac.h" + +static int v_generate(dstr *v) +{ + grand *g; + dstr d = DSTR_INIT; + int ok = 1; + + g = tlsprf_rand(&md5_hmac, &sha_hmac, + v[0].buf, v[0].len, v[1].buf, v[1].len); + dstr_ensure(&d, v[2].len); + d.len = v[2].len; + g->ops->fill(g, d.buf, d.len); + g->ops->destroy(g); + if (memcmp(v[2].buf, d.buf, d.len) != 0) { + ok = 0; + printf("\nfail tlsprf:" + "\n\tkey = "); + type_hex.dump(&v[0], stdout); + printf("\n\tseed = "); type_hex.dump(&v[1], stdout); + printf("\n\texpected = "); type_hex.dump(&v[2], stdout); + printf("\n\tcalculated = "); type_hex.dump(&d, stdout); + putchar('\n'); + } + return (ok); +} + +static test_chunk defs[] = { + { "tlsprf", v_generate, { &type_hex, &type_hex, &type_hex, 0 } }, + { 0, 0, { 0 } } +}; + +int main(int argc, char *argv[]) +{ + test_run(argc, argv, defs, SRCDIR"/t/tlsprf"); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/