xref: /openbmc/phosphor-net-ipmid/crypt_algo.cpp (revision 584fa8877dca6181c84a9ad687a814ec90fb0c6f)
1d08b5235STom Joseph #include <openssl/evp.h>
2d08b5235STom Joseph #include <openssl/hmac.h>
3d08b5235STom Joseph #include <openssl/rand.h>
4518ecceaSTom Joseph #include <algorithm>
5d08b5235STom Joseph #include <numeric>
6d08b5235STom Joseph #include "crypt_algo.hpp"
7d08b5235STom Joseph #include "message_parsers.hpp"
8d08b5235STom Joseph 
9d08b5235STom Joseph namespace cipher
10d08b5235STom Joseph {
11d08b5235STom Joseph 
12d08b5235STom Joseph namespace crypt
13d08b5235STom Joseph {
14d08b5235STom Joseph 
15518ecceaSTom Joseph constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1>
16518ecceaSTom Joseph         AlgoAES128::confPadBytes;
17518ecceaSTom Joseph 
1870fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::decryptPayload(
1970fd29cfSVernon Mauery         const std::vector<uint8_t>& packet,
20518ecceaSTom Joseph         const size_t sessHeaderLen,
21518ecceaSTom Joseph         const size_t payloadLen) const
22518ecceaSTom Joseph {
23518ecceaSTom Joseph     auto plainPayload = decryptData(
24518ecceaSTom Joseph             packet.data() + sessHeaderLen,
25518ecceaSTom Joseph             packet.data() + sessHeaderLen + AESCBC128ConfHeader,
26518ecceaSTom Joseph             payloadLen - AESCBC128ConfHeader);
27518ecceaSTom Joseph 
28518ecceaSTom Joseph     /*
29518ecceaSTom Joseph      * The confidentiality pad length is the last byte in the payload, it would
30518ecceaSTom Joseph      * tell the number of pad bytes in the payload. We added a condition, so
3162ec622eSGunnar Mills      * that buffer overrun doesn't happen.
32518ecceaSTom Joseph      */
33518ecceaSTom Joseph     size_t confPadLength = plainPayload.back();
34518ecceaSTom Joseph     auto padLength = std::min(plainPayload.size() -1, confPadLength);
35518ecceaSTom Joseph 
36518ecceaSTom Joseph     auto plainPayloadLen = plainPayload.size() - padLength - 1;
37518ecceaSTom Joseph 
38518ecceaSTom Joseph     // Additional check if the confidentiality pad bytes are as expected
39518ecceaSTom Joseph     if(!std::equal(plainPayload.begin() + plainPayloadLen,
40518ecceaSTom Joseph                    plainPayload.begin() + plainPayloadLen + padLength,
41518ecceaSTom Joseph                    confPadBytes.begin()))
42518ecceaSTom Joseph     {
43518ecceaSTom Joseph         throw std::runtime_error("Confidentiality pad bytes check failed");
44518ecceaSTom Joseph     }
45518ecceaSTom Joseph 
46518ecceaSTom Joseph     plainPayload.resize(plainPayloadLen);
47518ecceaSTom Joseph 
48518ecceaSTom Joseph     return plainPayload;
49518ecceaSTom Joseph }
50518ecceaSTom Joseph 
5170fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::encryptPayload(
5270fd29cfSVernon Mauery         std::vector<uint8_t>& payload) const
53518ecceaSTom Joseph {
54518ecceaSTom Joseph     auto payloadLen = payload.size();
55518ecceaSTom Joseph 
56518ecceaSTom Joseph     /*
57518ecceaSTom Joseph      * The following logic calculates the number of padding bytes to be added to
58518ecceaSTom Joseph      * the payload data. This would ensure that the length is a multiple of the
59518ecceaSTom Joseph      * block size of algorithm being used. For the AES algorithm, the block size
60518ecceaSTom Joseph      * is 16 bytes.
61518ecceaSTom Joseph      */
62518ecceaSTom Joseph     auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF);
63518ecceaSTom Joseph 
64518ecceaSTom Joseph     /*
65518ecceaSTom Joseph      * The additional field is for the Confidentiality Pad Length field. For the
66518ecceaSTom Joseph      * AES algorithm, this number will range from 0 to 15 bytes. This field is
67518ecceaSTom Joseph      * mandatory.
68518ecceaSTom Joseph      */
69518ecceaSTom Joseph     payload.resize(payloadLen + paddingLen + 1);
70518ecceaSTom Joseph 
71518ecceaSTom Joseph     /*
72518ecceaSTom Joseph      * If no Confidentiality Pad bytes are required, the Confidentiality Pad
73518ecceaSTom Joseph      * Length field is set to 00h. If present, the value of the first byte of
74518ecceaSTom Joseph      * Confidentiality Pad shall be one (01h) and all subsequent bytes shall
75518ecceaSTom Joseph      * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc).
76518ecceaSTom Joseph      */
77518ecceaSTom Joseph     if (0 != paddingLen)
78518ecceaSTom Joseph     {
79518ecceaSTom Joseph         std::iota(payload.begin() + payloadLen,
80518ecceaSTom Joseph                   payload.begin() + payloadLen + paddingLen,
81518ecceaSTom Joseph                   1);
82518ecceaSTom Joseph     }
83518ecceaSTom Joseph 
84518ecceaSTom Joseph     payload.back() = paddingLen;
85518ecceaSTom Joseph 
86518ecceaSTom Joseph     return encryptData(payload.data(), payload.size());
87518ecceaSTom Joseph }
88518ecceaSTom Joseph 
8970fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::decryptData(const uint8_t* iv,
90518ecceaSTom Joseph                                const uint8_t* input,
91518ecceaSTom Joseph                                const int inputLen) const
92518ecceaSTom Joseph {
93518ecceaSTom Joseph     // Initializes Cipher context
94*584fa887SAdriana Kobylak     EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
95518ecceaSTom Joseph 
96f6c97e11STom Joseph     auto cleanupFunc = [](EVP_CIPHER_CTX* ctx)
97f6c97e11STom Joseph     {
98*584fa887SAdriana Kobylak         EVP_CIPHER_CTX_free(ctx);
99f6c97e11STom Joseph     };
100f6c97e11STom Joseph 
101f6c97e11STom Joseph     std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)>
102*584fa887SAdriana Kobylak             ctxPtr(ctx, cleanupFunc);
103f6c97e11STom Joseph 
104518ecceaSTom Joseph     /*
105518ecceaSTom Joseph      * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type
106518ecceaSTom Joseph      * AES-CBC-128. ctx must be initialized before calling this function. K2 is
107518ecceaSTom Joseph      * the symmetric key used and iv is the initialization vector used.
108518ecceaSTom Joseph      */
109f6c97e11STom Joseph     if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
110f6c97e11STom Joseph                             iv))
111518ecceaSTom Joseph     {
112518ecceaSTom Joseph         throw std::runtime_error("EVP_DecryptInit_ex failed for type "
113518ecceaSTom Joseph                                  "AES-CBC-128");
114518ecceaSTom Joseph     }
115518ecceaSTom Joseph 
116518ecceaSTom Joseph     /*
117518ecceaSTom Joseph      * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
118518ecceaSTom Joseph      * parameter is zero then no padding is performed. This function always
119518ecceaSTom Joseph      * returns 1.
120518ecceaSTom Joseph      */
121f6c97e11STom Joseph     EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
122518ecceaSTom Joseph 
12370fd29cfSVernon Mauery     std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
124518ecceaSTom Joseph 
125518ecceaSTom Joseph     int outputLen = 0;
126518ecceaSTom Joseph 
127518ecceaSTom Joseph     /*
128518ecceaSTom Joseph      * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any
129518ecceaSTom Joseph      * more data and it will return an error if any data remains in a partial
130518ecceaSTom Joseph      * block: that is if the total data length is not a multiple of the block
131518ecceaSTom Joseph      * size. Since AES-CBC-128 encrypted payload format adds padding bytes and
132518ecceaSTom Joseph      * ensures that payload is a multiple of block size, we are not making the
133518ecceaSTom Joseph      * call to  EVP_DecryptFinal_ex().
134518ecceaSTom Joseph      */
135f6c97e11STom Joseph     if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input,
136f6c97e11STom Joseph                            inputLen))
137518ecceaSTom Joseph     {
138518ecceaSTom Joseph         throw std::runtime_error("EVP_DecryptUpdate failed");
139518ecceaSTom Joseph     }
140518ecceaSTom Joseph 
141518ecceaSTom Joseph     output.resize(outputLen);
142518ecceaSTom Joseph 
143518ecceaSTom Joseph     return output;
144518ecceaSTom Joseph }
145518ecceaSTom Joseph 
14670fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::encryptData(const uint8_t* input,
14770fd29cfSVernon Mauery         const int inputLen) const
148518ecceaSTom Joseph {
14970fd29cfSVernon Mauery     std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
150518ecceaSTom Joseph 
151518ecceaSTom Joseph     // Generate the initialization vector
152518ecceaSTom Joseph     if (!RAND_bytes(output.data(), AESCBC128ConfHeader))
153518ecceaSTom Joseph     {
154518ecceaSTom Joseph         throw std::runtime_error("RAND_bytes failed");
155518ecceaSTom Joseph     }
156518ecceaSTom Joseph 
157518ecceaSTom Joseph     // Initializes Cipher context
158*584fa887SAdriana Kobylak     EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
159518ecceaSTom Joseph 
160f6c97e11STom Joseph     auto cleanupFunc = [](EVP_CIPHER_CTX* ctx)
161f6c97e11STom Joseph     {
162*584fa887SAdriana Kobylak         EVP_CIPHER_CTX_free(ctx);
163f6c97e11STom Joseph     };
164f6c97e11STom Joseph 
165f6c97e11STom Joseph     std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)>
166*584fa887SAdriana Kobylak             ctxPtr(ctx, cleanupFunc);
167f6c97e11STom Joseph 
168518ecceaSTom Joseph     /*
169518ecceaSTom Joseph      * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type
170518ecceaSTom Joseph      * AES-CBC-128. ctx must be initialized before calling this function. K2 is
171518ecceaSTom Joseph      * the symmetric key used and iv is the initialization vector used.
172518ecceaSTom Joseph      */
173f6c97e11STom Joseph     if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
174518ecceaSTom Joseph                             output.data()))
175518ecceaSTom Joseph     {
176518ecceaSTom Joseph         throw std::runtime_error("EVP_EncryptInit_ex failed for type "
177518ecceaSTom Joseph                                  "AES-CBC-128");
178518ecceaSTom Joseph     }
179518ecceaSTom Joseph 
180518ecceaSTom Joseph     /*
181518ecceaSTom Joseph      * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
182518ecceaSTom Joseph      * parameter is zero then no padding is performed. This function always
183518ecceaSTom Joseph      * returns 1.
184518ecceaSTom Joseph      */
185f6c97e11STom Joseph     EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
186518ecceaSTom Joseph 
187518ecceaSTom Joseph     int outputLen = 0;
188518ecceaSTom Joseph 
189518ecceaSTom Joseph     /*
190518ecceaSTom Joseph      * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any
191518ecceaSTom Joseph      * more data and it will return an error if any data remains in a partial
192518ecceaSTom Joseph      * block: that is if the total data length is not a multiple of the block
193518ecceaSTom Joseph      * size. Since we are adding padding bytes and ensures that payload is a
194518ecceaSTom Joseph      * multiple of block size, we are not making the call to
195518ecceaSTom Joseph      * EVP_DecryptFinal_ex()
196518ecceaSTom Joseph      */
197f6c97e11STom Joseph     if (!EVP_EncryptUpdate(ctxPtr.get(),
198518ecceaSTom Joseph                            output.data() + AESCBC128ConfHeader,
199518ecceaSTom Joseph                            &outputLen,
200518ecceaSTom Joseph                            input, inputLen))
201518ecceaSTom Joseph     {
202518ecceaSTom Joseph         throw std::runtime_error("EVP_EncryptUpdate failed for type "
203518ecceaSTom Joseph                                  "AES-CBC-128");
204518ecceaSTom Joseph     }
205518ecceaSTom Joseph 
206518ecceaSTom Joseph     output.resize(AESCBC128ConfHeader + outputLen);
207518ecceaSTom Joseph 
208518ecceaSTom Joseph     return output;
209518ecceaSTom Joseph }
210518ecceaSTom Joseph 
211d08b5235STom Joseph }// namespace crypt
212d08b5235STom Joseph 
213d08b5235STom Joseph }// namespace cipher
214d08b5235STom Joseph 
215d08b5235STom Joseph 
216