NetBSD/sbin/cgdconfig/params.c

1111 lines
24 KiB
C

/* $NetBSD: params.c,v 1.35 2024/05/12 18:02:16 christos Exp $ */
/*-
* Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Roland C. Dowdeswell.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: params.c,v 1.35 2024/05/12 18:02:16 christos Exp $");
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sha2.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <util.h>
#include <uuid.h>
#ifdef HAVE_ARGON2
#include <argon2.h>
#include "argon2_utils.h"
#endif
#include "params.h"
#include "pkcs5_pbkdf2.h"
#include "utils.h"
#include "cgdconfig.h"
#include "extern.h"
static void params_init(struct params *);
static void print_kvpair_cstr(FILE *, int, const char *, const char *);
static void print_kvpair_string(FILE *, int, const char *, const string_t *);
static void print_kvpair_int(FILE *, int, const char *, size_t);
static void print_kvpair_b64(FILE *, int, int, const char *, bits_t *);
static void spaces(FILE *, int);
/* keygen defaults */
#define DEFAULT_SALTLEN 128
#define DEFAULT_ITERATION_TIME 2000000 /* 1 second in microseconds */
/* crypto defaults functions */
static struct crypto_defaults {
char alg[32];
int keylen;
} crypto_defaults[] = {
{ "adiantum", 256 },
{ "aes-cbc", 128 },
{ "aes-xts", 256 },
{ "3des-cbc", 192 },
{ "blowfish-cbc", 128 }
};
static int crypt_defaults_lookup(const char *);
struct params *
params_new(void)
{
struct params *p;
p = emalloc(sizeof(*p));
params_init(p);
return p;
}
static void
params_init(struct params *p)
{
p->algorithm = NULL;
p->ivmeth = NULL;
p->key = NULL;
p->keylen = (size_t)-1;
p->bsize = (size_t)-1;
p->verify_method = VERIFY_UNKNOWN;
p->dep_keygen = NULL;
p->keygen = NULL;
}
void
params_free(struct params *p)
{
if (!p)
return;
string_free(p->algorithm);
string_free(p->ivmeth);
keygen_free(p->dep_keygen);
keygen_free(p->keygen);
}
struct params *
params_combine(struct params *p1, struct params *p2)
{
struct params *p;
if (p1)
p = p1;
else
p = params_new();
if (!p2)
return p;
if (p2->algorithm)
string_assign(&p->algorithm, p2->algorithm);
if (p2->ivmeth)
string_assign(&p->ivmeth, p2->ivmeth);
if (p2->keylen != (size_t)-1)
p->keylen = p2->keylen;
if (p2->bsize != (size_t)-1)
p->bsize = p2->bsize;
if (p2->verify_method != VERIFY_UNKNOWN)
p->verify_method = p2->verify_method;
p->dep_keygen = keygen_combine(p->dep_keygen, p2->dep_keygen);
keygen_addlist(&p->keygen, p2->keygen);
/*
* at this point we should have moved all allocated data
* in p2 into p, so we can free it.
*/
free(p2);
return p;
}
int
params_filldefaults(struct params *p)
{
size_t i;
if (p->verify_method == VERIFY_UNKNOWN)
p->verify_method = VERIFY_NONE;
if (!p->ivmeth)
p->ivmeth = string_fromcharstar("encblkno1");
if (p->keylen == (size_t)-1) {
if (p->algorithm == NULL)
return -1;
i = crypt_defaults_lookup(string_tocharstar(p->algorithm));
if (i != (size_t)-1) {
p->keylen = crypto_defaults[i].keylen;
} else {
warnx("could not determine key length for unknown "
"algorithm \"%s\"",
string_tocharstar(p->algorithm));
return -1;
}
}
return 0;
}
/*
* params_verify traverses the parameters and all of the keygen methods
* looking for inconsistencies. It outputs warnings on non-fatal errors
* such as unknown encryption methods, but returns failure on fatal
* conditions such as a PKCS5_PBKDF2 keygen without a salt. It is intended
* to run before key generation.
*/
int
params_verify(const struct params *p)
{
static const char *encblkno[] = {
"encblkno", "encblkno1", "encblkno8"
};
static size_t i;
const char *meth;
if (!p->algorithm) {
warnx("unspecified algorithm");
return 0;
}
/*
* we only warn for the encryption method so that it is possible
* to use an older cgdconfig(8) with a new kernel that supports
* additional crypto algorithms.
*/
if (crypt_defaults_lookup(string_tocharstar(p->algorithm)) == -1)
warnx("unknown algorithm \"%s\"(warning)",
string_tocharstar(p->algorithm));
/* same rationale with IV methods. */
if (!p->ivmeth) {
warnx("unspecified IV method");
return 0;
}
meth = string_tocharstar(p->ivmeth);
for (i = 0; i < __arraycount(encblkno); i++)
if (strcmp(encblkno[i], meth) == 0)
break;
if (i == __arraycount(encblkno))
warnx("unknown IV method \"%s\" (warning)", meth);
if (p->keylen == (size_t)-1) {
warnx("unspecified key length");
return 0;
}
return keygen_verify(p->keygen);
}
struct params *
params_algorithm(string_t *in)
{
struct params *p = params_new();
p->algorithm = in;
return p;
}
struct params *
params_ivmeth(string_t *in)
{
struct params *p = params_new();
p->ivmeth = in;
return p;
}
struct params *
params_keylen(size_t in)
{
struct params *p = params_new();
p->keylen = in;
return p;
}
struct params *
params_bsize(size_t in)
{
struct params *p = params_new();
p->bsize = in;
return p;
}
struct params *
params_verify_method(string_t *in)
{
struct params *p = params_new();
const char *vm = string_tocharstar(in);
if (!strcmp("none", vm))
p->verify_method = VERIFY_NONE;
if (!strcmp("disklabel", vm))
p->verify_method = VERIFY_DISKLABEL;
if (!strcmp("ffs", vm))
p->verify_method = VERIFY_FFS;
if (!strcmp("re-enter", vm))
p->verify_method = VERIFY_REENTER;
if (!strcmp("mbr", vm))
p->verify_method = VERIFY_MBR;
if (!strcmp("gpt", vm))
p->verify_method = VERIFY_GPT;
#ifdef HAVE_ZFS
if (!strcmp("zfs", vm))
p->verify_method = VERIFY_ZFS;
#endif
string_free(in);
if (p->verify_method == VERIFY_UNKNOWN)
warnx("params_setverify_method: unrecognized "
"verify method \"%s\"", vm);
return p;
}
struct params *
params_keygen(struct keygen *in)
{
struct params *p = params_new();
p->keygen = in;
return p;
}
struct params *
params_dep_keygen(struct keygen *in)
{
struct params *p = params_new();
p->dep_keygen = in;
return p;
}
struct keygen *
keygen_new(void)
{
struct keygen *kg;
kg = emalloc(sizeof(*kg));
kg->kg_method = KEYGEN_UNKNOWN;
kg->kg_iterations = (size_t)-1;
kg->kg_memory = (size_t)-1;
kg->kg_parallelism = (size_t)-1;
kg->kg_version = (size_t)-1;
kg->kg_salt = NULL;
kg->kg_key = NULL;
kg->kg_cmd = NULL;
kg->kg_sharedid = NULL;
kg->kg_sharedalg = SHARED_ALG_UNKNOWN;
kg->kg_sharedlen = (size_t)-1;
kg->kg_sharedinfo = NULL;
kg->next = NULL;
return kg;
}
void
keygen_free(struct keygen *kg)
{
if (!kg)
return;
bits_free(kg->kg_salt);
bits_free(kg->kg_key);
string_free(kg->kg_cmd);
string_free(kg->kg_sharedid);
bits_free(kg->kg_sharedinfo);
keygen_free(kg->next);
free(kg);
}
/*
* keygen_verify traverses the keygen structures and ensures
* that the appropriate information is available.
*/
int
keygen_verify(const struct keygen *kg)
{
if (!kg)
return 1;
switch (kg->kg_method) {
#ifdef HAVE_ARGON2
case KEYGEN_ARGON2ID:
if (kg->kg_iterations == (size_t)-1) {
warnx("keygen argon2id must provide `iterations'");
return 0;
}
if (kg->kg_memory == (size_t)-1) {
warnx("keygen argon2id must provide `memory'");
return 0;
}
if (kg->kg_parallelism == (size_t)-1) {
warnx("keygen argon2id must provide `parallelism'");
return 0;
}
if (kg->kg_version == (size_t)-1) {
warnx("keygen argon2id must provide `version'");
return 0;
}
if (kg->kg_cmd)
warnx("keygen argon2id does not need a `cmd'");
if (kg->kg_key)
warnx("keygen argon2id does not need a `key'");
if (!kg->kg_salt) {
warnx("keygen argon2id must provide a salt");
return 0;
}
break;
#endif
case KEYGEN_PKCS5_PBKDF2_OLD:
if (kg->kg_iterations == (size_t)-1) {
warnx("keygen pkcs5_pbkdf2 must provide `iterations'");
return 0;
}
if (kg->kg_key)
warnx("keygen pkcs5_pbkdf2 does not need a `key'");
if (!kg->kg_salt) {
warnx("keygen pkcs5_pbkdf2 must provide a salt");
return 0;
}
if (kg->kg_cmd)
warnx("keygen pkcs5_pbkdf2 does not need a `cmd'");
break;
case KEYGEN_PKCS5_PBKDF2_SHA1:
if (kg->kg_iterations == (size_t)-1) {
warnx("keygen pkcs5_pbkdf2/sha1 must provide `iterations'");
return 0;
}
if (kg->kg_key)
warnx("keygen pkcs5_pbkdf2/sha1 does not need a `key'");
if (!kg->kg_salt) {
warnx("keygen pkcs5_pbkdf2/sha1 must provide a salt");
return 0;
}
if (kg->kg_cmd)
warnx("keygen pkcs5_pbkdf2/sha1 does not need a `cmd'");
break;
case KEYGEN_STOREDKEY:
if (kg->kg_iterations != (size_t)-1)
warnx("keygen storedkey does not need `iterations'");
if (!kg->kg_key) {
warnx("keygen storedkey must provide a key");
return 0;
}
if (kg->kg_salt)
warnx("keygen storedkey does not need `salt'");
if (kg->kg_cmd)
warnx("keygen storedkey does not need `cmd'");
break;
case KEYGEN_RANDOMKEY:
case KEYGEN_URANDOMKEY:
if (kg->kg_iterations != (size_t)-1)
warnx("keygen [u]randomkey does not need `iterations'");
if (kg->kg_key)
warnx("keygen [u]randomkey does not need `key'");
if (kg->kg_salt)
warnx("keygen [u]randomkey does not need `salt'");
if (kg->kg_cmd)
warnx("keygen [u]randomkey does not need `cmd'");
if (kg->kg_sharedid)
warnx("keygen [u]randomkey makes no sense shared");
break;
case KEYGEN_SHELL_CMD:
if (kg->kg_iterations != (size_t)-1)
warnx("keygen shell_cmd does not need `iterations'");
if (kg->kg_key)
warnx("keygen shell_cmd does not need `key'");
if (kg->kg_salt)
warnx("keygen shell_cmd does not need `salt'");
if (!kg->kg_cmd) {
warnx("keygen shell_cmd must provide a `cmd'");
return 0;
}
break;
}
return keygen_verify(kg->next);
}
struct keygen *
keygen_generate(int method)
{
struct keygen *kg;
kg = keygen_new();
if (!kg)
return NULL;
kg->kg_method = method;
return kg;
}
/*
* keygen_filldefaults walks the keygen list and fills in
* default values. The defaults may be either calibrated
* or randomly generated so this function is designed to be
* called when generating a new parameters file, not when
* reading a parameters file.
*/
int
keygen_filldefaults(struct keygen *kg, size_t keylen)
{
if (!kg)
return 0;
switch (kg->kg_method) {
case KEYGEN_RANDOMKEY:
case KEYGEN_URANDOMKEY:
case KEYGEN_SHELL_CMD:
break;
#ifdef HAVE_ARGON2
case KEYGEN_ARGON2ID:
kg->kg_version = ARGON2_VERSION_NUMBER;
kg->kg_salt = bits_getrandombits(DEFAULT_SALTLEN, 1);
argon2id_calibrate(BITS2BYTES(keylen), DEFAULT_SALTLEN,
&kg->kg_iterations, &kg->kg_memory, &kg->kg_parallelism);
break;
#endif
case KEYGEN_PKCS5_PBKDF2_OLD:
case KEYGEN_PKCS5_PBKDF2_SHA1:
kg->kg_salt = bits_getrandombits(DEFAULT_SALTLEN, 1);
kg->kg_iterations = pkcs5_pbkdf2_calibrate(BITS2BYTES(keylen),
DEFAULT_ITERATION_TIME);
if (kg->kg_iterations < 1) {
warnx("could not calibrate pkcs5_pbkdf2");
return -1;
}
break;
case KEYGEN_STOREDKEY:
/* Generate a random stored key */
kg->kg_key = bits_getrandombits(keylen, 1);
if (!kg->kg_key) {
warnx("can't generate random bits for storedkey");
return -1;
}
break;
default:
return -1;
}
return keygen_filldefaults(kg->next, keylen);
}
/*
* Strip the storedkey entries in preparation for inserting a shared
* clause with a newly generated info string to derive this key from
* KDF. The result is that the key generated here is independent of
* whatever storedkeys were involved in the old one, so there is no
* need to keep them around,
*/
void
keygen_stripstored(struct keygen **kgp)
{
struct keygen *kg, *to_free = NULL;
while ((kg = *kgp) != NULL) {
if (kg->kg_method == KEYGEN_STOREDKEY) {
*kgp = kg->next;
kg->next = to_free;
to_free = kg;
} else {
kgp = &kg->next;
}
}
keygen_free(to_free);
}
int
keygen_makeshared(struct keygen *kg0)
{
struct keygen *kg;
for (kg = kg0; kg != NULL; kg = kg->next) {
switch (kg->kg_method) {
case KEYGEN_RANDOMKEY:
case KEYGEN_URANDOMKEY:
warnx("(u)randomkey keygen cannot be shared");
return -1;
case KEYGEN_SHELL_CMD:
#ifdef HAVE_ARGON2
case KEYGEN_ARGON2ID:
#endif
case KEYGEN_PKCS5_PBKDF2_OLD:
case KEYGEN_PKCS5_PBKDF2_SHA1:
break;
case KEYGEN_STOREDKEY:
warnx("storedkey does not make sense as shared");
return -1;
default:
return -1;
}
if (kg->kg_sharedid != NULL) {
warnx("keygen already shared");
return -1;
}
}
for (kg = kg0; kg != NULL; kg = kg->next) {
struct uuid id;
char *idstr;
uint32_t status;
if (uuidgen(&id, 1) == -1) {
warn("uuidgen");
return -1;
}
uuid_to_string(&id, &idstr, &status);
if (status != uuid_s_ok) {
warnx("uuid_to_string: %"PRIu32, status);
return -1;
}
kg->kg_sharedid = string_fromcharstar(idstr);
kg->kg_sharedalg = SHARED_ALG_HKDF_HMAC_SHA256;
kg->kg_sharedlen = 8*SHA256_DIGEST_LENGTH;
kg->kg_sharedinfo = bits_getrandombits(DEFAULT_SALTLEN, 0);
free(idstr);
}
return 0;
}
int
keygen_tweakshared(struct keygen *kg0)
{
struct keygen *kg;
for (kg = kg0; kg != NULL; kg = kg->next) {
switch (kg->kg_method) {
case KEYGEN_RANDOMKEY:
case KEYGEN_URANDOMKEY:
warnx("(u)randomkey keygen cannot be shared");
return -1;
case KEYGEN_SHELL_CMD:
#ifdef HAVE_ARGON2
case KEYGEN_ARGON2ID:
#endif
case KEYGEN_PKCS5_PBKDF2_OLD:
case KEYGEN_PKCS5_PBKDF2_SHA1:
break;
case KEYGEN_STOREDKEY:
warnx("storedkey does not make sense as shared");
return -1;
default:
return -1;
}
if (kg->kg_sharedid == NULL) {
warnx("keygen not shared");
return -1;
}
}
for (kg = kg0; kg != NULL; kg = kg->next) {
if (kg->kg_method == KEYGEN_STOREDKEY)
continue;
bits_free(kg->kg_sharedinfo);
kg->kg_sharedinfo = bits_getrandombits(DEFAULT_SALTLEN, 0);
}
return 0;
}
struct keygen *
keygen_combine(struct keygen *kg1, struct keygen *kg2)
{
if (!kg1 && !kg2)
return NULL;
if (!kg1)
kg1 = keygen_new();
if (!kg2)
return kg1;
if (kg2->kg_method != KEYGEN_UNKNOWN)
kg1->kg_method = kg2->kg_method;
if (kg2->kg_iterations != (size_t)-1 && kg2->kg_iterations > 0)
kg1->kg_iterations = kg2->kg_iterations;
if (kg2->kg_memory != (size_t)-1 && kg2->kg_memory > 0)
kg1->kg_memory = kg2->kg_memory;
if (kg2->kg_parallelism != (size_t)-1 && kg2->kg_parallelism > 0)
kg1->kg_parallelism = kg2->kg_parallelism;
if (kg2->kg_version != (size_t)-1 && kg2->kg_version > 0)
kg1->kg_version = kg2->kg_version;
if (kg2->kg_salt)
bits_assign(&kg1->kg_salt, kg2->kg_salt);
if (kg2->kg_key)
bits_assign(&kg1->kg_key, kg2->kg_key);
if (kg2->kg_cmd)
string_assign(&kg1->kg_cmd, kg2->kg_cmd);
if (kg2->kg_sharedid)
string_assign(&kg1->kg_sharedid, kg2->kg_sharedid);
if (kg2->kg_sharedalg != SHARED_ALG_UNKNOWN) {
kg1->kg_sharedalg = kg2->kg_sharedalg;
kg1->kg_sharedlen = kg2->kg_sharedlen;
}
if (kg2->kg_sharedinfo)
bits_assign(&kg1->kg_sharedinfo, kg2->kg_sharedinfo);
return kg1;
}
struct keygen *
keygen_method(string_t *in)
{
struct keygen *kg = keygen_new();
const char *kgm = string_tocharstar(in);
#ifdef HAVE_ARGON2
if (!strcmp("argon2id", kgm))
kg->kg_method = KEYGEN_ARGON2ID;
#endif
if (!strcmp("pkcs5_pbkdf2", kgm))
kg->kg_method = KEYGEN_PKCS5_PBKDF2_OLD;
if (!strcmp("pkcs5_pbkdf2/sha1", kgm))
kg->kg_method = KEYGEN_PKCS5_PBKDF2_SHA1;
if (!strcmp("randomkey", kgm))
kg->kg_method = KEYGEN_RANDOMKEY;
if (!strcmp("storedkey", kgm))
kg->kg_method = KEYGEN_STOREDKEY;
if (!strcmp("urandomkey", kgm))
kg->kg_method = KEYGEN_URANDOMKEY;
if (!strcmp("shell_cmd", kgm))
kg->kg_method = KEYGEN_SHELL_CMD;
string_free(in);
if (kg->kg_method == KEYGEN_UNKNOWN)
warnx("unrecognized key generation method \"%s\"", kgm);
return kg;
}
struct keygen *
keygen_set_method(struct keygen *kg, string_t *in)
{
return keygen_combine(kg, keygen_method(in));
}
struct keygen *
keygen_salt(bits_t *in)
{
struct keygen *kg = keygen_new();
kg->kg_salt = in;
return kg;
}
struct keygen *
keygen_iterations(size_t in)
{
struct keygen *kg = keygen_new();
kg->kg_iterations = in;
return kg;
}
struct keygen *
keygen_memory(size_t in)
{
struct keygen *kg = keygen_new();
kg->kg_memory = in;
return kg;
}
struct keygen *
keygen_parallelism(size_t in)
{
struct keygen *kg = keygen_new();
kg->kg_parallelism = in;
return kg;
}
struct keygen *
keygen_version(size_t in)
{
struct keygen *kg = keygen_new();
kg->kg_version = in;
return kg;
}
void
keygen_addlist(struct keygen **l, struct keygen *e)
{
struct keygen *t;
if (*l) {
t = *l;
for (;t->next; t = t->next)
;
t->next = e;
} else {
*l = e;
}
}
struct keygen *
keygen_key(bits_t *in)
{
struct keygen *kg = keygen_new();
kg->kg_key = in;
return kg;
}
struct keygen *
keygen_cmd(string_t *in)
{
struct keygen *kg = keygen_new();
kg->kg_cmd = in;
return kg;
}
struct keygen *
keygen_shared(string_t *id, string_t *alg, bits_t *info)
{
struct keygen *kg = keygen_new();
const char *algname = string_tocharstar(alg);
if (!strcmp("hkdf-hmac-sha256", algname)) {
kg->kg_sharedalg = SHARED_ALG_HKDF_HMAC_SHA256;
kg->kg_sharedlen = 8*SHA256_DIGEST_LENGTH;
}
if (kg->kg_sharedalg == SHARED_ALG_UNKNOWN) {
warnx("unrecognized shared key derivation algorithm \"%s\"",
algname);
}
kg->kg_sharedid = id;
kg->kg_sharedinfo = info;
return kg;
}
struct params *
params_fget(FILE *f)
{
struct params *p;
p = cgdparsefile(f);
if (!p)
return NULL;
/*
* We deal with the deprecated keygen structure by prepending it
* to the list of keygens, so that the rest of the code does not
* have to deal with this backwards compat issue. The deprecated
* ``xor_key'' field may be stored in p->dep_keygen->kg_key. If
* it exists, we construct a storedkey keygen struct as well. Also,
* default the iteration count to 128 as the old code did.
*/
if (p->dep_keygen) {
if (p->dep_keygen->kg_iterations == (size_t)-1)
p->dep_keygen->kg_iterations = 128;
p->dep_keygen->next = p->keygen;
if (p->dep_keygen->kg_key) {
p->keygen = keygen_generate(KEYGEN_STOREDKEY);
p->keygen->kg_key = p->dep_keygen->kg_key;
p->dep_keygen->kg_key = NULL;
p->keygen->next = p->dep_keygen;
} else {
p->keygen = p->dep_keygen;
}
p->dep_keygen = NULL;
}
return p;
}
struct params *
params_cget(const char *fn)
{
struct params *p;
FILE *f;
char filename[MAXPATHLEN];
if ((f = fopen(fn, "r")) == NULL && fn[0] != '/') {
snprintf(filename, sizeof(filename), "%s/%s",
CGDCONFIG_DIR, fn);
fn = filename;
f = fopen(fn, "r");
}
if (f == NULL) {
warn("failed to open params file \"%s\"", fn);
return NULL;
}
p = params_fget(f);
(void)fclose(f);
return p;
}
#define WRAP_COL 50
#define TAB_COL 8
static void
spaces(FILE *f, int len)
{
while (len-- > 0)
(void)fputc(' ', f);
}
static void
print_kvpair_cstr(FILE *f, int ts, const char *key, const char *val)
{
spaces(f, ts);
(void)fprintf(f, "%s %s;\n", key, val);
}
static void
print_kvpair_string(FILE *f, int ts, const char *key, const string_t *val)
{
print_kvpair_cstr(f, ts, key, string_tocharstar(val));
}
static void
print_kvpair_int(FILE *f, int ts, const char *key, size_t val)
{
char *tmp;
if (!key || val == (size_t)-1)
return;
if (asprintf(&tmp, "%zu", val) == -1)
err(1, NULL);
print_kvpair_cstr(f, ts, key, tmp);
free(tmp);
}
/*
* prints out a base64 encoded k-v pair to f. It encodes the length
* of the bitstream as a 32bit unsigned integer in network byte order
* up front.
*/
static void
print_kvpair_b64(FILE *f, int curpos, int ts, const char *key, bits_t *val)
{
string_t *str;
int i;
int len;
int pos;
const char *out;
if (!key || !val)
return;
str = bits_encode(val);
out = string_tocharstar(str);
len = strlen(out);
spaces(f, ts);
(void)fprintf(f, "%s ", key);
curpos += ts + strlen(key) + 1;
ts = curpos;
for (i=0, pos=curpos; i < len; i++, pos++) {
if (pos > WRAP_COL) {
(void)fprintf(f, " \\\n");
spaces(f, ts);
pos = ts;
}
(void)fputc(out[i], f);
}
(void)fprintf(f, ";\n");
string_free(str);
}
static void
print_shared(FILE *f, int ts, struct keygen *kg)
{
static const char *const sharedalgs[] = {
[SHARED_ALG_UNKNOWN] = "unknown",
[SHARED_ALG_HKDF_HMAC_SHA256] = "hkdf-hmac-sha256",
};
if (kg->kg_sharedid == NULL ||
kg->kg_sharedalg < 0 ||
(size_t)kg->kg_sharedalg >= __arraycount(sharedalgs))
return;
fprintf(f, "%*sshared \"%s\" \\\n", ts, "",
string_tocharstar(kg->kg_sharedid));
ts += 4;
fprintf(f, "%*salgorithm %s \\\n", ts, "",
sharedalgs[kg->kg_sharedalg]);
print_kvpair_b64(f, 0, ts, "subkey", kg->kg_sharedinfo);
}
int
keygen_fput(struct keygen *kg, int ts, FILE *f)
{
int curpos = 0;
if (!kg)
return 0;
(void)fprintf(f, "keygen ");
curpos += strlen("keygen ");
switch (kg->kg_method) {
case KEYGEN_STOREDKEY:
(void)fprintf(f, "storedkey ");
curpos += strlen("storedkey ");
print_kvpair_b64(f, curpos, 0, "key", kg->kg_key);
break;
case KEYGEN_RANDOMKEY:
(void)fprintf(f, "randomkey;\n");
break;
case KEYGEN_URANDOMKEY:
(void)fprintf(f, "urandomkey;\n");
break;
#ifdef HAVE_ARGON2
case KEYGEN_ARGON2ID:
(void)fprintf(f, "argon2id {\n");
print_kvpair_int(f, ts, "iterations", kg->kg_iterations);
print_kvpair_int(f, ts, "memory", kg->kg_memory);
print_kvpair_int(f, ts, "parallelism", kg->kg_parallelism);
print_kvpair_int(f, ts, "version", kg->kg_version);
print_kvpair_b64(f, 0, ts, "salt", kg->kg_salt);
print_shared(f, ts, kg);
(void)fprintf(f, "};\n");
break;
#endif
case KEYGEN_PKCS5_PBKDF2_OLD:
(void)fprintf(f, "pkcs5_pbkdf2 {\n");
print_kvpair_int(f, ts, "iterations", kg->kg_iterations);
print_kvpair_b64(f, 0, ts, "salt", kg->kg_salt);
print_shared(f, ts, kg);
(void)fprintf(f, "};\n");
break;
case KEYGEN_PKCS5_PBKDF2_SHA1:
(void)fprintf(f, "pkcs5_pbkdf2/sha1 {\n");
print_kvpair_int(f, ts, "iterations", kg->kg_iterations);
print_kvpair_b64(f, 0, ts, "salt", kg->kg_salt);
print_shared(f, ts, kg);
(void)fprintf(f, "};\n");
break;
default:
warnx("keygen_fput: %d not a valid method", kg->kg_method);
break;
}
return keygen_fput(kg->next, ts, f);
}
int
params_fput(struct params *p, FILE *f)
{
int ts = 0; /* tabstop of 0 spaces */
print_kvpair_string(f, ts, "algorithm", p->algorithm);
print_kvpair_string(f, ts, "iv-method", p->ivmeth);
print_kvpair_int(f, ts, "keylength", p->keylen);
print_kvpair_int(f, ts, "blocksize", p->bsize);
switch (p->verify_method) {
case VERIFY_NONE:
print_kvpair_cstr(f, ts, "verify_method", "none");
break;
case VERIFY_DISKLABEL:
print_kvpair_cstr(f, ts, "verify_method", "disklabel");
break;
case VERIFY_FFS:
print_kvpair_cstr(f, ts, "verify_method", "ffs");
break;
case VERIFY_REENTER:
print_kvpair_cstr(f, ts, "verify_method", "re-enter");
break;
case VERIFY_MBR:
print_kvpair_cstr(f, ts, "verify_method", "mbr");
break;
case VERIFY_GPT:
print_kvpair_cstr(f, ts, "verify_method", "gpt");
break;
#ifdef HAVE_ZFS
case VERIFY_ZFS:
print_kvpair_cstr(f, ts, "verify_method", "zfs");
break;
#endif
default:
warnx("unsupported verify_method (%d)", p->verify_method);
return -1;
}
return keygen_fput(p->keygen, TAB_COL, f);
}
int
params_cput(struct params *p, const char *fn)
{
FILE *f;
if (fn && *fn) {
if ((f = fopen(fn, "w")) == NULL) {
warn("could not open outfile \"%s\"", fn);
return -1;
}
} else {
f = stdout;
}
return params_fput(p, f);
}
static int
crypt_defaults_lookup(const char *alg)
{
unsigned i;
for (i=0; i < (sizeof(crypto_defaults) / sizeof(crypto_defaults[0])); i++)
if (!strcmp(alg, crypto_defaults[i].alg))
return i;
return -1;
}