1 #include <openssl/evp.h> 2 #include <openssl/hmac.h> 3 #include <openssl/rand.h> 4 #include <openssl/sha.h> 5 #include <iostream> 6 #include <vector> 7 #include "crypt_algo.hpp" 8 #include "integrity_algo.hpp" 9 #include "message_parsers.hpp" 10 #include <gtest/gtest.h> 11 12 TEST(IntegrityAlgo, HMAC_SHA1_96_GenerateIntegrityDataCheck) 13 { 14 /* 15 * Step-1 Generate Integrity Data for the packet, using the implemented API 16 */ 17 // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes) 18 std::vector<uint8_t> packet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; 19 20 // Hardcoded Session Integrity Key 21 std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 22 13, 14, 15, 16}; 23 24 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik); 25 26 ASSERT_EQ(true, (algoPtr != NULL)); 27 28 // Generate the Integrity Data 29 auto response = algoPtr->generateIntegrityData(packet); 30 31 EXPECT_EQ(true, (response.size() == 32 cipher::integrity::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH)); 33 34 /* 35 * Step-2 Generate Integrity data using OpenSSL SHA1 algorithm 36 */ 37 cipher::integrity::Key K1; 38 constexpr cipher::integrity::Key const1 = { 0x01, 0x01, 0x01, 0x01, 0x01, 39 0x01, 0x01, 0x01, 0x01, 0x01, 40 0x01, 0x01, 0x01, 0x01, 0x01, 41 0x01, 0x01, 0x01, 0x01, 0x01 42 }; 43 44 // Generated K1 for the integrity algorithm with the additional key keyed 45 // with SIK. 46 unsigned int mdLen = 0; 47 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), 48 const1.size(), K1.data(), &mdLen) == NULL) 49 { 50 FAIL() << "Generating Key1 failed"; 51 } 52 53 mdLen = 0; 54 std::vector<uint8_t> output(SHA_DIGEST_LENGTH); 55 size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE; 56 57 if (HMAC(EVP_sha1(), K1.data(), K1.size(), 58 packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE, 59 length, 60 output.data(), &mdLen) == NULL) 61 { 62 FAIL() << "Generating integrity data failed"; 63 } 64 65 output.resize(cipher::integrity::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH); 66 67 /* 68 * Step-3 Check if the integrity data we generated using the implemented API 69 * matches with one generated by OpenSSL SHA1 algorithm. 70 */ 71 auto check = std::equal(output.begin(), output.end(), response.begin()); 72 EXPECT_EQ(true, check); 73 } 74 75 TEST(IntegrityAlgo, HMAC_SHA1_96_VerifyIntegrityDataPass) 76 { 77 /* 78 * Step-1 Generate Integrity data using OpenSSL SHA1 algorithm 79 */ 80 81 // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes) 82 std::vector<uint8_t> packet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; 83 84 // Hardcoded Session Integrity Key 85 std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 86 13, 14, 15, 16}; 87 88 cipher::integrity::Key K1; 89 constexpr cipher::integrity::Key const1 = { 0x01, 0x01, 0x01, 0x01, 0x01, 90 0x01, 0x01, 0x01, 0x01, 0x01, 91 0x01, 0x01, 0x01, 0x01, 0x01, 92 0x01, 0x01, 0x01, 0x01, 0x01 93 }; 94 95 // Generated K1 for the integrity algorithm with the additional key keyed 96 // with SIK. 97 unsigned int mdLen = 0; 98 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), 99 const1.size(), K1.data(), &mdLen) == NULL) 100 { 101 FAIL() << "Generating Key1 failed"; 102 } 103 104 mdLen = 0; 105 std::vector<uint8_t> output(SHA_DIGEST_LENGTH); 106 size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE; 107 108 if (HMAC(EVP_sha1(), K1.data(), K1.size(), 109 packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE, 110 length, 111 output.data(), &mdLen) == NULL) 112 { 113 FAIL() << "Generating integrity data failed"; 114 } 115 116 output.resize(cipher::integrity::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH); 117 118 /* 119 * Step-2 Insert the integrity data into the packet 120 */ 121 auto packetSize = packet.size(); 122 packet.insert(packet.end(), output.begin(), output.end()); 123 124 // Point to the integrity data in the packet 125 auto integrityIter = packet.cbegin(); 126 std::advance(integrityIter, packetSize); 127 128 /* 129 * Step-3 Invoke the verifyIntegrityData API and validate the response 130 */ 131 132 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik); 133 ASSERT_EQ(true, (algoPtr != NULL)); 134 135 auto check = algoPtr->verifyIntegrityData( 136 packet, 137 packetSize - message::parser::RMCP_SESSION_HEADER_SIZE, 138 integrityIter); 139 140 EXPECT_EQ(true, check); 141 } 142 143 TEST(IntegrityAlgo, HMAC_SHA1_96_VerifyIntegrityDataFail) 144 { 145 /* 146 * Step-1 Add hardcoded Integrity data to the packet 147 */ 148 149 // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes) 150 std::vector<uint8_t> packet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; 151 152 std::vector<uint8_t> integrity = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; 153 154 packet.insert(packet.end(), integrity.begin(), integrity.end()); 155 156 // Point to the integrity data in the packet 157 auto integrityIter = packet.cbegin(); 158 std::advance(integrityIter, packet.size()); 159 160 /* 161 * Step-2 Invoke the verifyIntegrityData API and validate the response 162 */ 163 164 // Hardcoded Session Integrity Key 165 std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 166 13, 14, 15, 16}; 167 168 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik); 169 170 ASSERT_EQ(true, (algoPtr != NULL)); 171 172 173 // Verify the Integrity Data 174 auto check = algoPtr->verifyIntegrityData( 175 packet, 176 packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE, 177 integrityIter); 178 179 EXPECT_EQ(false, check); 180 } 181 182 TEST(CryptAlgo, AES_CBC_128_EncryptPayloadValidate) 183 { 184 /* 185 * Step-1 Generate the encrypted data using the implemented API for 186 * AES-CBC-128 187 */ 188 std::vector<uint8_t> payload = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; 189 190 // Hardcoded Session Integrity Key 191 std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 192 13, 14, 15, 16}; 193 194 auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(sik); 195 196 ASSERT_EQ(true, (cryptPtr != NULL)); 197 198 auto cipher = cryptPtr->encryptPayload(payload); 199 200 /* 201 * Step-2 Decrypt the encrypted payload using OpenSSL EVP_aes_128_cbc() 202 * implementation 203 */ 204 205 EVP_CIPHER_CTX ctx; 206 EVP_CIPHER_CTX_init(&ctx); 207 cipher::crypt::key k2; 208 unsigned int mdLen = 0; 209 constexpr cipher::crypt::key const1 = { 0x02, 0x02, 0x02, 0x02, 0x02, 210 0x02, 0x02, 0x02, 0x02, 0x02, 211 0x02, 0x02, 0x02, 0x02, 0x02, 212 0x02, 0x02, 0x02, 0x02, 0x02 213 }; 214 215 // Generated K2 for the confidentiality algorithm with the additional key 216 // keyed with SIK. 217 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), 218 const1.size(), k2.data(), &mdLen) == NULL) 219 { 220 FAIL() << "Generating K2 for confidentiality algorithm failed"; 221 } 222 223 if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(), 224 cipher.data())) 225 { 226 EVP_CIPHER_CTX_cleanup(&ctx); 227 FAIL() << "EVP_DecryptInit_ex failed for type AES-CBC-128"; 228 } 229 230 EVP_CIPHER_CTX_set_padding(&ctx, 0); 231 std::vector<uint8_t> output( 232 cipher.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize); 233 int outputLen = 0; 234 235 if (!EVP_DecryptUpdate(&ctx, output.data(), &outputLen, 236 cipher.data() + 237 cipher::crypt::AlgoAES128::AESCBC128ConfHeader, 238 cipher.size() - 239 cipher::crypt::AlgoAES128::AESCBC128ConfHeader)) 240 { 241 EVP_CIPHER_CTX_cleanup(&ctx); 242 FAIL() << "EVP_DecryptUpdate failed"; 243 } 244 245 output.resize(outputLen); 246 EVP_CIPHER_CTX_cleanup(&ctx); 247 248 /* 249 * Step -3 Check if the plain payload matches with the decrypted one 250 */ 251 auto check = std::equal(payload.begin(), payload.end(), output.begin()); 252 EXPECT_EQ(true, check); 253 } 254 255 TEST(CryptAlgo, AES_CBC_128_DecryptPayloadValidate) 256 { 257 /* 258 * Step-1 Encrypt the payload using OpenSSL EVP_aes_128_cbc() 259 * implementation 260 */ 261 262 std::vector<uint8_t> payload = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 263 13, 14, 15, 16}; 264 payload.resize(payload.size() + 1); 265 payload.back() = 0; 266 267 // Hardcoded Session Integrity Key 268 std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 269 13, 14, 15, 16}; 270 EVP_CIPHER_CTX ctx; 271 EVP_CIPHER_CTX_init(&ctx); 272 cipher::crypt::key k2; 273 unsigned int mdLen = 0; 274 constexpr cipher::crypt::key const1 = { 0x02, 0x02, 0x02, 0x02, 0x02, 275 0x02, 0x02, 0x02, 0x02, 0x02, 276 0x02, 0x02, 0x02, 0x02, 0x02, 277 0x02, 0x02, 0x02, 0x02, 0x02 278 }; 279 std::vector<uint8_t> output( 280 payload.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize); 281 282 if (!RAND_bytes(output.data(), 283 cipher::crypt::AlgoAES128::AESCBC128ConfHeader)) 284 { 285 FAIL() << "RAND_bytes failed"; 286 } 287 288 // Generated K2 for the confidentiality algorithm with the additional key 289 // keyed with SIK. 290 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), 291 const1.size(), k2.data(), &mdLen) == NULL) 292 { 293 FAIL() << "Generating K2 for confidentiality algorithm failed"; 294 } 295 296 if (!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(), 297 output.data())) 298 { 299 EVP_CIPHER_CTX_cleanup(&ctx); 300 FAIL() << "EVP_EncryptInit_ex failed for type AES-CBC-128"; 301 } 302 303 EVP_CIPHER_CTX_set_padding(&ctx, 0); 304 int outputLen = 0; 305 306 if (!EVP_EncryptUpdate(&ctx, 307 output.data() + 308 cipher::crypt::AlgoAES128::AESCBC128ConfHeader, 309 &outputLen, 310 payload.data(), 311 payload.size())) 312 { 313 EVP_CIPHER_CTX_cleanup(&ctx); 314 FAIL() << "EVP_EncryptUpdate failed"; 315 } 316 317 output.resize(cipher::crypt::AlgoAES128::AESCBC128ConfHeader + outputLen); 318 EVP_CIPHER_CTX_cleanup(&ctx); 319 320 /* 321 * Step-2 Decrypt the encrypted payload using the implemented API for 322 * AES-CBC-128 323 */ 324 325 auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(sik); 326 327 ASSERT_EQ(true, (cryptPtr != NULL)); 328 329 auto plain = cryptPtr->decryptPayload(output, 0, output.size()); 330 331 /* 332 * Step -3 Check if the plain payload matches with the decrypted one 333 */ 334 auto check = std::equal(payload.begin(), payload.end(), plain.begin()); 335 EXPECT_EQ(true, check); 336 } 337