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(CryptAlgo, AES_CBC_128_EncryptPayloadValidate)
184 {
185     /*
186      * Step-1 Generate the encrypted data using the implemented API for
187      * AES-CBC-128
188      */
189     std::vector<uint8_t> payload = { 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 };
194 
195     std::vector<uint8_t> k2(SHA_DIGEST_LENGTH);
196     unsigned int mdLen = 0;
197     constexpr rmcp::Const_n const1 = { 0x02, 0x02, 0x02, 0x02, 0x02,
198                                        0x02, 0x02, 0x02, 0x02, 0x02,
199                                        0x02, 0x02, 0x02, 0x02, 0x02,
200                                        0x02, 0x02, 0x02, 0x02, 0x02
201                                      };
202 
203     // Generated K2 for the confidentiality algorithm with the additional key
204     // keyed with SIK.
205     if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(),
206              const1.size(), k2.data(), &mdLen) == NULL)
207     {
208         FAIL() << "Generating K2 for confidentiality algorithm failed";
209     }
210 
211     auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(k2);
212 
213     ASSERT_EQ(true, (cryptPtr != NULL));
214 
215     auto cipher = cryptPtr->encryptPayload(payload);
216 
217     /*
218      * Step-2 Decrypt the encrypted payload using OpenSSL EVP_aes_128_cbc()
219      * implementation
220      */
221 
222     EVP_CIPHER_CTX ctx;
223     EVP_CIPHER_CTX_init(&ctx);
224     if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(),
225                             cipher.data()))
226     {
227         EVP_CIPHER_CTX_cleanup(&ctx);
228         FAIL() << "EVP_DecryptInit_ex failed for type AES-CBC-128";
229     }
230 
231     EVP_CIPHER_CTX_set_padding(&ctx, 0);
232     std::vector<uint8_t> output(
233             cipher.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize);
234     int outputLen = 0;
235 
236     if (!EVP_DecryptUpdate(&ctx, output.data(), &outputLen,
237                            cipher.data() +
238                            cipher::crypt::AlgoAES128::AESCBC128ConfHeader,
239                            cipher.size() -
240                            cipher::crypt::AlgoAES128::AESCBC128ConfHeader))
241     {
242         EVP_CIPHER_CTX_cleanup(&ctx);
243         FAIL() << "EVP_DecryptUpdate failed";
244     }
245 
246     output.resize(outputLen);
247     EVP_CIPHER_CTX_cleanup(&ctx);
248 
249     /*
250      * Step -3 Check if the plain payload matches with the decrypted one
251      */
252     auto check = std::equal(payload.begin(), payload.end(), output.begin());
253     EXPECT_EQ(true, check);
254 }
255 
256 TEST(CryptAlgo, AES_CBC_128_DecryptPayloadValidate)
257 {
258     /*
259      * Step-1 Encrypt the payload using OpenSSL EVP_aes_128_cbc()
260      * implementation
261      */
262 
263     std::vector<uint8_t> payload = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
264                                      13, 14, 15, 16};
265     payload.resize(payload.size() + 1);
266     payload.back() = 0;
267 
268     // Hardcoded Session Integrity Key
269     std::vector<uint8_t> sik = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
270                                  13, 14, 15, 16, 17, 18, 19, 20 };
271     EVP_CIPHER_CTX ctx;
272     EVP_CIPHER_CTX_init(&ctx);
273     std::vector<uint8_t> k2(SHA_DIGEST_LENGTH);
274     unsigned int mdLen = 0;
275     constexpr rmcp::Const_n const1 = { 0x02, 0x02, 0x02, 0x02, 0x02,
276                                        0x02, 0x02, 0x02, 0x02, 0x02,
277                                        0x02, 0x02, 0x02, 0x02, 0x02,
278                                        0x02, 0x02, 0x02, 0x02, 0x02
279                                      };
280     std::vector<uint8_t> output(
281             payload.size() + cipher::crypt::AlgoAES128::AESCBC128BlockSize);
282 
283     if (!RAND_bytes(output.data(),
284                     cipher::crypt::AlgoAES128::AESCBC128ConfHeader))
285     {
286         FAIL() << "RAND_bytes failed";
287     }
288 
289     // Generated K2 for the confidentiality algorithm with the additional key
290     // keyed with SIK.
291     if (HMAC(EVP_sha1(), sik.data(), sik.size(), const1.data(),
292              const1.size(), k2.data(), &mdLen) == NULL)
293     {
294         FAIL() << "Generating K2 for confidentiality algorithm failed";
295     }
296 
297     if (!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(),
298                             output.data()))
299     {
300         EVP_CIPHER_CTX_cleanup(&ctx);
301         FAIL() << "EVP_EncryptInit_ex failed for type AES-CBC-128";
302     }
303 
304     EVP_CIPHER_CTX_set_padding(&ctx, 0);
305     int outputLen = 0;
306 
307     if (!EVP_EncryptUpdate(&ctx,
308                            output.data() +
309                            cipher::crypt::AlgoAES128::AESCBC128ConfHeader,
310                            &outputLen,
311                            payload.data(),
312                            payload.size()))
313     {
314         EVP_CIPHER_CTX_cleanup(&ctx);
315         FAIL() << "EVP_EncryptUpdate failed";
316     }
317 
318     output.resize(cipher::crypt::AlgoAES128::AESCBC128ConfHeader + outputLen);
319     EVP_CIPHER_CTX_cleanup(&ctx);
320 
321     /*
322      * Step-2 Decrypt the encrypted payload using the implemented API for
323      * AES-CBC-128
324      */
325 
326     auto cryptPtr = std::make_unique<cipher::crypt::AlgoAES128>(k2);
327 
328     ASSERT_EQ(true, (cryptPtr != NULL));
329 
330     auto plain = cryptPtr->decryptPayload(output, 0, output.size());
331 
332     /*
333      * Step -3 Check if the plain payload matches with the decrypted one
334      */
335     auto check = std::equal(payload.begin(), payload.end(), plain.begin());
336     EXPECT_EQ(true, check);
337 }
338