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