diff options
author | Tom Joseph <tomjoseph@in.ibm.com> | 2017-01-30 19:25:06 +0530 |
---|---|---|
committer | Patrick Williams <patrick@stwcx.xyz> | 2017-02-28 17:00:32 +0000 |
commit | 1e5a76a058dda15ec234483152f5fb9b5109589e (patch) | |
tree | 9506f67e27c237fff10f3f4351a35c29b1ced5a0 | |
parent | 1e7aa196037367c44fc7e61903890505a1104615 (diff) | |
download | phosphor-net-ipmid-1e5a76a058dda15ec234483152f5fb9b5109589e.tar.gz phosphor-net-ipmid-1e5a76a058dda15ec234483152f5fb9b5109589e.zip |
Enable gtest and write testcases for Integrity & Conf algorithms
Resolves openbmc/openbmc#870
Resolves openbmc/openbmc#447
Change-Id: Idfeaf7b0c458faefc0e825419539f9500ee3ae8c
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 23 | ||||
-rw-r--r-- | test/Makefile.am | 14 | ||||
-rw-r--r-- | test/cipher.cpp | 336 |
4 files changed, 374 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index f4d7856..fbd2116 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,3 +45,5 @@ netipmid_CPPFLAGS = -DNET_IPMID_LIB_PATH=\"/usr/lib/net-ipmid/\" netipmid_LDFLAGS = $(SYSTEMD_LIBS) $(CRYPTO_LIBS) $(libmapper_LIBS) $(LIBADD_DLOPEN) -export-dynamic netipmid_CXXFLAGS = $(SYSTEMD_CFLAGS) $(libmapper_CFLAGS) +SUBDIRS = test + diff --git a/configure.ac b/configure.ac index a16ddc8..715bf56 100644 --- a/configure.ac +++ b/configure.ac @@ -14,6 +14,27 @@ AC_PROG_MAKE_SET # Surpress the --with-libtool-sysroot error LT_INIT +# Check/set gtest specific functions. +AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"]) +AC_SUBST(GTEST_CPPFLAGS) +AC_ARG_ENABLE([oe-sdk], + AS_HELP_STRING([--enable-oe-sdk], [Link testcases absolutely against OE SDK so they can be ran within it.]) +) +AC_ARG_VAR(OECORE_TARGET_SYSROOT, + [Path to the OE SDK SYSROOT]) +AS_IF([test "x$enable_oe_sdk" == "xyes"], + AS_IF([test "x$OECORE_TARGET_SYSROOT" == "x"], + AC_MSG_ERROR([OECORE_TARGET_SYSROOT must be set with --enable-oe-sdk]) + ) + AC_MSG_NOTICE([Enabling OE-SDK at $OECORE_TARGET_SYSROOT]) + [ + testcase_flags="-Wl,-rpath,\${OECORE_TARGET_SYSROOT}/lib" + testcase_flags="${testcase_flags} -Wl,-rpath,\${OECORE_TARGET_SYSROOT}/usr/lib" + testcase_flags="${testcase_flags} -Wl,-dynamic-linker,`find \${OECORE_TARGET_SYSROOT}/lib/ld-*.so | sort -r -n | head -n1`" + ] + AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags]) +) + # Checks for typedefs, structures, and compiler characteristics. AX_CXX_COMPILE_STDCXX_14([noext]) AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS]) @@ -31,5 +52,5 @@ AC_CHECK_HEADER(host-ipmid/ipmid-api.h, ,[AC_MSG_ERROR([Could not find host-ipmi LT_INIT([dlopen disable-static shared]) LT_LIB_DLLOAD -AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([Makefile test/Makefile]) AC_OUTPUT diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..b4996d0 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,14 @@ +AM_CPPFLAGS = -I$(top_srcdir) + +# Run all 'check' test programs +TESTS = $(check_PROGRAMS) + +# # Build/add utest to test suite +check_PROGRAMS = utest +utest_CPPFLAGS = -Igtest $(GTEST_CPPFLAGS) $(AM_CPPFLAGS) +utest_CXXFLAGS = $(PTHREAD_CFLAGS) +utest_LDFLAGS = -lgtest_main -lgtest $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS) $(CRYPTO_LIBS) +utest_SOURCES = cipher.cpp +utest_LDADD = \ + $(top_builddir)/integrity_algo.cpp \ + $(top_builddir)/crypt_algo.cpp
\ No newline at end of file diff --git a/test/cipher.cpp b/test/cipher.cpp new file mode 100644 index 0000000..84929d6 --- /dev/null +++ b/test/cipher.cpp @@ -0,0 +1,336 @@ +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> +#include <openssl/sha.h> +#include <iostream> +#include <vector> +#include "crypt_algo.hpp" +#include "integrity_algo.hpp" +#include "message_parsers.hpp" +#include <gtest/gtest.h> + +TEST(IntegrityAlgo, HMAC_SHA1_96_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}; + + auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik); + + ASSERT_EQ(true, (algoPtr != NULL)); + + // Generate the Integrity Data + auto response = algoPtr->generateIntegrityData(packet); + + EXPECT_EQ(true, (response.size() == + cipher::integrity::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH)); + + /* + * Step-2 Generate Integrity data using OpenSSL SHA1 algorithm + */ + cipher::integrity::Key K1; + constexpr cipher::integrity::Key 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_sha1(), sik.data(), sik.size(), const1.data(), + const1.size(), K1.data(), &mdLen) == NULL) + { + FAIL() << "Generating Key1 failed"; + } + + mdLen = 0; + cipher::integrity::Buffer output(SHA_DIGEST_LENGTH); + size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE; + + if (HMAC(EVP_sha1(), 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::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH); + + /* + * Step-3 Check if the integrity data we generated using the implemented API + * matches with one generated by OpenSSL SHA1 algorithm. + */ + auto check = std::equal(output.begin(), output.end(), response.begin()); + EXPECT_EQ(true, check); +} + +TEST(IntegrityAlgo, HMAC_SHA1_96_VerifyIntegrityDataPass) +{ + /* + * Step-1 Generate Integrity data using OpenSSL SHA1 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}; + + cipher::integrity::Key K1; + constexpr cipher::integrity::Key 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_sha1(), sik.data(), sik.size(), const1.data(), + const1.size(), K1.data(), &mdLen) == NULL) + { + FAIL() << "Generating Key1 failed"; + } + + mdLen = 0; + cipher::integrity::Buffer output(SHA_DIGEST_LENGTH); + size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE; + + if (HMAC(EVP_sha1(), 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::AlgoSHA1::SHA1_96_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::AlgoSHA1>(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_SHA1_96_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}; + + auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(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) +{ + /* + * Step-1 Generate the encrypted data using the implemented API for + * AES-CBC-128 + */ + std::vector<uint8_t> payload = { 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}; + + auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(sik); + + ASSERT_EQ(true, (cryptPtr != NULL)); + + auto cipher = cryptPtr->encryptPayload(payload); + + /* + * Step-2 Decrypt the encrypted payload using OpenSSL EVP_aes_128_cbc() + * implementation + */ + + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + cipher::crypt::key k2; + unsigned int mdLen = 0; + constexpr cipher::crypt::key const1 = { 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02 + }; + + // Generated K2 for the confidentiality algorithm with the additional key + // keyed with SIK. + if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), + const1.size(), k2.data(), &mdLen) == NULL) + { + FAIL() << "Generating K2 for confidentiality algorithm failed"; + } + + if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(), + cipher.data())) + { + EVP_CIPHER_CTX_cleanup(&ctx); + FAIL() << "EVP_DecryptInit_ex failed for type AES-CBC-128"; + } + + EVP_CIPHER_CTX_set_padding(&ctx, 0); + std::vector<uint8_t> output( + cipher.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize); + int outputLen = 0; + + if (!EVP_DecryptUpdate(&ctx, output.data(), &outputLen, + cipher.data() + + cipher::crypt::AlgoAES128::AESCBC128ConfHeader, + cipher.size() - + cipher::crypt::AlgoAES128::AESCBC128ConfHeader)) + { + EVP_CIPHER_CTX_cleanup(&ctx); + FAIL() << "EVP_DecryptUpdate failed"; + } + + output.resize(outputLen); + EVP_CIPHER_CTX_cleanup(&ctx); + + /* + * Step -3 Check if the plain payload matches with the decrypted one + */ + auto check = std::equal(payload.begin(), payload.end(), output.begin()); + EXPECT_EQ(true, check); +} + +TEST(CryptAlgo, AES_CBC_128_DecryptPayloadValidate) +{ + /* + * Step-1 Encrypt the payload using OpenSSL EVP_aes_128_cbc() + * implementation + */ + + std::vector<uint8_t> payload = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16}; + payload.resize(payload.size() + 1); + payload.back() = 0; + + // 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}; + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + cipher::crypt::key k2; + unsigned int mdLen = 0; + constexpr cipher::crypt::key const1 = { 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02 + }; + std::vector<uint8_t> output( + payload.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize); + + if (!RAND_bytes(output.data(), + cipher::crypt::AlgoAES128::AESCBC128ConfHeader)) + { + FAIL() << "RAND_bytes failed"; + } + + // Generated K2 for the confidentiality algorithm with the additional key + // keyed with SIK. + if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), + const1.size(), k2.data(), &mdLen) == NULL) + { + FAIL() << "Generating K2 for confidentiality algorithm failed"; + } + + if (!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(), + output.data())) + { + EVP_CIPHER_CTX_cleanup(&ctx); + FAIL() << "EVP_EncryptInit_ex failed for type AES-CBC-128"; + } + + EVP_CIPHER_CTX_set_padding(&ctx, 0); + int outputLen = 0; + + if (!EVP_EncryptUpdate(&ctx, + output.data() + + cipher::crypt::AlgoAES128::AESCBC128ConfHeader, + &outputLen, + payload.data(), + payload.size())) + { + EVP_CIPHER_CTX_cleanup(&ctx); + FAIL() << "EVP_EncryptUpdate failed"; + } + + output.resize(cipher::crypt::AlgoAES128::AESCBC128ConfHeader + outputLen); + EVP_CIPHER_CTX_cleanup(&ctx); + + /* + * Step-2 Decrypt the encrypted payload using the implemented API for + * AES-CBC-128 + */ + + auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(sik); + + ASSERT_EQ(true, (cryptPtr != NULL)); + + auto plain = cryptPtr->decryptPayload(output, 0, output.size()); + + /* + * Step -3 Check if the plain payload matches with the decrypted one + */ + auto check = std::equal(payload.begin(), payload.end(), plain.begin()); + EXPECT_EQ(true, check); +} |