blob: 75eea15eddc89da2e4be9ddbc39f5dbb742ed9d5 [file] [log] [blame]
Vernon Mauery9e801a22018-10-12 13:20:49 -07001#include "crypt_algo.hpp"
2
3#include "message_parsers.hpp"
4
Tom Josephd08b5232017-01-24 18:15:39 +05305#include <openssl/evp.h>
6#include <openssl/hmac.h>
7#include <openssl/rand.h>
Vernon Mauery9e801a22018-10-12 13:20:49 -07008
Tom Joseph518ecce2017-01-25 14:34:19 +05309#include <algorithm>
Tom Josephd08b5232017-01-24 18:15:39 +053010#include <numeric>
Tom Josephd08b5232017-01-24 18:15:39 +053011
12namespace cipher
13{
14
15namespace crypt
16{
17
Tom Joseph518ecce2017-01-25 14:34:19 +053018constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1>
Vernon Mauery9e801a22018-10-12 13:20:49 -070019 AlgoAES128::confPadBytes;
Tom Joseph518ecce2017-01-25 14:34:19 +053020
Patrick Williams84256242024-08-16 15:20:21 -040021std::vector<uint8_t> AlgoAES128::decryptPayload(
22 const std::vector<uint8_t>& packet, const size_t sessHeaderLen,
23 const size_t payloadLen) const
Tom Joseph518ecce2017-01-25 14:34:19 +053024{
Zhikui Ren2b1edef2020-07-24 14:32:13 -070025 // verify packet size minimal: sessHeaderLen + payloadLen
26 // and payloadLen is more than AESCBC128ConfHeader
27 if (packet.size() < (sessHeaderLen + payloadLen) ||
28 payloadLen < AESCBC128ConfHeader)
29 {
30 throw std::runtime_error("Invalid data length");
31 }
32
Vernon Mauery9e801a22018-10-12 13:20:49 -070033 auto plainPayload =
34 decryptData(packet.data() + sessHeaderLen,
35 packet.data() + sessHeaderLen + AESCBC128ConfHeader,
36 payloadLen - AESCBC128ConfHeader);
Tom Joseph518ecce2017-01-25 14:34:19 +053037
38 /*
39 * The confidentiality pad length is the last byte in the payload, it would
40 * tell the number of pad bytes in the payload. We added a condition, so
Gunnar Mills62ec6222018-04-08 16:28:23 -050041 * that buffer overrun doesn't happen.
Tom Joseph518ecce2017-01-25 14:34:19 +053042 */
43 size_t confPadLength = plainPayload.back();
Vernon Mauery9e801a22018-10-12 13:20:49 -070044 auto padLength = std::min(plainPayload.size() - 1, confPadLength);
Tom Joseph518ecce2017-01-25 14:34:19 +053045
46 auto plainPayloadLen = plainPayload.size() - padLength - 1;
47
48 // Additional check if the confidentiality pad bytes are as expected
Vernon Mauery9e801a22018-10-12 13:20:49 -070049 if (!std::equal(plainPayload.begin() + plainPayloadLen,
50 plainPayload.begin() + plainPayloadLen + padLength,
51 confPadBytes.begin()))
Tom Joseph518ecce2017-01-25 14:34:19 +053052 {
53 throw std::runtime_error("Confidentiality pad bytes check failed");
54 }
55
56 plainPayload.resize(plainPayloadLen);
57
58 return plainPayload;
59}
60
Vernon Mauery9e801a22018-10-12 13:20:49 -070061std::vector<uint8_t>
62 AlgoAES128::encryptPayload(std::vector<uint8_t>& payload) const
Tom Joseph518ecce2017-01-25 14:34:19 +053063{
64 auto payloadLen = payload.size();
65
66 /*
67 * The following logic calculates the number of padding bytes to be added to
68 * the payload data. This would ensure that the length is a multiple of the
69 * block size of algorithm being used. For the AES algorithm, the block size
70 * is 16 bytes.
71 */
72 auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF);
73
74 /*
75 * The additional field is for the Confidentiality Pad Length field. For the
76 * AES algorithm, this number will range from 0 to 15 bytes. This field is
77 * mandatory.
78 */
79 payload.resize(payloadLen + paddingLen + 1);
80
81 /*
82 * If no Confidentiality Pad bytes are required, the Confidentiality Pad
83 * Length field is set to 00h. If present, the value of the first byte of
84 * Confidentiality Pad shall be one (01h) and all subsequent bytes shall
85 * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc).
86 */
87 if (0 != paddingLen)
88 {
89 std::iota(payload.begin() + payloadLen,
Vernon Mauery9e801a22018-10-12 13:20:49 -070090 payload.begin() + payloadLen + paddingLen, 1);
Tom Joseph518ecce2017-01-25 14:34:19 +053091 }
92
93 payload.back() = paddingLen;
94
95 return encryptData(payload.data(), payload.size());
96}
97
Patrick Williams84256242024-08-16 15:20:21 -040098std::vector<uint8_t> AlgoAES128::decryptData(
99 const uint8_t* iv, const uint8_t* input, const int inputLen) const
Tom Joseph518ecce2017-01-25 14:34:19 +0530100{
Tom Joseph518ecce2017-01-25 14:34:19 +0530101 // Initializes Cipher context
Adriana Kobylak584fa882018-09-06 15:52:05 -0500102 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
Tom Joseph518ecce2017-01-25 14:34:19 +0530103
Vernon Mauery9e801a22018-10-12 13:20:49 -0700104 auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) { EVP_CIPHER_CTX_free(ctx); };
Tom Josephf6c97e12017-08-03 00:23:57 +0530105
Patrick Williams84256242024-08-16 15:20:21 -0400106 std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> ctxPtr(
107 ctx, cleanupFunc);
Tom Josephf6c97e12017-08-03 00:23:57 +0530108
Tom Joseph518ecce2017-01-25 14:34:19 +0530109 /*
110 * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type
111 * AES-CBC-128. ctx must be initialized before calling this function. K2 is
112 * the symmetric key used and iv is the initialization vector used.
113 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530114 if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
115 iv))
Tom Joseph518ecce2017-01-25 14:34:19 +0530116 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530117 throw std::runtime_error("EVP_DecryptInit_ex failed for type "
118 "AES-CBC-128");
119 }
120
121 /*
122 * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
123 * parameter is zero then no padding is performed. This function always
124 * returns 1.
125 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530126 EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
Tom Joseph518ecce2017-01-25 14:34:19 +0530127
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800128 std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
Tom Joseph518ecce2017-01-25 14:34:19 +0530129
130 int outputLen = 0;
131
132 /*
133 * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any
134 * more data and it will return an error if any data remains in a partial
135 * block: that is if the total data length is not a multiple of the block
136 * size. Since AES-CBC-128 encrypted payload format adds padding bytes and
137 * ensures that payload is a multiple of block size, we are not making the
138 * call to EVP_DecryptFinal_ex().
139 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530140 if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input,
141 inputLen))
Tom Joseph518ecce2017-01-25 14:34:19 +0530142 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530143 throw std::runtime_error("EVP_DecryptUpdate failed");
144 }
145
146 output.resize(outputLen);
Tom Joseph518ecce2017-01-25 14:34:19 +0530147
148 return output;
149}
150
Patrick Williams84256242024-08-16 15:20:21 -0400151std::vector<uint8_t>
152 AlgoAES128::encryptData(const uint8_t* input, const int inputLen) const
Tom Joseph518ecce2017-01-25 14:34:19 +0530153{
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800154 std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
Tom Joseph518ecce2017-01-25 14:34:19 +0530155
156 // Generate the initialization vector
157 if (!RAND_bytes(output.data(), AESCBC128ConfHeader))
158 {
159 throw std::runtime_error("RAND_bytes failed");
160 }
161
Tom Joseph518ecce2017-01-25 14:34:19 +0530162 // Initializes Cipher context
Adriana Kobylak584fa882018-09-06 15:52:05 -0500163 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
Tom Joseph518ecce2017-01-25 14:34:19 +0530164
Vernon Mauery9e801a22018-10-12 13:20:49 -0700165 auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) { EVP_CIPHER_CTX_free(ctx); };
Tom Josephf6c97e12017-08-03 00:23:57 +0530166
Patrick Williams84256242024-08-16 15:20:21 -0400167 std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> ctxPtr(
168 ctx, cleanupFunc);
Tom Josephf6c97e12017-08-03 00:23:57 +0530169
Tom Joseph518ecce2017-01-25 14:34:19 +0530170 /*
171 * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type
172 * AES-CBC-128. ctx must be initialized before calling this function. K2 is
173 * the symmetric key used and iv is the initialization vector used.
174 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530175 if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
Tom Joseph518ecce2017-01-25 14:34:19 +0530176 output.data()))
177 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530178 throw std::runtime_error("EVP_EncryptInit_ex failed for type "
179 "AES-CBC-128");
180 }
181
182 /*
183 * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
184 * parameter is zero then no padding is performed. This function always
185 * returns 1.
186 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530187 EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
Tom Joseph518ecce2017-01-25 14:34:19 +0530188
189 int outputLen = 0;
190
191 /*
192 * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any
193 * more data and it will return an error if any data remains in a partial
194 * block: that is if the total data length is not a multiple of the block
195 * size. Since we are adding padding bytes and ensures that payload is a
196 * multiple of block size, we are not making the call to
197 * EVP_DecryptFinal_ex()
198 */
Vernon Mauery9e801a22018-10-12 13:20:49 -0700199 if (!EVP_EncryptUpdate(ctxPtr.get(), output.data() + AESCBC128ConfHeader,
200 &outputLen, input, inputLen))
Tom Joseph518ecce2017-01-25 14:34:19 +0530201 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530202 throw std::runtime_error("EVP_EncryptUpdate failed for type "
203 "AES-CBC-128");
204 }
205
206 output.resize(AESCBC128ConfHeader + outputLen);
Tom Joseph518ecce2017-01-25 14:34:19 +0530207
208 return output;
209}
210
Vernon Mauery9e801a22018-10-12 13:20:49 -0700211} // namespace crypt
Tom Josephd08b5232017-01-24 18:15:39 +0530212
Vernon Mauery9e801a22018-10-12 13:20:49 -0700213} // namespace cipher