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