keygen.c 5.35 KB
Newer Older
1
/*
2
 * Copyright (C) 2009, 2012-2016  Internet Systems Consortium, Inc. ("ISC")
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 8
 */

9
/* $Id: keygen.c,v 1.4 2009/11/12 14:02:38 marka Exp $ */
10 11 12 13 14 15 16 17 18 19 20 21 22 23

/*! \file */

#include <config.h>

#include <stdlib.h>
#include <stdarg.h>

#include <isc/base64.h>
#include <isc/buffer.h>
#include <isc/entropy.h>
#include <isc/file.h>
#include <isc/keyboard.h>
#include <isc/mem.h>
24
#include <isc/print.h>
25 26 27
#include <isc/result.h>
#include <isc/string.h>

28 29
#include <pk11/site.h>

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
#include <dns/keyvalues.h>
#include <dns/name.h>

#include <dst/dst.h>
#include <confgen/os.h>

#include "util.h"
#include "keygen.h"

/*%
 * Convert algorithm type to string.
 */
const char *
alg_totext(dns_secalg_t alg) {
	switch (alg) {
45
#ifndef PK11_MD5_DISABLE
46 47
	    case DST_ALG_HMACMD5:
		return "hmac-md5";
48
#endif
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
	    case DST_ALG_HMACSHA1:
		return "hmac-sha1";
	    case DST_ALG_HMACSHA224:
		return "hmac-sha224";
	    case DST_ALG_HMACSHA256:
		return "hmac-sha256";
	    case DST_ALG_HMACSHA384:
		return "hmac-sha384";
	    case DST_ALG_HMACSHA512:
		return "hmac-sha512";
	    default:
		return "(unknown)";
	}
}

/*%
 * Convert string to algorithm type.
 */
dns_secalg_t
alg_fromtext(const char *name) {
Evan Hunt's avatar
Evan Hunt committed
69 70 71 72
	const char *p = name;
	if (strncasecmp(p, "hmac-", 5) == 0)
		p = &name[5];

73
#ifndef PK11_MD5_DISABLE
Evan Hunt's avatar
Evan Hunt committed
74
	if (strcasecmp(p, "md5") == 0)
Automatic Updater's avatar
Automatic Updater committed
75
		return DST_ALG_HMACMD5;
76
#endif
Evan Hunt's avatar
Evan Hunt committed
77
	if (strcasecmp(p, "sha1") == 0)
Automatic Updater's avatar
Automatic Updater committed
78
		return DST_ALG_HMACSHA1;
Evan Hunt's avatar
Evan Hunt committed
79
	if (strcasecmp(p, "sha224") == 0)
Automatic Updater's avatar
Automatic Updater committed
80
		return DST_ALG_HMACSHA224;
Evan Hunt's avatar
Evan Hunt committed
81
	if (strcasecmp(p, "sha256") == 0)
Automatic Updater's avatar
Automatic Updater committed
82
		return DST_ALG_HMACSHA256;
Evan Hunt's avatar
Evan Hunt committed
83
	if (strcasecmp(p, "sha384") == 0)
Automatic Updater's avatar
Automatic Updater committed
84
		return DST_ALG_HMACSHA384;
Evan Hunt's avatar
Evan Hunt committed
85
	if (strcasecmp(p, "sha512") == 0)
Automatic Updater's avatar
Automatic Updater committed
86 87
		return DST_ALG_HMACSHA512;
	return DST_ALG_UNKNOWN;
88 89 90 91 92 93 94
}

/*%
 * Return default keysize for a given algorithm type.
 */
int
alg_bits(dns_secalg_t alg) {
Automatic Updater's avatar
Automatic Updater committed
95
	switch (alg) {
96 97 98 99 100 101 102 103 104 105 106 107 108 109
	    case DST_ALG_HMACMD5:
		return 128;
	    case DST_ALG_HMACSHA1:
		return 160;
	    case DST_ALG_HMACSHA224:
		return 224;
	    case DST_ALG_HMACSHA256:
		return 256;
	    case DST_ALG_HMACSHA384:
		return 384;
	    case DST_ALG_HMACSHA512:
		return 512;
	    default:
		return 0;
Automatic Updater's avatar
Automatic Updater committed
110
	}
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
}

/*%
 * Generate a key of size 'keysize' using entropy source 'randomfile',
 * and place it in 'key_txtbuffer'
 */
void
generate_key(isc_mem_t *mctx, const char *randomfile, dns_secalg_t alg,
	     int keysize, isc_buffer_t *key_txtbuffer) {
	isc_result_t result = ISC_R_SUCCESS;
	isc_entropysource_t *entropy_source = NULL;
	int open_keyboard = ISC_ENTROPY_KEYBOARDMAYBE;
	int entropy_flags = 0;
	isc_entropy_t *ectx = NULL;
	isc_buffer_t key_rawbuffer;
	isc_region_t key_rawregion;
	char key_rawsecret[64];
	dst_key_t *key = NULL;

	switch (alg) {
131
#ifndef PK11_MD5_DISABLE
132
	    case DST_ALG_HMACMD5:
133
#endif
134 135
	    case DST_ALG_HMACSHA1:
	    case DST_ALG_HMACSHA224:
Evan Hunt's avatar
Evan Hunt committed
136 137 138
	    case DST_ALG_HMACSHA256:
		if (keysize < 1 || keysize > 512)
			fatal("keysize %d out of range (must be 1-512)\n",
139 140 141
			      keysize);
		break;
	    case DST_ALG_HMACSHA384:
Evan Hunt's avatar
Evan Hunt committed
142 143 144
	    case DST_ALG_HMACSHA512:
		if (keysize < 1 || keysize > 1024)
			fatal("keysize %d out of range (must be 1-1024)\n",
145 146
			      keysize);
		break;
147 148 149
	    default:
		fatal("unsupported algorithm %d\n", alg);
	}
Automatic Updater's avatar
Automatic Updater committed
150

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205

	DO("create entropy context", isc_entropy_create(mctx, &ectx));

	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
		randomfile = NULL;
		open_keyboard = ISC_ENTROPY_KEYBOARDYES;
	}
	DO("start entropy source", isc_entropy_usebestsource(ectx,
							     &entropy_source,
							     randomfile,
							     open_keyboard));

	entropy_flags = ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY;

	DO("initialize dst library", dst_lib_init(mctx, ectx, entropy_flags));

	DO("generate key", dst_key_generate(dns_rootname, alg,
					    keysize, 0, 0,
					    DNS_KEYPROTO_ANY,
					    dns_rdataclass_in, mctx, &key));

	isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));

	DO("dump key to buffer", dst_key_tobuffer(key, &key_rawbuffer));

	isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);

	DO("bsse64 encode secret", isc_base64_totext(&key_rawregion, -1, "",
						     key_txtbuffer));

	/*
	 * Shut down the entropy source now so the "stop typing" message
	 * does not muck with the output.
	 */
	if (entropy_source != NULL)
		isc_entropy_destroysource(&entropy_source);

	if (key != NULL)
		dst_key_free(&key);

	isc_entropy_detach(&ectx);
	dst_lib_destroy();
}

/*%
 * Write a key file to 'keyfile'.  If 'user' is non-NULL,
 * make that user the owner of the file.  The key will have
 * the name 'keyname' and the secret in the buffer 'secret'.
 */
void
write_key_file(const char *keyfile, const char *user,
	       const char *keyname, isc_buffer_t *secret,
	       dns_secalg_t alg) {
	isc_result_t result;
	const char *algname = alg_totext(alg);
206
	FILE *fd = NULL;
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

	DO("create keyfile", isc_file_safecreate(keyfile, &fd));

	if (user != NULL) {
		if (set_user(fd, user) == -1)
			fatal("unable to set file owner\n");
	}

	fprintf(fd, "key \"%s\" {\n\talgorithm %s;\n"
		"\tsecret \"%.*s\";\n};\n",
		keyname, algname,
		(int)isc_buffer_usedlength(secret),
		(char *)isc_buffer_base(secret));
	fflush(fd);
	if (ferror(fd))
		fatal("write to %s failed\n", keyfile);
	if (fclose(fd))
		fatal("fclose(%s) failed\n", keyfile);
	fprintf(stderr, "wrote key file \"%s\"\n", keyfile);
}