summaryrefslogtreecommitdiff
path: root/src/plugins/lanplus/lanplus_crypt.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-07-23 15:03:01 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-07-23 15:03:01 +0200
commit777af8a8761d05c30588abec7444b143fe7393f0 (patch)
tree5a135c37eaa9ac94772819a28ce5beedd18e5c4a /src/plugins/lanplus/lanplus_crypt.c
parentc3445516ecd58e97de483cf4b7fafcc1104890d7 (diff)
parentb32d92e890caac903491116e9d817aa780c0323b (diff)
Merge tag 'upstream/1.8.14'
Upstream version 1.8.14
Diffstat (limited to 'src/plugins/lanplus/lanplus_crypt.c')
-rw-r--r--src/plugins/lanplus/lanplus_crypt.c934
1 files changed, 934 insertions, 0 deletions
diff --git a/src/plugins/lanplus/lanplus_crypt.c b/src/plugins/lanplus/lanplus_crypt.c
new file mode 100644
index 0000000..54fd5cb
--- /dev/null
+++ b/src/plugins/lanplus/lanplus_crypt.c
@@ -0,0 +1,934 @@
+/*
+ * 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;
+
+ if (ipmi_oem_active(intf, "i82571spt")) {
+ /*
+ * The HMAC calculation code in the Intel 82571 GbE
+ * skips this bit! Looks like a GbE bug, but we need
+ * to work around it here anyway...
+ */
+ buffer[56] &= ~0x10;
+ }
+
+ /* 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);
+ buffer = NULL;
+
+
+ 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);
+ buffer = NULL;
+ 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") || ipmi_oem_active(intf, "i82571spt"))
+ 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);
+ input_buffer = NULL;
+
+ 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, struct ipmi_intf * intf)
+{
+ 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;
+
+ if (ipmi_oem_active(intf, "i82571spt")) {
+ /*
+ * The HMAC calculation code in the Intel 82571 GbE
+ * skips this bit! Looks like a GbE bug, but we need
+ * to work around it here anyway...
+ */
+ input_buffer[32] &= ~0x10;
+ }
+
+ /* 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);
+ input_buffer = NULL;
+ 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");
+ if (padded_input != NULL) {
+ free(padded_input);
+ padded_input = NULL;
+ }
+ 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);
+ padded_input = NULL;
+
+ 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 + 1))
+ {
+ 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);
+ decrypted_payload = NULL;
+ return (bytes_decrypted == 0);
+}