/* * Copyright (c) 2024 Lucas Gabriel Vuotto * * 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 #include #include #include #include #include #include #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, ¶ms, 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); }