1 /* Copyright (c) 2007-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
16 #include "lib/cc/compat_compiler.h"
17 #include "lib/crypt_ops/crypto_init.h"
18 #include "lib/crypt_ops/crypto_openssl_mgt.h"
21 /* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice in
22 * x509.h and x509_vfy.h. Suppress the GCC warning so we can build with
23 * -Wredundant-decl. */
24 DISABLE_GCC_WARNING("-Wredundant-decls")
26 #include <openssl/evp.h>
27 #include <openssl/pem.h>
28 #include <openssl/rsa.h>
29 #include <openssl/objects.h>
30 #include <openssl/obj_mac.h>
31 #include <openssl/err.h>
33 ENABLE_GCC_WARNING("-Wredundant-decls")
34 #endif /* defined(ENABLE_OPENSSL) */
38 #include "lib/crypt_ops/crypto_digest.h"
39 #include "lib/crypt_ops/crypto_rand.h"
40 #include "lib/crypt_ops/crypto_rsa.h"
41 #include "lib/crypt_ops/crypto_util.h"
42 #include "lib/encoding/binascii.h"
43 #include "lib/encoding/time_fmt.h"
44 #include "lib/fs/files.h"
45 #include "lib/log/log.h"
46 #include "lib/malloc/malloc.h"
47 #include "lib/net/address.h"
48 #include "lib/net/inaddr.h"
49 #include "lib/net/resolve.h"
50 #include "lib/string/compat_string.h"
51 #include "lib/string/printf.h"
53 #define IDENTITY_KEY_BITS 3072
54 #define SIGNING_KEY_BITS 2048
55 #define DEFAULT_LIFETIME 12
57 /* These globals are set via command line options. */
58 static char *identity_key_file
= NULL
;
59 static char *signing_key_file
= NULL
;
60 static char *certificate_file
= NULL
;
61 static int reuse_signing_key
= 0;
62 static int verbose
= 0;
63 static int make_new_id
= 0;
64 static int months_lifetime
= DEFAULT_LIFETIME
;
65 static int passphrase_fd
= -1;
66 static char *address
= NULL
;
68 static char *passphrase
= NULL
;
69 static size_t passphrase_len
= 0;
71 static EVP_PKEY
*identity_key
= NULL
;
72 static EVP_PKEY
*signing_key
= NULL
;
74 /** Write a usage message for tor-gencert to stderr. */
78 fprintf(stderr
, "Syntax:\n"
79 "tor-gencert [-h|--help] [-v] [-r|--reuse] [--create-identity-key]\n"
80 " [-i identity_key_file] [-s signing_key_file] "
81 "[-c certificate_file]\n"
82 " [-m lifetime_in_months] [-a address:port] "
83 "[--passphrase-fd <fd>]\n");
86 /** Read the passphrase from the passphrase fd. */
91 char buf
[1024]; /* "Ought to be enough for anybody." */
92 memset(buf
, 0, sizeof(buf
)); /* should be needless */
93 ssize_t n
= read_all_from_fd(passphrase_fd
, buf
, sizeof(buf
));
95 log_err(LD_GENERAL
, "Couldn't read from passphrase fd: %s",
99 /* We'll take everything from the buffer except for optional terminating
101 cp
= memchr(buf
, '\n', n
);
105 passphrase_len
= cp
-buf
;
107 passphrase
= tor_strndup(buf
, passphrase_len
);
108 memwipe(buf
, 0, sizeof(buf
));
113 clear_passphrase(void)
116 memwipe(passphrase
, 0, passphrase_len
);
117 tor_free(passphrase
);
121 /** Read the command line options from <b>argc</b> and <b>argv</b>,
122 * setting global option vars as needed.
125 parse_commandline(int argc
, char **argv
)
128 log_severity_list_t s
;
129 for (i
= 1; i
< argc
; ++i
) {
130 if (!strcmp(argv
[i
], "--help") || !strcmp(argv
[i
], "-h")) {
133 } else if (!strcmp(argv
[i
], "-i")) {
135 fprintf(stderr
, "No argument to -i\n");
138 if (identity_key_file
) {
139 fprintf(stderr
, "Duplicate values for -i\n");
142 identity_key_file
= tor_strdup(argv
[++i
]);
143 } else if (!strcmp(argv
[i
], "-s")) {
145 fprintf(stderr
, "No argument to -s\n");
148 if (signing_key_file
) {
149 fprintf(stderr
, "Duplicate values for -s\n");
152 signing_key_file
= tor_strdup(argv
[++i
]);
153 } else if (!strcmp(argv
[i
], "-c")) {
155 fprintf(stderr
, "No argument to -c\n");
158 if (certificate_file
) {
159 fprintf(stderr
, "Duplicate values for -c\n");
162 certificate_file
= tor_strdup(argv
[++i
]);
163 } else if (!strcmp(argv
[i
], "-m")) {
165 fprintf(stderr
, "No argument to -m\n");
168 months_lifetime
= atoi(argv
[++i
]);
169 if (months_lifetime
> 24 || months_lifetime
< 0) {
170 fprintf(stderr
, "Lifetime (in months) was out of range.\n");
173 } else if (!strcmp(argv
[i
], "-r") || !strcmp(argv
[i
], "--reuse")) {
174 reuse_signing_key
= 1;
175 } else if (!strcmp(argv
[i
], "-v")) {
177 } else if (!strcmp(argv
[i
], "-a")) {
181 fprintf(stderr
, "No argument to -a\n");
184 const char *addr_arg
= argv
[++i
];
185 if (tor_addr_port_lookup(addr_arg
, &addr
, &port
)<0) {
186 fprintf(stderr
, "Can't resolve address/port for %s", addr_arg
);
189 if (tor_addr_family(&addr
) != AF_INET
) {
190 fprintf(stderr
, "%s must resolve to an IPv4 address", addr_arg
);
194 address
= tor_strdup(fmt_addrport(&addr
, port
));
195 } else if (!strcmp(argv
[i
], "--create-identity-key")) {
197 } else if (!strcmp(argv
[i
], "--passphrase-fd")) {
199 fprintf(stderr
, "No argument to --passphrase-fd\n");
202 passphrase_fd
= atoi(argv
[++i
]);
204 fprintf(stderr
, "Unrecognized option %s\n", argv
[i
]);
209 memwipe(&s
, 0, sizeof(s
));
211 set_log_severity_config(LOG_DEBUG
, LOG_ERR
, &s
);
213 set_log_severity_config(LOG_WARN
, LOG_ERR
, &s
);
214 add_stream_log(&s
, "<stderr>", fileno(stderr
));
216 if (!identity_key_file
) {
217 identity_key_file
= tor_strdup("./authority_identity_key");
218 log_info(LD_GENERAL
, "No identity key file given; defaulting to %s",
221 if (!signing_key_file
) {
222 signing_key_file
= tor_strdup("./authority_signing_key");
223 log_info(LD_GENERAL
, "No signing key file given; defaulting to %s",
226 if (!certificate_file
) {
227 certificate_file
= tor_strdup("./authority_certificate");
228 log_info(LD_GENERAL
, "No signing key file given; defaulting to %s",
231 if (passphrase_fd
>= 0) {
232 if (load_passphrase()<0)
239 generate_key(int bits
)
242 crypto_pk_t
*env
= crypto_pk_new();
243 if (crypto_pk_generate_key_with_bits(env
,bits
)<0)
245 rsa
= crypto_pk_get_openssl_rsa_(env
);
251 #define MIN_PASSPHRASE_LEN 4
253 /** Try to read the identity key from <b>identity_key_file</b>. If no such
254 * file exists and create_identity_key is set, make a new identity key and
255 * store it. Return 0 on success, nonzero on failure.
258 load_identity_key(void)
260 file_status_t status
= file_status(identity_key_file
);
264 open_file_t
*open_file
= NULL
;
266 if (status
!= FN_NOENT
) {
267 log_err(LD_GENERAL
, "--create-identity-key was specified, but %s "
268 "already exists.", identity_key_file
);
271 log_notice(LD_GENERAL
, "Generating %d-bit RSA identity key.",
273 if (!(key
= generate_key(IDENTITY_KEY_BITS
))) {
274 log_err(LD_GENERAL
, "Couldn't generate identity key.");
275 crypto_openssl_log_errors(LOG_ERR
, "Generating identity key");
278 identity_key
= EVP_PKEY_new();
279 if (!(EVP_PKEY_assign_RSA(identity_key
, key
))) {
280 log_err(LD_GENERAL
, "Couldn't assign identity key.");
284 if (!(f
= start_writing_to_stdio_file(identity_key_file
,
285 OPEN_FLAGS_REPLACE
| O_TEXT
, 0400,
289 /* Write the key to the file. If passphrase is not set, takes it from
291 if (!PEM_write_PKCS8PrivateKey_nid(f
, identity_key
,
292 NID_pbe_WithSHA1And3_Key_TripleDES_CBC
,
293 passphrase
, (int) passphrase_len
,
295 if ((int) passphrase_len
< MIN_PASSPHRASE_LEN
) {
296 log_err(LD_GENERAL
, "Passphrase empty or too short. Passphrase needs "
297 "to be at least %d characters.", MIN_PASSPHRASE_LEN
);
299 log_err(LD_GENERAL
, "Couldn't write identity key to %s",
301 crypto_openssl_log_errors(LOG_ERR
, "Writing identity key");
303 abort_writing_to_file(open_file
);
306 finish_writing_to_file(open_file
);
308 if (status
!= FN_FILE
) {
310 "No identity key found in %s. To specify a location "
311 "for an identity key, use -i. To generate a new identity key, "
312 "use --create-identity-key.", identity_key_file
);
316 if (!(f
= fopen(identity_key_file
, "r"))) {
317 log_err(LD_GENERAL
, "Couldn't open %s for reading: %s",
318 identity_key_file
, strerror(errno
));
322 /* Read the key. If passphrase is not set, takes it from the terminal. */
323 identity_key
= PEM_read_PrivateKey(f
, NULL
, NULL
, passphrase
);
325 log_err(LD_GENERAL
, "Couldn't read identity key from %s",
335 /** Load a saved signing key from disk. Return 0 on success, nonzero on
338 load_signing_key(void)
341 if (!(f
= fopen(signing_key_file
, "r"))) {
342 log_err(LD_GENERAL
, "Couldn't open %s for reading: %s",
343 signing_key_file
, strerror(errno
));
346 if (!(signing_key
= PEM_read_PrivateKey(f
, NULL
, NULL
, NULL
))) {
347 log_err(LD_GENERAL
, "Couldn't read siging key from %s", signing_key_file
);
355 /** Generate a new signing key and write it to disk. Return 0 on success,
356 * nonzero on failure. */
358 generate_signing_key(void)
360 open_file_t
*open_file
;
363 log_notice(LD_GENERAL
, "Generating %d-bit RSA signing key.",
365 if (!(key
= generate_key(SIGNING_KEY_BITS
))) {
366 log_err(LD_GENERAL
, "Couldn't generate signing key.");
367 crypto_openssl_log_errors(LOG_ERR
, "Generating signing key");
370 signing_key
= EVP_PKEY_new();
371 if (!(EVP_PKEY_assign_RSA(signing_key
, key
))) {
372 log_err(LD_GENERAL
, "Couldn't assign signing key.");
376 if (!(f
= start_writing_to_stdio_file(signing_key_file
,
377 OPEN_FLAGS_REPLACE
| O_TEXT
, 0600,
381 /* Write signing key with no encryption. */
382 if (!PEM_write_RSAPrivateKey(f
, key
, NULL
, NULL
, 0, NULL
, NULL
)) {
383 crypto_openssl_log_errors(LOG_WARN
, "writing signing key");
384 abort_writing_to_file(open_file
);
388 finish_writing_to_file(open_file
);
393 /** Encode <b>key</b> in the format used in directory documents; return
394 * a newly allocated string holding the result or NULL on failure. */
396 key_to_string(EVP_PKEY
*key
)
400 RSA
*rsa
= EVP_PKEY_get1_RSA(key
);
405 b
= BIO_new(BIO_s_mem());
406 if (!PEM_write_bio_RSAPublicKey(b
, rsa
)) {
407 crypto_openssl_log_errors(LOG_WARN
, "writing public key to string");
412 BIO_get_mem_ptr(b
, &buf
);
413 result
= tor_malloc(buf
->length
+ 1);
414 memcpy(result
, buf
->data
, buf
->length
);
415 result
[buf
->length
] = 0;
423 /** Set <b>out</b> to the hex-encoded fingerprint of <b>pkey</b>. */
425 get_fingerprint(EVP_PKEY
*pkey
, char *out
)
428 crypto_pk_t
*pk
= crypto_new_pk_from_openssl_rsa_(EVP_PKEY_get1_RSA(pkey
));
430 r
= crypto_pk_get_fingerprint(pk
, out
, 0);
436 /** Set <b>out</b> to the hex-encoded fingerprint of <b>pkey</b>. */
438 get_digest(EVP_PKEY
*pkey
, char *out
)
441 crypto_pk_t
*pk
= crypto_new_pk_from_openssl_rsa_(EVP_PKEY_get1_RSA(pkey
));
443 r
= crypto_pk_get_digest(pk
, out
);
449 /** Generate a new certificate for our loaded or generated keys, and write it
450 * to disk. Return 0 on success, nonzero on failure. */
452 generate_certificate(void)
455 time_t now
= time(NULL
);
457 char published
[ISO_TIME_LEN
+1];
458 char expires
[ISO_TIME_LEN
+1];
459 char id_digest
[DIGEST_LEN
];
460 char fingerprint
[FINGERPRINT_LEN
+1];
463 char digest
[DIGEST_LEN
];
464 char signature
[1024]; /* handles up to 8192-bit keys. */
467 if (get_fingerprint(identity_key
, fingerprint
) < 0) {
470 if (get_digest(identity_key
, id_digest
)) {
473 char *ident
= key_to_string(identity_key
);
474 char *signing
= key_to_string(signing_key
);
476 tor_localtime_r(&now
, &tm
);
477 tm
.tm_mon
+= months_lifetime
;
479 format_iso_time(published
, now
);
480 format_iso_time(expires
, mktime(&tm
));
482 tor_snprintf(buf
, sizeof(buf
),
483 "dir-key-certificate-version 3"
486 "dir-key-published %s\n"
487 "dir-key-expires %s\n"
488 "dir-identity-key\n%s"
489 "dir-signing-key\n%s"
490 "dir-key-crosscert\n"
491 "-----BEGIN ID SIGNATURE-----\n",
492 address
?"\ndir-address ":"", address
?address
:"",
493 fingerprint
, published
, expires
, ident
, signing
498 /* Append a cross-certification */
499 RSA
*rsa
= EVP_PKEY_get1_RSA(signing_key
);
500 r
= RSA_private_encrypt(DIGEST_LEN
, (unsigned char*)id_digest
,
501 (unsigned char*)signature
,
506 signed_len
= strlen(buf
);
507 base64_encode(buf
+signed_len
, sizeof(buf
)-signed_len
, signature
, r
,
508 BASE64_ENCODE_MULTILINE
);
511 "-----END ID SIGNATURE-----\n"
512 "dir-key-certification\n", sizeof(buf
));
514 signed_len
= strlen(buf
);
515 SHA1((const unsigned char*)buf
,signed_len
,(unsigned char*)digest
);
517 rsa
= EVP_PKEY_get1_RSA(identity_key
);
518 r
= RSA_private_encrypt(DIGEST_LEN
, (unsigned char*)digest
,
519 (unsigned char*)signature
,
523 strlcat(buf
, "-----BEGIN SIGNATURE-----\n", sizeof(buf
));
524 signed_len
= strlen(buf
);
525 base64_encode(buf
+signed_len
, sizeof(buf
)-signed_len
, signature
, r
,
526 BASE64_ENCODE_MULTILINE
);
527 strlcat(buf
, "-----END SIGNATURE-----\n", sizeof(buf
));
529 if (!(f
= fopen(certificate_file
, "w"))) {
530 log_err(LD_GENERAL
, "Couldn't open %s for writing: %s",
531 certificate_file
, strerror(errno
));
535 if (fputs(buf
, f
) < 0) {
536 log_err(LD_GENERAL
, "Couldn't write to %s: %s",
537 certificate_file
, strerror(errno
));
545 /** Entry point to tor-gencert */
547 main(int argc
, char **argv
)
552 /* Don't bother using acceleration. */
553 if (crypto_global_init(0, NULL
, NULL
)) {
554 fprintf(stderr
, "Couldn't initialize crypto library.\n");
557 if (crypto_seed_rng()) {
558 fprintf(stderr
, "Couldn't seed RNG.\n");
561 /* Make sure that files are made private. */
564 if (parse_commandline(argc
, argv
))
566 if (load_identity_key())
568 if (reuse_signing_key
) {
569 if (load_signing_key())
572 if (generate_signing_key())
575 if (generate_certificate())
582 EVP_PKEY_free(identity_key
);
584 EVP_PKEY_free(signing_key
);
586 tor_free(identity_key_file
);
587 tor_free(signing_key_file
);
588 tor_free(certificate_file
);
591 crypto_global_cleanup();