xref: /openbmc/phosphor-net-ipmid/crypt_algo.cpp (revision 518eccea1365f9f6301e796382ed51c83bc5c2e7)
1d08b5235STom Joseph #include <openssl/evp.h>
2d08b5235STom Joseph #include <openssl/hmac.h>
3d08b5235STom Joseph #include <openssl/rand.h>
4*518ecceaSTom 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 
29*518ecceaSTom Joseph constexpr key AlgoAES128::const2;
30*518ecceaSTom Joseph 
31*518ecceaSTom Joseph constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1>
32*518ecceaSTom Joseph         AlgoAES128::confPadBytes;
33*518ecceaSTom Joseph 
34*518ecceaSTom Joseph buffer AlgoAES128::decryptPayload(const buffer& packet,
35*518ecceaSTom Joseph                                   const size_t sessHeaderLen,
36*518ecceaSTom Joseph                                   const size_t payloadLen) const
37*518ecceaSTom Joseph {
38*518ecceaSTom Joseph     auto plainPayload = decryptData(
39*518ecceaSTom Joseph             packet.data() + sessHeaderLen,
40*518ecceaSTom Joseph             packet.data() + sessHeaderLen + AESCBC128ConfHeader,
41*518ecceaSTom Joseph             payloadLen - AESCBC128ConfHeader);
42*518ecceaSTom Joseph 
43*518ecceaSTom Joseph     /*
44*518ecceaSTom Joseph      * The confidentiality pad length is the last byte in the payload, it would
45*518ecceaSTom Joseph      * tell the number of pad bytes in the payload. We added a condition, so
46*518ecceaSTom Joseph      * that buffer overrun does't happen.
47*518ecceaSTom Joseph      */
48*518ecceaSTom Joseph     size_t confPadLength = plainPayload.back();
49*518ecceaSTom Joseph     auto padLength = std::min(plainPayload.size() -1, confPadLength);
50*518ecceaSTom Joseph 
51*518ecceaSTom Joseph     auto plainPayloadLen = plainPayload.size() - padLength - 1;
52*518ecceaSTom Joseph 
53*518ecceaSTom Joseph     // Additional check if the confidentiality pad bytes are as expected
54*518ecceaSTom Joseph     if(!std::equal(plainPayload.begin() + plainPayloadLen,
55*518ecceaSTom Joseph                    plainPayload.begin() + plainPayloadLen + padLength,
56*518ecceaSTom Joseph                    confPadBytes.begin()))
57*518ecceaSTom Joseph     {
58*518ecceaSTom Joseph         throw std::runtime_error("Confidentiality pad bytes check failed");
59*518ecceaSTom Joseph     }
60*518ecceaSTom Joseph 
61*518ecceaSTom Joseph     plainPayload.resize(plainPayloadLen);
62*518ecceaSTom Joseph 
63*518ecceaSTom Joseph     return plainPayload;
64*518ecceaSTom Joseph }
65*518ecceaSTom Joseph 
66*518ecceaSTom Joseph buffer AlgoAES128::encryptPayload(buffer& payload) const
67*518ecceaSTom Joseph {
68*518ecceaSTom Joseph     auto payloadLen = payload.size();
69*518ecceaSTom Joseph 
70*518ecceaSTom Joseph     /*
71*518ecceaSTom Joseph      * The following logic calculates the number of padding bytes to be added to
72*518ecceaSTom Joseph      * the payload data. This would ensure that the length is a multiple of the
73*518ecceaSTom Joseph      * block size of algorithm being used. For the AES algorithm, the block size
74*518ecceaSTom Joseph      * is 16 bytes.
75*518ecceaSTom Joseph      */
76*518ecceaSTom Joseph     auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF);
77*518ecceaSTom Joseph 
78*518ecceaSTom Joseph     /*
79*518ecceaSTom Joseph      * The additional field is for the Confidentiality Pad Length field. For the
80*518ecceaSTom Joseph      * AES algorithm, this number will range from 0 to 15 bytes. This field is
81*518ecceaSTom Joseph      * mandatory.
82*518ecceaSTom Joseph      */
83*518ecceaSTom Joseph     payload.resize(payloadLen + paddingLen + 1);
84*518ecceaSTom Joseph 
85*518ecceaSTom Joseph     /*
86*518ecceaSTom Joseph      * If no Confidentiality Pad bytes are required, the Confidentiality Pad
87*518ecceaSTom Joseph      * Length field is set to 00h. If present, the value of the first byte of
88*518ecceaSTom Joseph      * Confidentiality Pad shall be one (01h) and all subsequent bytes shall
89*518ecceaSTom Joseph      * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc).
90*518ecceaSTom Joseph      */
91*518ecceaSTom Joseph     if (0 != paddingLen)
92*518ecceaSTom Joseph     {
93*518ecceaSTom Joseph         std::iota(payload.begin() + payloadLen,
94*518ecceaSTom Joseph                   payload.begin() + payloadLen + paddingLen,
95*518ecceaSTom Joseph                   1);
96*518ecceaSTom Joseph     }
97*518ecceaSTom Joseph 
98*518ecceaSTom Joseph     payload.back() = paddingLen;
99*518ecceaSTom Joseph 
100*518ecceaSTom Joseph     return encryptData(payload.data(), payload.size());
101*518ecceaSTom Joseph }
102*518ecceaSTom Joseph 
103*518ecceaSTom Joseph buffer AlgoAES128::decryptData(const uint8_t* iv,
104*518ecceaSTom Joseph                                const uint8_t* input,
105*518ecceaSTom Joseph                                const int inputLen) const
106*518ecceaSTom Joseph {
107*518ecceaSTom Joseph     EVP_CIPHER_CTX ctx;
108*518ecceaSTom Joseph 
109*518ecceaSTom Joseph     // Initializes Cipher context
110*518ecceaSTom Joseph     EVP_CIPHER_CTX_init(&ctx);
111*518ecceaSTom Joseph 
112*518ecceaSTom Joseph     /*
113*518ecceaSTom Joseph      * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type
114*518ecceaSTom Joseph      * AES-CBC-128. ctx must be initialized before calling this function. K2 is
115*518ecceaSTom Joseph      * the symmetric key used and iv is the initialization vector used.
116*518ecceaSTom Joseph      */
117*518ecceaSTom Joseph     if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(), iv))
118*518ecceaSTom Joseph     {
119*518ecceaSTom Joseph         EVP_CIPHER_CTX_cleanup(&ctx);
120*518ecceaSTom Joseph         throw std::runtime_error("EVP_DecryptInit_ex failed for type "
121*518ecceaSTom Joseph                                  "AES-CBC-128");
122*518ecceaSTom Joseph     }
123*518ecceaSTom Joseph 
124*518ecceaSTom Joseph     /*
125*518ecceaSTom Joseph      * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
126*518ecceaSTom Joseph      * parameter is zero then no padding is performed. This function always
127*518ecceaSTom Joseph      * returns 1.
128*518ecceaSTom Joseph      */
129*518ecceaSTom Joseph     EVP_CIPHER_CTX_set_padding(&ctx, 0);
130*518ecceaSTom Joseph 
131*518ecceaSTom Joseph     buffer output(inputLen + AESCBC128BlockSize);
132*518ecceaSTom Joseph 
133*518ecceaSTom Joseph     int outputLen = 0;
134*518ecceaSTom Joseph 
135*518ecceaSTom Joseph     /*
136*518ecceaSTom Joseph      * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any
137*518ecceaSTom Joseph      * more data and it will return an error if any data remains in a partial
138*518ecceaSTom Joseph      * block: that is if the total data length is not a multiple of the block
139*518ecceaSTom Joseph      * size. Since AES-CBC-128 encrypted payload format adds padding bytes and
140*518ecceaSTom Joseph      * ensures that payload is a multiple of block size, we are not making the
141*518ecceaSTom Joseph      * call to  EVP_DecryptFinal_ex().
142*518ecceaSTom Joseph      */
143*518ecceaSTom Joseph     if (!EVP_DecryptUpdate(&ctx, output.data(), &outputLen, input, inputLen))
144*518ecceaSTom Joseph     {
145*518ecceaSTom Joseph         EVP_CIPHER_CTX_cleanup(&ctx);
146*518ecceaSTom Joseph         throw std::runtime_error("EVP_DecryptUpdate failed");
147*518ecceaSTom Joseph     }
148*518ecceaSTom Joseph 
149*518ecceaSTom Joseph     output.resize(outputLen);
150*518ecceaSTom Joseph     EVP_CIPHER_CTX_cleanup(&ctx);
151*518ecceaSTom Joseph 
152*518ecceaSTom Joseph     return output;
153*518ecceaSTom Joseph }
154*518ecceaSTom Joseph 
155*518ecceaSTom Joseph buffer AlgoAES128::encryptData(const uint8_t* input, const int inputLen) const
156*518ecceaSTom Joseph {
157*518ecceaSTom Joseph     buffer output(inputLen + AESCBC128BlockSize);
158*518ecceaSTom Joseph 
159*518ecceaSTom Joseph     // Generate the initialization vector
160*518ecceaSTom Joseph     if (!RAND_bytes(output.data(), AESCBC128ConfHeader))
161*518ecceaSTom Joseph     {
162*518ecceaSTom Joseph         throw std::runtime_error("RAND_bytes failed");
163*518ecceaSTom Joseph     }
164*518ecceaSTom Joseph 
165*518ecceaSTom Joseph     EVP_CIPHER_CTX ctx;
166*518ecceaSTom Joseph 
167*518ecceaSTom Joseph     // Initializes Cipher context
168*518ecceaSTom Joseph     EVP_CIPHER_CTX_init(&ctx);
169*518ecceaSTom Joseph 
170*518ecceaSTom Joseph     /*
171*518ecceaSTom Joseph      * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type
172*518ecceaSTom Joseph      * AES-CBC-128. ctx must be initialized before calling this function. K2 is
173*518ecceaSTom Joseph      * the symmetric key used and iv is the initialization vector used.
174*518ecceaSTom Joseph      */
175*518ecceaSTom Joseph     if (!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(),
176*518ecceaSTom Joseph                             output.data()))
177*518ecceaSTom Joseph     {
178*518ecceaSTom Joseph         EVP_CIPHER_CTX_cleanup(&ctx);
179*518ecceaSTom Joseph         throw std::runtime_error("EVP_EncryptInit_ex failed for type "
180*518ecceaSTom Joseph                                  "AES-CBC-128");
181*518ecceaSTom Joseph     }
182*518ecceaSTom Joseph 
183*518ecceaSTom Joseph     /*
184*518ecceaSTom Joseph      * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
185*518ecceaSTom Joseph      * parameter is zero then no padding is performed. This function always
186*518ecceaSTom Joseph      * returns 1.
187*518ecceaSTom Joseph      */
188*518ecceaSTom Joseph     EVP_CIPHER_CTX_set_padding(&ctx, 0);
189*518ecceaSTom Joseph 
190*518ecceaSTom Joseph     int outputLen = 0;
191*518ecceaSTom Joseph 
192*518ecceaSTom Joseph     /*
193*518ecceaSTom Joseph      * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any
194*518ecceaSTom Joseph      * more data and it will return an error if any data remains in a partial
195*518ecceaSTom Joseph      * block: that is if the total data length is not a multiple of the block
196*518ecceaSTom Joseph      * size. Since we are adding padding bytes and ensures that payload is a
197*518ecceaSTom Joseph      * multiple of block size, we are not making the call to
198*518ecceaSTom Joseph      * EVP_DecryptFinal_ex()
199*518ecceaSTom Joseph      */
200*518ecceaSTom Joseph     if (!EVP_EncryptUpdate(&ctx,
201*518ecceaSTom Joseph                            output.data() + AESCBC128ConfHeader,
202*518ecceaSTom Joseph                            &outputLen,
203*518ecceaSTom Joseph                            input, inputLen))
204*518ecceaSTom Joseph     {
205*518ecceaSTom Joseph         EVP_CIPHER_CTX_cleanup(&ctx);
206*518ecceaSTom Joseph         throw std::runtime_error("EVP_EncryptUpdate failed for type "
207*518ecceaSTom Joseph                                  "AES-CBC-128");
208*518ecceaSTom Joseph     }
209*518ecceaSTom Joseph 
210*518ecceaSTom Joseph     output.resize(AESCBC128ConfHeader + outputLen);
211*518ecceaSTom Joseph     EVP_CIPHER_CTX_cleanup(&ctx);
212*518ecceaSTom Joseph 
213*518ecceaSTom Joseph     return output;
214*518ecceaSTom Joseph }
215*518ecceaSTom Joseph 
216d08b5235STom Joseph }// namespace crypt
217d08b5235STom Joseph 
218d08b5235STom Joseph }// namespace cipher
219d08b5235STom Joseph 
220d08b5235STom Joseph 
221