X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba6e6b64033b1f9de49feccb5c9cd438354481f7..0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a:/key/key-text.c?ds=sidebyside diff --git a/key/key-text.c b/key/key-text.c new file mode 100644 index 0000000..98c1fba --- /dev/null +++ b/key/key-text.c @@ -0,0 +1,344 @@ +/* -*-c-*- + * + * Key textual encoding + * + * (c) 1999 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 +#include +#include +#include +#include +#include + +#include "key-data.h" +#include "mp.h" +#include "mptext.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @key_read@ --- * + * + * Arguments: @const char *p@ = pointer to textual key representation + * @char **pp@ = where to store the end pointer + * + * Returns: The newly-read key data, or null if it failed. + * + * Use: Parses a textual key description. + */ + +key_data *key_read(const char *p, char **pp) +{ + unsigned e; + key_data *kd; + + /* --- Read the encoding type --- * + * + * The key format is `[FLAGS:]DATA'. If there is no encoding type + * named, assume that it's `binary' for backwards compatibility. + */ + + if (strchr(p, ':') == 0) + e = 0; + else { + char *q; + if (key_readflags(p, &q, &e, 0)) + return (0); + p = q + 1; + } + + /* --- Now scan the data based on the encoding type --- */ + + switch (e & KF_ENCMASK) { + + /* --- Binary encoding --- * + * + * Simply read out the Base64-encoded data. Since `,' and `]' are our + * delimeter characters, and they can't appear in Base64-encoded data, I + * can just do a simple search to find the end of the encoded data. + */ + + case KENC_BINARY: + case KENC_ENCRYPT: { + dstr d = DSTR_INIT; + base64_ctx b; + size_t sz = strcspn(p, ",]"); + + base64_init(&b); + base64_decode(&b, p, sz, &d); + base64_decode(&b, 0, 0, &d); + kd = key_newbinary(e, d.buf, d.len); + dstr_destroy(&d); + p += sz; + } break; + + /* --- Multiprecision integer encoding --- * + * + * Multiprecision integers have a convenient reading function. + */ + + case KENC_MP: { + char *q; + mp *m = mp_readstring(e & KF_BURN ? MP_NEWSEC : MP_NEW, p, &q, 0); + if (!m) + return (0); + kd = key_newmp(e, m); + MP_DROP(m); + p = q; + } break; + + /* --- String encoding --- * + * + * We use form-urlencoding to ensure that evil characters don't get out. + */ + + case KENC_STRING: { + dstr d = DSTR_INIT; + size_t sz = strcspn(p, ",]"); + const char *l = p + sz; + unsigned int ch; + int x, n; + + while (p < l) { + switch (*p) { + case '+': + DPUTC(&d, ' '); break; + case '%': + x = sscanf(p + 1, "%2x%n", &ch, &n); + if (x == 1) { DPUTC(&d, ch); p += n; break; } + default: + DPUTC(&d, *p); break; + } + p++; + } + DPUTZ(&d); + kd = key_newstring(e, d.buf); + dstr_destroy(&d); + } break; + + /* --- Elliptic curve encoding --- * + * + * Again, we have a convenient function. Assume for now that points + * aren't secret. (Reasonably safe.) + */ + + case KENC_EC: { + ec pt = EC_INIT; + qd_parse qd; + qd.p = p; + qd.e = 0; + if (!ec_ptparse(&qd, &pt)) + return (0); + kd = key_newec(e, &pt); + EC_DESTROY(&pt); + p = qd.p; + } break; + + /* --- Structured information encoding --- * + * + * The format for structured key data is `[NAME=KEY,...]', where the + * brackets are part of the syntax. Structured keys have no flags apart + * from the encoding. + * + * The binary encoding only allows names up to 255 bytes long. Check for + * this here. + */ + + case KENC_STRUCT: { + dstr d = DSTR_INIT; + key_data *nkd; + char *q; + + /* --- Read the opening bracket --- */ + + kd = key_newstruct(); + if (*p != '[') + return (0); + p++; + + /* --- Read named key subparts --- */ + + for (;;) { + size_t sz; + + /* --- Stop if there's a close-bracket --- * + * + * This allows `[]' to be an empty structured key, which is good. It + * also makes `[foo=enc:bar,]' legal, and that's less good but I can + * live with it. + */ + + if (*p == ']') + break; + + /* --- Read the name out and check the length --- */ + + if ((q = strchr(p, '=')) == 0) + goto fail; + sz = q - p; + if (sz >= 256) + goto fail; + DRESET(&d); + DPUTM(&d, p, sz); + DPUTZ(&d); + + /* --- Read the key data for the subkey --- */ + + if ((nkd = key_read(q + 1, &q)) == 0) + goto fail; + key_structsteal(kd, d.buf, nkd); + p = q; + + /* --- Read the comma or close-bracket --- */ + + if (*p == ']') + break; + else if (*p == ',') + p++; + else + goto fail; + } + + /* --- Step past the close bracket --- */ + + p++; + dstr_destroy(&d); + break; + + /* --- Tidy up after a failure --- */ + + fail: + dstr_destroy(&d); + return (0); + } break; + + /* --- Anything else is unknown --- */ + + default: + return (0); + } + + /* --- Return the end pointer --- */ + + kd->e = e; + if (pp) + *pp = (char *)p; + return (kd); +} + +/* --- @key_write@ --- * + * + * Arguments: @key_data *k@ = pointer to key data + * @dstr *d@ = destination string to write on + * @const key_filter *kf@ = pointer to key selection block + * + * Returns: Nonzero if an item was actually written. + * + * Use: Writes a key in a textual encoding. + */ + +int key_write(key_data *k, dstr *d, const key_filter *kf) +{ + int rc = 0; + if (!KEY_MATCH(k, kf)) + return (0); + switch (k->e & KF_ENCMASK) { + case KENC_BINARY: + case KENC_ENCRYPT: { + base64_ctx b; + + if ((k->e & KF_ENCMASK) == KENC_BINARY) + key_writeflags(k->e, d); + else + DPUTS(d, "encrypt,secret"); + DPUTC(d, ':'); + base64_init(&b); + b.indent = ""; + b.maxline = 0; + base64_encode(&b, k->u.k.k, k->u.k.sz, d); + base64_encode(&b, 0, 0, d); + rc = 1; + } break; + case KENC_MP: + key_writeflags(k->e, d); + DPUTC(d, ':'); + mp_writedstr(k->u.m, d, 10); + rc = 1; + break; + case KENC_STRING: { + const char *p = k->u.p; + key_writeflags(k->e, d); + DPUTC(d, ':'); + while (*p) { + if (*p == ' ') DPUTC(d, '+'); + else if (!isalnum((unsigned char)*p)) dstr_putf(d, "%%%02x", *p); + else DPUTC(d, *p); + p++; + } + rc = 1; + } break; + case KENC_EC: + key_writeflags(k->e, d); + DPUTS(d, ":0x"); mp_writedstr(k->u.e.x, d, 16); + DPUTS(d, ",0x"); mp_writedstr(k->u.e.y, d, 16); + rc = 1; + break; + case KENC_STRUCT: { + key_subkeyiter i; + const char *tag; + char del = 0; + size_t n = d->len; + + DPUTS(d, "struct:["); + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, &tag, &k); ) { + size_t o = d->len; + if (del) + DPUTC(d, del); + DPUTS(d, tag); + DPUTC(d, '='); + if (!key_write(k, d, kf)) + d->len = o; + else { + del = ','; + rc = 1; + } + } + if (!rc) + d->len = n; + else + DPUTC(d, ']'); + } break; + } + DPUTZ(d); + + return (rc); +} + +/*----- That's all, folks -------------------------------------------------*/