text: /*
   * 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 <assert.h>
  #include <string.h>
  #if defined(HAVE_CONFIG_H)
  # include <config.h>
  #endif
  #include <ipmitool/bswap.h>
  #include <ipmitool/log.h>
  #include "lanplus.h"
  #include "lanplus_crypt.h"
  #include "lanplus_crypt_impl.h"
  
  
  
  /*
   * lanplus_rakp2_hmac_matches
   * 
   * param session holds all the state data that we need to generate the hmac
   * param hmac is the HMAC sent by the BMC in the RAKP 2 message
   *
   * The HMAC was generated [per RFC2404] from : 
   * 
   *     SIDm     - Remote console session ID    
   *     SIDc     - BMC session ID
   *     Rm       - Remote console random number
   *     Rc       - BMC random number
   *     GUIDc    - BMC guid
   *     ROLEm    - Requested privilege level (entire byte)
   *     ULENGTHm - Username length
   *     <UNAMEm> - Username (absent for null user names)
   *
   * generated by using Kuid.  I am aware that the subscripts on the values
   * look backwards, but that's the way they are written in the specification.
   *
   * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success.
   * 
   * return 0 on success (the authcode matches)
   *        1 on failure (the authcode does not match)
   */
  int lanplus_rakp2_hmac_matches(const struct ipmi_session * session,
  							   const uint8_t    * bmc_mac,
  							   struct ipmi_intf * intf)
  {
  	uint8_t       * buffer;
  	int           bufferLength, i;
  	uint8_t       mac[20];
  	uint32_t      macLength;
  
  	uint32_t SIDm_lsbf, SIDc_lsbf;
  
  
  	if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
  		return 1;
  	
  	/* We don't yet support other algorithms */
  	assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
  	
  
  	bufferLength =
  		4  +                       /* SIDm     */
  		4  +                       /* SIDc     */
  		16 +                       /* Rm       */
  		16 +                       /* Rc       */
  		16 +                       /* GUIDc    */
  		1  +                       /* ROLEm    */
  		1  +                       /* ULENGTHm */
  		strlen((const char *)session->username); /* optional */
  
  	buffer = malloc(bufferLength);
  	if (buffer == NULL) {
  		lprintf(LOG_ERR, "ipmitool: malloc failure");
  		return 1;
  	}
  
  	/*
  	 * Fill the buffer.  I'm assuming that we're using the LSBF representation of the
  	 * multibyte numbers in use.
  	 */
  
  	/* SIDm */
  	SIDm_lsbf = session->v2_data.console_id;
  	#if WORDS_BIGENDIAN
  	SIDm_lsbf = BSWAP_32(SIDm_lsbf);
  	#endif
  
  	memcpy(buffer, &SIDm_lsbf, 4);
  
  	/* SIDc */
  	SIDc_lsbf = session->v2_data.bmc_id;
  	#if WORDS_BIGENDIAN
  	SIDc_lsbf = BSWAP_32(SIDc_lsbf);
  	#endif
  	memcpy(buffer + 4, &SIDc_lsbf, 4);
  
  	/* Rm */
  	#if WORDS_BIGENDIAN
  	for (i = 0; i < 16; ++i)
  		buffer[8 + i] = session->v2_data.console_rand[16 - 1 - i];
  	#else
  	for (i = 0; i < 16; ++i)
  		buffer[8 + i] = session->v2_data.console_rand[i];
  	#endif
  
  	/* Rc */
  	#if WORDS_BIGENDIAN
  	for (i = 0; i < 16; ++i)
  		buffer[24 + i] = session->v2_data.bmc_rand[16 - 1 - i];
  	#else
  	for (i = 0; i < 16; ++i)
  		buffer[24 + i] = session->v2_data.bmc_rand[i];
  	#endif
  
  	/* GUIDc */
  	#if WORDS_BIGENDIAN
  	for (i = 0; i < 16; ++i)
  		buffer[40 + i] = session->v2_data.bmc_guid[16 - 1 - i];
  	#else
  	for (i = 0; i < 16; ++i)
  		buffer[40 + i] = session->v2_data.bmc_guid[i];
  	#endif
  
  	/* ROLEm */
  	buffer[56] = session->v2_data.requested_role;
  
  	/* ULENGTHm */
  	buffer[57] = strlen((const char *)session->username);
  
  	/* UserName [optional] */
  	for (i = 0; i < buffer[57]; ++i)
  		buffer[58 + i] = session->username[i];
  
  	if (verbose > 2)
  	{
  		printbuf((const uint8_t *)buffer, bufferLength, ">> rakp2 mac input buffer");
  		printbuf((const uint8_t *)session->authcode, IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp2 mac key");
  	}
  
  	/*
  	 * The buffer is complete.  Let's hash.
  	 */
  	lanplus_HMAC(session->v2_data.auth_alg,
  				 session->authcode,
  				 IPMI_AUTHCODE_BUFFER_SIZE,
  				 buffer,
  				 bufferLength,
  				 mac,
  				 &macLength);
  
  	free(buffer);
  
  
  	if (verbose > 2)
  	{
  		printbuf(mac, macLength, ">> rakp2 mac as computed by the remote console");
  	}
  
  	return (memcmp(bmc_mac, mac, macLength) == 0);
  }
  
  
  
  /*
   * lanplus_rakp4_hmac_matches
   * 
   * param session holds all the state data that we need to generate the hmac
   * param hmac is the HMAC sent by the BMC in the RAKP 4 message
   *
   * The HMAC was generated [per RFC2404] from : 
   * 
   *     Rm       - Remote console random number
   *     SIDc     - BMC session ID
   *     GUIDc    - BMC guid
   *
   * generated by using SIK (the session integrity key).  I am aware that the
   * subscripts on the values look backwards, but that's the way they are
   * written in the specification.
   *
   * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success.
   * 
   * return 1 on success (the authcode matches)
   *        0 on failure (the authcode does not match)
   *
   */
  int lanplus_rakp4_hmac_matches(const struct ipmi_session * session,
  							   const uint8_t    * bmc_mac,
  							   struct ipmi_intf * intf)
  {
  	uint8_t       * buffer;
  	int           bufferLength, i;
  	uint8_t       mac[20];
  	uint32_t      macLength;
  	uint32_t      SIDc_lsbf;
  
  	if (ipmi_oem_active(intf, "intelplus")){
  		/* Intel BMC responds with the integrity Algorithm in RAKP4 */
  		if (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE)
  			return 1;
  
  		/* We don't yet support other algorithms */
  		assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96);
  	} else {
  		if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
  			return 1;
  
  		/* We don't yet support other algorithms */
  		assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
  	}
  
  	bufferLength =
  		16 +   /* Rm    */
  		4  +   /* SIDc  */
  		16;    /* GUIDc */
  
  	buffer = (uint8_t *)malloc(bufferLength);
  	if (buffer == NULL) {
  		lprintf(LOG_ERR, "ipmitool: malloc failure");
  		return 1;
  	}
  
  	/*
  	 * Fill the buffer.  I'm assuming that we're using the LSBF representation of the
  	 * multibyte numbers in use.
  	 */
  
  	/* Rm */
  	#if WORDS_BIGENDIAN
  	for (i = 0; i < 16; ++i)
  		buffer[i] = session->v2_data.console_rand[16 - 1 - i];
  	#else
  	for (i = 0; i < 16; ++i)
  		buffer[i] = session->v2_data.console_rand[i];
  	#endif
  
  
  	/* SIDc */
  	SIDc_lsbf = session->v2_data.bmc_id;
  	#if WORDS_BIGENDIAN
  	SIDc_lsbf = BSWAP_32(SIDc_lsbf);
  	#endif
  	memcpy(buffer + 16, &SIDc_lsbf, 4);
  	
  
  	/* GUIDc */
  	#if WORDS_BIGENDIAN
  	for (i = 0; i < 16; ++i)
  		buffer[i +  20] = session->v2_data.bmc_guid[16 - 1 - i];
  	#else
  	for (i = 0; i < 16; ++i)
  		buffer[i +  20] = session->v2_data.bmc_guid[i];
  	#endif
  
  
  	if (verbose > 2)
  	{
  		printbuf((const uint8_t *)buffer, bufferLength, ">> rakp4 mac input buffer");
  		printbuf(session->v2_data.sik, 20l, ">> rakp4 mac key (sik)");
  	}
  
  
  	/*
  	 * The buffer is complete.  Let's hash.
  	 */
  	lanplus_HMAC((ipmi_oem_active(intf, "intelplus")) 
  	             ? session->v2_data.integrity_alg 
  	             : session->v2_data.auth_alg ,
  				 session->v2_data.sik,
  				 IPMI_SIK_BUFFER_SIZE,
  				 buffer,
  				 bufferLength,
  				 mac,
  				 &macLength);
  
  	if (verbose > 2)
  	{
  		printbuf(bmc_mac, macLength, ">> rakp4 mac as computed by the BMC");
  		printbuf(mac,     macLength, ">> rakp4 mac as computed by the remote console");
  	}
  
  
  
  	free(buffer);
  	assert(macLength == 20);
  	return (memcmp(bmc_mac, mac, 12) == 0);
  }
  
  
  
  /*
   * lanplus_generate_rakp3_auth_code
   *
   * This auth code is an HMAC generated with :
   *
   *     Rc         - BMC random number
   *     SIDm       - Console session ID
   *     ROLEm      - Requested privilege level (entire byte)
   *     ULENGTHm   - Username length
   *     <USERNAME> - Usename (absent for null usernames)
   *
   * The key used to generated the MAC is Kuid
   *
   * I am aware that the subscripts look backwards, but that is the way they are
   * written in the spec.
   * 
   * param output_buffer [out] will hold the generated MAC
   * param session       [in]  holds all the state data we need to generate the auth code
   * param mac_length    [out] will be set to the length of the auth code
   *
   * returns 0 on success
   *         1 on failure
   */
  int lanplus_generate_rakp3_authcode(uint8_t                      * output_buffer,
  									const struct ipmi_session * session,
  									uint32_t                  * mac_length,
  									struct ipmi_intf          * intf)
  {
  	int ret = 0;
  	int input_buffer_length, i;
  	uint8_t * input_buffer;
  	uint32_t SIDm_lsbf;
  	
  
  	if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
  	{
  		*mac_length = 0;
  		return 0;
  	}
  
  	/* We don't yet support other algorithms */
  	assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
  
  	input_buffer_length =
  		16 + /* Rc       */
  		4  + /* SIDm     */
  		1  + /* ROLEm    */
  		1  + /* ULENGTHm */
  		strlen((const char *)session->username);
  
  	input_buffer = malloc(input_buffer_length);
  	if (input_buffer == NULL) {
  		lprintf(LOG_ERR, "ipmitool: malloc failure");
  		return 1;
  	}
  
  	/*
  	 * Fill the buffer.  I'm assuming that we're using the LSBF representation of the
  	 * multibyte numbers in use.
  	 */
  	
  	/* Rc */
  	#if WORDS_BIGENDIAN
  	for (i = 0; i < 16; ++i)
  		input_buffer[i] = session->v2_data.bmc_rand[16 - 1 - i];
  	#else
  	for (i = 0; i < 16; ++i)
  		input_buffer[i] = session->v2_data.bmc_rand[i];
  	#endif
  
  	/* SIDm */
  	SIDm_lsbf = session->v2_data.console_id;
  	#if WORDS_BIGENDIAN
  	SIDm_lsbf = BSWAP_32(SIDm_lsbf);
  	#endif
  	memcpy(input_buffer + 16, &SIDm_lsbf, 4);
  	
  	/* ROLEm */
  	if (ipmi_oem_active(intf, "intelplus")) 
  		input_buffer[20] = session->privlvl;
  	else 
  		input_buffer[20] = session->v2_data.requested_role;
  
  	/* ULENGTHm */
  	input_buffer[21] = strlen((const char *)session->username);
  
  	/* USERNAME */
  	for (i = 0; i < input_buffer[21]; ++i)
  		input_buffer[22 + i] = session->username[i];
  
  	if (verbose > 2)
  	{
  		printbuf((const uint8_t *)input_buffer, input_buffer_length, ">> rakp3 mac input buffer");
  		printbuf((const uint8_t *)session->authcode, IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp3 mac key");
  	}
      
  	lanplus_HMAC(session->v2_data.auth_alg,
  				 session->authcode,
  				 IPMI_AUTHCODE_BUFFER_SIZE,
  				 input_buffer,
  				 input_buffer_length,
  				 output_buffer,
  				 mac_length);
  
  	if (verbose > 2)
  		printbuf((const uint8_t *)output_buffer, *mac_length, "generated rakp3 mac");
  
  	
  	free(input_buffer);
  
  	return ret;
  }
  
  
  
  /*
   * lanplus_generate_sik
   *
   * Generate the session integrity key (SIK) used for integrity checking 
   * during the IPMI v2 / RMCP+ session
   *
   * This session integrity key is a HMAC generated with :
   *
   *     Rm         - Console generated random number
   *     Rc         - BMC generated random number
   *     ROLEm      - Requested privilege level (entire byte)
   *     ULENGTHm   - Username length
   *     <USERNAME> - Usename (absent for null usernames)
   *
   * The key used to generated the SIK is Kg if Kg is not null (two-key logins are
   * enabled).  Otherwise Kuid (the user authcode) is used as the key to genereate
   * the SIK.
   *
   * I am aware that the subscripts look backwards, but that is the way they are
   * written in the spec.
   * 
   * param session [in/out] contains our input and output fields.
   *
   * returns 0 on success
   *         1 on failure
   */
  int lanplus_generate_sik(struct ipmi_session * session)
  {
  	uint8_t * input_buffer;
  	int input_buffer_length, i;
  	uint8_t * input_key;
  	uint32_t mac_length;
  	
  
  	memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE);
  
  	if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
  		return 0;
  
  	/* We don't yet support other algorithms */
  	assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
  
  	input_buffer_length =
  		16 +  /* Rm       */
  		16 +  /* Rc       */
  		1  +  /* ROLEm     */
  		1  +  /* ULENGTHm  */
  		strlen((const char *)session->username);
  
  	input_buffer = malloc(input_buffer_length);
  	if (input_buffer == NULL) {
  		lprintf(LOG_ERR, "ipmitool: malloc failure");
  		return 1;
  	}
  
  	/*
  	 * Fill the buffer.  I'm assuming that we're using the LSBF representation of the
  	 * multibyte numbers in use.
  	 */
  
  	/* Rm */
  	#if WORDS_BIGENDIAN
  	for (i = 0; i < 16; ++i)
  		input_buffer[i] = session->v2_data.console_rand[16 - 1 - i];
  	#else
  	for (i = 0; i < 16; ++i)
  		input_buffer[i] = session->v2_data.console_rand[i];
  	#endif
  	
  
  	/* Rc */
  	#if WORDS_BIGENDIAN
  	for (i = 0; i < 16; ++i)
  		input_buffer[16 + i] = session->v2_data.bmc_rand[16 - 1 - i];
  	#else
  	for (i = 0; i < 16; ++i)
  		input_buffer[16 + i] = session->v2_data.bmc_rand[i];
  	#endif
  
  	/* ROLEm */
  	input_buffer[32] = session->v2_data.requested_role;
  
  	/* ULENGTHm */
  	input_buffer[33] = strlen((const char *)session->username);
  
  	/* USERNAME */
  	for (i = 0; i < input_buffer[33]; ++i)
  		input_buffer[34 + i] = session->username[i];
  
  	if (session->v2_data.kg[0])
  	{
  		/* We will be hashing with Kg */
  		/*
  		 * Section 13.31 of the IPMI v2 spec describes the SIK creation
  		 * using Kg.  It specifies that Kg should not be truncated.
  		 * Kg is set in ipmi_intf.
  		 */
  		input_key        = session->v2_data.kg;
  	}
  	else
  	{
  		/* We will be hashing with Kuid */
  		input_key        = session->authcode;
  	}
  
  	
  	if (verbose >= 2)
  		printbuf((const uint8_t *)input_buffer, input_buffer_length, "session integrity key input");
  
  	lanplus_HMAC(session->v2_data.auth_alg,
  				 input_key,
  				 IPMI_AUTHCODE_BUFFER_SIZE,
  				 input_buffer,
  				 input_buffer_length,
  				 session->v2_data.sik,
  				 &mac_length);
  
  	free(input_buffer);
  	assert(mac_length == 20);
  
  	/*
  	 * The key MAC generated is 20 bytes, but we will only be using the first
  	 * 12 for SHA1 96
  	 */
  	if (verbose >= 2)
  		printbuf(session->v2_data.sik, 20, "Generated session integrity key");
  
  	return 0;
  }
  
  
  
  /*
   * lanplus_generate_k1
   *
   * Generate K1, the key presumably used to generate integrity authcodes
   *
   * We use the authentication algorithm to generated the HMAC, using
   * the session integrity key (SIK) as our key.
   *
   * param session [in/out].
   *
   * returns 0 on success
   *         1 on failure
   */
  int lanplus_generate_k1(struct ipmi_session * session)
  {
  	uint32_t mac_length;
  
  	uint8_t CONST_1[] =
  		{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  		 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
  
  	if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
  		memcpy(session->v2_data.k1, CONST_1, 20);
  	else
  	{
  		lanplus_HMAC(session->v2_data.auth_alg,
  					 session->v2_data.sik,
  					 IPMI_SIK_BUFFER_SIZE, /* SIK length */
  					 CONST_1,
  					 20,
  					 session->v2_data.k1,
  					 &mac_length);
  		assert(mac_length == 20);
  	}
  
  	if (verbose >= 2)
  		printbuf(session->v2_data.k1, 20, "Generated K1");
  
  	return 0;
  }
  
  
  
  /*
   * lanplus_generate_k2
   *
   * Generate K2, the key used for RMCP+ AES encryption.
   *
   * We use the authentication algorithm to generated the HMAC, using
   * the session integrity key (SIK) as our key.
   *
   * param session [in/out].
   *
   * returns 0 on success
   *         1 on failure
   */
  int lanplus_generate_k2(struct ipmi_session * session)
  {
  	uint32_t mac_length;
  
  	uint8_t CONST_2[] =
  		{0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
  		 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
  
  	if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
  		memcpy(session->v2_data.k2, CONST_2, 20);
  	else
  	{
  		lanplus_HMAC(session->v2_data.auth_alg,
  					 session->v2_data.sik,
  					 IPMI_SIK_BUFFER_SIZE, /* SIK length */
  					 CONST_2,
  					 20,
  					 session->v2_data.k2,
  					 &mac_length);
  		assert(mac_length == 20);
  	}
  
  	if (verbose >= 2)
  		printbuf(session->v2_data.k2, 20, "Generated K2");
  
  	return 0;
  }
  
  
  
  /*
   * lanplus_encrypt_payload
   *
   * Perform the appropriate encryption on the input data.  Output the encrypted
   * data to output, including the required confidentiality header and trailer.
   * If the crypt_alg is IPMI_CRYPT_NONE, simply copy the input to the output and
   * set bytes_written to input_length.
   * 
   * param crypt_alg specifies the encryption algorithm (from table 13-19 of the
   *       IPMI v2 spec)
   * param key is the used as input to the encryption algorithmf
   * param input is the input data to be encrypted
   * param input_length is the length of the input data to be encrypted
   * param output is the cipher text generated by the encryption process
   * param bytes_written is the number of bytes written during the encryption
   *       process
   *
   * returns 0 on success
   *         1 on failure
   */
  int lanplus_encrypt_payload(uint8_t         crypt_alg,
  							const uint8_t * key,
  							const uint8_t * input,
  							uint32_t          input_length,
  							uint8_t       * output,
  							uint16_t      * bytes_written)
  {
  	uint8_t * padded_input;
  	uint32_t    mod, i, bytes_encrypted;
  	uint8_t   pad_length = 0;
  
  	if (crypt_alg == IPMI_CRYPT_NONE)
  	{
  		/* Just copy the input to the output */
  		*bytes_written = input_length;
  		return 0;
  	}
  	
  	/* Currently, we only support AES */
  	assert(crypt_alg == IPMI_CRYPT_AES_CBC_128);
  	assert(input_length <= IPMI_MAX_PAYLOAD_SIZE);
  
  
  	/*
  	 * The input to the AES encryption algorithm has to be a multiple of the
  	 * block size (16 bytes).  The extra byte we are adding is the pad length
  	 * byte.
  	 */
  	mod = (input_length + 1) % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE;
  	if (mod)
  		pad_length = IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE - mod;
  
  	padded_input = (uint8_t*)malloc(input_length + pad_length + 1);
  	if (padded_input == NULL) {
  		lprintf(LOG_ERR, "ipmitool: malloc failure");
  		return 1;
  	}
  	memcpy(padded_input, input, input_length);
  
  	/* add the pad */
  	for (i = 0; i < pad_length; ++i)
  		padded_input[input_length + i] = i + 1;
  
  	/* add the pad length */
  	padded_input[input_length + pad_length] = pad_length;
  
  	/* Generate an initialization vector, IV, for the encryption process */
  	if (lanplus_rand(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE))
  	{
  		lprintf(LOG_ERR, "lanplus_encrypt_payload: Error generating IV");
  		return 1;
  	}
  
  	if (verbose > 2)
  		printbuf(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, ">> Initialization vector");
  
  
  
  	lanplus_encrypt_aes_cbc_128(output,                                     /* IV              */
  								key,                                        /* K2              */
  								padded_input,                               /* Data to encrypt */
  								input_length + pad_length + 1,              /* Input length    */
  								output + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* output          */
  								&bytes_encrypted);                          /* bytes written   */
  
  	*bytes_written =
  		IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE + /* IV */
  		bytes_encrypted;
  
  	free(padded_input);
  
  	return 0;
  }
  
  
  
  /*
   * lanplus_has_valid_auth_code
   *
   * Determine whether the packets authcode field is valid for packet.
   * 
   * We always return success if any of the following are true. 
   *  - this is not an IPMIv2 packet
   *  - the session is not yet active
   *  - the packet specifies that it is not authenticated
   *  - the integrity algorithm agreed upon during session creation is "none"
   *
   * The authcode is computed using the specified integrity algorithm starting
   * with the AuthType / Format field, and ending with the field immediately
   * preceeding the authcode itself.
   *
   * The key key used to generate the authcode MAC is K1.
   * 
   * param rs holds the response structure.
   * param session holds our session state, including our chosen algorithm, key, etc.
   *
   * returns 1 on success (authcode is valid)
   *         0 on failure (autchode integrity check failed)
   */
  int lanplus_has_valid_auth_code(struct ipmi_rs * rs,
  								struct ipmi_session * session)
  {
  	uint8_t * bmc_authcode;
  	uint8_t   generated_authcode[IPMI_MAX_MAC_SIZE];
  	uint32_t    generated_authcode_length;
  	
  
  	if ((rs->session.authtype != IPMI_SESSION_AUTHTYPE_RMCP_PLUS) ||
  		(session->v2_data.session_state != LANPLUS_STATE_ACTIVE)  ||
  		(! rs->session.bAuthenticated)                            ||
  		(session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE))
  		return 1;
  	
  	/* We only support SHA1-96 now */
  	assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96);
  
  	/*
  	 * For SHA1-96, the authcode will be the last 12 bytes in the packet
  	 */
  	bmc_authcode = rs->data + (rs->data_len - IPMI_SHA1_AUTHCODE_SIZE);
  
  	lanplus_HMAC(session->v2_data.integrity_alg,
  				 session->v2_data.k1,
  				 IPMI_AUTHCODE_BUFFER_SIZE,
  				 rs->data + IPMI_LANPLUS_OFFSET_AUTHTYPE,
  				 rs->data_len - IPMI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE,
  				 generated_authcode,
  				 &generated_authcode_length);
  
  	if (verbose > 3)
  	{
  		lprintf(LOG_DEBUG+2, "Validating authcode");
  		printbuf(session->v2_data.k1, 20, "K1");
  		printbuf(rs->data + IPMI_LANPLUS_OFFSET_AUTHTYPE,
  				 rs->data_len - IPMI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE,
  				 "Authcode Input Data");
  		printbuf(generated_authcode, 12, "Generated authcode");
  		printbuf(bmc_authcode,       12, "Expected authcode");
  	}
  
  	
  	assert(generated_authcode_length == 20);
  	return (memcmp(bmc_authcode, generated_authcode, 12) == 0);
  }
  
  
  
  /*
   * lanplus_decrypt_payload
   *
   * 
   * param input points to the beginning of the payload (which will be the IV if
   *       we are using AES)
   * param payload_size [out] will be set to the size of the payload EXCLUDING
   * padding
   * 
   * returns 0 on success (we were able to successfully decrypt the packet)
   *         1 on failure (we were unable to successfully decrypt the packet)
   */
  int lanplus_decrypt_payload(uint8_t         crypt_alg,
  							const uint8_t * key,
  							const uint8_t * input,
  							uint32_t          input_length,
  							uint8_t       * output,
  							uint16_t      * payload_size)
  {
  	uint8_t * decrypted_payload;
  	uint32_t    bytes_decrypted;
  
  	if (crypt_alg == IPMI_CRYPT_NONE)
  	{
  		/* We are not encrypted.  The paylaod size is is everything. */
  		*payload_size = input_length;
  		memmove(output, input, input_length);
  		return 0;
  	}
  
  	/* We only support AES */
  	assert(crypt_alg == IPMI_CRYPT_AES_CBC_128);
  
  	decrypted_payload = (uint8_t*)malloc(input_length);
  	if (decrypted_payload == NULL) {
  		lprintf(LOG_ERR, "ipmitool: malloc failure");
  		return 1;
  	}
  
  
  	lanplus_decrypt_aes_cbc_128(input,                                /* IV              */
  								key,                                  /* Key             */
  								input                        +
  								IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE,    /* Data to decrypt */
  								input_length -
  								IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE,    /* Input length    */
  								decrypted_payload,                    /* output          */
  								&bytes_decrypted);                    /* bytes written   */
  
  	if (bytes_decrypted != 0)
  	{
  		/* Success */
  		uint8_t conf_pad_length;
  		int i;
  
  		memmove(output,
  				decrypted_payload,
  				bytes_decrypted);
  
  		/*
  		 * We have to determine the payload size, by substracting the padding, etc.
  		 * The last byte of the decrypted payload is the confidentiality pad length.
  		 */
  		conf_pad_length = decrypted_payload[bytes_decrypted - 1];
  		*payload_size = bytes_decrypted - conf_pad_length - 1;
  
  		/*
  		 * Extra test to make sure that the padding looks like it should (should start
  		 * with 0x01, 0x02, 0x03, etc...
  		 */
  		for (i = 0; i < conf_pad_length; ++i)
  		{
  			if (decrypted_payload[*payload_size + i] == i)
  			{
  				lprintf(LOG_ERR, "Malformed payload padding");
  				assert(0);
  			}
  		}
  	}
  	else
  	{
  		lprintf(LOG_ERR, "ERROR: lanplus_decrypt_aes_cbc_128 decryptd 0 bytes");
  		assert(0);
  	}
  
  	free(decrypted_payload);
  	return (bytes_decrypted == 0);
  }
  