Panda3D
|
00001 // Filename: makePrcKey.cxx 00002 // Created by: drose (19Oct04) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "dtoolbase.h" 00016 #include "prcKeyRegistry.h" 00017 #include "filename.h" 00018 #include "pvector.h" 00019 #include <stdio.h> 00020 00021 // Pick up the public key definitions. 00022 #ifdef PRC_PUBLIC_KEYS_INCLUDE 00023 #include PRC_PUBLIC_KEYS_INCLUDE 00024 #endif 00025 00026 #include "openssl/rsa.h" 00027 #include "openssl/err.h" 00028 #include "openssl/pem.h" 00029 #include "openssl/rand.h" 00030 #include "openssl/bio.h" 00031 00032 #ifndef HAVE_GETOPT 00033 #include "gnu_getopt.h" 00034 #else 00035 #ifdef PHAVE_GETOPT_H 00036 #include <getopt.h> 00037 #endif 00038 #endif 00039 00040 class KeyNumber { 00041 public: 00042 int _number; 00043 bool _got_pass_phrase; 00044 string _pass_phrase; 00045 }; 00046 typedef pvector<KeyNumber> KeyNumbers; 00047 00048 //////////////////////////////////////////////////////////////////// 00049 // Function: output_ssl_errors 00050 // Description: A convenience function that is itself a wrapper 00051 // around the OpenSSL convenience function to output the 00052 // recent OpenSSL errors. This function sends the error 00053 // string to cerr. 00054 //////////////////////////////////////////////////////////////////// 00055 void 00056 output_ssl_errors() { 00057 cerr << "Error occurred in SSL routines.\n"; 00058 00059 static bool strings_loaded = false; 00060 if (!strings_loaded) { 00061 ERR_load_crypto_strings(); 00062 strings_loaded = true; 00063 } 00064 00065 unsigned long e = ERR_get_error(); 00066 while (e != 0) { 00067 static const size_t buffer_len = 256; 00068 char buffer[buffer_len]; 00069 ERR_error_string_n(e, buffer, buffer_len); 00070 cerr << buffer << "\n"; 00071 e = ERR_get_error(); 00072 } 00073 } 00074 00075 //////////////////////////////////////////////////////////////////// 00076 // Function: output_c_string 00077 // Description: Extracts the data written to the indicated memory bio 00078 // and writes it to the indicated stream, formatting it 00079 // to be compiled into a C or C++ program as a string. 00080 //////////////////////////////////////////////////////////////////// 00081 void 00082 output_c_string(ostream &out, const string &string_name, 00083 int index, BIO *mbio) { 00084 char *data_ptr; 00085 size_t data_size = BIO_get_mem_data(mbio, &data_ptr); 00086 00087 out << "static const char * const " << string_name 00088 << index << "_data =\n" 00089 << " \""; 00090 00091 bool last_nl = false; 00092 for (size_t i = 0; i < data_size; i++) { 00093 if (data_ptr[i] == '\n') { 00094 out << "\\n"; 00095 last_nl = true; 00096 00097 } else { 00098 if (last_nl) { 00099 out << "\"\n \""; 00100 last_nl = false; 00101 } 00102 00103 if (isprint(data_ptr[i])) { 00104 out << data_ptr[i]; 00105 00106 } else { 00107 out << "\\x" << hex << setw(2) << setfill('0') 00108 << (unsigned int)(unsigned char)data_ptr[i] << dec; 00109 } 00110 } 00111 } 00112 out << "\";\nstatic const unsigned int " << string_name << index 00113 << "_length = " << data_size << ";\n"; 00114 } 00115 00116 //////////////////////////////////////////////////////////////////// 00117 // Function: generate_key 00118 // Description: Generates a new public and private key pair. 00119 //////////////////////////////////////////////////////////////////// 00120 EVP_PKEY * 00121 generate_key() { 00122 RSA *rsa = RSA_generate_key(1024, 7, NULL, NULL); 00123 00124 if (rsa == (RSA *)NULL) { 00125 output_ssl_errors(); 00126 exit(1); 00127 } 00128 00129 EVP_PKEY *pkey = EVP_PKEY_new(); 00130 EVP_PKEY_assign_RSA(pkey, rsa); 00131 00132 return pkey; 00133 } 00134 00135 //////////////////////////////////////////////////////////////////// 00136 // Function: write_public_keys 00137 // Description: Writes the list of public keys stored in the 00138 // PrcKeyRegistry to the indicated output filename as a 00139 // compilable list of KeyDef entries, suitable for 00140 // passing to PrcKeyRegistry::record_keys(). 00141 //////////////////////////////////////////////////////////////////// 00142 void 00143 write_public_keys(Filename outfile) { 00144 outfile.set_text(); 00145 cerr << "Rewriting " << outfile << "\n"; 00146 00147 pofstream out; 00148 if (!outfile.open_write(out)) { 00149 cerr << "Unable to open " << outfile << " for writing.\n"; 00150 exit(1); 00151 } 00152 00153 out << 00154 "\n" 00155 "// This file was generated by make-prc-key. It defines the public keys\n" 00156 "// that will be used to validate signed prc files.\n" 00157 "\n" 00158 "#include \"prcKeyRegistry.h\"\n" 00159 "\n"; 00160 00161 PrcKeyRegistry *pkr = PrcKeyRegistry::get_global_ptr(); 00162 00163 BIO *mbio = BIO_new(BIO_s_mem()); 00164 00165 int num_keys = pkr->get_num_keys(); 00166 int i; 00167 for (i = 0; i < num_keys; i++) { 00168 EVP_PKEY *pkey = pkr->get_key(i); 00169 00170 if (pkey != (EVP_PKEY *)NULL) { 00171 if (!PEM_write_bio_PUBKEY(mbio, pkey)) { 00172 output_ssl_errors(); 00173 exit(1); 00174 } 00175 00176 output_c_string(out, "prc_pubkey", i, mbio); 00177 BIO_reset(mbio); 00178 out << "\n"; 00179 } 00180 } 00181 00182 BIO_free(mbio); 00183 00184 // Now output the table that indexes all of the above. 00185 out << "static PrcKeyRegistry::KeyDef const prc_pubkeys[" << num_keys << "] = {\n"; 00186 00187 for (i = 0; i < num_keys; i++) { 00188 EVP_PKEY *pkey = pkr->get_key(i); 00189 time_t generated_time = pkr->get_generated_time(i); 00190 00191 if (pkey != (EVP_PKEY *)NULL) { 00192 out << " { prc_pubkey" << i << "_data, prc_pubkey" << i 00193 << "_length, " << generated_time << " },\n"; 00194 } else { 00195 out << " { NULL, 0, 0 },\n"; 00196 } 00197 }; 00198 00199 out << "};\n" 00200 << "static const int num_prc_pubkeys = " << num_keys << ";\n\n"; 00201 } 00202 00203 //////////////////////////////////////////////////////////////////// 00204 // Function: write_private_key 00205 // Description: Generates a C++ program that can be used to sign a 00206 // prc file with the indicated private key into the 00207 // given output filename. 00208 //////////////////////////////////////////////////////////////////// 00209 void 00210 write_private_key(EVP_PKEY *pkey, Filename outfile, int n, time_t now, 00211 const char *pp) { 00212 outfile.set_text(); 00213 cerr << "Rewriting " << outfile << "\n"; 00214 00215 pofstream out; 00216 if (!outfile.open_write(out)) { 00217 cerr << "Unable to open " << outfile << " for writing.\n"; 00218 exit(1); 00219 } 00220 00221 out << 00222 "\n" 00223 "// This file was generated by make-prc-key. It can be compiled against\n" 00224 "// dtool to produce a program that will sign a prc file using key number " << n << ".\n\n"; 00225 00226 BIO *mbio = BIO_new(BIO_s_mem()); 00227 00228 int write_result; 00229 if (pp != NULL && *pp == '\0') { 00230 // The supplied password was the empty string. This means not to 00231 // encrypt the private key. 00232 write_result = 00233 PEM_write_bio_PKCS8PrivateKey(mbio, pkey, NULL, NULL, 0, NULL, NULL); 00234 00235 } else { 00236 // Otherwise, the default is to encrypt it. 00237 write_result = 00238 PEM_write_bio_PKCS8PrivateKey(mbio, pkey, EVP_des_ede3_cbc(), 00239 NULL, 0, NULL, (void *)pp); 00240 } 00241 00242 if (!write_result) { 00243 output_ssl_errors(); 00244 exit(1); 00245 } 00246 00247 output_c_string(out, "prc_privkey", n, mbio); 00248 00249 BIO_free(mbio); 00250 00251 out << 00252 "\n\n" 00253 "#define KEY_NUMBER " << n << "\n" 00254 "#define KEY_DATA prc_privkey" << n << "_data\n" 00255 "#define KEY_LENGTH prc_privkey" << n << "_length\n" 00256 "#define PROGNAME \"" << outfile.get_basename_wo_extension() << "\"\n" 00257 "#define GENERATED_TIME " << now << "\n\n" 00258 00259 "#include \"signPrcFile_src.cxx\"\n\n"; 00260 } 00261 00262 //////////////////////////////////////////////////////////////////// 00263 // Function: usage 00264 // Description: 00265 //////////////////////////////////////////////////////////////////// 00266 void 00267 usage() { 00268 cerr << 00269 "\nmake-prc-key [opts] 1[,\"pass_phrase\"] [2[,\"pass phrase\"] 3 ...]\n\n" 00270 00271 "This program generates one or more new keys to be used for signing\n" 00272 "a prc file. The key itself is a completely arbitrary random bit\n" 00273 "sequence. It is divided into a public and a private key; the public\n" 00274 "key is not secret and will be compiled into libdtool, while the private\n" 00275 "key should be safeguarded and will be written into a .cxx file that\n" 00276 "can be compiled as a standalone application.\n\n" 00277 00278 "The output is a public and private key pair for each trust level. The\n" 00279 "form of the output for both public and private keys will be compilable\n" 00280 "C++ code; see -a and -b, below, for a complete description.\n\n" 00281 00282 "After the options, the remaining arguments list the individual trust\n" 00283 "level keys to generate. For each integer specified, a different key\n" 00284 "will be created. There should be one key for each trust level\n" 00285 "required; a typical application will only need one or two keys.\n\n" 00286 00287 "Options:\n\n" 00288 00289 " -a pub_outfile.cxx\n" 00290 " Specifies the name and location of the public key output file\n" 00291 " to generate. This file must then be named by the Config.pp\n" 00292 " variable PRC_PUBLIC_KEYS_FILENAME so that it will be compiled\n" 00293 " in with libdtool and available to verify signatures. If this\n" 00294 " option is omitted, the previously-compiled value is used.\n\n" 00295 00296 " -b priv_outfile#.cxx\n" 00297 " Specifies the name and location of the private key output file(s)\n" 00298 " to generate. A different output file will be generated for each\n" 00299 " different trust level; the hash mark '#' appearing in the file\n" 00300 " name will be filled in with the corresponding numeric trust level.\n" 00301 " The hash mark may be omitted if you only require one trust level.\n" 00302 " When compiled against dtool, each of these files will generate\n" 00303 " a program that can be used to sign a prc file with the corresponding\n" 00304 " trust level.\n\n" 00305 00306 " -p \"[pass phrase]\"\n" 00307 " Uses the indicated pass phrase to encrypt the private key.\n" 00308 " This specifies an overall pass phrase; you may also specify\n" 00309 " a different pass phrase for each key by using the key,\"pass phrase\"\n" 00310 " syntax.\n\n" 00311 00312 " If a pass phrase is not specified on the command line, you will be\n" 00313 " prompted interactively. Every user of the signing programs\n" 00314 " (outfile_sign1.cxx, etc.) will need to know the pass phrase\n" 00315 " in order to sign prc files.\n\n" 00316 00317 " If this is specified as the empty string (\"\"), then the key\n" 00318 " will not be encrypted, and anyone can run the signing\n" 00319 " programs without having to supply a pass phrase.\n\n"; 00320 } 00321 00322 //////////////////////////////////////////////////////////////////// 00323 // Function: main 00324 // Description: 00325 //////////////////////////////////////////////////////////////////// 00326 int 00327 main(int argc, char *argv[]) { 00328 extern char *optarg; 00329 extern int optind; 00330 const char *optstr = "a:b:p:h"; 00331 00332 Filename pub_outfile; 00333 bool got_pub_outfile = false; 00334 Filename priv_outfile; 00335 bool got_priv_outfile = false; 00336 string pass_phrase; 00337 bool got_pass_phrase = false; 00338 00339 int flag = getopt(argc, argv, optstr); 00340 00341 while (flag != EOF) { 00342 switch (flag) { 00343 case 'a': 00344 pub_outfile = optarg; 00345 got_pub_outfile = true; 00346 break; 00347 00348 case 'b': 00349 priv_outfile = optarg; 00350 got_priv_outfile = true; 00351 break; 00352 00353 case 'p': 00354 pass_phrase = optarg; 00355 got_pass_phrase = true; 00356 break; 00357 00358 case 'h': 00359 usage(); 00360 exit(0); 00361 00362 default: 00363 exit(1); 00364 } 00365 flag = getopt(argc, argv, optstr); 00366 } 00367 00368 argc -= (optind-1); 00369 argv += (optind-1); 00370 00371 if (argc < 2) { 00372 usage(); 00373 exit(1); 00374 } 00375 00376 if (got_pub_outfile) { 00377 if (pub_outfile.get_extension() != "cxx") { 00378 cerr << "Public key output file '" << pub_outfile 00379 << "' should have a .cxx extension.\n"; 00380 exit(1); 00381 } 00382 } else { 00383 #ifdef PRC_PUBLIC_KEYS_INCLUDE 00384 PrcKeyRegistry::get_global_ptr()->record_keys(prc_pubkeys, num_prc_pubkeys); 00385 pub_outfile = PRC_PUBLIC_KEYS_FILENAME; 00386 #endif 00387 00388 if (pub_outfile.empty()) { 00389 cerr << "No -a specified, and no PRC_PUBLIC_KEYS_FILENAME variable\n" 00390 << "compiled in.\n\n"; 00391 exit(1); 00392 } 00393 } 00394 00395 if (got_priv_outfile) { 00396 if (priv_outfile.get_extension() != "cxx") { 00397 cerr << "Private key output file '" << priv_outfile 00398 << "' should have a .cxx extension.\n"; 00399 exit(1); 00400 } 00401 00402 } else { 00403 cerr << "You must use the -b option to specify the private key output filenames.\n"; 00404 exit(1); 00405 } 00406 00407 KeyNumbers key_numbers; 00408 for (int i = 1; i < argc; i++) { 00409 KeyNumber key; 00410 char *endptr; 00411 key._number = strtol(argv[i], &endptr, 0); 00412 key._got_pass_phrase = got_pass_phrase; 00413 key._pass_phrase = pass_phrase; 00414 00415 if (*endptr == ',') { 00416 // Here's a pass phrase for this particular key. 00417 key._got_pass_phrase = true; 00418 key._pass_phrase = endptr + 1; 00419 } else if (*endptr) { 00420 cerr << "Parameter '" << argv[i] << "' should be an integer.\n"; 00421 exit(1); 00422 } 00423 if (key._number <= 0) { 00424 cerr << "Key numbers must be greater than 0; you specified " 00425 << key._number << ".\n"; 00426 exit(1); 00427 } 00428 key_numbers.push_back(key); 00429 } 00430 00431 // Seed the random number generator. 00432 RAND_status(); 00433 00434 // Load the OpenSSL algorithms. 00435 OpenSSL_add_all_algorithms(); 00436 00437 time_t now = time(NULL); 00438 00439 string name = priv_outfile.get_fullpath_wo_extension(); 00440 string prefix, suffix; 00441 bool got_hash; 00442 00443 size_t hash = name.find('#'); 00444 if (hash == string::npos) { 00445 prefix = name; 00446 suffix = ".cxx"; 00447 got_hash = false; 00448 00449 } else { 00450 prefix = name.substr(0, hash); 00451 suffix = name.substr(hash + 1) + ".cxx"; 00452 got_hash = true; 00453 } 00454 00455 KeyNumbers::iterator ki; 00456 for (ki = key_numbers.begin(); ki != key_numbers.end(); ++ki) { 00457 int n = (*ki)._number; 00458 const char *pp = NULL; 00459 if ((*ki)._got_pass_phrase) { 00460 pp = (*ki)._pass_phrase.c_str(); 00461 } 00462 00463 EVP_PKEY *pkey = generate_key(); 00464 PrcKeyRegistry::get_global_ptr()->set_key(n, pkey, now); 00465 00466 ostringstream strm; 00467 if (got_hash || n != 1) { 00468 // If we got an explicit hash mark, we always output the number. 00469 // If we did not get an explicit hash mark, we output the number 00470 // only if it is other than 1. 00471 strm << prefix << n << suffix; 00472 00473 } else { 00474 // If we did not get an explicit hash mark in the filename, we 00475 // omit the number for key 1 (this might be the only key, and 00476 // so maybe the user doesn't require a number designator). 00477 strm << prefix << suffix; 00478 } 00479 00480 write_private_key(pkey, strm.str(), n, now, pp); 00481 } 00482 00483 write_public_keys(pub_outfile); 00484 00485 return (0); 00486 }