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