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