1 #include "crypt_algo.hpp" 2 #include "integrity_algo.hpp" 3 #include "message_parsers.hpp" 4 #include "rmcp.hpp" 5 6 #include <openssl/evp.h> 7 #include <openssl/hmac.h> 8 #include <openssl/rand.h> 9 #include <openssl/sha.h> 10 11 #include <vector> 12 13 #include <gtest/gtest.h> 14 15 TEST(IntegrityAlgo, HMAC_SHA1_96_GenerateIntegrityDataCheck) 16 { 17 /* 18 * Step-1 Generate Integrity Data for the packet, using the implemented API 19 */ 20 // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes) 21 std::vector<uint8_t> packet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 22 23 // Hardcoded Session Integrity Key 24 std::vector<uint8_t> sik = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 25 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; 26 27 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik); 28 29 ASSERT_EQ(true, (algoPtr != NULL)); 30 31 // Generate the Integrity Data 32 auto response = algoPtr->generateIntegrityData(packet); 33 34 EXPECT_EQ(true, (response.size() == 35 cipher::integrity::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH)); 36 37 /* 38 * Step-2 Generate Integrity data using OpenSSL SHA1 algorithm 39 */ 40 std::vector<uint8_t> k1(SHA_DIGEST_LENGTH); 41 constexpr rmcp::Const_n const1 = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 42 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 43 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; 44 45 // Generated K1 for the integrity algorithm with the additional key keyed 46 // with SIK. 47 unsigned int mdLen = 0; 48 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), const1.size(), 49 k1.data(), &mdLen) == NULL) 50 { 51 FAIL() << "Generating Key1 failed"; 52 } 53 54 mdLen = 0; 55 std::vector<uint8_t> output(SHA_DIGEST_LENGTH); 56 size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE; 57 58 if (HMAC(EVP_sha1(), k1.data(), k1.size(), 59 packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE, 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, 86 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; 87 88 std::vector<uint8_t> k1(SHA_DIGEST_LENGTH); 89 constexpr rmcp::Const_n const1 = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 90 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 91 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; 92 93 // Generated K1 for the integrity algorithm with the additional key keyed 94 // with SIK. 95 unsigned int mdLen = 0; 96 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), const1.size(), 97 k1.data(), &mdLen) == NULL) 98 { 99 FAIL() << "Generating Key1 failed"; 100 } 101 102 mdLen = 0; 103 std::vector<uint8_t> output(SHA_DIGEST_LENGTH); 104 size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE; 105 106 if (HMAC(EVP_sha1(), k1.data(), k1.size(), 107 packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE, length, 108 output.data(), &mdLen) == NULL) 109 { 110 FAIL() << "Generating integrity data failed"; 111 } 112 113 output.resize(cipher::integrity::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH); 114 115 /* 116 * Step-2 Insert the integrity data into the packet 117 */ 118 auto packetSize = packet.size(); 119 packet.insert(packet.end(), output.begin(), output.end()); 120 121 // Point to the integrity data in the packet 122 auto integrityIter = packet.cbegin(); 123 std::advance(integrityIter, packetSize); 124 125 /* 126 * Step-3 Invoke the verifyIntegrityData API and validate the response 127 */ 128 129 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik); 130 ASSERT_EQ(true, (algoPtr != NULL)); 131 132 auto check = algoPtr->verifyIntegrityData( 133 packet, packetSize - message::parser::RMCP_SESSION_HEADER_SIZE, 134 integrityIter, packet.cend()); 135 136 EXPECT_EQ(true, check); 137 } 138 139 TEST(IntegrityAlgo, HMAC_SHA1_96_VerifyIntegrityDataFail) 140 { 141 /* 142 * Step-1 Add hardcoded Integrity data to the packet 143 */ 144 145 // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes) 146 std::vector<uint8_t> packet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 147 148 std::vector<uint8_t> integrity = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 149 150 packet.insert(packet.end(), integrity.begin(), integrity.end()); 151 152 // Point to the integrity data in the packet 153 auto integrityIter = packet.cbegin(); 154 std::advance(integrityIter, integrity.size()); 155 156 /* 157 * Step-2 Invoke the verifyIntegrityData API and validate the response 158 */ 159 160 // Hardcoded Session Integrity Key 161 std::vector<uint8_t> sik = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 162 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; 163 164 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik); 165 166 ASSERT_EQ(true, (algoPtr != NULL)); 167 168 // Verify the Integrity Data 169 auto check = algoPtr->verifyIntegrityData( 170 packet, packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE, 171 integrityIter, packet.cend()); 172 173 EXPECT_EQ(false, check); 174 } 175 176 TEST(IntegrityAlgo, HMAC_SHA256_128_GenerateIntegrityDataCheck) 177 { 178 /* 179 * Step-1 Generate Integrity Data for the packet, using the implemented API 180 */ 181 // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes) 182 std::vector<uint8_t> packet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 183 184 // Hardcoded Session Integrity Key 185 std::vector<uint8_t> sik = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 186 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 187 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; 188 189 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA256>(sik); 190 191 ASSERT_EQ(true, (algoPtr != NULL)); 192 193 // Generate the Integrity Data 194 auto response = algoPtr->generateIntegrityData(packet); 195 196 EXPECT_EQ(true, 197 (response.size() == 198 cipher::integrity::AlgoSHA256::SHA256_128_AUTHCODE_LENGTH)); 199 200 /* 201 * Step-2 Generate Integrity data using OpenSSL SHA256 algorithm 202 */ 203 std::vector<uint8_t> k1(SHA256_DIGEST_LENGTH); 204 constexpr rmcp::Const_n const1 = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 205 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 206 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; 207 208 // Generated K1 for the integrity algorithm with the additional key keyed 209 // with SIK. 210 unsigned int mdLen = 0; 211 if (HMAC(EVP_sha256(), sik.data(), sik.size(), const1.data(), const1.size(), 212 k1.data(), &mdLen) == NULL) 213 { 214 FAIL() << "Generating Key1 failed"; 215 } 216 217 mdLen = 0; 218 std::vector<uint8_t> output(SHA256_DIGEST_LENGTH); 219 size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE; 220 221 if (HMAC(EVP_sha256(), k1.data(), k1.size(), 222 packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE, length, 223 output.data(), &mdLen) == NULL) 224 { 225 FAIL() << "Generating integrity data failed"; 226 } 227 228 output.resize(cipher::integrity::AlgoSHA256::SHA256_128_AUTHCODE_LENGTH); 229 230 /* 231 * Step-3 Check if the integrity data we generated using the implemented API 232 * matches with one generated by OpenSSL SHA256 algorithm. 233 */ 234 auto check = std::equal(output.begin(), output.end(), response.begin()); 235 EXPECT_EQ(true, check); 236 } 237 238 TEST(IntegrityAlgo, HMAC_SHA256_128_VerifyIntegrityDataPass) 239 { 240 /* 241 * Step-1 Generate Integrity data using OpenSSL SHA256 algorithm 242 */ 243 244 // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes) 245 std::vector<uint8_t> packet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 246 247 // Hardcoded Session Integrity Key 248 std::vector<uint8_t> sik = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 249 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 250 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; 251 252 std::vector<uint8_t> k1(SHA256_DIGEST_LENGTH); 253 constexpr rmcp::Const_n const1 = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 254 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 255 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; 256 257 // Generated K1 for the integrity algorithm with the additional key keyed 258 // with SIK. 259 unsigned int mdLen = 0; 260 if (HMAC(EVP_sha256(), sik.data(), sik.size(), const1.data(), const1.size(), 261 k1.data(), &mdLen) == NULL) 262 { 263 FAIL() << "Generating Key1 failed"; 264 } 265 266 mdLen = 0; 267 std::vector<uint8_t> output(SHA256_DIGEST_LENGTH); 268 size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE; 269 270 if (HMAC(EVP_sha256(), k1.data(), k1.size(), 271 packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE, length, 272 output.data(), &mdLen) == NULL) 273 { 274 FAIL() << "Generating integrity data failed"; 275 } 276 277 output.resize(cipher::integrity::AlgoSHA256::SHA256_128_AUTHCODE_LENGTH); 278 279 /* 280 * Step-2 Insert the integrity data into the packet 281 */ 282 auto packetSize = packet.size(); 283 packet.insert(packet.end(), output.begin(), output.end()); 284 285 // Point to the integrity data in the packet 286 auto integrityIter = packet.cbegin(); 287 std::advance(integrityIter, packetSize); 288 289 /* 290 * Step-3 Invoke the verifyIntegrityData API and validate the response 291 */ 292 293 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA256>(sik); 294 ASSERT_EQ(true, (algoPtr != NULL)); 295 296 auto check = algoPtr->verifyIntegrityData( 297 packet, packetSize - message::parser::RMCP_SESSION_HEADER_SIZE, 298 integrityIter, packet.cend()); 299 300 EXPECT_EQ(true, check); 301 } 302 303 TEST(IntegrityAlgo, HMAC_SHA256_128_VerifyIntegrityDataFail) 304 { 305 /* 306 * Step-1 Add hardcoded Integrity data to the packet 307 */ 308 309 // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes) 310 std::vector<uint8_t> packet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 311 312 std::vector<uint8_t> integrity = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 313 314 packet.insert(packet.end(), integrity.begin(), integrity.end()); 315 316 // Point to the integrity data in the packet 317 auto integrityIter = packet.cbegin(); 318 std::advance(integrityIter, integrity.size()); 319 320 /* 321 * Step-2 Invoke the verifyIntegrityData API and validate the response 322 */ 323 324 // Hardcoded Session Integrity Key 325 std::vector<uint8_t> sik = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 326 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 327 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; 328 329 auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA256>(sik); 330 331 ASSERT_EQ(true, (algoPtr != NULL)); 332 333 // Verify the Integrity Data 334 auto check = algoPtr->verifyIntegrityData( 335 packet, packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE, 336 integrityIter, packet.cend()); 337 338 EXPECT_EQ(false, check); 339 } 340 341 TEST(CryptAlgo, AES_CBC_128_EncryptPayloadValidate) 342 { 343 /* 344 * Step-1 Generate the encrypted data using the implemented API for 345 * AES-CBC-128 346 */ 347 std::vector<uint8_t> payload = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 348 349 // Hardcoded Session Integrity Key 350 std::vector<uint8_t> sik = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 351 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; 352 353 std::vector<uint8_t> k2(SHA_DIGEST_LENGTH); 354 unsigned int mdLen = 0; 355 constexpr rmcp::Const_n const1 = {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 356 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 357 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}; 358 359 // Generated K2 for the confidentiality algorithm with the additional key 360 // keyed with SIK. 361 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), const1.size(), 362 k2.data(), &mdLen) == NULL) 363 { 364 FAIL() << "Generating K2 for confidentiality algorithm failed"; 365 } 366 367 auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(k2); 368 369 ASSERT_EQ(true, (cryptPtr != NULL)); 370 371 auto cipher = cryptPtr->encryptPayload(payload); 372 373 /* 374 * Step-2 Decrypt the encrypted payload using OpenSSL EVP_aes_128_cbc() 375 * implementation 376 */ 377 378 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 379 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, k2.data(), 380 cipher.data())) 381 { 382 EVP_CIPHER_CTX_free(ctx); 383 FAIL() << "EVP_DecryptInit_ex failed for type AES-CBC-128"; 384 } 385 386 EVP_CIPHER_CTX_set_padding(ctx, 0); 387 std::vector<uint8_t> output(cipher.size() + 388 cipher::crypt::AlgoAES128::AESCBC128BlockSize); 389 int outputLen = 0; 390 391 if (!EVP_DecryptUpdate( 392 ctx, output.data(), &outputLen, 393 cipher.data() + cipher::crypt::AlgoAES128::AESCBC128ConfHeader, 394 cipher.size() - cipher::crypt::AlgoAES128::AESCBC128ConfHeader)) 395 { 396 EVP_CIPHER_CTX_free(ctx); 397 FAIL() << "EVP_DecryptUpdate failed"; 398 } 399 400 output.resize(outputLen); 401 EVP_CIPHER_CTX_free(ctx); 402 403 /* 404 * Step -3 Check if the plain payload matches with the decrypted one 405 */ 406 auto check = std::equal(payload.begin(), payload.end(), output.begin()); 407 EXPECT_EQ(true, check); 408 } 409 410 TEST(CryptAlgo, AES_CBC_128_DecryptPayloadValidate) 411 { 412 /* 413 * Step-1 Encrypt the payload using OpenSSL EVP_aes_128_cbc() 414 * implementation 415 */ 416 417 std::vector<uint8_t> payload = {1, 2, 3, 4, 5, 6, 7, 8, 418 9, 10, 11, 12, 13, 14, 15, 16}; 419 payload.resize(payload.size() + 1); 420 payload.back() = 0; 421 422 // Hardcoded Session Integrity Key 423 std::vector<uint8_t> sik = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 424 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; 425 EVP_CIPHER_CTX* ctx; 426 ctx = EVP_CIPHER_CTX_new(); 427 std::vector<uint8_t> k2(SHA_DIGEST_LENGTH); 428 unsigned int mdLen = 0; 429 constexpr rmcp::Const_n const1 = {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 430 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 431 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}; 432 std::vector<uint8_t> output(payload.size() + 433 cipher::crypt::AlgoAES128::AESCBC128BlockSize); 434 435 if (!RAND_bytes(output.data(), 436 cipher::crypt::AlgoAES128::AESCBC128ConfHeader)) 437 { 438 FAIL() << "RAND_bytes failed"; 439 } 440 441 // Generated K2 for the confidentiality algorithm with the additional key 442 // keyed with SIK. 443 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), const1.size(), 444 k2.data(), &mdLen) == NULL) 445 { 446 FAIL() << "Generating K2 for confidentiality algorithm failed"; 447 } 448 449 if (!EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, k2.data(), 450 output.data())) 451 { 452 EVP_CIPHER_CTX_free(ctx); 453 FAIL() << "EVP_EncryptInit_ex failed for type AES-CBC-128"; 454 } 455 456 EVP_CIPHER_CTX_set_padding(ctx, 0); 457 int outputLen = 0; 458 459 if (!EVP_EncryptUpdate( 460 ctx, output.data() + cipher::crypt::AlgoAES128::AESCBC128ConfHeader, 461 &outputLen, payload.data(), payload.size())) 462 { 463 EVP_CIPHER_CTX_free(ctx); 464 FAIL() << "EVP_EncryptUpdate failed"; 465 } 466 467 output.resize(cipher::crypt::AlgoAES128::AESCBC128ConfHeader + outputLen); 468 EVP_CIPHER_CTX_free(ctx); 469 470 /* 471 * Step-2 Decrypt the encrypted payload using the implemented API for 472 * AES-CBC-128 473 */ 474 475 auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(k2); 476 477 ASSERT_EQ(true, (cryptPtr != NULL)); 478 479 auto plain = cryptPtr->decryptPayload(output, 0, output.size()); 480 481 /* 482 * Step -3 Check if the plain payload matches with the decrypted one 483 */ 484 auto check = std::equal(payload.begin(), payload.end(), plain.begin()); 485 EXPECT_EQ(true, check); 486 } 487