keygen.c revision 254402
1/*
2 * Copyright (C) 2009, 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id: keygen.c,v 1.4 2009/11/12 14:02:38 marka Exp $ */
18
19/*! \file */
20
21#include <config.h>
22
23#include <stdlib.h>
24#include <stdarg.h>
25
26#include <isc/base64.h>
27#include <isc/buffer.h>
28#include <isc/entropy.h>
29#include <isc/file.h>
30#include <isc/keyboard.h>
31#include <isc/mem.h>
32#include <isc/result.h>
33#include <isc/string.h>
34
35#include <dns/keyvalues.h>
36#include <dns/name.h>
37
38#include <dst/dst.h>
39#include <confgen/os.h>
40
41#include "util.h"
42#include "keygen.h"
43
44/*%
45 * Convert algorithm type to string.
46 */
47const char *
48alg_totext(dns_secalg_t alg) {
49	switch (alg) {
50	    case DST_ALG_HMACMD5:
51		return "hmac-md5";
52	    case DST_ALG_HMACSHA1:
53		return "hmac-sha1";
54	    case DST_ALG_HMACSHA224:
55		return "hmac-sha224";
56	    case DST_ALG_HMACSHA256:
57		return "hmac-sha256";
58	    case DST_ALG_HMACSHA384:
59		return "hmac-sha384";
60	    case DST_ALG_HMACSHA512:
61		return "hmac-sha512";
62	    default:
63		return "(unknown)";
64	}
65}
66
67/*%
68 * Convert string to algorithm type.
69 */
70dns_secalg_t
71alg_fromtext(const char *name) {
72	if (strcmp(name, "hmac-md5") == 0)
73		return DST_ALG_HMACMD5;
74	if (strcmp(name, "hmac-sha1") == 0)
75		return DST_ALG_HMACSHA1;
76	if (strcmp(name, "hmac-sha224") == 0)
77		return DST_ALG_HMACSHA224;
78	if (strcmp(name, "hmac-sha256") == 0)
79		return DST_ALG_HMACSHA256;
80	if (strcmp(name, "hmac-sha384") == 0)
81		return DST_ALG_HMACSHA384;
82	if (strcmp(name, "hmac-sha512") == 0)
83		return DST_ALG_HMACSHA512;
84	return DST_ALG_UNKNOWN;
85}
86
87/*%
88 * Return default keysize for a given algorithm type.
89 */
90int
91alg_bits(dns_secalg_t alg) {
92	switch (alg) {
93	    case DST_ALG_HMACMD5:
94		return 128;
95	    case DST_ALG_HMACSHA1:
96		return 160;
97	    case DST_ALG_HMACSHA224:
98		return 224;
99	    case DST_ALG_HMACSHA256:
100		return 256;
101	    case DST_ALG_HMACSHA384:
102		return 384;
103	    case DST_ALG_HMACSHA512:
104		return 512;
105	    default:
106		return 0;
107	}
108}
109
110/*%
111 * Generate a key of size 'keysize' using entropy source 'randomfile',
112 * and place it in 'key_txtbuffer'
113 */
114void
115generate_key(isc_mem_t *mctx, const char *randomfile, dns_secalg_t alg,
116	     int keysize, isc_buffer_t *key_txtbuffer) {
117	isc_result_t result = ISC_R_SUCCESS;
118	isc_entropysource_t *entropy_source = NULL;
119	int open_keyboard = ISC_ENTROPY_KEYBOARDMAYBE;
120	int entropy_flags = 0;
121	isc_entropy_t *ectx = NULL;
122	isc_buffer_t key_rawbuffer;
123	isc_region_t key_rawregion;
124	char key_rawsecret[64];
125	dst_key_t *key = NULL;
126
127	switch (alg) {
128	    case DST_ALG_HMACMD5:
129	    case DST_ALG_HMACSHA1:
130	    case DST_ALG_HMACSHA224:
131	    case DST_ALG_HMACSHA256:
132		if (keysize < 1 || keysize > 512)
133			fatal("keysize %d out of range (must be 1-512)\n",
134			      keysize);
135		break;
136	    case DST_ALG_HMACSHA384:
137	    case DST_ALG_HMACSHA512:
138		if (keysize < 1 || keysize > 1024)
139			fatal("keysize %d out of range (must be 1-1024)\n",
140			      keysize);
141		break;
142	    default:
143		fatal("unsupported algorithm %d\n", alg);
144	}
145
146
147	DO("create entropy context", isc_entropy_create(mctx, &ectx));
148
149	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
150		randomfile = NULL;
151		open_keyboard = ISC_ENTROPY_KEYBOARDYES;
152	}
153	DO("start entropy source", isc_entropy_usebestsource(ectx,
154							     &entropy_source,
155							     randomfile,
156							     open_keyboard));
157
158	entropy_flags = ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY;
159
160	DO("initialize dst library", dst_lib_init(mctx, ectx, entropy_flags));
161
162	DO("generate key", dst_key_generate(dns_rootname, alg,
163					    keysize, 0, 0,
164					    DNS_KEYPROTO_ANY,
165					    dns_rdataclass_in, mctx, &key));
166
167	isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));
168
169	DO("dump key to buffer", dst_key_tobuffer(key, &key_rawbuffer));
170
171	isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);
172
173	DO("bsse64 encode secret", isc_base64_totext(&key_rawregion, -1, "",
174						     key_txtbuffer));
175
176	/*
177	 * Shut down the entropy source now so the "stop typing" message
178	 * does not muck with the output.
179	 */
180	if (entropy_source != NULL)
181		isc_entropy_destroysource(&entropy_source);
182
183	if (key != NULL)
184		dst_key_free(&key);
185
186	isc_entropy_detach(&ectx);
187	dst_lib_destroy();
188}
189
190/*%
191 * Write a key file to 'keyfile'.  If 'user' is non-NULL,
192 * make that user the owner of the file.  The key will have
193 * the name 'keyname' and the secret in the buffer 'secret'.
194 */
195void
196write_key_file(const char *keyfile, const char *user,
197	       const char *keyname, isc_buffer_t *secret,
198	       dns_secalg_t alg) {
199	isc_result_t result;
200	const char *algname = alg_totext(alg);
201	FILE *fd = NULL;
202
203	DO("create keyfile", isc_file_safecreate(keyfile, &fd));
204
205	if (user != NULL) {
206		if (set_user(fd, user) == -1)
207			fatal("unable to set file owner\n");
208	}
209
210	fprintf(fd, "key \"%s\" {\n\talgorithm %s;\n"
211		"\tsecret \"%.*s\";\n};\n",
212		keyname, algname,
213		(int)isc_buffer_usedlength(secret),
214		(char *)isc_buffer_base(secret));
215	fflush(fd);
216	if (ferror(fd))
217		fatal("write to %s failed\n", keyfile);
218	if (fclose(fd))
219		fatal("fclose(%s) failed\n", keyfile);
220	fprintf(stderr, "wrote key file \"%s\"\n", keyfile);
221}
222
223