Panda3D

makePrcKey.cxx

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 }
 All Classes Functions Variables Enumerations