/* * 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. */ #include "ipmitool/log.h" #include "ipmitool/ipmi_constants.h" #include "lanplus.h" #include "lanplus_crypt_impl.h" #include #include #include #include #include /* * 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) { if (! RAND_load_file("/dev/urandom", bytes)) return 1; else 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 SHA1, SHA256 and MD5 * are 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; 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(); #ifdef HAVE_CRYPTO_SHA256 else if ((mac == IPMI_AUTH_RAKP_HMAC_SHA256) || (mac == IPMI_INTEGRITY_HMAC_SHA256_128)) evp_md = EVP_sha256(); #endif /* HAVE_CRYPTO_SHA256 */ else { lprintf(LOG_DEBUG, "Invalid mac type 0x%x in lanplus_HMAC\n", mac); assert(0); } return HMAC(evp_md, key, key_len, d, n, md, (unsigned int *)md_len); } /* * 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) { 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); if(!EVP_EncryptUpdate(&ctx, output, (int *)bytes_written, input, input_length)) { /* Error */ *bytes_written = 0; return; } else { uint32_t tmplen; if(!EVP_EncryptFinal_ex(&ctx, output + *bytes_written, (int *)&tmplen)) { *bytes_written = 0; return; /* Error */ } else { /* Success */ *bytes_written += 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) { 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); if (!EVP_DecryptUpdate(&ctx, output, (int *)bytes_written, input, input_length)) { /* Error */ lprintf(LOG_DEBUG, "ERROR: decrypt update failed"); *bytes_written = 0; return; } else { uint32_t tmplen; if (!EVP_DecryptFinal_ex(&ctx, output + *bytes_written, (int *)&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 += 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"); } }