summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVernon Mauery <vernon.mauery@linux.intel.com>2017-11-29 08:36:29 -0800
committerBrad Bishop <bradleyb@us.ibm.com>2017-12-08 21:55:44 -0500
commit7e9e2ef68a1c53e022970d85566945d5bdc5a78e (patch)
tree946c6772654bdd8ecaed04498938999f49154b53
parent9b307be647ff786f05c03fa742b982d99dd341ae (diff)
downloadphosphor-net-ipmid-7e9e2ef68a1c53e022970d85566945d5bdc5a78e.tar.gz
phosphor-net-ipmid-7e9e2ef68a1c53e022970d85566945d5bdc5a78e.zip
Add support for cipher suite 17
cipher suite 17 uses RAKP_HMAC_SHA256 for authentication and RAKP_HMAC_SHA256_128 for integrity. This adds those in and fixes up the lookups so the stack knows about the new algorithms. Change-Id: Icdc66563d08060fc0e541ceaf3bee9dd5f89fdb2 Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
-rw-r--r--auth_algo.cpp34
-rw-r--r--auth_algo.hpp36
-rw-r--r--command/rakp34.cpp7
-rw-r--r--integrity_algo.cpp67
-rw-r--r--integrity_algo.hpp89
-rw-r--r--sessions_manager.cpp7
-rw-r--r--test/cipher.cpp173
7 files changed, 411 insertions, 2 deletions
diff --git a/auth_algo.cpp b/auth_algo.cpp
index 0bc2555..94a8c91 100644
--- a/auth_algo.cpp
+++ b/auth_algo.cpp
@@ -45,6 +45,40 @@ std::vector<uint8_t> AlgoSHA1::generateICV(
return output;
}
+std::vector<uint8_t> AlgoSHA256::generateHMAC(
+ const std::vector<uint8_t>& input) const
+{
+ std::vector<uint8_t> output(SHA256_DIGEST_LENGTH);
+ unsigned int mdLen = 0;
+
+ if (HMAC(EVP_sha256(), userKey.data(), userKey.size(), input.data(),
+ input.size(), output.data(), &mdLen) == NULL)
+ {
+ std::cerr << "Generate HMAC_SHA256 failed\n";
+ output.resize(0);
+ }
+
+ return output;
+}
+
+std::vector<uint8_t> AlgoSHA256::generateICV(
+ const std::vector<uint8_t>& input) const
+{
+ std::vector<uint8_t> output(SHA256_DIGEST_LENGTH);
+ unsigned int mdLen = 0;
+
+ if (HMAC(EVP_sha256(),
+ sessionIntegrityKey.data(), sessionIntegrityKey.size(),
+ input.data(), input.size(), output.data(), &mdLen) == NULL)
+ {
+ std::cerr << "Generate HMAC_SHA256_128 Integrity Check Value failed\n";
+ output.resize(0);
+ }
+ output.resize(integrityCheckValueLength);
+
+ return output;
+}
+
} // namespace auth
} // namespace cipher
diff --git a/auth_algo.hpp b/auth_algo.hpp
index b6fda94..682c091 100644
--- a/auth_algo.hpp
+++ b/auth_algo.hpp
@@ -103,7 +103,8 @@ class Interface
static bool isAlgorithmSupported(Algorithms algo)
{
if (algo == Algorithms::RAKP_NONE ||
- algo == Algorithms::RAKP_HMAC_SHA1)
+ algo == Algorithms::RAKP_HMAC_SHA1 ||
+ algo == Algorithms::RAKP_HMAC_SHA256)
{
return true;
}
@@ -178,6 +179,39 @@ class AlgoSHA1 : public Interface
const std::vector<uint8_t>& input) const override;
};
+/**
+ * @class AlgoSHA256
+ *
+ * RAKP-HMAC-SHA256 specifies the use of RAKP messages for the key exchange
+ * portion of establishing the session, and that HMAC-SHA256 (per [FIPS 180-2]
+ * and [RFC4634] and is used to create a 32-byte Key Exchange Authentication
+ * Code fields in RAKP Message 2 and RAKP Message 3. HMAC-SHA256-128 (per
+ * [RFC4868]) is used for generating a 16-byte Integrity Check Value field for
+ * RAKP Message 4.
+ */
+
+class AlgoSHA256 : public Interface
+{
+ public:
+ static constexpr size_t integrityCheckValueLength = 16;
+
+ explicit AlgoSHA256(integrity::Algorithms intAlgo,
+ crypt::Algorithms cryptAlgo) :
+ Interface(intAlgo, cryptAlgo) {}
+
+ ~AlgoSHA256() = default;
+ AlgoSHA256(const AlgoSHA256&) = default;
+ AlgoSHA256& operator=(const AlgoSHA256&) = default;
+ AlgoSHA256(AlgoSHA256&&) = default;
+ AlgoSHA256& operator=(AlgoSHA256&&) = default;
+
+ std::vector<uint8_t> generateHMAC(
+ const std::vector<uint8_t>& input) const override;
+
+ std::vector<uint8_t> generateICV(
+ const std::vector<uint8_t>& input) const override;
+};
+
}// namespace auth
}// namespace cipher
diff --git a/command/rakp34.cpp b/command/rakp34.cpp
index 5ba9aa1..24deac0 100644
--- a/command/rakp34.cpp
+++ b/command/rakp34.cpp
@@ -29,6 +29,13 @@ void applyIntegrityAlgo(const uint32_t bmcSessionID)
authAlgo->sessionIntegrityKey));
break;
}
+ case cipher::integrity::Algorithms::HMAC_SHA256_128:
+ {
+ session->setIntegrityAlgo(
+ std::make_unique<cipher::integrity::AlgoSHA256>(
+ authAlgo->sessionIntegrityKey));
+ break;
+ }
default:
break;
}
diff --git a/integrity_algo.cpp b/integrity_algo.cpp
index 62c2653..0c3efe8 100644
--- a/integrity_algo.cpp
+++ b/integrity_algo.cpp
@@ -77,6 +77,73 @@ std::vector<uint8_t> AlgoSHA1::generateKn(const std::vector<uint8_t>& sik,
return Kn;
}
+AlgoSHA256::AlgoSHA256(const std::vector<uint8_t>& sik)
+ : Interface(SHA256_128_AUTHCODE_LENGTH)
+{
+ k1 = generateKn(sik, rmcp::const_1);
+}
+
+std::vector<uint8_t> AlgoSHA256::generateHMAC(const uint8_t* input,
+ const size_t len) const
+{
+ std::vector<uint8_t> output(SHA256_DIGEST_LENGTH);
+ unsigned int mdLen = 0;
+
+ if (HMAC(EVP_sha256(), k1.data(), k1.size(), input, len,
+ output.data(), &mdLen) == NULL)
+ {
+ throw std::runtime_error("Generating HMAC_SHA256_128 failed");
+ }
+
+ // HMAC generates Message Digest to the size of SHA256_DIGEST_LENGTH, the
+ // AuthCode field length is based on the integrity algorithm. So we are
+ // interested only in the AuthCode field length of the generated Message
+ // digest.
+ output.resize(authCodeLength);
+
+ return output;
+}
+
+bool AlgoSHA256::verifyIntegrityData(
+ const std::vector<uint8_t>& packet,
+ const size_t length,
+ std::vector<uint8_t>::const_iterator integrityData) const
+{
+
+ auto output = generateHMAC(
+ packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE,
+ length);
+
+ // Verify if the generated integrity data for the packet and the received
+ // integrity data matches.
+ return (std::equal(output.begin(), output.end(), integrityData));
+}
+
+std::vector<uint8_t> AlgoSHA256::generateIntegrityData(
+ const std::vector<uint8_t>& packet) const
+{
+ return generateHMAC(
+ packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE,
+ packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE);
+}
+
+std::vector<uint8_t> AlgoSHA256::generateKn(const std::vector<uint8_t>& sik,
+ const rmcp::Const_n& const_n) const
+{
+ unsigned int mdLen = 0;
+ std::vector<uint8_t> Kn(sik.size());
+
+ // Generated Kn for the integrity algorithm with the additional key keyed
+ // with SIK.
+ if (HMAC(EVP_sha256(), sik.data(), sik.size(), const_n.data(),
+ const_n.size(), Kn.data(), &mdLen) == NULL)
+ {
+ throw std::runtime_error("Generating KeyN for integrity "
+ "algorithm HMAC_SHA256 failed");
+ }
+ return Kn;
+}
+
}// namespace integrity
}// namespace cipher
diff --git a/integrity_algo.hpp b/integrity_algo.hpp
index 3e003b6..81f7da7 100644
--- a/integrity_algo.hpp
+++ b/integrity_algo.hpp
@@ -90,7 +90,9 @@ class Interface
*/
static bool isAlgorithmSupported(Algorithms algo)
{
- if (algo == Algorithms::NONE || algo == Algorithms::HMAC_SHA1_96)
+ if (algo == Algorithms::NONE ||
+ algo == Algorithms::HMAC_SHA1_96 ||
+ algo == Algorithms::HMAC_SHA256_128)
{
return true;
}
@@ -227,6 +229,91 @@ class AlgoSHA1 final : public Interface
const size_t len) const;
};
+/**
+ * @class AlgoSHA256
+ *
+ * @brief Implementation of the HMAC-SHA256-128 Integrity algorithm
+ *
+ * HMAC-SHA256-128 take the Session Integrity Key and use it to generate K1. K1
+ * is then used as the key for use in HMAC to produce the AuthCode field. For
+ * “one-key” logins, the user’s key (password) is used in the creation of the
+ * Session Integrity Key. When the HMAC-SHA256-128 Integrity Algorithm is used
+ * the resulting AuthCode field is 16 bytes (128 bits).
+ */
+class AlgoSHA256 final : public Interface
+{
+ public:
+ static constexpr size_t SHA256_128_AUTHCODE_LENGTH = 16;
+
+ /**
+ * @brief Constructor for AlgoSHA256
+ *
+ * @param[in] - Session Integrity Key
+ */
+ explicit AlgoSHA256(const std::vector<uint8_t>& sik);
+
+ AlgoSHA256() = delete;
+ ~AlgoSHA256() = default;
+ AlgoSHA256(const AlgoSHA256&) = default;
+ AlgoSHA256& operator=(const AlgoSHA256&) = default;
+ AlgoSHA256(AlgoSHA256&&) = default;
+ AlgoSHA256& operator=(AlgoSHA256&&) = default;
+
+ /**
+ * @brief Verify the integrity data of the packet
+ *
+ * @param[in] packet - Incoming IPMI packet
+ * @param[in] length - Length of the data in the packet to calculate
+ * the integrity data
+ * @param[in] integrityData - Iterator to the authCode in the packet
+ *
+ * @return true if authcode in the packet is equal to one generated
+ * using integrity algorithm on the packet data, false otherwise
+ */
+ bool verifyIntegrityData(
+ const std::vector<uint8_t>& packet,
+ const size_t length,
+ std::vector<uint8_t>::const_iterator integrityData)
+ const override;
+
+ /**
+ * @brief Generate integrity data for the outgoing IPMI packet
+ *
+ * @param[in] packet - Outgoing IPMI packet
+ *
+ * @return on success return the integrity data for the outgoing IPMI
+ * packet
+ */
+ std::vector<uint8_t> generateIntegrityData(
+ const std::vector<uint8_t>& packet) const override;
+
+ /**
+ * @brief Generate additional keying material based on SIK
+ *
+ * @param[in] sik - session integrity key
+ * @param[in] data - 20-byte Const_n
+ *
+ * @return on success returns the Kn based on HMAC-SHA256
+ *
+ */
+ std::vector<uint8_t> generateKn(
+ const std::vector<uint8_t>& sik,
+ const rmcp::Const_n& const_n) const;
+
+ private:
+ /**
+ * @brief Generate HMAC based on HMAC-SHA256-128 algorithm
+ *
+ * @param[in] input - pointer to the message
+ * @param[in] len - length of the message
+ *
+ * @return on success returns the message authentication code
+ *
+ */
+ std::vector<uint8_t> generateHMAC(const uint8_t* input,
+ const size_t len) const;
+};
+
}// namespace integrity
}// namespace cipher
diff --git a/sessions_manager.cpp b/sessions_manager.cpp
index ddeca4c..0f49a2d 100644
--- a/sessions_manager.cpp
+++ b/sessions_manager.cpp
@@ -74,6 +74,13 @@ std::weak_ptr<Session> Manager::startSession(SessionID remoteConsoleSessID,
cryptAlgo));
break;
}
+ case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
+ {
+ session->setAuthAlgo(
+ std::make_unique<cipher::rakp_auth::AlgoSHA256>(
+ intAlgo, cryptAlgo));
+ break;
+ }
default:
{
throw std::runtime_error("Invalid Authentication Algorithm");
diff --git a/test/cipher.cpp b/test/cipher.cpp
index ed8ccb7..3fa985c 100644
--- a/test/cipher.cpp
+++ b/test/cipher.cpp
@@ -180,6 +180,179 @@ TEST(IntegrityAlgo, HMAC_SHA1_96_VerifyIntegrityDataFail)
EXPECT_EQ(false, check);
}
+TEST(IntegrityAlgo, HMAC_SHA256_128_GenerateIntegrityDataCheck)
+{
+ /*
+ * Step-1 Generate Integrity Data for the packet, using the implemented API
+ */
+ // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes)
+ std::vector<uint8_t> packet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+
+ // Hardcoded Session Integrity Key
+ std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 };
+
+ auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA256>(sik);
+
+ ASSERT_EQ(true, (algoPtr != NULL));
+
+ // Generate the Integrity Data
+ auto response = algoPtr->generateIntegrityData(packet);
+
+ EXPECT_EQ(true, (response.size() ==
+ cipher::integrity::AlgoSHA256::SHA256_128_AUTHCODE_LENGTH));
+
+ /*
+ * Step-2 Generate Integrity data using OpenSSL SHA256 algorithm
+ */
+ std::vector<uint8_t> k1(SHA256_DIGEST_LENGTH);
+ constexpr rmcp::Const_n const1 = { 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01
+ };
+
+ // Generated K1 for the integrity algorithm with the additional key keyed
+ // with SIK.
+ unsigned int mdLen = 0;
+ if (HMAC(EVP_sha256(), sik.data(), sik.size(), const1.data(),
+ const1.size(), k1.data(), &mdLen) == NULL)
+ {
+ FAIL() << "Generating Key1 failed";
+ }
+
+ mdLen = 0;
+ std::vector<uint8_t> output(SHA256_DIGEST_LENGTH);
+ size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE;
+
+ if (HMAC(EVP_sha256(), k1.data(), k1.size(),
+ packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE,
+ length,
+ output.data(), &mdLen) == NULL)
+ {
+ FAIL() << "Generating integrity data failed";
+ }
+
+ output.resize(cipher::integrity::AlgoSHA256::SHA256_128_AUTHCODE_LENGTH);
+
+ /*
+ * Step-3 Check if the integrity data we generated using the implemented API
+ * matches with one generated by OpenSSL SHA256 algorithm.
+ */
+ auto check = std::equal(output.begin(), output.end(), response.begin());
+ EXPECT_EQ(true, check);
+}
+
+TEST(IntegrityAlgo, HMAC_SHA256_128_VerifyIntegrityDataPass)
+{
+ /*
+ * Step-1 Generate Integrity data using OpenSSL SHA256 algorithm
+ */
+
+ // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes)
+ std::vector<uint8_t> packet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+
+ // Hardcoded Session Integrity Key
+ std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 };
+
+ std::vector<uint8_t> k1(SHA256_DIGEST_LENGTH);
+ constexpr rmcp::Const_n const1 = { 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01
+ };
+
+ // Generated K1 for the integrity algorithm with the additional key keyed
+ // with SIK.
+ unsigned int mdLen = 0;
+ if (HMAC(EVP_sha256(), sik.data(), sik.size(), const1.data(),
+ const1.size(), k1.data(), &mdLen) == NULL)
+ {
+ FAIL() << "Generating Key1 failed";
+ }
+
+ mdLen = 0;
+ std::vector<uint8_t> output(SHA256_DIGEST_LENGTH);
+ size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE;
+
+ if (HMAC(EVP_sha256(), k1.data(), k1.size(),
+ packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE,
+ length,
+ output.data(), &mdLen) == NULL)
+ {
+ FAIL() << "Generating integrity data failed";
+ }
+
+ output.resize(cipher::integrity::AlgoSHA256::SHA256_128_AUTHCODE_LENGTH);
+
+ /*
+ * Step-2 Insert the integrity data into the packet
+ */
+ auto packetSize = packet.size();
+ packet.insert(packet.end(), output.begin(), output.end());
+
+ // Point to the integrity data in the packet
+ auto integrityIter = packet.cbegin();
+ std::advance(integrityIter, packetSize);
+
+ /*
+ * Step-3 Invoke the verifyIntegrityData API and validate the response
+ */
+
+ auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA256>(sik);
+ ASSERT_EQ(true, (algoPtr != NULL));
+
+ auto check = algoPtr->verifyIntegrityData(
+ packet,
+ packetSize - message::parser::RMCP_SESSION_HEADER_SIZE,
+ integrityIter);
+
+ EXPECT_EQ(true, check);
+}
+
+TEST(IntegrityAlgo, HMAC_SHA256_128_VerifyIntegrityDataFail)
+{
+ /*
+ * Step-1 Add hardcoded Integrity data to the packet
+ */
+
+ // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes)
+ std::vector<uint8_t> packet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+
+ std::vector<uint8_t> integrity = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+
+ packet.insert(packet.end(), integrity.begin(), integrity.end());
+
+ // Point to the integrity data in the packet
+ auto integrityIter = packet.cbegin();
+ std::advance(integrityIter, packet.size());
+
+ /*
+ * Step-2 Invoke the verifyIntegrityData API and validate the response
+ */
+
+ // Hardcoded Session Integrity Key
+ std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 };
+
+ auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA256>(sik);
+
+ ASSERT_EQ(true, (algoPtr != NULL));
+
+
+ // Verify the Integrity Data
+ auto check = algoPtr->verifyIntegrityData(
+ packet,
+ packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE,
+ integrityIter);
+
+ EXPECT_EQ(false, check);
+}
+
TEST(CryptAlgo, AES_CBC_128_EncryptPayloadValidate)
{
/*
OpenPOWER on IntegriCloud