X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=crypt.c;h=8116fb6eeacdbeaa0f1f22d14cfcc3726ee3bbbf;hb=refs%2Fheads%2Fmaint;hp=f227eb39907691d24f0eae360c04027c1653be75;hpb=35fcd604e2ec04f8667c85fa1552932b1a0e6efb;p=paraslash.git diff --git a/crypt.c b/crypt.c deleted file mode 100644 index f227eb39..00000000 --- a/crypt.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (C) 2005 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file crypt.c Openssl-based encryption/decryption routines. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "para.h" -#include "error.h" -#include "string.h" -#include "crypt.h" -#include "fd.h" -#include "crypt_backend.h" -#include "base64.h" - -struct asymmetric_key { - RSA *rsa; -}; - -void get_random_bytes_or_die(unsigned char *buf, int num) -{ - unsigned long err; - - /* RAND_bytes() returns 1 on success, 0 otherwise. */ - if (RAND_bytes(buf, num) == 1) - return; - err = ERR_get_error(); - PARA_EMERG_LOG("%s\n", ERR_reason_error_string(err)); - exit(EXIT_FAILURE); -} - -/* - * Read 64 bytes from /dev/urandom and adds them to the SSL PRNG. Seed the PRNG - * used by random() with a random seed obtained from SSL. If /dev/random is not - * readable the function calls exit(). - * - * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3), - * random(3), \ref para_random(). - */ -void init_random_seed_or_die(void) -{ - int seed, ret = RAND_load_file("/dev/urandom", 64); - - if (ret != 64) { - PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret); - exit(EXIT_FAILURE); - } - get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed)); - srandom(seed); -} - -static EVP_PKEY *load_key(const char *file, int private) -{ - BIO *key; - EVP_PKEY *pkey = NULL; - int ret = check_key_file(file, private); - - if (ret < 0) { - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - return NULL; - } - key = BIO_new(BIO_s_file()); - if (!key) - return NULL; - if (BIO_read_filename(key, file) > 0) { - if (private == LOAD_PRIVATE_KEY) - pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL); - else - pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL); - } - BIO_free(key); - return pkey; -} - -static int get_openssl_key(const char *key_file, RSA **rsa, int private) -{ - EVP_PKEY *key = load_key(key_file, private); - - if (!key) - return (private == LOAD_PRIVATE_KEY)? -E_PRIVATE_KEY - : -E_PUBLIC_KEY; - *rsa = EVP_PKEY_get1_RSA(key); - EVP_PKEY_free(key); - if (!*rsa) - return -E_RSA; - return RSA_size(*rsa); -} - -/* - * The public key loading functions below were inspired by corresponding code - * of openssh-5.2p1, Copyright (c) 1995 Tatu Ylonen , Espoo, - * Finland. However, not much of the original code remains. - */ - -static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result) -{ - const unsigned char *p = buf, *end = buf + len; - uint32_t bnsize; - BIGNUM *bn; - - if (p + 4 < p) - return -E_BIGNUM; - if (p + 4 > end) - return -E_BIGNUM; - bnsize = read_ssh_u32(p); - PARA_DEBUG_LOG("bnsize: %u\n", bnsize); - p += 4; - if (p + bnsize < p) - return -E_BIGNUM; - if (p + bnsize > end) - return -E_BIGNUM; - if (bnsize > 8192) - return -E_BIGNUM; - bn = BN_bin2bn(p, bnsize, NULL); - if (!bn) - return -E_BIGNUM; - *result = bn; - return bnsize + 4; -} - -static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result) -{ - int ret; - RSA *rsa; - const unsigned char *p = blob, *end = blob + blen; - - rsa = RSA_new(); - if (!rsa) - return -E_BIGNUM; - ret = read_bignum(p, end - p, &rsa->e); - if (ret < 0) - goto fail; - p += ret; - ret = read_bignum(p, end - p, &rsa->n); - if (ret < 0) - goto fail; - *result = rsa; - return 1; -fail: - RSA_free(rsa); - return ret; -} - -int get_asymmetric_key(const char *key_file, int private, - struct asymmetric_key **result) -{ - struct asymmetric_key *key = NULL; - void *map = NULL; - unsigned char *blob = NULL; - size_t map_size, encoded_size, decoded_size; - int ret, ret2; - char *cp; - - key = para_malloc(sizeof(*key)); - if (private) { - ret = get_openssl_key(key_file, &key->rsa, LOAD_PRIVATE_KEY); - goto out; - } - ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL); - if (ret < 0) - goto out; - ret = is_ssh_rsa_key(map, map_size); - if (!ret) { - ret = para_munmap(map, map_size); - map = NULL; - if (ret < 0) - goto out; - ret = get_openssl_key(key_file, &key->rsa, LOAD_PUBLIC_KEY); - goto out; - } - cp = map + ret; - encoded_size = map_size - ret; - PARA_INFO_LOG("decoding public rsa-ssh key %s\n", key_file); - ret = uudecode(cp, encoded_size, (char **)&blob, &decoded_size); - if (ret < 0) - goto out_unmap; - ret = check_ssh_key_header(blob, decoded_size); - if (ret < 0) - goto out_unmap; - ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa); - if (ret < 0) - goto out_unmap; - ret = RSA_size(key->rsa); -out_unmap: - ret2 = para_munmap(map, map_size); - if (ret >= 0 && ret2 < 0) - ret = ret2; -out: - if (ret < 0) { - free(key); - *result = NULL; - PARA_ERROR_LOG("key %s: %s\n", key_file, para_strerror(-ret)); - } else - *result = key; - free(blob); - return ret; -} - -void free_asymmetric_key(struct asymmetric_key *key) -{ - if (!key) - return; - RSA_free(key->rsa); - free(key); -} - -int priv_decrypt(const char *key_file, unsigned char *outbuf, - unsigned char *inbuf, int inlen) -{ - struct asymmetric_key *priv; - int ret; - - if (inlen < 0) - return -E_RSA; - ret = get_asymmetric_key(key_file, LOAD_PRIVATE_KEY, &priv); - if (ret < 0) - return ret; - /* - * RSA is vulnerable to timing attacks. Generate a random blinding - * factor to protect against this kind of attack. - */ - ret = -E_BLINDING; - if (RSA_blinding_on(priv->rsa, NULL) == 0) - goto out; - ret = RSA_private_decrypt(inlen, inbuf, outbuf, priv->rsa, - RSA_PKCS1_OAEP_PADDING); - RSA_blinding_off(priv->rsa); - if (ret <= 0) - ret = -E_DECRYPT; -out: - free_asymmetric_key(priv); - return ret; -} - -int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, - unsigned len, unsigned char *outbuf) -{ - int ret, flen = len; /* RSA_public_encrypt expects a signed int */ - - if (flen < 0) - return -E_ENCRYPT; - ret = RSA_public_encrypt(flen, inbuf, outbuf, pub->rsa, - RSA_PKCS1_OAEP_PADDING); - return ret < 0? -E_ENCRYPT : ret; -} - -struct aes_ctr_128_context { - AES_KEY key; - unsigned char ivec[AES_CRT128_BLOCK_SIZE]; - unsigned char ecount[AES_CRT128_BLOCK_SIZE]; - unsigned int num; -}; - -struct stream_cipher { - bool use_aes; - union { - RC4_KEY rc4_key; - struct aes_ctr_128_context aes; - } context; -}; - -struct stream_cipher *sc_new(const unsigned char *data, int len, - bool use_aes) -{ - int ret; - struct stream_cipher *sc = para_malloc(sizeof(*sc)); - struct aes_ctr_128_context *aes; - - sc->use_aes = use_aes; - if (!use_aes) { - RC4_set_key(&sc->context.rc4_key, len, data); - return sc; - } - assert(len >= 2 * AES_CRT128_BLOCK_SIZE); - aes = &sc->context.aes; - ret = AES_set_encrypt_key(data, AES_CRT128_BLOCK_SIZE * 8 /* bits */, - &aes->key); - assert(ret == 0); - memcpy(aes->ivec, data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE); - aes->num = 0; - return sc; -} - -void sc_free(struct stream_cipher *sc) -{ - free(sc); -} - -/** - * The RC4() implementation of openssl apparently reads and writes data in - * blocks of 8 bytes. So we have to make sure our buffer sizes are a multiple - * of this. - */ -#define RC4_ALIGN 8 - -static void rc4_crypt(RC4_KEY *key, struct iovec *src, struct iovec *dst) -{ - size_t len = src->iov_len, l1, l2; - - assert(len > 0); - assert(len < ((typeof(src->iov_len))-1) / 2); - l1 = ROUND_DOWN(len, RC4_ALIGN); - l2 = ROUND_UP(len, RC4_ALIGN); - - *dst = (typeof(*dst)) { - /* Add one for the terminating zero byte. */ - .iov_base = para_malloc(l2 + 1), - .iov_len = len - }; - RC4(key, l1, src->iov_base, dst->iov_base); - if (len > l1) { - unsigned char remainder[RC4_ALIGN] = ""; - memcpy(remainder, src->iov_base + l1, len - l1); - RC4(key, len - l1, remainder, dst->iov_base + l1); - } - ((char *)dst->iov_base)[len] = '\0'; -} - -static void aes_ctr128_crypt(struct aes_ctr_128_context *aes, struct iovec *src, - struct iovec *dst) -{ - size_t len = src->iov_len; - - *dst = (typeof(*dst)) { - /* Add one for the terminating zero byte. */ - .iov_base = para_malloc(len + 1), - .iov_len = len - }; - AES_ctr128_encrypt(src->iov_base, dst->iov_base, len, - &aes->key, aes->ivec, aes->ecount, &aes->num); - ((char *)dst->iov_base)[len] = '\0'; -} - -void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst) -{ - if (sc->use_aes) - return aes_ctr128_crypt(&sc->context.aes, src, dst); - return rc4_crypt(&sc->context.rc4_key, src, dst); -} - -void hash_function(const char *data, unsigned long len, unsigned char *hash) -{ - SHA_CTX c; - SHA1_Init(&c); - SHA1_Update(&c, data, len); - SHA1_Final(hash, &c); -}