diff options
Diffstat (limited to 'lib/lanplus/lanplus_crypt_impl.c')
-rw-r--r-- | lib/lanplus/lanplus_crypt_impl.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/lib/lanplus/lanplus_crypt_impl.c b/lib/lanplus/lanplus_crypt_impl.c new file mode 100644 index 0000000..3f9679e --- /dev/null +++ b/lib/lanplus/lanplus_crypt_impl.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ +/* ARCress, TODO: improve error handling and remove all assert() calls here. */ + +#include "ipmitool/log.h" +#include "ipmitool/ipmi_constants.h" +#include "lanplus.h" +#include "lanplus_crypt_impl.h" +#include <openssl/hmac.h> +#include <openssl/evp.h> +#include <openssl/rand.h> +#include <openssl/err.h> +#include <assert.h> + + + +/* + * lanplus_seed_prng + * + * Seed our PRNG with the specified number of bytes from /dev/random + * + * param bytes specifies the number of bytes to read from /dev/random + * + * returns 0 on success + * 1 on failure + */ +int lanplus_seed_prng(uint32_t bytes) +{ +#ifdef WIN32 + const char *randfile; + char buffer[200]; + static FILE *fp = NULL; + size_t i; + randfile = RAND_file_name(buffer, sizeof buffer); /* usu C:\.rnd */ + if ((randfile != NULL) && (fp == NULL)) { + /*first time, so open/create file*/ + fp = fopen(randfile,"w"); /*create the randfile*/ + if (fp != NULL) { + i = fwrite(" \n",2,1,fp); + fclose(fp); + } + } + if (verbose > 0) + printf("seed_prng: RAND_file_name = %s, fp=%p\n",randfile,fp); +#else + const char *randfile = "/dev/urandom"; +#endif + + if (RAND_load_file(randfile, bytes) == 0) { + printf("seed_prng: RAND_load_file(%s) failed\n",randfile); + return 1; + } else { /*success*/ + return 0; + } +} + +/* + * lanplus_rand + * + * Generate a random number of the specified size + * + * param num_bytes [in] is the size of the random number to be + * generated + * param buffer [out] is where we will place our random number + * + * return 0 on success + * 1 on failure + */ +int +lanplus_rand(uint8_t * buffer, uint32_t num_bytes) +{ +#undef IPMI_LANPLUS_FAKE_RAND +#ifdef IPMI_LANPLUS_FAKE_RAND + /* + * This code exists so that we can easily find the generated random + * number in the hex dumps. + */ + int i; + for (i = 0; i < num_bytes; ++i) + buffer[i] = 0x70 | i; + return 0; +#else + return (! RAND_bytes(buffer, num_bytes)); +#endif +} + +/* + * lanplus_HMAC + * + * param mac specifies the algorithm to be used, currently only SHA1 is supported + * param key is the key used for HMAC generation + * param key_len is the lenght of key + * param d is the data to be MAC'd + * param n is the length of the data at d + * param md is the result of the HMAC algorithm + * param md_len is the length of md + * + * returns a pointer to md + */ +uint8_t * +lanplus_HMAC(uint8_t mac, + const void *key, + int key_len, + const uint8_t *d, + int n, + uint8_t *md, + uint32_t *md_len) +{ + const EVP_MD *evp_md = NULL; + unsigned int mlen; + uint8_t *pnew; + + *md_len = 0; /*if return NULL, also return zero length*/ + if ((mac == IPMI_AUTH_RAKP_HMAC_SHA1) || + (mac == IPMI_INTEGRITY_HMAC_SHA1_96)) + evp_md = EVP_sha1(); + else if ((mac == IPMI_AUTH_RAKP_HMAC_MD5) || + (mac == IPMI_INTEGRITY_HMAC_MD5_128)) + evp_md = EVP_md5(); + else if ((mac == IPMI_AUTH_RAKP_HMAC_SHA256) || + (mac == IPMI_INTEGRITY_HMAC_SHA256_128)) { +#ifdef HAVE_SHA256 + evp_md = EVP_sha256(); +#else + lprintf(LOG_ERR, "Invalid EVP_sha256 in lanplus_HMAC"); + return NULL; // assert(0); +#endif + } else { + lprintf(LOG_ERR,"Invalid mac type 0x%x in lanplus_HMAC",mac); + return NULL; // assert(0); + } + mlen = 20; /* *md_len is usually not initialized */ + pnew = HMAC(evp_md, key, key_len, d, n, md, &mlen); + *md_len = (uint32_t)mlen; + return(pnew); +} + + +/* + * lanplus_encrypt_aes_cbc_128 + * + * Encrypt with the AES CBC 128 algorithm + * + * param iv is the 16 byte initialization vector + * param key is the 16 byte key used by the AES algorithm + * param input is the data to be encrypted + * param input_length is the number of bytes to be encrypted. This MUST + * be a multiple of the block size, 16. + * param output is the encrypted output + * param bytes_written is the number of bytes written. This param is set + * to 0 on failure, or if 0 bytes were input. + */ +void +lanplus_encrypt_aes_cbc_128(const uint8_t * iv, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint32_t * bytes_written) +{ + int nwritten = 0; + int inlen = 0; + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + *bytes_written = 0; + if (input_length == 0) return; + + if (verbose >= 5) + { + printbuf(iv, 16, "encrypting with this IV"); + printbuf(key, 16, "encrypting with this key"); + printbuf(input, input_length, "encrypting this data"); + } + + /* + * The default implementation adds a whole block of padding if the input + * data is perfectly aligned. We would like to keep that from happening. + * We have made a point to have our input perfectly padded. + */ + assert((input_length % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE) == 0); + inlen = input_length; + + if(!EVP_EncryptUpdate(&ctx, output, &nwritten, input, inlen)) + { + /* Error */ + *bytes_written = 0; + return; + } + else + { + int tmplen; + + if(!EVP_EncryptFinal_ex(&ctx, output + nwritten, &tmplen)) + { + *bytes_written = 0; + return; /* Error */ + } + else + { + /* Success */ + *bytes_written = nwritten + tmplen; + EVP_CIPHER_CTX_cleanup(&ctx); + } + } +} + + +/* + * lanplus_decrypt_aes_cbc_128 + * + * Decrypt with the AES CBC 128 algorithm + * + * param iv is the 16 byte initialization vector + * param key is the 16 byte key used by the AES algorithm + * param input is the data to be decrypted + * param input_length is the number of bytes to be decrypted. This MUST + * be a multiple of the block size, 16. + * param output is the decrypted output + * param bytes_written is the number of bytes written. This param is set + * to 0 on failure, or if 0 bytes were input. + */ +void +lanplus_decrypt_aes_cbc_128(const uint8_t * iv, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint32_t * bytes_written) +{ + int nwritten = 0; + int inlen = 0; + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + if (verbose >= 5) + { + printbuf(iv, 16, "decrypting with this IV"); + printbuf(key, 16, "decrypting with this key"); + printbuf(input, input_length, "decrypting this data"); + } + + *bytes_written = 0; + if (input_length == 0) return; + + /* + * The default implementation adds a whole block of padding if the input + * data is perfectly aligned. We would like to keep that from happening. + * We have made a point to have our input perfectly padded. + */ + assert((input_length % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE) == 0); + inlen = input_length; + + if (!EVP_DecryptUpdate(&ctx, output, &nwritten, input, inlen)) + { + /* Error */ + lprintf(LOG_DEBUG, "ERROR: decrypt update failed"); + *bytes_written = 0; + return; + } + else + { + int tmplen; + + if (!EVP_DecryptFinal_ex(&ctx, output + nwritten, &tmplen)) + { + char buffer[1000]; + ERR_error_string(ERR_get_error(), buffer); + lprintf(LOG_DEBUG, "the ERR error %s", buffer); + lprintf(LOG_DEBUG, "ERROR: decrypt final failed"); + *bytes_written = 0; + return; /* Error */ + } + else + { + /* Success */ + *bytes_written = nwritten + tmplen; + EVP_CIPHER_CTX_cleanup(&ctx); + } + } + + if (verbose >= 5) + { + lprintf(LOG_DEBUG, "Decrypted %d encrypted bytes",input_length); + printbuf(output, *bytes_written, "Decrypted this data"); + } +} |