blob: f33bca4df0e487a00059fa20ee76b66cafb82df5 [file] [log] [blame]
Tom Josephd08b5232017-01-24 18:15:39 +05301#include <openssl/evp.h>
2#include <openssl/hmac.h>
3#include <openssl/rand.h>
Tom Joseph518ecce2017-01-25 14:34:19 +05304#include <algorithm>
Tom Josephd08b5232017-01-24 18:15:39 +05305#include <numeric>
6#include "crypt_algo.hpp"
7#include "message_parsers.hpp"
8
9namespace cipher
10{
11
12namespace crypt
13{
14
Vernon Mauery70fd29c2017-11-30 13:11:43 -080015Interface::Interface(const std::vector<uint8_t>& sik, const key& addKey)
Tom Josephd08b5232017-01-24 18:15:39 +053016{
17 unsigned int mdLen = 0;
18
19 // Generated K2 for the confidentiality algorithm with the additional key
20 // keyed with SIK.
Vernon Mauery70fd29c2017-11-30 13:11:43 -080021 k2.resize(sik.size());
Tom Josephd08b5232017-01-24 18:15:39 +053022 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
Tom Joseph518ecce2017-01-25 14:34:19 +053030constexpr key AlgoAES128::const2;
31
32constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1>
33 AlgoAES128::confPadBytes;
34
Vernon Mauery70fd29c2017-11-30 13:11:43 -080035std::vector<uint8_t> AlgoAES128::decryptPayload(
36 const std::vector<uint8_t>& packet,
37 const size_t sessHeaderLen,
38 const size_t payloadLen) const
Tom Joseph518ecce2017-01-25 14:34:19 +053039{
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
Vernon Mauery70fd29c2017-11-30 13:11:43 -080068std::vector<uint8_t> AlgoAES128::encryptPayload(
69 std::vector<uint8_t>& payload) const
Tom Joseph518ecce2017-01-25 14:34:19 +053070{
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
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800106std::vector<uint8_t> AlgoAES128::decryptData(const uint8_t* iv,
Tom Joseph518ecce2017-01-25 14:34:19 +0530107 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
Tom Josephf6c97e12017-08-03 00:23:57 +0530115 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
Tom Joseph518ecce2017-01-25 14:34:19 +0530123 /*
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 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530128 if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
129 iv))
Tom Joseph518ecce2017-01-25 14:34:19 +0530130 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530131 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 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530140 EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
Tom Joseph518ecce2017-01-25 14:34:19 +0530141
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800142 std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
Tom Joseph518ecce2017-01-25 14:34:19 +0530143
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 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530154 if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input,
155 inputLen))
Tom Joseph518ecce2017-01-25 14:34:19 +0530156 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530157 throw std::runtime_error("EVP_DecryptUpdate failed");
158 }
159
160 output.resize(outputLen);
Tom Joseph518ecce2017-01-25 14:34:19 +0530161
162 return output;
163}
164
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800165std::vector<uint8_t> AlgoAES128::encryptData(const uint8_t* input,
166 const int inputLen) const
Tom Joseph518ecce2017-01-25 14:34:19 +0530167{
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800168 std::vector<uint8_t> output(inputLen + AESCBC128BlockSize);
Tom Joseph518ecce2017-01-25 14:34:19 +0530169
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
Tom Josephf6c97e12017-08-03 00:23:57 +0530181 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
Tom Joseph518ecce2017-01-25 14:34:19 +0530189 /*
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 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530194 if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(),
Tom Joseph518ecce2017-01-25 14:34:19 +0530195 output.data()))
196 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530197 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 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530206 EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0);
Tom Joseph518ecce2017-01-25 14:34:19 +0530207
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 */
Tom Josephf6c97e12017-08-03 00:23:57 +0530218 if (!EVP_EncryptUpdate(ctxPtr.get(),
Tom Joseph518ecce2017-01-25 14:34:19 +0530219 output.data() + AESCBC128ConfHeader,
220 &outputLen,
221 input, inputLen))
222 {
Tom Joseph518ecce2017-01-25 14:34:19 +0530223 throw std::runtime_error("EVP_EncryptUpdate failed for type "
224 "AES-CBC-128");
225 }
226
227 output.resize(AESCBC128ConfHeader + outputLen);
Tom Joseph518ecce2017-01-25 14:34:19 +0530228
229 return output;
230}
231
Tom Josephd08b5232017-01-24 18:15:39 +0530232}// namespace crypt
233
234}// namespace cipher
235
236