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 <gtest/gtest.h>
11 
12 TEST(IntegrityAlgo, HMAC_SHA1_96_GenerateIntegrityDataCheck)
13 {
14     /*
15      * Step-1 Generate Integrity Data for the packet, using the implemented API
16      */
17     // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes)
18     std::vector<uint8_t> packet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
19 
20     // Hardcoded Session Integrity Key
21     std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
22                                  13, 14, 15, 16};
23 
24     auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik);
25 
26     ASSERT_EQ(true, (algoPtr != NULL));
27 
28     // Generate the Integrity Data
29     auto response = algoPtr->generateIntegrityData(packet);
30 
31     EXPECT_EQ(true, (response.size() ==
32                     cipher::integrity::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH));
33 
34     /*
35      * Step-2 Generate Integrity data using OpenSSL SHA1 algorithm
36      */
37     cipher::integrity::Key K1;
38     constexpr cipher::integrity::Key const1 = { 0x01, 0x01, 0x01, 0x01, 0x01,
39                                                 0x01, 0x01, 0x01, 0x01, 0x01,
40                                                 0x01, 0x01, 0x01, 0x01, 0x01,
41                                                 0x01, 0x01, 0x01, 0x01, 0x01
42                                               };
43 
44     // Generated K1 for the integrity algorithm with the additional key keyed
45     // with SIK.
46     unsigned int mdLen = 0;
47     if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(),
48              const1.size(), K1.data(), &mdLen) == NULL)
49     {
50         FAIL() << "Generating Key1 failed";
51     }
52 
53     mdLen = 0;
54     std::vector<uint8_t> output(SHA_DIGEST_LENGTH);
55     size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE;
56 
57     if (HMAC(EVP_sha1(), K1.data(), K1.size(),
58              packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE,
59              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, 11, 12,
86                                  13, 14, 15, 16};
87 
88     cipher::integrity::Key K1;
89     constexpr cipher::integrity::Key const1 = { 0x01, 0x01, 0x01, 0x01, 0x01,
90                                                 0x01, 0x01, 0x01, 0x01, 0x01,
91                                                 0x01, 0x01, 0x01, 0x01, 0x01,
92                                                 0x01, 0x01, 0x01, 0x01, 0x01
93                                               };
94 
95     // Generated K1 for the integrity algorithm with the additional key keyed
96     // with SIK.
97     unsigned int mdLen = 0;
98     if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(),
99              const1.size(), K1.data(), &mdLen) == NULL)
100     {
101         FAIL() << "Generating Key1 failed";
102     }
103 
104     mdLen = 0;
105     std::vector<uint8_t> output(SHA_DIGEST_LENGTH);
106     size_t length = packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE;
107 
108     if (HMAC(EVP_sha1(), K1.data(), K1.size(),
109              packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE,
110              length,
111              output.data(), &mdLen) == NULL)
112     {
113         FAIL() << "Generating integrity data failed";
114     }
115 
116     output.resize(cipher::integrity::AlgoSHA1::SHA1_96_AUTHCODE_LENGTH);
117 
118     /*
119      * Step-2 Insert the integrity data into the packet
120      */
121     auto packetSize = packet.size();
122     packet.insert(packet.end(), output.begin(), output.end());
123 
124      // Point to the integrity data in the packet
125      auto integrityIter = packet.cbegin();
126      std::advance(integrityIter, packetSize);
127 
128      /*
129       * Step-3 Invoke the verifyIntegrityData API and validate the response
130       */
131 
132      auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik);
133      ASSERT_EQ(true, (algoPtr != NULL));
134 
135      auto check = algoPtr->verifyIntegrityData(
136              packet,
137              packetSize - message::parser::RMCP_SESSION_HEADER_SIZE,
138              integrityIter);
139 
140      EXPECT_EQ(true, check);
141 }
142 
143 TEST(IntegrityAlgo, HMAC_SHA1_96_VerifyIntegrityDataFail)
144 {
145     /*
146      * Step-1 Add hardcoded Integrity data to the packet
147      */
148 
149     // Packet = RMCP Session Header (4 bytes) + Packet (8 bytes)
150     std::vector<uint8_t> packet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
151 
152     std::vector<uint8_t> integrity = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
153 
154     packet.insert(packet.end(), integrity.begin(), integrity.end());
155 
156     // Point to the integrity data in the packet
157     auto integrityIter = packet.cbegin();
158     std::advance(integrityIter, packet.size());
159 
160     /*
161      * Step-2 Invoke the verifyIntegrityData API and validate the response
162      */
163 
164     // Hardcoded Session Integrity Key
165     std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
166                                  13, 14, 15, 16};
167 
168     auto algoPtr = std::make_unique<cipher::integrity::AlgoSHA1>(sik);
169 
170     ASSERT_EQ(true, (algoPtr != NULL));
171 
172 
173     // Verify the Integrity Data
174     auto check = algoPtr->verifyIntegrityData(
175             packet,
176             packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE,
177             integrityIter);
178 
179     EXPECT_EQ(false, check);
180 }
181 
182 TEST(CryptAlgo, AES_CBC_128_EncryptPayloadValidate)
183 {
184     /*
185      * Step-1 Generate the encrypted data using the implemented API for
186      * AES-CBC-128
187      */
188     std::vector<uint8_t> payload = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
189 
190     // Hardcoded Session Integrity Key
191     std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
192                                  13, 14, 15, 16};
193 
194     auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(sik);
195 
196     ASSERT_EQ(true, (cryptPtr != NULL));
197 
198     auto cipher = cryptPtr->encryptPayload(payload);
199 
200     /*
201      * Step-2 Decrypt the encrypted payload using OpenSSL EVP_aes_128_cbc()
202      * implementation
203      */
204 
205     EVP_CIPHER_CTX ctx;
206     EVP_CIPHER_CTX_init(&ctx);
207     cipher::crypt::key k2;
208     unsigned int mdLen = 0;
209     constexpr cipher::crypt::key const1 = { 0x02, 0x02, 0x02, 0x02, 0x02,
210                                             0x02, 0x02, 0x02, 0x02, 0x02,
211                                             0x02, 0x02, 0x02, 0x02, 0x02,
212                                             0x02, 0x02, 0x02, 0x02, 0x02
213                                           };
214 
215     // Generated K2 for the confidentiality algorithm with the additional key
216     // keyed with SIK.
217     if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(),
218              const1.size(), k2.data(), &mdLen) == NULL)
219     {
220         FAIL() << "Generating K2 for confidentiality algorithm failed";
221     }
222 
223     if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(),
224                             cipher.data()))
225     {
226         EVP_CIPHER_CTX_cleanup(&ctx);
227         FAIL() << "EVP_DecryptInit_ex failed for type AES-CBC-128";
228     }
229 
230     EVP_CIPHER_CTX_set_padding(&ctx, 0);
231     std::vector<uint8_t> output(
232             cipher.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize);
233     int outputLen = 0;
234 
235     if (!EVP_DecryptUpdate(&ctx, output.data(), &outputLen,
236                            cipher.data() +
237                            cipher::crypt::AlgoAES128::AESCBC128ConfHeader,
238                            cipher.size() -
239                            cipher::crypt::AlgoAES128::AESCBC128ConfHeader))
240     {
241         EVP_CIPHER_CTX_cleanup(&ctx);
242         FAIL() << "EVP_DecryptUpdate failed";
243     }
244 
245     output.resize(outputLen);
246     EVP_CIPHER_CTX_cleanup(&ctx);
247 
248     /*
249      * Step -3 Check if the plain payload matches with the decrypted one
250      */
251     auto check = std::equal(payload.begin(), payload.end(), output.begin());
252     EXPECT_EQ(true, check);
253 }
254 
255 TEST(CryptAlgo, AES_CBC_128_DecryptPayloadValidate)
256 {
257     /*
258      * Step-1 Encrypt the payload using OpenSSL EVP_aes_128_cbc()
259      * implementation
260      */
261 
262     std::vector<uint8_t> payload = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
263                                      13, 14, 15, 16};
264     payload.resize(payload.size() + 1);
265     payload.back() = 0;
266 
267     // Hardcoded Session Integrity Key
268     std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
269                                  13, 14, 15, 16};
270     EVP_CIPHER_CTX ctx;
271     EVP_CIPHER_CTX_init(&ctx);
272     cipher::crypt::key k2;
273     unsigned int mdLen = 0;
274     constexpr cipher::crypt::key const1 = { 0x02, 0x02, 0x02, 0x02, 0x02,
275                                             0x02, 0x02, 0x02, 0x02, 0x02,
276                                             0x02, 0x02, 0x02, 0x02, 0x02,
277                                             0x02, 0x02, 0x02, 0x02, 0x02
278                                           };
279     std::vector<uint8_t> output(
280             payload.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize);
281 
282     if (!RAND_bytes(output.data(),
283                     cipher::crypt::AlgoAES128::AESCBC128ConfHeader))
284     {
285         FAIL() << "RAND_bytes failed";
286     }
287 
288     // Generated K2 for the confidentiality algorithm with the additional key
289     // keyed with SIK.
290     if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(),
291              const1.size(), k2.data(), &mdLen) == NULL)
292     {
293         FAIL() << "Generating K2 for confidentiality algorithm failed";
294     }
295 
296     if (!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(),
297                             output.data()))
298     {
299         EVP_CIPHER_CTX_cleanup(&ctx);
300         FAIL() << "EVP_EncryptInit_ex failed for type AES-CBC-128";
301     }
302 
303     EVP_CIPHER_CTX_set_padding(&ctx, 0);
304     int outputLen = 0;
305 
306     if (!EVP_EncryptUpdate(&ctx,
307                            output.data() +
308                            cipher::crypt::AlgoAES128::AESCBC128ConfHeader,
309                            &outputLen,
310                            payload.data(),
311                            payload.size()))
312     {
313         EVP_CIPHER_CTX_cleanup(&ctx);
314         FAIL() << "EVP_EncryptUpdate failed";
315     }
316 
317     output.resize(cipher::crypt::AlgoAES128::AESCBC128ConfHeader + outputLen);
318     EVP_CIPHER_CTX_cleanup(&ctx);
319 
320     /*
321      * Step-2 Decrypt the encrypted payload using the implemented API for
322      * AES-CBC-128
323      */
324 
325     auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(sik);
326 
327     ASSERT_EQ(true, (cryptPtr != NULL));
328 
329     auto plain = cryptPtr->decryptPayload(output, 0, output.size());
330 
331     /*
332      * Step -3 Check if the plain payload matches with the decrypted one
333      */
334     auto check = std::equal(payload.begin(), payload.end(), plain.begin());
335     EXPECT_EQ(true, check);
336 }
337