#include <string.h>
#include <time.h>
+#include <mLib/codec.h>
+#include <mLib/base32.h>
#include <mLib/base64.h>
+#include <mLib/hex.h>
#include <mLib/mdwopt.h>
#include <mLib/quis.h>
#include <mLib/report.h>
#include "gfreduce.h"
#include "key.h"
#include "mp.h"
+#include "mpint.h"
#include "mpmont.h"
#include "mprand.h"
#include "mptext.h"
#include "pgen.h"
#include "ptab.h"
#include "rsa.h"
+#include "x25519.h"
+#include "x448.h"
+#include "ed25519.h"
#include "cc.h"
#include "sha-mgf.h"
static void doopen(key_file *f, unsigned how)
{
- if (key_open(f, keyfile, how, key_moan, 0))
- die(1, "couldn't open keyring `%s': %s", keyfile, strerror(errno));
+ if (key_open(f, keyfile, how, key_moan, 0)){
+ die(EXIT_FAILURE, "couldn't open keyring `%s': %s",
+ keyfile, strerror(errno));
+ }
}
/* --- @doclose@ --- *
unsigned bits, qbits; /* Bit length for the new key */
const char *curve; /* Elliptic curve name/info */
grand *r; /* Random number source */
+ mp *e; /* Public exponent */
key *p; /* Parameters key-data */
} keyopts;
/* --- @copyparam@ --- *
*
* Arguments: @keyopts *k@ = pointer to key options
- * @const char **pp@ = checklist of parameters
+ * @const char **pp@ = checklist of parameters, or null
*
* Returns: Nonzero if parameters copied; zero if you have to generate
* them.
key_attriter i;
key_data *kd;
const char *n, *v;
+ dstr t = DSTR_INIT;
kf.f = KCAT_SHARE;
kf.m = KF_CATMASK;
if (!k->p)
return (0);
- /* --- Run through the checklist --- */
+ /* --- Copy the key data if there's anything we want --- */
- while (*pp) {
- key_data *kd = key_structfind(k->p->k, *pp);
- if (!kd)
- die(EXIT_FAILURE, "bad parameter key: parameter `%s' not found", *pp);
- if (!KEY_MATCH(kd, &kf))
- die(EXIT_FAILURE, "bad parameter key: subkey `%s' is not shared", *pp);
- pp++;
- }
+ if (pp) {
- /* --- Copy over the parameters --- */
+ /* --- Run through the checklist --- */
- kd = key_copydata(k->p->k, &kf);
- assert(kd);
- key_setkeydata(k->kf, k->k, kd);
- key_drop(kd);
+ key_fulltag(k->p, &t);
+ if ((k->p->k->e & KF_ENCMASK) != KENC_STRUCT)
+ die(EXIT_FAILURE, "parameter key `%s' is not structured", t.buf);
+ while (*pp) {
+ key_data *kd = key_structfind(k->p->k, *pp);
+ if (!kd) {
+ die(EXIT_FAILURE,
+ "bad parameter key `%s': parameter `%s' not found", t.buf, *pp);
+ }
+ if (!KEY_MATCH(kd, &kf)) {
+ die(EXIT_FAILURE,
+ "bad parameter key `%s': subkey `%s' is not shared", t.buf, *pp);
+ }
+ pp++;
+ }
+
+ /* --- Copy over the parameters --- */
+
+ kd = key_copydata(k->p->k, &kf);
+ assert(kd);
+ key_setkeydata(k->kf, k->k, kd);
+ key_drop(kd);
+ }
/* --- Copy over attributes --- */
/* --- Done --- */
+ dstr_destroy(&t);
return (1);
}
if (!k)
die(EXIT_FAILURE, "unexpected failure looking up subkey `%s'", tag);
if ((k->e & KF_ENCMASK) != KENC_MP)
- die(EXIT_FAILURE, "subkey `%s' has an incompatible type");
+ die(EXIT_FAILURE, "subkey `%s' has an incompatible type", tag);
return (k->u.m);
}
/* --- Key generation algorithms --- */
+static void alg_empty(keyopts *k)
+{
+ copyparam(k, 0);
+ key_setkeydata(k->kf, k->k,
+ key_newstring(KCAT_SHARE, k->curve ? k->curve : "."));
+}
+
static void alg_binary(keyopts *k)
{
unsigned sz;
if (!k->bits)
k->bits = 128;
- if (k->p)
- die(EXIT_FAILURE, "no shared parameters for binary keys");
+ copyparam(k, 0);
sz = (k->bits + 7) >> 3;
p = sub_alloc(sz);
if (!k->bits)
k->bits = 168;
- if (k->p)
- die(EXIT_FAILURE, "no shared parameters for DES keys");
+ copyparam(k, 0);
if (k->bits % 56 || k->bits > 168)
die(EXIT_FAILURE, "DES keys must be 56, 112 or 168 bits long");
/* --- Sanity checking --- */
- if (k->p)
- die(EXIT_FAILURE, "no shared parameters for RSA keys");
+ copyparam(k, 0);
if (!k->bits)
k->bits = 1024;
+ if (k->bits < 64)
+ die(EXIT_FAILURE, "RSA key too tiny");
+ if (!k->e)
+ k->e = mp_fromulong(MP_NEW, 65537);
/* --- Generate the RSA parameters --- */
- if (rsa_gen(&rp, k->bits, k->r, 0,
- (k->f & f_quiet) ? 0 : pgen_ev, 0))
+ if (rsa_gen_e(&rp, k->bits, k->e, k->r, 0,
+ (k->f & f_quiet) ? 0 : pgen_ev, 0))
die(EXIT_FAILURE, "RSA key generation failed");
/* --- Run a test encryption --- */
octet *p;
size_t sz;
dstr d = DSTR_INIT;
- base64_ctx c;
+ codec *c;
key_data *kd;
dsa_seed ds;
/* --- Store the seed for future verification --- */
- base64_init(&c);
- c.maxline = 0;
- c.indent = "";
- base64_encode(&c, ds.p, ds.sz, &d);
- base64_encode(&c, 0, 0, &d);
+ c = base64_class.encoder(0, "", 0);
+ c->ops->code(c, ds.p, ds.sz, &d); c->ops->code(c, 0, 0, &d);
+ c->ops->destroy(c);
DPUTZ(&d);
key_putattr(k->kf, k->k, "seed", d.buf);
DRESET(&d);
/* --- Sanity checking --- */
- if (k->p)
- die(EXIT_FAILURE, "no shared parameters for Blum-Blum-Shub keys");
+ copyparam(k, 0);
if (!k->bits)
k->bits = 1024;
mp_drop(x);
}
+#define XDHS(_) \
+ _(x25519, X25519, "X25519") \
+ _(x448, X448, "X448")
+
+#define XDHALG(xdh, XDH, name) \
+ \
+ static void alg_##xdh(keyopts *k) \
+ { \
+ key_data *kd, *kkd; \
+ octet priv[XDH##_KEYSZ], pub[XDH##_PUBSZ]; \
+ \
+ copyparam(k, 0); \
+ k->r->ops->fill(k->r, priv, sizeof(priv)); \
+ xdh(pub, priv, xdh##_base); \
+ kkd = key_newstruct(); \
+ key_structsteal(kkd, "priv", \
+ key_newbinary(KCAT_PRIV | KF_BURN, \
+ priv, sizeof(priv))); \
+ kd = key_newstruct(); \
+ key_structsteal(kd, "private", kkd); \
+ key_structsteal(kd, "pub", \
+ key_newbinary(KCAT_PUB, pub, sizeof(pub))); \
+ \
+ key_setkeydata(k->kf, k->k, kd); \
+ }
+
+XDHS(XDHALG)
+#undef XDHALG
+
+#define EDDSAS(_) \
+ _(ed25519, ED25519, "Ed25519")
+
+#define EDDSAALG(ed, ED, name) \
+ \
+ static void alg_##ed(keyopts *k) \
+ { \
+ key_data *kd, *kkd; \
+ octet priv[ED##_KEYSZ], pub[ED##_PUBSZ]; \
+ \
+ copyparam(k, 0); \
+ k->r->ops->fill(k->r, priv, sizeof(priv)); \
+ ed##_pubkey(pub, priv, sizeof(priv)); \
+ kkd = key_newstruct(); \
+ key_structsteal(kkd, "priv", \
+ key_newbinary(KCAT_PRIV | KF_BURN, \
+ priv, sizeof(priv))); \
+ kd = key_newstruct(); \
+ key_structsteal(kd, "private", kkd); \
+ key_structsteal(kd, "pub", \
+ key_newbinary(KCAT_PUB, pub, sizeof(pub))); \
+ \
+ key_setkeydata(k->kf, k->k, kd); \
+ }
+
+EDDSAS(EDDSAALG)
+#undef EDDSAALG
+
/* --- The algorithm tables --- */
typedef struct keyalg {
{ "bindh-param", alg_binparam, "Binary-field DH parameters" },
{ "ec-param", alg_ecparam, "Elliptic curve parameters" },
{ "ec", alg_ec, "Elliptic curve crypto" },
+#define XDHTAB(xdh, XDH, name) \
+ { #xdh, alg_##xdh, "" name " key exchange" },
+ XDHS(XDHTAB)
+#undef XDHTAB
+#define EDDSATAB(ed, ED, name) \
+ { #ed, alg_##ed, "" name " digital signatures" },
+ EDDSAS(EDDSATAB)
+#undef EDDSATAB
+ { "empty", alg_empty, "Empty parametrs-only key" },
{ 0, 0 }
};
keyalg *alg = algtab;
const char *rtag = 0;
const struct seedalg *sa = SEEDALG_DEFAULT;
- keyopts k = { 0, 0, DSTR_INIT, 0, 0, 0, 0, 0 };
+ keyopts k = { 0, 0, DSTR_INIT, 0, 0, 0, 0, 0, 0 };
const char *seed = 0;
k.r = &rand_global;
{ "seedalg", OPTF_ARGREQ, 0, 'A' },
{ "seed", OPTF_ARGREQ, 0, 's' },
{ "newseed", OPTF_ARGREQ, 0, 'n' },
+ { "public-exponent", OPTF_ARGREQ, 0, 'E' },
{ "lock", 0, 0, 'l' },
{ "quiet", 0, 0, 'q' },
{ "lim-lee", 0, 0, 'L' },
{ "kcdsa", 0, 0, 'K' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "+a:b:B:p:e:c:t:R:I:C:A:s:n:lqrLKS",
+ int i = mdwopt(argc, argv, "+a:b:B:p:e:c:t:R:I:C:A:s:n:E:lqrLKS",
opt, 0, 0, 0);
if (i < 0)
break;
} break;
case 's': {
- base64_ctx b;
+ codec *c;
+ int rc;
dstr d = DSTR_INIT;
if (seed) die(EXIT_FAILURE, "seed already set");
- base64_init(&b);
- base64_decode(&b, optarg, strlen(optarg), &d);
- base64_decode(&b, 0, 0, &d);
+ c = base64_class.decoder(CDCF_IGNEQPAD);
+ if ((rc = c->ops->code(c, optarg, strlen(optarg), &d)) != 0 ||
+ (rc = c->ops->code(c, 0, 0, &d)) != 0)
+ die(EXIT_FAILURE, "invalid seed base64: %s", codec_strerror(rc));
+ c->ops->destroy(c);
k.r = sa->gen(d.buf, d.len);
seed = optarg;
dstr_destroy(&d);
} break;
case 'n': {
- base64_ctx b;
+ codec *c;
dstr d = DSTR_INIT;
char *p;
unsigned n = strtoul(optarg, &p, 0);
n /= 8;
p = xmalloc(n);
rand_get(RAND_GLOBAL, p, n);
- base64_init(&b);
- base64_encode(&b, p, n, &d);
- base64_encode(&b, 0, 0, &d);
+ c = base64_class.encoder(0, "", 0);
+ c->ops->code(c, p, n, &d); c->ops->code(c, 0, 0, &d);
+ c->ops->destroy(c);
seed = d.buf;
k.r = sa->gen(p, n);
} break;
kid = id;
} break;
+ /* --- Public exponent --- */
+
+ case 'E': {
+ char *p;
+ k.e = mp_readstring(k.e, optarg, &p, 0);
+ if (!k.e || *p || MP_CMP(k.e, <, MP_THREE) || MP_EVENP(k.e))
+ die(EXIT_FAILURE, "bad exponent `%s'", optarg);
+ } break;
+
/* --- Other flags --- */
case 'R':
/* --- Find the parameter key --- */
- if (ptag) {
- if ((k.p = key_bytag(&f, ptag)) == 0)
- die(EXIT_FAILURE, "parameter key `%s' not found", ptag);
- if ((k.p->k->e & KF_ENCMASK) != KENC_STRUCT)
- die(EXIT_FAILURE, "parameter key `%s' is not structured", ptag);
- }
+ if (ptag && (k.p = key_bytag(&f, ptag)) == 0)
+ die(EXIT_FAILURE, "parameter key `%s' not found", ptag);
/* --- Now generate the actual key data --- */
/* --- @cmd_finger@ --- */
-static void fingerprint(key *k, const gchash *ch, const key_filter *kf)
+static const struct fpres {
+ const char *name;
+ const codec_class *cdc;
+ unsigned short ival;
+ const char *sep;
+} fprestab[] = {
+ { "hex", &hex_class, 8, "-:" },
+ { "base32", &base32_class, 6, ":" },
+ { 0, 0 }
+};
+
+static void fingerprint(key *k, const struct fpres *fpres,
+ const gchash *ch, const key_filter *kf)
{
ghash *h;
- dstr d = DSTR_INIT;
+ dstr d = DSTR_INIT, dd = DSTR_INIT;
const octet *p;
size_t i;
+ codec *c;
h = GH_INIT(ch);
if (key_fingerprint(k, h, kf)) {
p = GH_DONE(h, 0);
- key_fulltag(k, &d);
- for (i = 0; i < ch->hashsz; i++) {
- if (i && i % 4 == 0)
- putchar('-');
- printf("%02x", p[i]);
+ c = fpres->cdc->encoder(CDCF_LOWERC | CDCF_NOEQPAD, "", 0);
+ c->ops->code(c, p, ch->hashsz, &dd); c->ops->code(c, 0, 0, &dd);
+ c->ops->destroy(c);
+ for (i = 0; i < dd.len; i++) {
+ if (i && i%fpres->ival == 0) dstr_putc(&d, fpres->sep[0]);
+ dstr_putc(&d, dd.buf[i]);
}
- printf(" %s\n", d.buf);
+ dstr_putc(&d, ' '); key_fulltag(k, &d); dstr_putc(&d, '\n');
+ dstr_write(&d, stdout);
}
- dstr_destroy(&d);
+ dstr_destroy(&d); dstr_destroy(&dd);
GH_DESTROY(h);
}
+static const struct fpres *lookup_fpres(const char *name)
+{
+ const struct fpres *fpres;
+ for (fpres = fprestab; fpres->name; fpres++)
+ if (strcmp(fpres->name, name) == 0) return (fpres);
+ die(EXIT_FAILURE, "unknown presentation syle `%s'", name);
+}
+
static int cmd_finger(int argc, char *argv[])
{
key_file f;
int rc = 0;
+ const struct fpres *fpres = fprestab;
const gchash *ch = &rmd160;
key_filter kf = { KF_NONSECRET, KF_NONSECRET };
for (;;) {
static struct option opt[] = {
{ "filter", OPTF_ARGREQ, 0, 'f' },
+ { "presentation", OPTF_ARGREQ, 0, 'p' },
{ "algorithm", OPTF_ARGREQ, 0, 'a' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "+f:a:", opt, 0, 0, 0);
+ int i = mdwopt(argc, argv, "+f:a:p:", opt, 0, 0, 0);
if (i < 0)
break;
switch (i) {
if (err || *p)
die(EXIT_FAILURE, "bad filter string `%s'", optarg);
} break;
+ case 'p':
+ fpres = lookup_fpres(optarg);
+ break;
case 'a':
if ((ch = ghash_byname(optarg)) == 0)
die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg);
}
argv += optind; argc -= optind;
- if (rc)
- die(EXIT_FAILURE, "Usage: fingerprint [-f FILTER] [TAG...]");
+ if (rc) {
+ die(EXIT_FAILURE,
+ "Usage: fingerprint [-a HASHALG] [-p STYLE] [-f FILTER] [TAG...]");
+ }
doopen(&f, KOPEN_READ);
for (i = 0; i < argc; i++) {
key *k = key_bytag(&f, argv[i]);
if (k)
- fingerprint(k, ch, &kf);
+ fingerprint(k, fpres, ch, &kf);
else {
rc = 1;
moan("key `%s' not found", argv[i]);
key_iter i;
key *k;
for (key_mkiter(&i, &f); (k = key_next(&i)) != 0; )
- fingerprint(k, ch, &kf);
+ fingerprint(k, fpres, ch, &kf);
}
return (rc);
}
/* --- @cmd_verify@ --- */
-static unsigned xdigit(char c)
-{
- if ('A' <= c && c <= 'Z') return (c + 10 - 'A');
- if ('a' <= c && c <= 'z') return (c + 10 - 'a');
- if ('0' <= c && c <= '9') return (c - '0');
- return (~0u);
-}
-
-static void unhexify(octet *q, char *p, size_t n)
-{
- unsigned a = 0;
- int i = 0;
-
- for (;;) {
- if (*p == '-' || *p == ':' || isspace((unsigned char)*p)) {
- p++;
- continue;
- }
- if (!n && !*p)
- break;
- if (!*p)
- die(EXIT_FAILURE, "hex string too short");
- if (!isxdigit((unsigned char)*p))
- die(EXIT_FAILURE, "bad hex string");
- if (!n)
- die(EXIT_FAILURE, "hex string too long");
- a = (a << 4) | xdigit(*p++);
- i++;
- if (i == 2) {
- *q++ = U8(a);
- a = 0;
- i = 0;
- n--;
- }
- }
-}
-
static int cmd_verify(int argc, char *argv[])
{
key_file f;
const gchash *ch = &rmd160;
ghash *h;
key *k;
- octet *buf;
const octet *fpr;
+ dstr d = DSTR_INIT, dd = DSTR_INIT;
+ codec *c;
+ const char *p;
+ const struct fpres *fpres = fprestab;
key_filter kf = { KF_NONSECRET, KF_NONSECRET };
for (;;) {
static struct option opt[] = {
{ "filter", OPTF_ARGREQ, 0, 'f' },
+ { "presentation", OPTF_ARGREQ, 0, 'p' },
{ "algorithm", OPTF_ARGREQ, 0, 'a' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "+f:a:", opt, 0, 0, 0);
+ int i = mdwopt(argc, argv, "+f:a:p:", opt, 0, 0, 0);
if (i < 0)
break;
switch (i) {
if (err || *p)
die(EXIT_FAILURE, "bad filter string `%s'", optarg);
} break;
+ case 'p':
+ fpres = lookup_fpres(optarg);
+ break;
case 'a':
if ((ch = ghash_byname(optarg)) == 0)
die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg);
}
argv += optind; argc -= optind;
- if (rc || argc != 2)
- die(EXIT_FAILURE, "Usage: verify [-f FILTER] TAG FINGERPRINT");
+ if (rc || argc != 2) {
+ die(EXIT_FAILURE,
+ "Usage: verify [-a HASHALG] [-p STYLE] [-f FILTER] TAG FINGERPRINT");
+ }
doopen(&f, KOPEN_READ);
if ((k = key_bytag(&f, argv[0])) == 0)
die(EXIT_FAILURE, "key `%s' not found", argv[0]);
- buf = xmalloc(ch->hashsz);
- unhexify(buf, argv[1], ch->hashsz);
+ for (p = argv[1]; *p; p++) {
+ if (strchr(fpres->sep, *p)) continue;
+ dstr_putc(&dd, *p);
+ }
+ c = fpres->cdc->decoder(CDCF_IGNCASE | CDCF_IGNEQPAD |
+ CDCF_IGNSPC | CDCF_IGNNEWL);
+ if ((rc = c->ops->code(c, dd.buf, dd.len, &d)) != 0 ||
+ (rc = c->ops->code(c, 0, 0, &d)) != 0)
+ die(EXIT_FAILURE, "invalid fingerprint: %s", codec_strerror(rc));
+ c->ops->destroy(c);
+ if (d.len != ch->hashsz) {
+ die(EXIT_FAILURE, "incorrect fingerprint length (%lu != %lu)",
+ (unsigned long)d.len, (unsigned long)ch->hashsz);
+ }
h = GH_INIT(ch);
if (!key_fingerprint(k, h, &kf))
die(EXIT_FAILURE, "key has no fingerprintable components (as filtered)");
fpr = GH_DONE(h, 0);
- if (memcmp(fpr, buf, ch->hashsz) != 0)
+ if (memcmp(fpr, d.buf, ch->hashsz) != 0)
die(EXIT_FAILURE, "key fingerprint mismatch");
+ dstr_destroy(&d); dstr_destroy(&dd);
doclose(&f);
return (0);
}
LI("Key-generation algorithms", keygen, \
algtab[i].name, algtab[i].name) \
LI("Random seeding algorithms", seed, \
- seedtab[i].p, seedtab[i].p)
+ seedtab[i].p, seedtab[i].p) \
+ LI("Fingerprint presentation styles", fpres, \
+ fprestab[i].name, fprestab[i].name)
MAKELISTTAB(listtab, LISTS)
-q, --quiet Show less information.\n\
-v, --verbose Show more information.\n\
" },
- { "fingerprint", cmd_finger, "fingerprint [-f FILTER] [TAG...]", "\
+ { "fingerprint", cmd_finger,
+ "fingerprint [-a HASHALG] [-p STYLE] [-f FILTER] [TAG...]", "\
Options:\n\
\n\
-f, --filter=FILT Only hash key components matching FILT.\n\
+-p, --presentation=STYLE Use STYLE for presenting fingerprints.\n\
-a, --algorithm=HASH Use the named HASH algorithm.\n\
($ show hash for list.)\n\
" },
- { "verify", cmd_verify, "verify [-f FILTER] TAG FINGERPRINT", "\
+ { "verify", cmd_verify,
+ "verify [-a HASH] [-p STYLE] [-f FILTER] TAG FINGERPRINT", "\
Options:\n\
\n\
-f, --filter=FILT Only hash key components matching FILT.\n\
+-p, --presentation=STYLE Expect FINGERPRINT in the given STYLE.\n\
-a, --algorithm=HASH Use the named HASH algorithm.\n\
($ show hash for list.)\n\
" },