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