00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
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
00044
00045
00046
00047
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
00071
00072
00073
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
00112
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
00131
00132
00133
00134
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
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
00199
00200
00201
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
00225
00226 write_result =
00227 PEM_write_bio_PKCS8PrivateKey(mbio, pkey, NULL, NULL, 0, NULL, NULL);
00228
00229 } else {
00230
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
00258
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
00318
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
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
00427 RAND_status();
00428
00429
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
00464
00465
00466 strm << prefix << n << suffix;
00467
00468 } else {
00469
00470
00471
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 }