blob: bf46c4e386cda5ac2c56bb908cfe30b946b01b3f [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
Vernon Mauery9e801a22018-10-12 13:20:49 -070021std::vector<uint8_t>
22 AlgoAES128::decryptPayload(const std::vector<uint8_t>& packet,
23 const size_t sessHeaderLen,
24 const size_t payloadLen) const
Tom Joseph518ecce2017-01-25 14:34:19 +053025{
Vernon Mauery9e801a22018-10-12 13:20:49 -070026 auto plainPayload =
27 decryptData(packet.data() + sessHeaderLen,
28 packet.data() + sessHeaderLen + AESCBC128ConfHeader,
29 payloadLen - AESCBC128ConfHeader);
Tom Joseph518ecce2017-01-25 14:34:19 +053030
31 /*
32 * The confidentiality pad length is the last byte in the payload, it would
33 * tell the number of pad bytes in the payload. We added a condition, so
Gunnar Mills62ec6222018-04-08 16:28:23 -050034 * that buffer overrun doesn't happen.
Tom Joseph518ecce2017-01-25 14:34:19 +053035 */
36 size_t confPadLength = plainPayload.back();
Vernon Mauery9e801a22018-10-12 13:20:49 -070037 auto padLength = std::min(plainPayload.size() - 1, confPadLength);
Tom Joseph518ecce2017-01-25 14:34:19 +053038
39 auto plainPayloadLen = plainPayload.size() - padLength - 1;
40
41 // Additional check if the confidentiality pad bytes are as expected
Vernon Mauery9e801a22018-10-12 13:20:49 -070042 if (!std::equal(plainPayload.begin() + plainPayloadLen,
43 plainPayload.begin() + plainPayloadLen + padLength,
44 confPadBytes.begin()))
Tom Joseph518ecce2017-01-25 14:34:19 +053045 {
46 throw std::runtime_error("Confidentiality pad bytes check failed");
47 }
48
49 plainPayload.resize(plainPayloadLen);
50
51 return plainPayload;
52}
53
Vernon Mauery9e801a22018-10-12 13:20:49 -070054std::vector<uint8_t>
55 AlgoAES128::encryptPayload(std::vector<uint8_t>& payload) const
Tom Joseph518ecce2017-01-25 14:34:19 +053056{
57 auto payloadLen = payload.size();
58
59 /*
60 * The following logic calculates the number of padding bytes to be added to
61 * the payload data. This would ensure that the length is a multiple of the
62 * block size of algorithm being used. For the AES algorithm, the block size
63 * is 16 bytes.
64 */
65 auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF);
66
67 /*
68 * The additional field is for the Confidentiality Pad Length field. For the
69 * AES algorithm, this number will range from 0 to 15 bytes. This field is
70 * mandatory.
71 */
72 payload.resize(payloadLen + paddingLen + 1);
73
74 /*
75 * If no Confidentiality Pad bytes are required, the Confidentiality Pad
76 * Length field is set to 00h. If present, the value of the first byte of
77 * Confidentiality Pad shall be one (01h) and all subsequent bytes shall
78 * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc).
79 */
80 if (0 != paddingLen)
81 {
82 std::iota(payload.begin() + payloadLen,
Vernon Mauery9e801a22018-10-12 13:20:49 -070083 payload.begin() + payloadLen + paddingLen, 1);
Tom Joseph518ecce2017-01-25 14:34:19 +053084 }
85
86 payload.back() = paddingLen;
87
88 return encryptData(payload.data(), payload.size());
89}
90
Vernon Mauery70fd29c2017-11-30 13:11:43 -080091std::vector<uint8_t> AlgoAES128::decryptData(const uint8_t* iv,
Vernon Mauery9e801a22018-10-12 13:20:49 -070092 const uint8_t* input,
93 const int inputLen) const
Tom Joseph518ecce2017-01-25 14:34:19 +053094{
Tom Joseph518ecce2017-01-25 14:34:19 +053095 // Initializes Cipher context
Adriana Kobylak584fa882018-09-06 15:52:05 -050096 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
Tom Joseph518ecce2017-01-25 14:34:19 +053097
Vernon Mauery9e801a22018-10-12 13:20:49 -070098 auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) { EVP_CIPHER_CTX_free(ctx); };
Tom Josephf6c97e12017-08-03 00:23:57 +053099
Vernon Mauery9e801a22018-10-12 13:20:49 -0700100 std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> ctxPtr(ctx,
101 cleanupFunc);
Tom Josephf6c97e12017-08-03 00:23:57 +0530102
Tom Joseph518ecce2017-01-25 14:34:19 +0530103 /*
104 * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type
105 * AES-CBC-128. ctx must be initialized before calling this function. K2 is
106 * the symmetric key used and iv is the initialization vector used.
107 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530108 if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
109 iv))
Tom Joseph518ecce2017-01-25 14:34:19 +0530110 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530111 throw std::runtime_error("EVP_DecryptInit_ex failed for type "
112 "AES-CBC-128");
113 }
114
115 /*
116 * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
117 * parameter is zero then no padding is performed. This function always
118 * returns 1.
119 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530120 EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
Tom Joseph518ecce2017-01-25 14:34:19 +0530121
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800122 std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
Tom Joseph518ecce2017-01-25 14:34:19 +0530123
124 int outputLen = 0;
125
126 /*
127 * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any
128 * more data and it will return an error if any data remains in a partial
129 * block: that is if the total data length is not a multiple of the block
130 * size. Since AES-CBC-128 encrypted payload format adds padding bytes and
131 * ensures that payload is a multiple of block size, we are not making the
132 * call to EVP_DecryptFinal_ex().
133 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530134 if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input,
135 inputLen))
Tom Joseph518ecce2017-01-25 14:34:19 +0530136 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530137 throw std::runtime_error("EVP_DecryptUpdate failed");
138 }
139
140 output.resize(outputLen);
Tom Joseph518ecce2017-01-25 14:34:19 +0530141
142 return output;
143}
144
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800145std::vector<uint8_t> AlgoAES128::encryptData(const uint8_t* input,
Vernon Mauery9e801a22018-10-12 13:20:49 -0700146 const int inputLen) const
Tom Joseph518ecce2017-01-25 14:34:19 +0530147{
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800148 std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
Tom Joseph518ecce2017-01-25 14:34:19 +0530149
150 // Generate the initialization vector
151 if (!RAND_bytes(output.data(), AESCBC128ConfHeader))
152 {
153 throw std::runtime_error("RAND_bytes failed");
154 }
155
Tom Joseph518ecce2017-01-25 14:34:19 +0530156 // Initializes Cipher context
Adriana Kobylak584fa882018-09-06 15:52:05 -0500157 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
Tom Joseph518ecce2017-01-25 14:34:19 +0530158
Vernon Mauery9e801a22018-10-12 13:20:49 -0700159 auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) { EVP_CIPHER_CTX_free(ctx); };
Tom Josephf6c97e12017-08-03 00:23:57 +0530160
Vernon Mauery9e801a22018-10-12 13:20:49 -0700161 std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> ctxPtr(ctx,
162 cleanupFunc);
Tom Josephf6c97e12017-08-03 00:23:57 +0530163
Tom Joseph518ecce2017-01-25 14:34:19 +0530164 /*
165 * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type
166 * AES-CBC-128. ctx must be initialized before calling this function. K2 is
167 * the symmetric key used and iv is the initialization vector used.
168 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530169 if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
Tom Joseph518ecce2017-01-25 14:34:19 +0530170 output.data()))
171 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530172 throw std::runtime_error("EVP_EncryptInit_ex failed for type "
173 "AES-CBC-128");
174 }
175
176 /*
177 * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
178 * parameter is zero then no padding is performed. This function always
179 * returns 1.
180 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530181 EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
Tom Joseph518ecce2017-01-25 14:34:19 +0530182
183 int outputLen = 0;
184
185 /*
186 * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any
187 * more data and it will return an error if any data remains in a partial
188 * block: that is if the total data length is not a multiple of the block
189 * size. Since we are adding padding bytes and ensures that payload is a
190 * multiple of block size, we are not making the call to
191 * EVP_DecryptFinal_ex()
192 */
Vernon Mauery9e801a22018-10-12 13:20:49 -0700193 if (!EVP_EncryptUpdate(ctxPtr.get(), output.data() + AESCBC128ConfHeader,
194 &outputLen, input, inputLen))
Tom Joseph518ecce2017-01-25 14:34:19 +0530195 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530196 throw std::runtime_error("EVP_EncryptUpdate failed for type "
197 "AES-CBC-128");
198 }
199
200 output.resize(AESCBC128ConfHeader + outputLen);
Tom Joseph518ecce2017-01-25 14:34:19 +0530201
202 return output;
203}
204
Vernon Mauery9e801a22018-10-12 13:20:49 -0700205} // namespace crypt
Tom Josephd08b5232017-01-24 18:15:39 +0530206
Vernon Mauery9e801a22018-10-12 13:20:49 -0700207} // namespace cipher