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(CryptAlgo, AES_CBC_128_EncryptPayloadValidate) 184 { 185 /* 186 * Step-1 Generate the encrypted data using the implemented API for 187 * AES-CBC-128 188 */ 189 std::vector<uint8_t> payload = { 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 }; 194 195 std::vector<uint8_t> k2(SHA_DIGEST_LENGTH); 196 unsigned int mdLen = 0; 197 constexpr rmcp::Const_n const1 = { 0x02, 0x02, 0x02, 0x02, 0x02, 198 0x02, 0x02, 0x02, 0x02, 0x02, 199 0x02, 0x02, 0x02, 0x02, 0x02, 200 0x02, 0x02, 0x02, 0x02, 0x02 201 }; 202 203 // Generated K2 for the confidentiality algorithm with the additional key 204 // keyed with SIK. 205 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), 206 const1.size(), k2.data(), &mdLen) == NULL) 207 { 208 FAIL() << "Generating K2 for confidentiality algorithm failed"; 209 } 210 211 auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(k2); 212 213 ASSERT_EQ(true, (cryptPtr != NULL)); 214 215 auto cipher = cryptPtr->encryptPayload(payload); 216 217 /* 218 * Step-2 Decrypt the encrypted payload using OpenSSL EVP_aes_128_cbc() 219 * implementation 220 */ 221 222 EVP_CIPHER_CTX ctx; 223 EVP_CIPHER_CTX_init(&ctx); 224 if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(), 225 cipher.data())) 226 { 227 EVP_CIPHER_CTX_cleanup(&ctx); 228 FAIL() << "EVP_DecryptInit_ex failed for type AES-CBC-128"; 229 } 230 231 EVP_CIPHER_CTX_set_padding(&ctx, 0); 232 std::vector<uint8_t> output( 233 cipher.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize); 234 int outputLen = 0; 235 236 if (!EVP_DecryptUpdate(&ctx, output.data(), &outputLen, 237 cipher.data() + 238 cipher::crypt::AlgoAES128::AESCBC128ConfHeader, 239 cipher.size() - 240 cipher::crypt::AlgoAES128::AESCBC128ConfHeader)) 241 { 242 EVP_CIPHER_CTX_cleanup(&ctx); 243 FAIL() << "EVP_DecryptUpdate failed"; 244 } 245 246 output.resize(outputLen); 247 EVP_CIPHER_CTX_cleanup(&ctx); 248 249 /* 250 * Step -3 Check if the plain payload matches with the decrypted one 251 */ 252 auto check = std::equal(payload.begin(), payload.end(), output.begin()); 253 EXPECT_EQ(true, check); 254 } 255 256 TEST(CryptAlgo, AES_CBC_128_DecryptPayloadValidate) 257 { 258 /* 259 * Step-1 Encrypt the payload using OpenSSL EVP_aes_128_cbc() 260 * implementation 261 */ 262 263 std::vector<uint8_t> payload = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 264 13, 14, 15, 16}; 265 payload.resize(payload.size() + 1); 266 payload.back() = 0; 267 268 // Hardcoded Session Integrity Key 269 std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 270 13, 14, 15, 16, 17, 18, 19, 20 }; 271 EVP_CIPHER_CTX ctx; 272 EVP_CIPHER_CTX_init(&ctx); 273 std::vector<uint8_t> k2(SHA_DIGEST_LENGTH); 274 unsigned int mdLen = 0; 275 constexpr rmcp::Const_n const1 = { 0x02, 0x02, 0x02, 0x02, 0x02, 276 0x02, 0x02, 0x02, 0x02, 0x02, 277 0x02, 0x02, 0x02, 0x02, 0x02, 278 0x02, 0x02, 0x02, 0x02, 0x02 279 }; 280 std::vector<uint8_t> output( 281 payload.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize); 282 283 if (!RAND_bytes(output.data(), 284 cipher::crypt::AlgoAES128::AESCBC128ConfHeader)) 285 { 286 FAIL() << "RAND_bytes failed"; 287 } 288 289 // Generated K2 for the confidentiality algorithm with the additional key 290 // keyed with SIK. 291 if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(), 292 const1.size(), k2.data(), &mdLen) == NULL) 293 { 294 FAIL() << "Generating K2 for confidentiality algorithm failed"; 295 } 296 297 if (!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(), 298 output.data())) 299 { 300 EVP_CIPHER_CTX_cleanup(&ctx); 301 FAIL() << "EVP_EncryptInit_ex failed for type AES-CBC-128"; 302 } 303 304 EVP_CIPHER_CTX_set_padding(&ctx, 0); 305 int outputLen = 0; 306 307 if (!EVP_EncryptUpdate(&ctx, 308 output.data() + 309 cipher::crypt::AlgoAES128::AESCBC128ConfHeader, 310 &outputLen, 311 payload.data(), 312 payload.size())) 313 { 314 EVP_CIPHER_CTX_cleanup(&ctx); 315 FAIL() << "EVP_EncryptUpdate failed"; 316 } 317 318 output.resize(cipher::crypt::AlgoAES128::AESCBC128ConfHeader + outputLen); 319 EVP_CIPHER_CTX_cleanup(&ctx); 320 321 /* 322 * Step-2 Decrypt the encrypted payload using the implemented API for 323 * AES-CBC-128 324 */ 325 326 auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(k2); 327 328 ASSERT_EQ(true, (cryptPtr != NULL)); 329 330 auto plain = cryptPtr->decryptPayload(output, 0, output.size()); 331 332 /* 333 * Step -3 Check if the plain payload matches with the decrypted one 334 */ 335 auto check = std::equal(payload.begin(), payload.end(), plain.begin()); 336 EXPECT_EQ(true, check); 337 } 338