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