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 
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 
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 
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 
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 
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 
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 
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(cipher.size() +
388                                 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 
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(payload.size() +
433                                 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