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