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