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
TEST(IntegrityAlgo,HMAC_SHA1_96_GenerateIntegrityDataCheck)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
TEST(IntegrityAlgo,HMAC_SHA1_96_VerifyIntegrityDataPass)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
TEST(IntegrityAlgo,HMAC_SHA1_96_VerifyIntegrityDataFail)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
TEST(IntegrityAlgo,HMAC_SHA256_128_GenerateIntegrityDataCheck)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
TEST(IntegrityAlgo,HMAC_SHA256_128_VerifyIntegrityDataPass)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
TEST(IntegrityAlgo,HMAC_SHA256_128_VerifyIntegrityDataFail)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
TEST(CryptAlgo,AES_CBC_128_EncryptPayloadValidate)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(
388 cipher.size() + 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
TEST(CryptAlgo,AES_CBC_128_DecryptPayloadValidate)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(
433 payload.size() + 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