Panda3D
Loading...
Searching...
No Matches
makePrcKey.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file makePrcKey.cxx
10 * @author drose
11 * @date 2004-10-19
12 */
13
14#include "dtoolbase.h"
15#include "prcKeyRegistry.h"
16#include "filename.h"
17#include "pvector.h"
18#include "panda_getopt.h"
19#include "preprocess_argv.h"
20#include <stdio.h>
21
22// Pick up the public key definitions.
23#ifdef PRC_PUBLIC_KEYS_INCLUDE
24#include PRC_PUBLIC_KEYS_INCLUDE
25#endif
26
27#include <openssl/rsa.h>
28#include <openssl/err.h>
29#include <openssl/pem.h>
30#include <openssl/rand.h>
31#include <openssl/bio.h>
32
33using std::cerr;
34using std::string;
35
36class KeyNumber {
37public:
38 int _number;
39 bool _got_pass_phrase;
40 string _pass_phrase;
41};
43
44/**
45 * A convenience function that is itself a wrapper around the OpenSSL
46 * convenience function to output the recent OpenSSL errors. This function
47 * sends the error string to cerr.
48 */
49void
51 cerr << "Error occurred in SSL routines.\n";
52
53 static bool strings_loaded = false;
54 if (!strings_loaded) {
55 ERR_load_crypto_strings();
56 strings_loaded = true;
57 }
58
59 unsigned long e = ERR_get_error();
60 while (e != 0) {
61 static const size_t buffer_len = 256;
62 char buffer[buffer_len];
63 ERR_error_string_n(e, buffer, buffer_len);
64 cerr << buffer << "\n";
65 e = ERR_get_error();
66 }
67}
68
69/**
70 * Extracts the data written to the indicated memory bio and writes it to the
71 * indicated stream, formatting it to be compiled into a C or C++ program as a
72 * string.
73 */
74void
75output_c_string(std::ostream &out, const string &string_name,
76 size_t index, BIO *mbio) {
77 char *data_ptr;
78 size_t data_size = BIO_get_mem_data(mbio, &data_ptr);
79
80 out << "static const char * const " << string_name
81 << index << "_data =\n"
82 << " \"";
83
84 bool last_nl = false;
85 for (size_t i = 0; i < data_size; i++) {
86 if (data_ptr[i] == '\n') {
87 out << "\\n";
88 last_nl = true;
89
90 } else {
91 if (last_nl) {
92 out << "\"\n \"";
93 last_nl = false;
94 }
95
96 if (data_ptr[i] == '\t') {
97 out << "\\t";
98 }
99 else if (isprint(data_ptr[i])) {
100 out << data_ptr[i];
101 }
102 else {
103 out << "\\x" << std::hex << std::setw(2) << std::setfill('0')
104 << (unsigned int)(unsigned char)data_ptr[i] << std::dec;
105 }
106 }
107 }
108 out << "\";\nstatic const unsigned int " << string_name << index
109 << "_length = " << data_size << ";\n";
110}
111
112/**
113 * Generates a new public and private key pair.
114 */
115EVP_PKEY *
117 RSA *rsa = RSA_new();
118 BIGNUM *e = BN_new();
119 if (rsa == nullptr || e == nullptr) {
121 exit(1);
122 }
123
124 BN_set_word(e, 7);
125
126 if (!RSA_generate_key_ex(rsa, 1024, e, nullptr)) {
127 BN_free(e);
128 RSA_free(rsa);
130 exit(1);
131 }
132 BN_free(e);
133
134 EVP_PKEY *pkey = EVP_PKEY_new();
135 EVP_PKEY_assign_RSA(pkey, rsa);
136 return pkey;
137}
138
139/**
140 * Writes the list of public keys stored in the PrcKeyRegistry to the
141 * indicated output filename as a compilable list of KeyDef entries, suitable
142 * for passing to PrcKeyRegistry::record_keys().
143 */
144void
146 outfile.set_text();
147 cerr << "Rewriting " << outfile << "\n";
148
149 pofstream out;
150 if (!outfile.open_write(out)) {
151 cerr << "Unable to open " << outfile << " for writing.\n";
152 exit(1);
153 }
154
155 out <<
156 "\n"
157 "// This file was generated by make-prc-key. It defines the public keys\n"
158 "// that will be used to validate signed prc files.\n"
159 "\n"
160 "#include \"prcKeyRegistry.h\"\n"
161 "\n";
162
163 PrcKeyRegistry *pkr = PrcKeyRegistry::get_global_ptr();
164
165 BIO *mbio = BIO_new(BIO_s_mem());
166
167 size_t num_keys = pkr->get_num_keys();
168 size_t i;
169 for (i = 0; i < num_keys; i++) {
170 EVP_PKEY *pkey = pkr->get_key(i);
171
172 if (pkey != nullptr) {
173 if (!PEM_write_bio_PUBKEY(mbio, pkey)) {
175 exit(1);
176 }
177
178 output_c_string(out, "prc_pubkey", i, mbio);
179 (void)BIO_reset(mbio);
180 out << "\n";
181 }
182 }
183
184 BIO_free(mbio);
185
186 // Now output the table that indexes all of the above.
187 out << "static PrcKeyRegistry::KeyDef const prc_pubkeys[" << num_keys << "] = {\n";
188
189 for (i = 0; i < num_keys; i++) {
190 EVP_PKEY *pkey = pkr->get_key(i);
191 time_t generated_time = pkr->get_generated_time(i);
192
193 if (pkey != nullptr) {
194 out << " { prc_pubkey" << i << "_data, prc_pubkey" << i
195 << "_length, " << generated_time << " },\n";
196 } else {
197 out << " { nullptr, 0, 0 },\n";
198 }
199 };
200
201 out << "};\n"
202 << "static const int num_prc_pubkeys = " << num_keys << ";\n\n";
203}
204
205/**
206 * Generates a C++ program that can be used to sign a prc file with the
207 * indicated private key into the given output filename.
208 */
209void
210write_private_key(EVP_PKEY *pkey, Filename outfile, int n, time_t now,
211 const char *pp) {
212 outfile.set_text();
213 cerr << "Rewriting " << outfile << "\n";
214
215 pofstream out;
216 if (!outfile.open_write(out)) {
217 cerr << "Unable to open " << outfile << " for writing.\n";
218 exit(1);
219 }
220
221 out <<
222 "\n"
223 "// This file was generated by make-prc-key. It can be compiled against\n"
224 "// dtool to produce a program that will sign a prc file using key number " << n << ".\n\n";
225
226 BIO *mbio = BIO_new(BIO_s_mem());
227
228 int write_result;
229 if (pp != nullptr && *pp == '\0') {
230 // The supplied password was the empty string. This means not to encrypt
231 // the private key.
232 write_result =
233 PEM_write_bio_PKCS8PrivateKey(mbio, pkey, nullptr, nullptr, 0, nullptr, nullptr);
234
235 } else {
236 // Otherwise, the default is to encrypt it.
237 write_result =
238 PEM_write_bio_PKCS8PrivateKey(mbio, pkey, EVP_des_ede3_cbc(),
239 nullptr, 0, nullptr, (void *)pp);
240 }
241
242 if (!write_result) {
244 exit(1);
245 }
246
247 output_c_string(out, "prc_privkey", n, mbio);
248
249 BIO_free(mbio);
250
251 out <<
252 "\n\n"
253 "#define KEY_NUMBER " << n << "\n"
254 "#define KEY_DATA prc_privkey" << n << "_data\n"
255 "#define KEY_LENGTH prc_privkey" << n << "_length\n"
256 "#define PROGNAME \"" << outfile.get_basename_wo_extension() << "\"\n"
257 "#define GENERATED_TIME " << now << "\n\n"
258
259 "#include \"signPrcFile_src.cxx\"\n\n";
260}
261
262/**
263 *
264 */
265void
266usage() {
267 cerr <<
268 "\nmake-prc-key [opts] 1[,\"pass_phrase\"] [2[,\"pass phrase\"] 3 ...]\n\n"
269
270 "This program generates one or more new keys to be used for signing\n"
271 "a prc file. The key itself is a completely arbitrary random bit\n"
272 "sequence. It is divided into a public and a private key; the public\n"
273 "key is not secret and will be compiled into libdtool, while the private\n"
274 "key should be safeguarded and will be written into a .cxx file that\n"
275 "can be compiled as a standalone application.\n\n"
276
277 "The output is a public and private key pair for each trust level. The\n"
278 "form of the output for both public and private keys will be compilable\n"
279 "C++ code; see -a and -b, below, for a complete description.\n\n"
280
281 "After the options, the remaining arguments list the individual trust\n"
282 "level keys to generate. For each integer specified, a different key\n"
283 "will be created. There should be one key for each trust level\n"
284 "required; a typical application will only need one or two keys.\n\n"
285
286 "Options:\n\n"
287
288 " -a pub_outfile.cxx\n"
289 " Specifies the name and location of the public key output file\n"
290 " to generate. This file must then be named by the Config.pp\n"
291 " variable PRC_PUBLIC_KEYS_FILENAME so that it will be compiled\n"
292 " in with libdtool and available to verify signatures. If this\n"
293 " option is omitted, the previously-compiled value is used.\n\n"
294
295 " -b priv_outfile#.cxx\n"
296 " Specifies the name and location of the private key output file(s)\n"
297 " to generate. A different output file will be generated for each\n"
298 " different trust level; the hash mark '#' appearing in the file\n"
299 " name will be filled in with the corresponding numeric trust level.\n"
300 " The hash mark may be omitted if you only require one trust level.\n"
301 " When compiled against dtool, each of these files will generate\n"
302 " a program that can be used to sign a prc file with the corresponding\n"
303 " trust level.\n\n"
304
305 " -p \"[pass phrase]\"\n"
306 " Uses the indicated pass phrase to encrypt the private key.\n"
307 " This specifies an overall pass phrase; you may also specify\n"
308 " a different pass phrase for each key by using the key,\"pass phrase\"\n"
309 " syntax.\n\n"
310
311 " If a pass phrase is not specified on the command line, you will be\n"
312 " prompted interactively. Every user of the signing programs\n"
313 " (outfile_sign1.cxx, etc.) will need to know the pass phrase\n"
314 " in order to sign prc files.\n\n"
315
316 " If this is specified as the empty string (\"\"), then the key\n"
317 " will not be encrypted, and anyone can run the signing\n"
318 " programs without having to supply a pass phrase.\n\n";
319}
320
321/**
322 *
323 */
324int
325main(int argc, char **argv) {
326 extern char *optarg;
327 extern int optind;
328 const char *optstr = "a:b:p:h";
329
330 Filename pub_outfile;
331 bool got_pub_outfile = false;
332 Filename priv_outfile;
333 bool got_priv_outfile = false;
334 string pass_phrase;
335 bool got_pass_phrase = false;
336
337 preprocess_argv(argc, argv);
338 int flag = getopt(argc, argv, optstr);
339
340 while (flag != EOF) {
341 switch (flag) {
342 case 'a':
343 pub_outfile = optarg;
344 got_pub_outfile = true;
345 break;
346
347 case 'b':
348 priv_outfile = optarg;
349 got_priv_outfile = true;
350 break;
351
352 case 'p':
353 pass_phrase = optarg;
354 got_pass_phrase = true;
355 break;
356
357 case 'h':
358 usage();
359 exit(0);
360
361 default:
362 exit(1);
363 }
364 flag = getopt(argc, argv, optstr);
365 }
366
367 argc -= (optind-1);
368 argv += (optind-1);
369
370 if (argc < 2) {
371 usage();
372 exit(1);
373 }
374
375 if (got_pub_outfile) {
376 if (pub_outfile.get_extension() != "cxx") {
377 cerr << "Public key output file '" << pub_outfile
378 << "' should have a .cxx extension.\n";
379 exit(1);
380 }
381 } else {
382#ifdef PRC_PUBLIC_KEYS_INCLUDE
383 PrcKeyRegistry::get_global_ptr()->record_keys(prc_pubkeys, num_prc_pubkeys);
384 pub_outfile = PRC_PUBLIC_KEYS_FILENAME;
385#endif
386
387 if (pub_outfile.empty()) {
388 cerr << "No -a specified, and no PRC_PUBLIC_KEYS_FILENAME variable\n"
389 << "compiled in.\n\n";
390 exit(1);
391 }
392 }
393
394 if (got_priv_outfile) {
395 if (priv_outfile.get_extension() != "cxx") {
396 cerr << "Private key output file '" << priv_outfile
397 << "' should have a .cxx extension.\n";
398 exit(1);
399 }
400
401 } else {
402 cerr << "You must use the -b option to specify the private key output filenames.\n";
403 exit(1);
404 }
405
406 KeyNumbers key_numbers;
407 for (int i = 1; i < argc; i++) {
408 KeyNumber key;
409 char *endptr;
410 key._number = (int)strtol(argv[i], &endptr, 0);
411 key._got_pass_phrase = got_pass_phrase;
412 key._pass_phrase = pass_phrase;
413
414 if (*endptr == ',') {
415 // Here's a pass phrase for this particular key.
416 key._got_pass_phrase = true;
417 key._pass_phrase = endptr + 1;
418 } else if (*endptr) {
419 cerr << "Parameter '" << argv[i] << "' should be an integer.\n";
420 exit(1);
421 }
422 if (key._number <= 0) {
423 cerr << "Key numbers must be greater than 0; you specified "
424 << key._number << ".\n";
425 exit(1);
426 }
427 key_numbers.push_back(key);
428 }
429
430 // Seed the random number generator.
431 RAND_status();
432
433 // Load the OpenSSL algorithms.
434 OpenSSL_add_all_algorithms();
435
436 time_t now = time(nullptr);
437
438 string name = priv_outfile.get_fullpath_wo_extension();
439 string prefix, suffix;
440 bool got_hash;
441
442 size_t hash = name.find('#');
443 if (hash == string::npos) {
444 prefix = name;
445 suffix = ".cxx";
446 got_hash = false;
447
448 } else {
449 prefix = name.substr(0, hash);
450 suffix = name.substr(hash + 1) + ".cxx";
451 got_hash = true;
452 }
453
454 KeyNumbers::iterator ki;
455 for (ki = key_numbers.begin(); ki != key_numbers.end(); ++ki) {
456 int n = (*ki)._number;
457 const char *pp = nullptr;
458 if ((*ki)._got_pass_phrase) {
459 pp = (*ki)._pass_phrase.c_str();
460 }
461
462 EVP_PKEY *pkey = generate_key();
463 PrcKeyRegistry::get_global_ptr()->set_key(n, pkey, now);
464
465 std::ostringstream strm;
466 if (got_hash || n != 1) {
467 // If we got an explicit hash mark, we always output the number. If we
468 // did not get an explicit hash mark, we output the number only if it is
469 // other than 1.
470 strm << prefix << n << suffix;
471
472 } else {
473 // If we did not get an explicit hash mark in the filename, we omit the
474 // number for key 1 (this might be the only key, and so maybe the user
475 // doesn't require a number designator).
476 strm << prefix << suffix;
477 }
478
479 write_private_key(pkey, strm.str(), n, now, pp);
480 }
481
482 write_public_keys(pub_outfile);
483
484 return (0);
485}
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
std::string get_fullpath_wo_extension() const
Returns the full filename–directory and basename parts–except for the extension.
Definition filename.I:377
void set_text()
Indicates that the filename represents a text file.
Definition filename.I:424
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
std::string get_extension() const
Returns the file extension.
Definition filename.I:400
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition filename.I:386
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EVP_PKEY * generate_key()
Generates a new public and private key pair.
void write_private_key(EVP_PKEY *pkey, Filename outfile, int n, time_t now, const char *pp)
Generates a C++ program that can be used to sign a prc file with the indicated private key into the g...
void write_public_keys(Filename outfile)
Writes the list of public keys stored in the PrcKeyRegistry to the indicated output filename as a com...
void output_ssl_errors()
A convenience function that is itself a wrapper around the OpenSSL convenience function to output the...
void output_c_string(std::ostream &out, const string &string_name, size_t index, BIO *mbio)
Extracts the data written to the indicated memory bio and writes it to the indicated stream,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void preprocess_argv(int &argc, char **&argv)
Processes the argc, argv pair as needed before passing it to getopt().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.