xref: /openbmc/phosphor-net-ipmid/crypt_algo.cpp (revision f6c97e11c33babc49c44fde38f997640a182610b)
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 
15d08b5235STom Joseph Interface::Interface(const buffer& sik, const key& addKey)
16d08b5235STom Joseph {
17d08b5235STom Joseph     unsigned int mdLen = 0;
18d08b5235STom Joseph 
19d08b5235STom Joseph     // Generated K2 for the confidentiality algorithm with the additional key
20d08b5235STom Joseph     // keyed with SIK.
21d08b5235STom Joseph     if (HMAC(EVP_sha1(), sik.data(), sik.size(), addKey.data(),
22d08b5235STom Joseph              addKey.size(), k2.data(), &mdLen) == NULL)
23d08b5235STom Joseph     {
24d08b5235STom Joseph         throw std::runtime_error("Generating K2 for confidentiality algorithm"
25d08b5235STom Joseph                                  "failed");
26d08b5235STom Joseph     }
27d08b5235STom Joseph }
28d08b5235STom Joseph 
29518ecceaSTom Joseph constexpr key AlgoAES128::const2;
30518ecceaSTom Joseph 
31518ecceaSTom Joseph constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1>
32518ecceaSTom Joseph         AlgoAES128::confPadBytes;
33518ecceaSTom Joseph 
34518ecceaSTom Joseph buffer AlgoAES128::decryptPayload(const buffer& packet,
35518ecceaSTom Joseph                                   const size_t sessHeaderLen,
36518ecceaSTom Joseph                                   const size_t payloadLen) const
37518ecceaSTom Joseph {
38518ecceaSTom Joseph     auto plainPayload = decryptData(
39518ecceaSTom Joseph             packet.data() + sessHeaderLen,
40518ecceaSTom Joseph             packet.data() + sessHeaderLen + AESCBC128ConfHeader,
41518ecceaSTom Joseph             payloadLen - AESCBC128ConfHeader);
42518ecceaSTom Joseph 
43518ecceaSTom Joseph     /*
44518ecceaSTom Joseph      * The confidentiality pad length is the last byte in the payload, it would
45518ecceaSTom Joseph      * tell the number of pad bytes in the payload. We added a condition, so
46518ecceaSTom Joseph      * that buffer overrun does't happen.
47518ecceaSTom Joseph      */
48518ecceaSTom Joseph     size_t confPadLength = plainPayload.back();
49518ecceaSTom Joseph     auto padLength = std::min(plainPayload.size() -1, confPadLength);
50518ecceaSTom Joseph 
51518ecceaSTom Joseph     auto plainPayloadLen = plainPayload.size() - padLength - 1;
52518ecceaSTom Joseph 
53518ecceaSTom Joseph     // Additional check if the confidentiality pad bytes are as expected
54518ecceaSTom Joseph     if(!std::equal(plainPayload.begin() + plainPayloadLen,
55518ecceaSTom Joseph                    plainPayload.begin() + plainPayloadLen + padLength,
56518ecceaSTom Joseph                    confPadBytes.begin()))
57518ecceaSTom Joseph     {
58518ecceaSTom Joseph         throw std::runtime_error("Confidentiality pad bytes check failed");
59518ecceaSTom Joseph     }
60518ecceaSTom Joseph 
61518ecceaSTom Joseph     plainPayload.resize(plainPayloadLen);
62518ecceaSTom Joseph 
63518ecceaSTom Joseph     return plainPayload;
64518ecceaSTom Joseph }
65518ecceaSTom Joseph 
66518ecceaSTom Joseph buffer AlgoAES128::encryptPayload(buffer& payload) const
67518ecceaSTom Joseph {
68518ecceaSTom Joseph     auto payloadLen = payload.size();
69518ecceaSTom Joseph 
70518ecceaSTom Joseph     /*
71518ecceaSTom Joseph      * The following logic calculates the number of padding bytes to be added to
72518ecceaSTom Joseph      * the payload data. This would ensure that the length is a multiple of the
73518ecceaSTom Joseph      * block size of algorithm being used. For the AES algorithm, the block size
74518ecceaSTom Joseph      * is 16 bytes.
75518ecceaSTom Joseph      */
76518ecceaSTom Joseph     auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF);
77518ecceaSTom Joseph 
78518ecceaSTom Joseph     /*
79518ecceaSTom Joseph      * The additional field is for the Confidentiality Pad Length field. For the
80518ecceaSTom Joseph      * AES algorithm, this number will range from 0 to 15 bytes. This field is
81518ecceaSTom Joseph      * mandatory.
82518ecceaSTom Joseph      */
83518ecceaSTom Joseph     payload.resize(payloadLen + paddingLen + 1);
84518ecceaSTom Joseph 
85518ecceaSTom Joseph     /*
86518ecceaSTom Joseph      * If no Confidentiality Pad bytes are required, the Confidentiality Pad
87518ecceaSTom Joseph      * Length field is set to 00h. If present, the value of the first byte of
88518ecceaSTom Joseph      * Confidentiality Pad shall be one (01h) and all subsequent bytes shall
89518ecceaSTom Joseph      * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc).
90518ecceaSTom Joseph      */
91518ecceaSTom Joseph     if (0 != paddingLen)
92518ecceaSTom Joseph     {
93518ecceaSTom Joseph         std::iota(payload.begin() + payloadLen,
94518ecceaSTom Joseph                   payload.begin() + payloadLen + paddingLen,
95518ecceaSTom Joseph                   1);
96518ecceaSTom Joseph     }
97518ecceaSTom Joseph 
98518ecceaSTom Joseph     payload.back() = paddingLen;
99518ecceaSTom Joseph 
100518ecceaSTom Joseph     return encryptData(payload.data(), payload.size());
101518ecceaSTom Joseph }
102518ecceaSTom Joseph 
103518ecceaSTom Joseph buffer AlgoAES128::decryptData(const uint8_t* iv,
104518ecceaSTom Joseph                                const uint8_t* input,
105518ecceaSTom Joseph                                const int inputLen) const
106518ecceaSTom Joseph {
107518ecceaSTom Joseph     EVP_CIPHER_CTX ctx;
108518ecceaSTom Joseph 
109518ecceaSTom Joseph     // Initializes Cipher context
110518ecceaSTom Joseph     EVP_CIPHER_CTX_init(&ctx);
111518ecceaSTom Joseph 
112*f6c97e11STom Joseph     auto cleanupFunc = [](EVP_CIPHER_CTX* ctx)
113*f6c97e11STom Joseph     {
114*f6c97e11STom Joseph         EVP_CIPHER_CTX_cleanup(ctx);
115*f6c97e11STom Joseph     };
116*f6c97e11STom Joseph 
117*f6c97e11STom Joseph     std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)>
118*f6c97e11STom Joseph             ctxPtr(&ctx, cleanupFunc);
119*f6c97e11STom Joseph 
120518ecceaSTom Joseph     /*
121518ecceaSTom Joseph      * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type
122518ecceaSTom Joseph      * AES-CBC-128. ctx must be initialized before calling this function. K2 is
123518ecceaSTom Joseph      * the symmetric key used and iv is the initialization vector used.
124518ecceaSTom Joseph      */
125*f6c97e11STom Joseph     if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
126*f6c97e11STom Joseph                             iv))
127518ecceaSTom Joseph     {
128518ecceaSTom Joseph         throw std::runtime_error("EVP_DecryptInit_ex failed for type "
129518ecceaSTom Joseph                                  "AES-CBC-128");
130518ecceaSTom Joseph     }
131518ecceaSTom Joseph 
132518ecceaSTom Joseph     /*
133518ecceaSTom Joseph      * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
134518ecceaSTom Joseph      * parameter is zero then no padding is performed. This function always
135518ecceaSTom Joseph      * returns 1.
136518ecceaSTom Joseph      */
137*f6c97e11STom Joseph     EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
138518ecceaSTom Joseph 
139518ecceaSTom Joseph     buffer output(inputLen + AESCBC128BlockSize);
140518ecceaSTom Joseph 
141518ecceaSTom Joseph     int outputLen = 0;
142518ecceaSTom Joseph 
143518ecceaSTom Joseph     /*
144518ecceaSTom Joseph      * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any
145518ecceaSTom Joseph      * more data and it will return an error if any data remains in a partial
146518ecceaSTom Joseph      * block: that is if the total data length is not a multiple of the block
147518ecceaSTom Joseph      * size. Since AES-CBC-128 encrypted payload format adds padding bytes and
148518ecceaSTom Joseph      * ensures that payload is a multiple of block size, we are not making the
149518ecceaSTom Joseph      * call to  EVP_DecryptFinal_ex().
150518ecceaSTom Joseph      */
151*f6c97e11STom Joseph     if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input,
152*f6c97e11STom Joseph                            inputLen))
153518ecceaSTom Joseph     {
154518ecceaSTom Joseph         throw std::runtime_error("EVP_DecryptUpdate failed");
155518ecceaSTom Joseph     }
156518ecceaSTom Joseph 
157518ecceaSTom Joseph     output.resize(outputLen);
158518ecceaSTom Joseph 
159518ecceaSTom Joseph     return output;
160518ecceaSTom Joseph }
161518ecceaSTom Joseph 
162518ecceaSTom Joseph buffer AlgoAES128::encryptData(const uint8_t* input, const int inputLen) const
163518ecceaSTom Joseph {
164518ecceaSTom Joseph     buffer output(inputLen + AESCBC128BlockSize);
165518ecceaSTom Joseph 
166518ecceaSTom Joseph     // Generate the initialization vector
167518ecceaSTom Joseph     if (!RAND_bytes(output.data(), AESCBC128ConfHeader))
168518ecceaSTom Joseph     {
169518ecceaSTom Joseph         throw std::runtime_error("RAND_bytes failed");
170518ecceaSTom Joseph     }
171518ecceaSTom Joseph 
172518ecceaSTom Joseph     EVP_CIPHER_CTX ctx;
173518ecceaSTom Joseph 
174518ecceaSTom Joseph     // Initializes Cipher context
175518ecceaSTom Joseph     EVP_CIPHER_CTX_init(&ctx);
176518ecceaSTom Joseph 
177*f6c97e11STom Joseph     auto cleanupFunc = [](EVP_CIPHER_CTX* ctx)
178*f6c97e11STom Joseph     {
179*f6c97e11STom Joseph         EVP_CIPHER_CTX_cleanup(ctx);
180*f6c97e11STom Joseph     };
181*f6c97e11STom Joseph 
182*f6c97e11STom Joseph     std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)>
183*f6c97e11STom Joseph             ctxPtr(&ctx, cleanupFunc);
184*f6c97e11STom Joseph 
185518ecceaSTom Joseph     /*
186518ecceaSTom Joseph      * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type
187518ecceaSTom Joseph      * AES-CBC-128. ctx must be initialized before calling this function. K2 is
188518ecceaSTom Joseph      * the symmetric key used and iv is the initialization vector used.
189518ecceaSTom Joseph      */
190*f6c97e11STom Joseph     if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
191518ecceaSTom Joseph                             output.data()))
192518ecceaSTom Joseph     {
193518ecceaSTom Joseph         throw std::runtime_error("EVP_EncryptInit_ex failed for type "
194518ecceaSTom Joseph                                  "AES-CBC-128");
195518ecceaSTom Joseph     }
196518ecceaSTom Joseph 
197518ecceaSTom Joseph     /*
198518ecceaSTom Joseph      * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
199518ecceaSTom Joseph      * parameter is zero then no padding is performed. This function always
200518ecceaSTom Joseph      * returns 1.
201518ecceaSTom Joseph      */
202*f6c97e11STom Joseph     EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
203518ecceaSTom Joseph 
204518ecceaSTom Joseph     int outputLen = 0;
205518ecceaSTom Joseph 
206518ecceaSTom Joseph     /*
207518ecceaSTom Joseph      * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any
208518ecceaSTom Joseph      * more data and it will return an error if any data remains in a partial
209518ecceaSTom Joseph      * block: that is if the total data length is not a multiple of the block
210518ecceaSTom Joseph      * size. Since we are adding padding bytes and ensures that payload is a
211518ecceaSTom Joseph      * multiple of block size, we are not making the call to
212518ecceaSTom Joseph      * EVP_DecryptFinal_ex()
213518ecceaSTom Joseph      */
214*f6c97e11STom Joseph     if (!EVP_EncryptUpdate(ctxPtr.get(),
215518ecceaSTom Joseph                            output.data() + AESCBC128ConfHeader,
216518ecceaSTom Joseph                            &outputLen,
217518ecceaSTom Joseph                            input, inputLen))
218518ecceaSTom Joseph     {
219518ecceaSTom Joseph         throw std::runtime_error("EVP_EncryptUpdate failed for type "
220518ecceaSTom Joseph                                  "AES-CBC-128");
221518ecceaSTom Joseph     }
222518ecceaSTom Joseph 
223518ecceaSTom Joseph     output.resize(AESCBC128ConfHeader + outputLen);
224518ecceaSTom Joseph 
225518ecceaSTom Joseph     return output;
226518ecceaSTom Joseph }
227518ecceaSTom Joseph 
228d08b5235STom Joseph }// namespace crypt
229d08b5235STom Joseph 
230d08b5235STom Joseph }// namespace cipher
231d08b5235STom Joseph 
232d08b5235STom Joseph 
233