/* * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file crypt.c openssl-based RSA encryption/decryption routines */ #include "para.h" #include "error.h" #include "string.h" #include "crypt.h" static EVP_PKEY *load_key(const char *file, int private) { BIO *key; EVP_PKEY *pkey = 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; } /** * read an RSA key from a file * * \param key_file the file containing the key * \param rsa RSA structure is returned here * \param private if non-zero, read the private key, otherwise the public key * * \return The size of the RSA key on success, negative on errors. * * \sa openssl(1), rsa(1). */ int get_rsa_key(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); } /** * free an RSA structure * * \param rsa pointer to the RSA struct to free * * This must be called for any key obtained by get_rsa_key(). */ void rsa_free(RSA *rsa) { if (rsa) RSA_free(rsa); } /** * decrypt a buffer using an RSA key * * \param key_file full path of the rsa key * \param outbuf the output buffer * \param inbuf the encrypted input buffer * \param rsa_inlen the length of \a inbuf * * The \a outbuf must be large enough to hold at least \a rsa_inlen bytes. * * \return The size of the recovered plaintext on success, negative on errors. * * \sa RSA_private_decrypt(3) **/ int para_decrypt_buffer(char *key_file, unsigned char *outbuf, unsigned char *inbuf, unsigned rsa_inlen) { RSA *rsa; int ret, inlen = rsa_inlen; if (inlen < 0) return -E_RSA; ret = get_rsa_key(key_file, &rsa, LOAD_PRIVATE_KEY); if (ret < 0) return ret; ret = RSA_private_decrypt(inlen, inbuf, outbuf, rsa, RSA_PKCS1_PADDING); rsa_free(rsa); return (ret > 0)? ret : -E_DECRYPT; } /** * decrypt the challenge number sent by para_server * * \param key_file full path of the rsa key * \param challenge_nr result is stored here * \param inbuf the input buffer * \param rsa_inlen the length of \a inbuf * * \return positive on success, negative on errors * * \sa para_decrypt_buffer() */ int para_decrypt_challenge(char *key_file, long unsigned *challenge_nr, unsigned char *inbuf, unsigned rsa_inlen) { unsigned char *rsa_out = OPENSSL_malloc(rsa_inlen + 1); int ret = para_decrypt_buffer(key_file, rsa_out, inbuf, rsa_inlen); if (ret >= 0) { rsa_out[ret] = '\0'; ret = sscanf((char *)rsa_out, "%lu", challenge_nr) == 1? 1 : -E_CHALLENGE; } OPENSSL_free(rsa_out); return ret; } /** * encrypt a buffer using an RSA key * * \param rsa: public rsa key * \param inbuf the input buffer * \param len the length of \a inbuf * \param outbuf the output buffer * * \return The size of the encrypted data on success, negative on errors * * \sa RSA_public_encrypt(3) */ int para_encrypt_buffer(RSA *rsa, 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, rsa, RSA_PKCS1_PADDING); return ret < 0? -E_ENCRYPT : ret; } /** * encrypt the given challenge number * * \param rsa: public rsa key * \param challenge_nr the number to be encrypted * \param outbuf the output buffer * * \a outbuf must be at least 64 bytes long * * \return The size of the encrypted data on success, negative on errors * * \sa para_encrypt_buffer() * */ int para_encrypt_challenge(RSA* rsa, long unsigned challenge_nr, unsigned char *outbuf) { unsigned char *inbuf = (unsigned char*) make_message("%lu", challenge_nr); int ret = para_encrypt_buffer(rsa, inbuf, strlen((char *)inbuf), outbuf); free(inbuf); return ret; }