lilcrypto/wycheproof_hkdf.c

309 lines
6.8 KiB
C

/*
* Copyright (c) 2024 Lucas Gabriel Vuotto <lucas@lgv5.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <ctype.h>
#include <err.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lilcrypto.h"
#define nelems(_a) (sizeof((_a)) / sizeof((_a)[0]))
struct testcase {
uint8_t *ikm;
size_t ikmlen;
size_t ikmlenarg;
uint8_t *info;
size_t infolen;
uint8_t *okm;
size_t okmlen;
uint8_t *salt;
size_t saltlen;
size_t size;
};
struct kwrunner {
const char *kw;
int (*runner)(const struct testcase *, int);
};
static int hkdf_sha2_runner(const struct lc_hash_impl *,
const struct testcase *, int);
static int hkdf_sha224_runner(const struct testcase *, int);
static int hkdf_sha256_runner(const struct testcase *, int);
static int hkdf_sha384_runner(const struct testcase *, int);
static int hkdf_sha512_runner(const struct testcase *, int);
static inline uint8_t
hex2num(char s)
{
return s >= 'A' ? 10 + (s >= 'a' ? s - 'a' : s - 'A') : s - '0';
}
static int
hexparse(const char *s, uint8_t *out, size_t *outlen)
{
size_t l;
l = strlen(s);
if (l % 2 != 0)
return 0;
if (out == NULL) {
*outlen = l / 2;
return 1;
}
*outlen = 0;
while (*s != '\0') {
if (!isxdigit(s[0]) || !isxdigit(s[1]))
return 0;
*out++ = (hex2num(s[0]) << 4) | hex2num(s[1]);
(*outlen)++;
s += 2;
}
return 1;
}
static int
kwrunner_cmp(const void *k0, const void *h0)
{
const struct kwrunner *h = h0;
const char *k = k0;
return strcmp(k, h->kw);
}
static int
(*kw2runner(const char *s))(const struct testcase *, int)
{
/* Needs to be sorted. */
static const struct kwrunner tbl[] = {
{ "HKDF-SHA-224", &hkdf_sha224_runner },
{ "HKDF-SHA-256", &hkdf_sha256_runner },
{ "HKDF-SHA-384", &hkdf_sha384_runner },
{ "HKDF-SHA-512", &hkdf_sha512_runner },
};
struct kwrunner *match;
match = bsearch(s, tbl, nelems(tbl), sizeof(struct kwrunner),
&kwrunner_cmp);
return match != NULL ? match->runner : NULL;
}
static void
usage(void)
{
fprintf(stderr, "Usage: %s [options]\n", getprogname());
exit(1);
}
int
main(int argc, char *argv[])
{
int (*runner)(const struct testcase *, int);
const char *errstr;
struct testcase c;
size_t l;
int iflag, Kflag, kflag, oflag, sflag, zflag;
int ch, verbose;
if (argc < 2)
usage();
runner = kw2runner(argv[1]);
if (runner == NULL)
errx(1, "unsupported algorithm: %s", argv[1]);
optind = 2;
iflag = Kflag = kflag = oflag = sflag = zflag = 0;
verbose = 0;
while ((ch = getopt(argc, argv, "i:K:k:o:s:vz:")) != -1) {
switch (ch) {
case 'i':
iflag = 1;
(void)hexparse(optarg, NULL, &c.infolen);
if (c.infolen != 0) {
c.info = malloc(c.infolen);
if (c.info == NULL)
err(1, "out of memory");
} else
c.info = NULL;
if (!hexparse(optarg, c.info, &l) || l != c.infolen)
errx(1, "invalid hex string: %s", optarg);
break;
case 'K':
Kflag = 1;
c.ikmlenarg = strtonum(optarg, 0, LLONG_MAX, &errstr);
if (errstr != NULL)
errx(1, "keylen is %s: %s", errstr, optarg);
if (c.ikmlenarg % 8 != 0)
errx(1, "unsupport K value: %zu", c.ikmlenarg);
c.ikmlenarg /= 8;
break;
case 'k':
kflag = 1;
(void)hexparse(optarg, NULL, &c.ikmlen);
if (c.ikmlen != 0) {
c.ikm = malloc(c.ikmlen);
if (c.ikm == NULL)
err(1, "out of memory");
} else
c.ikm = NULL;
if (!hexparse(optarg, c.ikm, &l) || l != c.ikmlen)
errx(1, "invalid hex string: %s", optarg);
break;
case 'o':
oflag = 1;
(void)hexparse(optarg, NULL, &c.okmlen);
if (c.okmlen != 0) {
c.okm = malloc(c.okmlen);
if (c.okm == NULL)
err(1, "out of memory");
} else
c.okm = NULL;
if (!hexparse(optarg, c.okm, &l) || l != c.okmlen)
errx(1, "invalid hex string: %s", optarg);
break;
case 's':
sflag = 1;
(void)hexparse(optarg, NULL, &c.saltlen);
if (c.saltlen != 0) {
c.salt = malloc(c.saltlen);
if (c.salt == NULL)
err(1, "out of memory");
} else
c.salt = NULL;
if (!hexparse(optarg, c.salt, &l) || l != c.saltlen)
errx(1, "invalid hex string: %s", optarg);
break;
case 'v':
verbose = 1;
break;
case 'z':
zflag = 1;
c.size = strtonum(optarg, 0, LLONG_MAX, &errstr);
if (errstr != NULL)
errx(1, "taglen is %s: %s", errstr, optarg);
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (!(iflag && Kflag && kflag && oflag && sflag && zflag))
errx(1, "missing required arguments");
if (runner(&c, verbose)) {
puts("valid");
return 0;
} else {
puts("invalid");
return 0;
}
puts("valid");
return 0;
}
static int
hkdf_sha2_runner(const struct lc_hash_impl *impl, const struct testcase *c,
int verbose)
{
struct lc_hkdf_params params;
uint8_t *buf;
size_t olen;
if (c->ikmlen != c->ikmlenarg)
return 0;
params.ikm = c->ikm;
params.ikmlen = c->ikmlen;
params.info = c->info;
params.infolen = c->infolen;
params.salt = c->salt;
params.saltlen = c->saltlen;
params.hash = lc_hash_ctx_new(impl);
if (params.hash == NULL)
return 0;
params.hmac = lc_auth_ctx_new(lc_auth_impl_hmac());
if (params.hmac == NULL)
return 0;
buf = malloc(c->size);
if (buf == NULL)
err(1, "out of memory");
if (!lc_kdf(lc_kdf_impl_hkdf(), buf, &olen, &params, c->size))
return 0;
if (c->okmlen != olen ||
!lc_ct_cmp(buf, c->okm, olen)) {
if (verbose) {
fprintf(stderr, "okmlen (%zu, %zu)\n", c->okmlen,
olen);
lc_hexdump_fp(stderr, c->okm, c->okmlen);
fprintf(stderr, "\n");
lc_hexdump_fp(stderr, buf, olen);
fprintf(stderr, "\n");
}
return 0;
}
free(buf);
lc_hash_ctx_free(params.hash);
lc_auth_ctx_free(params.hmac);
return 1;
}
static int
hkdf_sha224_runner(const struct testcase *c, int verbose)
{
return hkdf_sha2_runner(lc_hash_impl_sha224(), c, verbose);
}
static int
hkdf_sha256_runner(const struct testcase *c, int verbose)
{
return hkdf_sha2_runner(lc_hash_impl_sha256(), c, verbose);
}
static int
hkdf_sha384_runner(const struct testcase *c, int verbose)
{
return hkdf_sha2_runner(lc_hash_impl_sha384(), c, verbose);
}
static int
hkdf_sha512_runner(const struct testcase *c, int verbose)
{
return hkdf_sha2_runner(lc_hash_impl_sha512(), c, verbose);
}