📄 crypto.c
字号:
/* Copyright (c) 2001, Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char crypto_c_id[] =
"$Id$";
/**
* \file crypto.c
* \brief Wrapper functions to present a consistent interface to
* public-key and symmetric cryptography operations from OpenSSL.
**/
#include "orconfig.h"
#ifdef MS_WINDOWS
#define WIN32_WINNT 0x400
#define _WIN32_WINNT 0x400
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
#endif
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/opensslv.h>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/conf.h>
#include <openssl/hmac.h>
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif
#define CRYPTO_PRIVATE
#include "crypto.h"
#include "log.h"
#include "aes.h"
#include "util.h"
#include "container.h"
#include "compat.h"
#if OPENSSL_VERSION_NUMBER < 0x00907000l
#error "We require openssl >= 0.9.7"
#endif
#include <openssl/engine.h>
/** Macro: is k a valid RSA public or private key? */
#define PUBLIC_KEY_OK(k) ((k) && (k)->key && (k)->key->n)
/** Macro: is k a valid RSA private key? */
#define PRIVATE_KEY_OK(k) ((k) && (k)->key && (k)->key->p)
#ifdef TOR_IS_MULTITHREADED
/** A number of prealloced mutexes for use by openssl. */
static tor_mutex_t **_openssl_mutexes = NULL;
/** How many mutexes have we allocated for use by openssl? */
static int _n_openssl_mutexes = 0;
#endif
/** A public key, or a public/private keypair. */
struct crypto_pk_env_t
{
int refs; /* reference counting so we don't have to copy keys */
RSA *key;
};
/** Key and stream information for a stream cipher. */
struct crypto_cipher_env_t
{
char key[CIPHER_KEY_LEN];
aes_cnt_cipher_t *cipher;
};
/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
* while we're waiting for the second.*/
struct crypto_dh_env_t {
DH *dh;
};
static int setup_openssl_threading(void);
static int tor_check_dh_key(BIGNUM *bn);
/** Return the number of bytes added by padding method <b>padding</b>.
*/
static INLINE int
crypto_get_rsa_padding_overhead(int padding)
{
switch (padding)
{
case RSA_NO_PADDING: return 0;
case RSA_PKCS1_OAEP_PADDING: return 42;
case RSA_PKCS1_PADDING: return 11;
default: tor_assert(0); return -1;
}
}
/** Given a padding method <b>padding</b>, return the correct OpenSSL constant.
*/
static INLINE int
crypto_get_rsa_padding(int padding)
{
switch (padding)
{
case PK_NO_PADDING: return RSA_NO_PADDING;
case PK_PKCS1_PADDING: return RSA_PKCS1_PADDING;
case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING;
default: tor_assert(0); return -1;
}
}
/** Boolean: has OpenSSL's crypto been initialized? */
static int _crypto_global_initialized = 0;
/** Log all pending crypto errors at level <b>severity</b>. Use
* <b>doing</b> to describe our current activities.
*/
static void
crypto_log_errors(int severity, const char *doing)
{
unsigned long err;
const char *msg, *lib, *func;
while ((err = ERR_get_error()) != 0) {
msg = (const char*)ERR_reason_error_string(err);
lib = (const char*)ERR_lib_error_string(err);
func = (const char*)ERR_func_error_string(err);
if (!msg) msg = "(null)";
if (!lib) lib = "(null)";
if (!func) func = "(null)";
if (doing) {
log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)",
doing, msg, lib, func);
} else {
log(severity, LD_CRYPTO, "crypto error: %s (in %s:%s)", msg, lib, func);
}
}
}
/** Log any OpenSSL engines we're using at NOTICE. */
static void
log_engine(const char *fn, ENGINE *e)
{
if (e) {
const char *name, *id;
name = ENGINE_get_name(e);
id = ENGINE_get_id(e);
log(LOG_NOTICE, LD_CRYPTO, "Using OpenSSL engine %s [%s] for %s",
name?name:"?", id?id:"?", fn);
} else {
log(LOG_INFO, LD_CRYPTO, "Using default implementation for %s", fn);
}
}
/** Initialize the crypto library. Return 0 on success, -1 on failure.
*/
int
crypto_global_init(int useAccel)
{
if (!_crypto_global_initialized) {
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
_crypto_global_initialized = 1;
setup_openssl_threading();
/* XXX the below is a bug, since we can't know if we're supposed
* to be using hardware acceleration or not. we should arrange
* for this function to be called before init_keys. But make it
* not complain loudly, at least until we make acceleration work. */
if (useAccel < 0) {
log_info(LD_CRYPTO, "Initializing OpenSSL via tor_tls_init().");
}
if (useAccel > 0) {
log_info(LD_CRYPTO, "Initializing OpenSSL engine support.");
ENGINE_load_builtin_engines();
if (!ENGINE_register_all_complete())
return -1;
/* XXXX make sure this isn't leaking. */
log_engine("RSA", ENGINE_get_default_RSA());
log_engine("DH", ENGINE_get_default_DH());
log_engine("RAND", ENGINE_get_default_RAND());
log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
log_engine("3DES", ENGINE_get_cipher_engine(NID_des_ede3_ecb));
log_engine("AES", ENGINE_get_cipher_engine(NID_aes_128_ecb));
}
}
return 0;
}
/** Free crypto resources held by this thread. */
void
crypto_thread_cleanup(void)
{
ERR_remove_state(0);
}
/** Uninitialize the crypto library. Return 0 on success, -1 on failure.
*/
int
crypto_global_cleanup(void)
{
EVP_cleanup();
ERR_remove_state(0);
ERR_free_strings();
ENGINE_cleanup();
CONF_modules_unload(1);
CRYPTO_cleanup_all_ex_data();
#ifdef TOR_IS_MULTITHREADED
if (_n_openssl_mutexes) {
int n = _n_openssl_mutexes;
tor_mutex_t **ms = _openssl_mutexes;
int i;
_openssl_mutexes = NULL;
_n_openssl_mutexes = 0;
for (i=0;i<n;++i) {
tor_mutex_free(ms[i]);
}
tor_free(ms);
}
#endif
return 0;
}
/** used by tortls.c: wrap an RSA* in a crypto_pk_env_t. */
crypto_pk_env_t *
_crypto_new_pk_env_rsa(RSA *rsa)
{
crypto_pk_env_t *env;
tor_assert(rsa);
env = tor_malloc(sizeof(crypto_pk_env_t));
env->refs = 1;
env->key = rsa;
return env;
}
/** used by tortls.c: wrap the RSA from an evp_pkey in a crypto_pk_env_t.
* returns NULL if this isn't an RSA key. */
crypto_pk_env_t *
_crypto_new_pk_env_evp_pkey(EVP_PKEY *pkey)
{
RSA *rsa;
if (!(rsa = EVP_PKEY_get1_RSA(pkey)))
return NULL;
return _crypto_new_pk_env_rsa(rsa);
}
/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_env_t. Iff
* private is set, include the private-key portion of the key. */
EVP_PKEY *
_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env, int private)
{
RSA *key = NULL;
EVP_PKEY *pkey = NULL;
tor_assert(env->key);
if (private) {
if (!(key = RSAPrivateKey_dup(env->key)))
goto error;
} else {
if (!(key = RSAPublicKey_dup(env->key)))
goto error;
}
if (!(pkey = EVP_PKEY_new()))
goto error;
if (!(EVP_PKEY_assign_RSA(pkey, key)))
goto error;
return pkey;
error:
if (pkey)
EVP_PKEY_free(pkey);
if (key)
RSA_free(key);
return NULL;
}
/** Used by tortls.c: Get the DH* from a crypto_dh_env_t.
*/
DH *
_crypto_dh_env_get_dh(crypto_dh_env_t *dh)
{
return dh->dh;
}
/** Allocate and return storage for a public key. The key itself will not yet
* be set.
*/
crypto_pk_env_t *
crypto_new_pk_env(void)
{
RSA *rsa;
rsa = RSA_new();
if (!rsa) return NULL;
return _crypto_new_pk_env_rsa(rsa);
}
/** Release a reference to an asymmetric key; when all the references
* are released, free the key.
*/
void
crypto_free_pk_env(crypto_pk_env_t *env)
{
tor_assert(env);
if (--env->refs > 0)
return;
if (env->key)
RSA_free(env->key);
tor_free(env);
}
/** Create a new symmetric cipher for a given key and encryption flag
* (1=encrypt, 0=decrypt). Return the crypto object on success; NULL
* on failure.
*/
crypto_cipher_env_t *
crypto_create_init_cipher(const char *key, int encrypt_mode)
{
int r;
crypto_cipher_env_t *crypto = NULL;
if (! (crypto = crypto_new_cipher_env())) {
log_warn(LD_CRYPTO, "Unable to allocate crypto object");
return NULL;
}
if (crypto_cipher_set_key(crypto, key)) {
crypto_log_errors(LOG_WARN, "setting symmetric key");
goto error;
}
if (encrypt_mode)
r = crypto_cipher_encrypt_init_cipher(crypto);
else
r = crypto_cipher_decrypt_init_cipher(crypto);
if (r)
goto error;
return crypto;
error:
if (crypto)
crypto_free_cipher_env(crypto);
return NULL;
}
/** Allocate and return a new symmetric cipher.
*/
crypto_cipher_env_t *
crypto_new_cipher_env(void)
{
crypto_cipher_env_t *env;
env = tor_malloc_zero(sizeof(crypto_cipher_env_t));
env->cipher = aes_new_cipher();
return env;
}
/** Free a symmetric cipher.
*/
void
crypto_free_cipher_env(crypto_cipher_env_t *env)
{
tor_assert(env);
tor_assert(env->cipher);
aes_free_cipher(env->cipher);
memset(env, 0, sizeof(crypto_cipher_env_t));
tor_free(env);
}
/* public key crypto */
/** Generate a new public/private keypair in <b>env</b>. Return 0 on
* success, -1 on failure.
*/
int
crypto_pk_generate_key(crypto_pk_env_t *env)
{
tor_assert(env);
if (env->key)
RSA_free(env->key);
env->key = RSA_generate_key(PK_BYTES*8,65537, NULL, NULL);
if (!env->key) {
crypto_log_errors(LOG_WARN, "generating RSA key");
return -1;
}
return 0;
}
/** Read a PEM-encoded private key from the string <b>s</b> into <b>env</b>.
* Return 0 on success, -1 on failure.
*/
/* Used here, and used for testing. */
int
crypto_pk_read_private_key_from_string(crypto_pk_env_t *env,
const char *s)
{
BIO *b;
tor_assert(env);
tor_assert(s);
/* Create a read-only memory BIO, backed by the nul-terminated string 's' */
b = BIO_new_mem_buf((char*)s, -1);
if (env->key)
RSA_free(env->key);
env->key = PEM_read_bio_RSAPrivateKey(b,NULL,NULL,NULL);
BIO_free(b);
if (!env->key) {
crypto_log_errors(LOG_WARN, "Error parsing private key");
return -1;
}
return 0;
}
/** Read a PEM-encoded private key from the file named by
* <b>keyfile</b> into <b>env</b>. Return 0 on success, -1 on failure.
*/
int
crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env,
const char *keyfile)
{
char *contents;
int r;
/* Read the file into a string. */
contents = read_file_to_str(keyfile, 0, NULL);
if (!contents) {
log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -