1 /* Copyright (c) 2007-2008 The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 /* $Id: /tor/trunk/src/tools/tor-resolve.c 12481 2007-04-21T17:27:19.371353Z nickm $ */
10 #include <sys/types.h>
15 #include <openssl/evp.h>
16 #include <openssl/pem.h>
17 #include <openssl/objects.h>
18 #include <openssl/obj_mac.h>
19 #include <openssl/err.h>
28 #define CRYPTO_PRIVATE
36 #define IDENTITY_KEY_BITS 3072
37 #define SIGNING_KEY_BITS 1024
38 #define DEFAULT_LIFETIME 12
40 /* These globals are set via command line options. */
41 char *identity_key_file
= NULL
;
42 char *signing_key_file
= NULL
;
43 char *certificate_file
= NULL
;
44 int reuse_signing_key
= 0;
47 int months_lifetime
= DEFAULT_LIFETIME
;
48 int passphrase_fd
= -1;
51 char *passphrase
= NULL
;
52 size_t passphrase_len
= 0;
54 EVP_PKEY
*identity_key
= NULL
;
55 EVP_PKEY
*signing_key
= NULL
;
57 /** Write a usage message for tor-gencert to stderr. */
61 fprintf(stderr
, "Syntax:\n"
62 "tor-gencert [-h|--help] [-v] [-r|--reuse] [--create-identity-key]\n"
63 " [-i identity_key_file] [-s signing_key_file] "
64 "[-c certificate_file]\n"
65 " [-m lifetime_in_months] [-a address:port] "
66 "[--passphrase-fd <fd>]\n");
70 /* XXXX copied from crypto.c */
72 crypto_log_errors(int severity
, const char *doing
)
75 const char *msg
, *lib
, *func
;
76 while ((err
= ERR_get_error()) != 0) {
77 msg
= (const char*)ERR_reason_error_string(err
);
78 lib
= (const char*)ERR_lib_error_string(err
);
79 func
= (const char*)ERR_func_error_string(err
);
80 if (!msg
) msg
= "(null)";
81 if (!lib
) lib
= "(null)";
82 if (!func
) func
= "(null)";
84 log(severity
, LD_CRYPTO
, "crypto error while %s: %s (in %s:%s)",
85 doing
, msg
, lib
, func
);
87 log(severity
, LD_CRYPTO
, "crypto error: %s (in %s:%s)", msg
, lib
, func
);
92 /** Read the passphrase from the passphrase fd. */
97 char buf
[1024]; /* "Ought to be enough for anybody." */
98 int n
= read_all(passphrase_fd
, buf
, sizeof(buf
), 0);
100 log_err(LD_GENERAL
, "Couldn't read from passphrase fd: %s",
104 cp
= memchr(buf
, '\n', n
);
105 passphrase_len
= cp
-buf
;
106 passphrase
= tor_strndup(buf
, passphrase_len
);
107 memset(buf
, 0, sizeof(buf
));
112 clear_passphrase(void)
115 memset(passphrase
, 0, passphrase_len
);
116 tor_free(passphrase
);
120 /** Read the command line options from <b>argc</b> and <b>argv</b>,
121 * setting global option vars as needed.
124 parse_commandline(int argc
, char **argv
)
127 log_severity_list_t
*s
;
128 for (i
= 1; i
< argc
; ++i
) {
129 if (!strcmp(argv
[i
], "--help") || !strcmp(argv
[i
], "-h")) {
132 } else if (!strcmp(argv
[i
], "-i")) {
134 fprintf(stderr
, "No argument to -i\n");
137 identity_key_file
= tor_strdup(argv
[++i
]);
138 } else if (!strcmp(argv
[i
], "-s")) {
140 fprintf(stderr
, "No argument to -s\n");
143 signing_key_file
= tor_strdup(argv
[++i
]);
144 } else if (!strcmp(argv
[i
], "-c")) {
146 fprintf(stderr
, "No argument to -c\n");
149 certificate_file
= tor_strdup(argv
[++i
]);
150 } else if (!strcmp(argv
[i
], "-m")) {
152 fprintf(stderr
, "No argument to -m\n");
155 months_lifetime
= atoi(argv
[++i
]);
156 if (months_lifetime
> 24 || months_lifetime
< 0) {
157 fprintf(stderr
, "Lifetime (in months) was out of range.");
160 } else if (!strcmp(argv
[i
], "-r") || !strcmp(argv
[i
], "--reuse")) {
161 reuse_signing_key
= 1;
162 } else if (!strcmp(argv
[i
], "-v")) {
164 } else if (!strcmp(argv
[i
], "-a")) {
167 char b
[INET_NTOA_BUF_LEN
];
170 fprintf(stderr
, "No argument to -a\n");
173 if (parse_addr_port(LOG_ERR
, argv
[++i
], NULL
, &addr
, &port
)<0)
175 in
.s_addr
= htonl(addr
);
176 tor_inet_ntoa(&in
, b
, sizeof(b
));
177 address
= tor_malloc(INET_NTOA_BUF_LEN
+32);
178 tor_snprintf(address
, INET_NTOA_BUF_LEN
+32, "%s:%d", b
, (int)port
);
179 } else if (!strcmp(argv
[i
], "--create-identity-key")) {
181 } else if (!strcmp(argv
[i
], "--passphrase-fd")) {
183 fprintf(stderr
, "No argument to --passphrase-fd\n");
186 passphrase_fd
= atoi(argv
[++i
]);
188 fprintf(stderr
, "Unrecognized option %s\n", argv
[i
]);
193 s
= tor_malloc_zero(sizeof(log_severity_list_t
));
195 set_log_severity_config(LOG_DEBUG
, LOG_ERR
, s
);
197 set_log_severity_config(LOG_WARN
, LOG_ERR
, s
);
198 add_stream_log(s
, "<stderr>", stderr
);
200 if (!identity_key_file
) {
201 identity_key_file
= tor_strdup("./authority_identity_key");
202 log_info(LD_GENERAL
, "No identity key file given; defaulting to %s",
205 if (!signing_key_file
) {
206 signing_key_file
= tor_strdup("./authority_signing_key");
207 log_info(LD_GENERAL
, "No signing key file given; defaulting to %s",
210 if (!certificate_file
) {
211 certificate_file
= tor_strdup("./authority_certificate");
212 log_info(LD_GENERAL
, "No signing key file given; defaulting to %s",
215 if (passphrase_fd
>= 0) {
216 if (load_passphrase()<0)
222 /** Try to read the identity key from <b>identity_key_file</b>. If no such
223 * file exists and create_identity_key is set, make a new identity key and
224 * store it. Return 0 on success, nonzero on failure.
227 load_identity_key(void)
229 file_status_t status
= file_status(identity_key_file
);
233 open_file_t
*open_file
= NULL
;
235 if (status
!= FN_NOENT
) {
236 log_err(LD_GENERAL
, "--create-identity-key was specified, but %s "
237 "already exists.", identity_key_file
);
240 log_notice(LD_GENERAL
, "Generating %d-bit RSA identity key.",
242 if (!(key
= RSA_generate_key(IDENTITY_KEY_BITS
, 65537, NULL
, NULL
))) {
243 log_err(LD_GENERAL
, "Couldn't generate identity key.");
244 crypto_log_errors(LOG_ERR
, "Generating identity key");
247 identity_key
= EVP_PKEY_new();
248 if (!(EVP_PKEY_assign_RSA(identity_key
, key
))) {
249 log_err(LD_GENERAL
, "Couldn't assign identity key.");
253 if (!(f
= start_writing_to_stdio_file(identity_key_file
,
254 OPEN_FLAGS_REPLACE
, 0400,
258 /* Write the key to the file. If passphrase is not set, takes it from
260 if (!PEM_write_PKCS8PrivateKey_nid(f
, identity_key
,
261 NID_pbe_WithSHA1And3_Key_TripleDES_CBC
,
262 passphrase
, (int)passphrase_len
,
264 log_err(LD_GENERAL
, "Couldn't write identity key to %s",
266 crypto_log_errors(LOG_ERR
, "Writing identity key");
267 abort_writing_to_file(open_file
);
270 finish_writing_to_file(open_file
);
272 if (status
!= FN_FILE
) {
274 "No identity key found in %s. To specify a location "
275 "for an identity key, use -i. To generate a new identity key, "
276 "use --create-identity-key.", identity_key_file
);
280 if (!(f
= fopen(identity_key_file
, "r"))) {
281 log_err(LD_GENERAL
, "Couldn't open %s for reading: %s",
282 identity_key_file
, strerror(errno
));
286 /* Read the key. If passphrase is not set, takes it from the terminal. */
287 identity_key
= PEM_read_PrivateKey(f
, NULL
, NULL
, passphrase
);
289 log_err(LD_GENERAL
, "Couldn't read identity key from %s",
298 /** Load a saved signing key from disk. Return 0 on success, nonzero on
301 load_signing_key(void)
304 if (!(f
= fopen(signing_key_file
, "r"))) {
305 log_err(LD_GENERAL
, "Couldn't open %s for reading: %s",
306 signing_key_file
, strerror(errno
));
309 if (!(signing_key
= PEM_read_PrivateKey(f
, NULL
, NULL
, NULL
))) {
310 log_err(LD_GENERAL
, "Couldn't read siging key from %s", signing_key_file
);
317 /** Generate a new signing key and write it to disk. Return 0 on success,
318 * nonzero on failure. */
320 generate_signing_key(void)
322 open_file_t
*open_file
;
325 log_notice(LD_GENERAL
, "Generating %d-bit RSA signing key.",
327 if (!(key
= RSA_generate_key(SIGNING_KEY_BITS
, 65537, NULL
, NULL
))) {
328 log_err(LD_GENERAL
, "Couldn't generate signing key.");
329 crypto_log_errors(LOG_ERR
, "Generating signing key");
332 signing_key
= EVP_PKEY_new();
333 if (!(EVP_PKEY_assign_RSA(signing_key
, key
))) {
334 log_err(LD_GENERAL
, "Couldn't assign signing key.");
338 if (!(f
= start_writing_to_stdio_file(signing_key_file
,
339 OPEN_FLAGS_REPLACE
, 0600,
343 /* Write signing key with no encryption. */
344 if (!PEM_write_RSAPrivateKey(f
, key
, NULL
, NULL
, 0, NULL
, NULL
)) {
345 crypto_log_errors(LOG_WARN
, "writing signing key");
346 abort_writing_to_file(open_file
);
350 finish_writing_to_file(open_file
);
355 /** Encode <b>key</b> in the format used in directory documents; return
356 * a newly allocated string holding the result or NULL on failure. */
358 key_to_string(EVP_PKEY
*key
)
362 RSA
*rsa
= EVP_PKEY_get1_RSA(key
);
367 b
= BIO_new(BIO_s_mem());
368 if (!PEM_write_bio_RSAPublicKey(b
, rsa
)) {
369 crypto_log_errors(LOG_WARN
, "writing public key to string");
373 BIO_get_mem_ptr(b
, &buf
);
374 (void) BIO_set_close(b
, BIO_NOCLOSE
);
376 result
= tor_malloc(buf
->length
+ 1);
377 memcpy(result
, buf
->data
, buf
->length
);
378 result
[buf
->length
] = 0;
384 /** Set <b>out</b> to the hex-encoded fingerprint of <b>pkey</b>. */
386 get_fingerprint(EVP_PKEY
*pkey
, char *out
)
389 crypto_pk_env_t
*pk
= _crypto_new_pk_env_rsa(EVP_PKEY_get1_RSA(pkey
));
391 r
= crypto_pk_get_fingerprint(pk
, out
, 0);
392 crypto_free_pk_env(pk
);
397 /** Generate a new certificate for our loaded or generated keys, and write it
398 * to disk. Return 0 on success, nonzero on failure. */
400 generate_certificate(void)
403 time_t now
= time(NULL
);
405 char published
[ISO_TIME_LEN
+1];
406 char expires
[ISO_TIME_LEN
+1];
407 char fingerprint
[FINGERPRINT_LEN
+1];
408 char *ident
= key_to_string(identity_key
);
409 char *signing
= key_to_string(signing_key
);
412 char digest
[DIGEST_LEN
];
413 char signature
[1024]; /* handles up to 8192-bit keys. */
416 get_fingerprint(identity_key
, fingerprint
);
418 tor_localtime_r(&now
, &tm
);
419 tm
.tm_mon
+= months_lifetime
;
421 format_iso_time(published
, now
);
422 format_iso_time(expires
, mktime(&tm
));
424 tor_snprintf(buf
, sizeof(buf
),
425 "dir-key-certificate-version 3"
428 "dir-key-published %s\n"
429 "dir-key-expires %s\n"
430 "dir-identity-key\n%s"
431 "dir-signing-key\n%s"
432 "dir-key-certification\n",
433 address
?"\ndir-address ":"", address
?address
:"",
434 fingerprint
, published
, expires
, ident
, signing
);
437 signed_len
= strlen(buf
);
438 SHA1((const unsigned char*)buf
,signed_len
,(unsigned char*)digest
);
440 r
= RSA_private_encrypt(DIGEST_LEN
, (unsigned char*)digest
,
441 (unsigned char*)signature
,
442 EVP_PKEY_get1_RSA(identity_key
),
444 strlcat(buf
, "-----BEGIN SIGNATURE-----\n", sizeof(buf
));
445 signed_len
= strlen(buf
);
446 base64_encode(buf
+signed_len
, sizeof(buf
)-signed_len
, signature
, r
);
447 strlcat(buf
, "-----END SIGNATURE-----\n", sizeof(buf
));
449 if (!(f
= fopen(certificate_file
, "w"))) {
450 log_err(LD_GENERAL
, "Couldn't open %s for writing: %s",
451 certificate_file
, strerror(errno
));
461 /** Entry point to tor-gencert */
463 main(int argc
, char **argv
)
468 /* Don't bother using acceleration. */
469 if (crypto_global_init(0)) {
470 fprintf(stderr
, "Couldn't initialize crypto library.\n");
473 if (crypto_seed_rng(1)) {
474 fprintf(stderr
, "Couldn't seed RNG.\n");
477 /* Make sure that files are made private. */
480 if (parse_commandline(argc
, argv
))
482 if (load_identity_key())
484 if (reuse_signing_key
) {
485 if (load_signing_key())
488 if (generate_signing_key())
491 if (generate_certificate())
498 EVP_PKEY_free(identity_key
);
500 EVP_PKEY_free(signing_key
);
503 crypto_global_cleanup();