1 /*
2  * Copyright (C) 2009, 2012, 2013, 2015  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/print.h>
33 #include <isc/result.h>
34 #include <isc/string.h>
35 
36 #include <dns/keyvalues.h>
37 #include <dns/name.h>
38 
39 #include <dst/dst.h>
40 #include <confgen/os.h>
41 
42 #include "util.h"
43 #include "keygen.h"
44 
45 /*%
46  * Convert algorithm type to string.
47  */
48 const char *
alg_totext(dns_secalg_t alg)49 alg_totext(dns_secalg_t alg) {
50 	switch (alg) {
51 	    case DST_ALG_HMACMD5:
52 		return "hmac-md5";
53 	    case DST_ALG_HMACSHA1:
54 		return "hmac-sha1";
55 	    case DST_ALG_HMACSHA224:
56 		return "hmac-sha224";
57 	    case DST_ALG_HMACSHA256:
58 		return "hmac-sha256";
59 	    case DST_ALG_HMACSHA384:
60 		return "hmac-sha384";
61 	    case DST_ALG_HMACSHA512:
62 		return "hmac-sha512";
63 	    default:
64 		return "(unknown)";
65 	}
66 }
67 
68 /*%
69  * Convert string to algorithm type.
70  */
71 dns_secalg_t
alg_fromtext(const char * name)72 alg_fromtext(const char *name) {
73 	if (strcmp(name, "hmac-md5") == 0)
74 		return DST_ALG_HMACMD5;
75 	if (strcmp(name, "hmac-sha1") == 0)
76 		return DST_ALG_HMACSHA1;
77 	if (strcmp(name, "hmac-sha224") == 0)
78 		return DST_ALG_HMACSHA224;
79 	if (strcmp(name, "hmac-sha256") == 0)
80 		return DST_ALG_HMACSHA256;
81 	if (strcmp(name, "hmac-sha384") == 0)
82 		return DST_ALG_HMACSHA384;
83 	if (strcmp(name, "hmac-sha512") == 0)
84 		return DST_ALG_HMACSHA512;
85 	return DST_ALG_UNKNOWN;
86 }
87 
88 /*%
89  * Return default keysize for a given algorithm type.
90  */
91 int
alg_bits(dns_secalg_t alg)92 alg_bits(dns_secalg_t alg) {
93 	switch (alg) {
94 	    case DST_ALG_HMACMD5:
95 		return 128;
96 	    case DST_ALG_HMACSHA1:
97 		return 160;
98 	    case DST_ALG_HMACSHA224:
99 		return 224;
100 	    case DST_ALG_HMACSHA256:
101 		return 256;
102 	    case DST_ALG_HMACSHA384:
103 		return 384;
104 	    case DST_ALG_HMACSHA512:
105 		return 512;
106 	    default:
107 		return 0;
108 	}
109 }
110 
111 /*%
112  * Generate a key of size 'keysize' using entropy source 'randomfile',
113  * and place it in 'key_txtbuffer'
114  */
115 void
generate_key(isc_mem_t * mctx,const char * randomfile,dns_secalg_t alg,int keysize,isc_buffer_t * key_txtbuffer)116 generate_key(isc_mem_t *mctx, const char *randomfile, dns_secalg_t alg,
117 	     int keysize, isc_buffer_t *key_txtbuffer) {
118 	isc_result_t result = ISC_R_SUCCESS;
119 	isc_entropysource_t *entropy_source = NULL;
120 	int open_keyboard = ISC_ENTROPY_KEYBOARDMAYBE;
121 	int entropy_flags = 0;
122 	isc_entropy_t *ectx = NULL;
123 	isc_buffer_t key_rawbuffer;
124 	isc_region_t key_rawregion;
125 	char key_rawsecret[64];
126 	dst_key_t *key = NULL;
127 
128 	switch (alg) {
129 	    case DST_ALG_HMACMD5:
130 	    case DST_ALG_HMACSHA1:
131 	    case DST_ALG_HMACSHA224:
132 	    case DST_ALG_HMACSHA256:
133 		if (keysize < 1 || keysize > 512)
134 			fatal("keysize %d out of range (must be 1-512)\n",
135 			      keysize);
136 		break;
137 	    case DST_ALG_HMACSHA384:
138 	    case DST_ALG_HMACSHA512:
139 		if (keysize < 1 || keysize > 1024)
140 			fatal("keysize %d out of range (must be 1-1024)\n",
141 			      keysize);
142 		break;
143 	    default:
144 		fatal("unsupported algorithm %d\n", alg);
145 	}
146 
147 
148 	DO("create entropy context", isc_entropy_create(mctx, &ectx));
149 
150 	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
151 		randomfile = NULL;
152 		open_keyboard = ISC_ENTROPY_KEYBOARDYES;
153 	}
154 	DO("start entropy source", isc_entropy_usebestsource(ectx,
155 							     &entropy_source,
156 							     randomfile,
157 							     open_keyboard));
158 
159 	entropy_flags = ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY;
160 
161 	DO("initialize dst library", dst_lib_init(mctx, ectx, entropy_flags));
162 
163 	DO("generate key", dst_key_generate(dns_rootname, alg,
164 					    keysize, 0, 0,
165 					    DNS_KEYPROTO_ANY,
166 					    dns_rdataclass_in, mctx, &key));
167 
168 	isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));
169 
170 	DO("dump key to buffer", dst_key_tobuffer(key, &key_rawbuffer));
171 
172 	isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);
173 
174 	DO("bsse64 encode secret", isc_base64_totext(&key_rawregion, -1, "",
175 						     key_txtbuffer));
176 
177 	/*
178 	 * Shut down the entropy source now so the "stop typing" message
179 	 * does not muck with the output.
180 	 */
181 	if (entropy_source != NULL)
182 		isc_entropy_destroysource(&entropy_source);
183 
184 	if (key != NULL)
185 		dst_key_free(&key);
186 
187 	isc_entropy_detach(&ectx);
188 	dst_lib_destroy();
189 }
190 
191 /*%
192  * Write a key file to 'keyfile'.  If 'user' is non-NULL,
193  * make that user the owner of the file.  The key will have
194  * the name 'keyname' and the secret in the buffer 'secret'.
195  */
196 void
write_key_file(const char * keyfile,const char * user,const char * keyname,isc_buffer_t * secret,dns_secalg_t alg)197 write_key_file(const char *keyfile, const char *user,
198 	       const char *keyname, isc_buffer_t *secret,
199 	       dns_secalg_t alg) {
200 	isc_result_t result;
201 	const char *algname = alg_totext(alg);
202 	FILE *fd = NULL;
203 
204 	DO("create keyfile", isc_file_safecreate(keyfile, &fd));
205 
206 	if (user != NULL) {
207 		if (set_user(fd, user) == -1)
208 			fatal("unable to set file owner\n");
209 	}
210 
211 	fprintf(fd, "key \"%s\" {\n\talgorithm %s;\n"
212 		"\tsecret \"%.*s\";\n};\n",
213 		keyname, algname,
214 		(int)isc_buffer_usedlength(secret),
215 		(char *)isc_buffer_base(secret));
216 	fflush(fd);
217 	if (ferror(fd))
218 		fatal("write to %s failed\n", keyfile);
219 	if (fclose(fd))
220 		fatal("fclose(%s) failed\n", keyfile);
221 	fprintf(stderr, "wrote key file \"%s\"\n", keyfile);
222 }
223 
224