Blame src/plugins/lanplus/lanplus_crypt_impl.c

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