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