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