xref: /openbmc/phosphor-net-ipmid/test/cipher.cpp (revision 8425624a9046f5a853e8596cc74441e622028494)
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  
TEST(IntegrityAlgo,HMAC_SHA1_96_GenerateIntegrityDataCheck)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  
TEST(IntegrityAlgo,HMAC_SHA1_96_VerifyIntegrityDataPass)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  
TEST(IntegrityAlgo,HMAC_SHA1_96_VerifyIntegrityDataFail)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  
TEST(IntegrityAlgo,HMAC_SHA256_128_GenerateIntegrityDataCheck)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  
TEST(IntegrityAlgo,HMAC_SHA256_128_VerifyIntegrityDataPass)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  
TEST(IntegrityAlgo,HMAC_SHA256_128_VerifyIntegrityDataFail)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  
TEST(CryptAlgo,AES_CBC_128_EncryptPayloadValidate)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(
388          cipher.size() + 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  
TEST(CryptAlgo,AES_CBC_128_DecryptPayloadValidate)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(
433          payload.size() + 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