1/* 2 * Copyright (C) 2009 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 if (keysize < 1 || keysize > 512) 130 fatal("keysize %d out of range (must be 1-512)\n", 131 keysize); 132 break; 133 case DST_ALG_HMACSHA256: 134 if (keysize < 1 || keysize > 256) 135 fatal("keysize %d out of range (must be 1-256)\n", 136 keysize); 137 break; 138 default: 139 fatal("unsupported algorithm %d\n", alg); 140 } 141 142 143 DO("create entropy context", isc_entropy_create(mctx, &ectx)); 144 145 if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) { 146 randomfile = NULL; 147 open_keyboard = ISC_ENTROPY_KEYBOARDYES; 148 } 149 DO("start entropy source", isc_entropy_usebestsource(ectx, 150 &entropy_source, 151 randomfile, 152 open_keyboard)); 153 154 entropy_flags = ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY; 155 156 DO("initialize dst library", dst_lib_init(mctx, ectx, entropy_flags)); 157 158 DO("generate key", dst_key_generate(dns_rootname, alg, 159 keysize, 0, 0, 160 DNS_KEYPROTO_ANY, 161 dns_rdataclass_in, mctx, &key)); 162 163 isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret)); 164 165 DO("dump key to buffer", dst_key_tobuffer(key, &key_rawbuffer)); 166 167 isc_buffer_usedregion(&key_rawbuffer, &key_rawregion); 168 169 DO("bsse64 encode secret", isc_base64_totext(&key_rawregion, -1, "", 170 key_txtbuffer)); 171 172 /* 173 * Shut down the entropy source now so the "stop typing" message 174 * does not muck with the output. 175 */ 176 if (entropy_source != NULL) 177 isc_entropy_destroysource(&entropy_source); 178 179 if (key != NULL) 180 dst_key_free(&key); 181 182 isc_entropy_detach(&ectx); 183 dst_lib_destroy(); 184} 185 186/*% 187 * Write a key file to 'keyfile'. If 'user' is non-NULL, 188 * make that user the owner of the file. The key will have 189 * the name 'keyname' and the secret in the buffer 'secret'. 190 */ 191void 192write_key_file(const char *keyfile, const char *user, 193 const char *keyname, isc_buffer_t *secret, 194 dns_secalg_t alg) { 195 isc_result_t result; 196 const char *algname = alg_totext(alg); 197 FILE *fd = NULL; 198 199 DO("create keyfile", isc_file_safecreate(keyfile, &fd)); 200 201 if (user != NULL) { 202 if (set_user(fd, user) == -1) 203 fatal("unable to set file owner\n"); 204 } 205 206 fprintf(fd, "key \"%s\" {\n\talgorithm %s;\n" 207 "\tsecret \"%.*s\";\n};\n", 208 keyname, algname, 209 (int)isc_buffer_usedlength(secret), 210 (char *)isc_buffer_base(secret)); 211 fflush(fd); 212 if (ferror(fd)) 213 fatal("write to %s failed\n", keyfile); 214 if (fclose(fd)) 215 fatal("fclose(%s) failed\n", keyfile); 216 fprintf(stderr, "wrote key file \"%s\"\n", keyfile); 217} 218 219