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