diff options
author | Vernon Mauery <vernon.mauery@linux.intel.com> | 2017-11-29 08:36:29 -0800 |
---|---|---|
committer | Brad Bishop <bradleyb@us.ibm.com> | 2017-12-08 21:55:44 -0500 |
commit | 7e9e2ef68a1c53e022970d85566945d5bdc5a78e (patch) | |
tree | 946c6772654bdd8ecaed04498938999f49154b53 | |
parent | 9b307be647ff786f05c03fa742b982d99dd341ae (diff) | |
download | phosphor-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.cpp | 34 | ||||
-rw-r--r-- | auth_algo.hpp | 36 | ||||
-rw-r--r-- | command/rakp34.cpp | 7 | ||||
-rw-r--r-- | integrity_algo.cpp | 67 | ||||
-rw-r--r-- | integrity_algo.hpp | 89 | ||||
-rw-r--r-- | sessions_manager.cpp | 7 | ||||
-rw-r--r-- | test/cipher.cpp | 173 |
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) { /* |