xref: /openbmc/phosphor-net-ipmid/crypt_algo.cpp (revision 2b1edef0b1e395591dcf751d7ccf45a85bb58d4c)
19e801a2bSVernon Mauery #include "crypt_algo.hpp"
29e801a2bSVernon Mauery 
39e801a2bSVernon Mauery #include "message_parsers.hpp"
49e801a2bSVernon Mauery 
5d08b5235STom Joseph #include <openssl/evp.h>
6d08b5235STom Joseph #include <openssl/hmac.h>
7d08b5235STom Joseph #include <openssl/rand.h>
89e801a2bSVernon Mauery 
9518ecceaSTom Joseph #include <algorithm>
10d08b5235STom Joseph #include <numeric>
11d08b5235STom Joseph 
12d08b5235STom Joseph namespace cipher
13d08b5235STom Joseph {
14d08b5235STom Joseph 
15d08b5235STom Joseph namespace crypt
16d08b5235STom Joseph {
17d08b5235STom Joseph 
18518ecceaSTom Joseph constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1>
19518ecceaSTom Joseph     AlgoAES128::confPadBytes;
20518ecceaSTom Joseph 
219e801a2bSVernon Mauery std::vector<uint8_t>
229e801a2bSVernon Mauery     AlgoAES128::decryptPayload(const std::vector<uint8_t>& packet,
23518ecceaSTom Joseph                                const size_t sessHeaderLen,
24518ecceaSTom Joseph                                const size_t payloadLen) const
25518ecceaSTom Joseph {
26*2b1edef0SZhikui Ren     // verify packet size minimal: sessHeaderLen + payloadLen
27*2b1edef0SZhikui Ren     // and payloadLen is more than AESCBC128ConfHeader
28*2b1edef0SZhikui Ren     if (packet.size() < (sessHeaderLen + payloadLen) ||
29*2b1edef0SZhikui Ren         payloadLen < AESCBC128ConfHeader)
30*2b1edef0SZhikui Ren     {
31*2b1edef0SZhikui Ren         throw std::runtime_error("Invalid data length");
32*2b1edef0SZhikui Ren     }
33*2b1edef0SZhikui Ren 
349e801a2bSVernon Mauery     auto plainPayload =
359e801a2bSVernon Mauery         decryptData(packet.data() + sessHeaderLen,
36518ecceaSTom Joseph                     packet.data() + sessHeaderLen + AESCBC128ConfHeader,
37518ecceaSTom Joseph                     payloadLen - AESCBC128ConfHeader);
38518ecceaSTom Joseph 
39518ecceaSTom Joseph     /*
40518ecceaSTom Joseph      * The confidentiality pad length is the last byte in the payload, it would
41518ecceaSTom Joseph      * tell the number of pad bytes in the payload. We added a condition, so
4262ec622eSGunnar Mills      * that buffer overrun doesn't happen.
43518ecceaSTom Joseph      */
44518ecceaSTom Joseph     size_t confPadLength = plainPayload.back();
45518ecceaSTom Joseph     auto padLength = std::min(plainPayload.size() - 1, confPadLength);
46518ecceaSTom Joseph 
47518ecceaSTom Joseph     auto plainPayloadLen = plainPayload.size() - padLength - 1;
48518ecceaSTom Joseph 
49518ecceaSTom Joseph     // Additional check if the confidentiality pad bytes are as expected
50518ecceaSTom Joseph     if (!std::equal(plainPayload.begin() + plainPayloadLen,
51518ecceaSTom Joseph                     plainPayload.begin() + plainPayloadLen + padLength,
52518ecceaSTom Joseph                     confPadBytes.begin()))
53518ecceaSTom Joseph     {
54518ecceaSTom Joseph         throw std::runtime_error("Confidentiality pad bytes check failed");
55518ecceaSTom Joseph     }
56518ecceaSTom Joseph 
57518ecceaSTom Joseph     plainPayload.resize(plainPayloadLen);
58518ecceaSTom Joseph 
59518ecceaSTom Joseph     return plainPayload;
60518ecceaSTom Joseph }
61518ecceaSTom Joseph 
629e801a2bSVernon Mauery std::vector<uint8_t>
639e801a2bSVernon Mauery     AlgoAES128::encryptPayload(std::vector<uint8_t>& payload) const
64518ecceaSTom Joseph {
65518ecceaSTom Joseph     auto payloadLen = payload.size();
66518ecceaSTom Joseph 
67518ecceaSTom Joseph     /*
68518ecceaSTom Joseph      * The following logic calculates the number of padding bytes to be added to
69518ecceaSTom Joseph      * the payload data. This would ensure that the length is a multiple of the
70518ecceaSTom Joseph      * block size of algorithm being used. For the AES algorithm, the block size
71518ecceaSTom Joseph      * is 16 bytes.
72518ecceaSTom Joseph      */
73518ecceaSTom Joseph     auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF);
74518ecceaSTom Joseph 
75518ecceaSTom Joseph     /*
76518ecceaSTom Joseph      * The additional field is for the Confidentiality Pad Length field. For the
77518ecceaSTom Joseph      * AES algorithm, this number will range from 0 to 15 bytes. This field is
78518ecceaSTom Joseph      * mandatory.
79518ecceaSTom Joseph      */
80518ecceaSTom Joseph     payload.resize(payloadLen + paddingLen + 1);
81518ecceaSTom Joseph 
82518ecceaSTom Joseph     /*
83518ecceaSTom Joseph      * If no Confidentiality Pad bytes are required, the Confidentiality Pad
84518ecceaSTom Joseph      * Length field is set to 00h. If present, the value of the first byte of
85518ecceaSTom Joseph      * Confidentiality Pad shall be one (01h) and all subsequent bytes shall
86518ecceaSTom Joseph      * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc).
87518ecceaSTom Joseph      */
88518ecceaSTom Joseph     if (0 != paddingLen)
89518ecceaSTom Joseph     {
90518ecceaSTom Joseph         std::iota(payload.begin() + payloadLen,
919e801a2bSVernon Mauery                   payload.begin() + payloadLen + paddingLen, 1);
92518ecceaSTom Joseph     }
93518ecceaSTom Joseph 
94518ecceaSTom Joseph     payload.back() = paddingLen;
95518ecceaSTom Joseph 
96518ecceaSTom Joseph     return encryptData(payload.data(), payload.size());
97518ecceaSTom Joseph }
98518ecceaSTom Joseph 
9970fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::decryptData(const uint8_t* iv,
100518ecceaSTom Joseph                                              const uint8_t* input,
101518ecceaSTom Joseph                                              const int inputLen) const
102518ecceaSTom Joseph {
103518ecceaSTom Joseph     // Initializes Cipher context
104584fa887SAdriana Kobylak     EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
105518ecceaSTom Joseph 
1069e801a2bSVernon Mauery     auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) { EVP_CIPHER_CTX_free(ctx); };
107f6c97e11STom Joseph 
1089e801a2bSVernon Mauery     std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> ctxPtr(ctx,
1099e801a2bSVernon Mauery                                                                   cleanupFunc);
110f6c97e11STom Joseph 
111518ecceaSTom Joseph     /*
112518ecceaSTom Joseph      * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type
113518ecceaSTom Joseph      * AES-CBC-128. ctx must be initialized before calling this function. K2 is
114518ecceaSTom Joseph      * the symmetric key used and iv is the initialization vector used.
115518ecceaSTom Joseph      */
116f6c97e11STom Joseph     if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
117f6c97e11STom Joseph                             iv))
118518ecceaSTom Joseph     {
119518ecceaSTom Joseph         throw std::runtime_error("EVP_DecryptInit_ex failed for type "
120518ecceaSTom Joseph                                  "AES-CBC-128");
121518ecceaSTom Joseph     }
122518ecceaSTom Joseph 
123518ecceaSTom Joseph     /*
124518ecceaSTom Joseph      * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
125518ecceaSTom Joseph      * parameter is zero then no padding is performed. This function always
126518ecceaSTom Joseph      * returns 1.
127518ecceaSTom Joseph      */
128f6c97e11STom Joseph     EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
129518ecceaSTom Joseph 
13070fd29cfSVernon Mauery     std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
131518ecceaSTom Joseph 
132518ecceaSTom Joseph     int outputLen = 0;
133518ecceaSTom Joseph 
134518ecceaSTom Joseph     /*
135518ecceaSTom Joseph      * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any
136518ecceaSTom Joseph      * more data and it will return an error if any data remains in a partial
137518ecceaSTom Joseph      * block: that is if the total data length is not a multiple of the block
138518ecceaSTom Joseph      * size. Since AES-CBC-128 encrypted payload format adds padding bytes and
139518ecceaSTom Joseph      * ensures that payload is a multiple of block size, we are not making the
140518ecceaSTom Joseph      * call to  EVP_DecryptFinal_ex().
141518ecceaSTom Joseph      */
142f6c97e11STom Joseph     if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input,
143f6c97e11STom Joseph                            inputLen))
144518ecceaSTom Joseph     {
145518ecceaSTom Joseph         throw std::runtime_error("EVP_DecryptUpdate failed");
146518ecceaSTom Joseph     }
147518ecceaSTom Joseph 
148518ecceaSTom Joseph     output.resize(outputLen);
149518ecceaSTom Joseph 
150518ecceaSTom Joseph     return output;
151518ecceaSTom Joseph }
152518ecceaSTom Joseph 
15370fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::encryptData(const uint8_t* input,
15470fd29cfSVernon Mauery                                              const int inputLen) const
155518ecceaSTom Joseph {
15670fd29cfSVernon Mauery     std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
157518ecceaSTom Joseph 
158518ecceaSTom Joseph     // Generate the initialization vector
159518ecceaSTom Joseph     if (!RAND_bytes(output.data(), AESCBC128ConfHeader))
160518ecceaSTom Joseph     {
161518ecceaSTom Joseph         throw std::runtime_error("RAND_bytes failed");
162518ecceaSTom Joseph     }
163518ecceaSTom Joseph 
164518ecceaSTom Joseph     // Initializes Cipher context
165584fa887SAdriana Kobylak     EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
166518ecceaSTom Joseph 
1679e801a2bSVernon Mauery     auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) { EVP_CIPHER_CTX_free(ctx); };
168f6c97e11STom Joseph 
1699e801a2bSVernon Mauery     std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> ctxPtr(ctx,
1709e801a2bSVernon Mauery                                                                   cleanupFunc);
171f6c97e11STom Joseph 
172518ecceaSTom Joseph     /*
173518ecceaSTom Joseph      * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type
174518ecceaSTom Joseph      * AES-CBC-128. ctx must be initialized before calling this function. K2 is
175518ecceaSTom Joseph      * the symmetric key used and iv is the initialization vector used.
176518ecceaSTom Joseph      */
177f6c97e11STom Joseph     if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
178518ecceaSTom Joseph                             output.data()))
179518ecceaSTom Joseph     {
180518ecceaSTom Joseph         throw std::runtime_error("EVP_EncryptInit_ex failed for type "
181518ecceaSTom Joseph                                  "AES-CBC-128");
182518ecceaSTom Joseph     }
183518ecceaSTom Joseph 
184518ecceaSTom Joseph     /*
185518ecceaSTom Joseph      * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
186518ecceaSTom Joseph      * parameter is zero then no padding is performed. This function always
187518ecceaSTom Joseph      * returns 1.
188518ecceaSTom Joseph      */
189f6c97e11STom Joseph     EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
190518ecceaSTom Joseph 
191518ecceaSTom Joseph     int outputLen = 0;
192518ecceaSTom Joseph 
193518ecceaSTom Joseph     /*
194518ecceaSTom Joseph      * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any
195518ecceaSTom Joseph      * more data and it will return an error if any data remains in a partial
196518ecceaSTom Joseph      * block: that is if the total data length is not a multiple of the block
197518ecceaSTom Joseph      * size. Since we are adding padding bytes and ensures that payload is a
198518ecceaSTom Joseph      * multiple of block size, we are not making the call to
199518ecceaSTom Joseph      * EVP_DecryptFinal_ex()
200518ecceaSTom Joseph      */
2019e801a2bSVernon Mauery     if (!EVP_EncryptUpdate(ctxPtr.get(), output.data() + AESCBC128ConfHeader,
2029e801a2bSVernon Mauery                            &outputLen, input, inputLen))
203518ecceaSTom Joseph     {
204518ecceaSTom Joseph         throw std::runtime_error("EVP_EncryptUpdate failed for type "
205518ecceaSTom Joseph                                  "AES-CBC-128");
206518ecceaSTom Joseph     }
207518ecceaSTom Joseph 
208518ecceaSTom Joseph     output.resize(AESCBC128ConfHeader + outputLen);
209518ecceaSTom Joseph 
210518ecceaSTom Joseph     return output;
211518ecceaSTom Joseph }
212518ecceaSTom Joseph 
213d08b5235STom Joseph } // namespace crypt
214d08b5235STom Joseph 
215d08b5235STom Joseph } // namespace cipher
216