blob: 3aab7e345693dfaf206911d0889811e92bc328d8 [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
112 /*
113 * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type
114 * AES-CBC-128. ctx must be initialized before calling this function. K2 is
115 * the symmetric key used and iv is the initialization vector used.
116 */
117 if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(), iv))
118 {
119 EVP_CIPHER_CTX_cleanup(&ctx);
120 throw std::runtime_error("EVP_DecryptInit_ex failed for type "
121 "AES-CBC-128");
122 }
123
124 /*
125 * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
126 * parameter is zero then no padding is performed. This function always
127 * returns 1.
128 */
129 EVP_CIPHER_CTX_set_padding(&ctx, 0);
130
131 buffer output(inputLen + AESCBC128BlockSize);
132
133 int outputLen = 0;
134
135 /*
136 * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any
137 * more data and it will return an error if any data remains in a partial
138 * block: that is if the total data length is not a multiple of the block
139 * size. Since AES-CBC-128 encrypted payload format adds padding bytes and
140 * ensures that payload is a multiple of block size, we are not making the
141 * call to EVP_DecryptFinal_ex().
142 */
143 if (!EVP_DecryptUpdate(&ctx, output.data(), &outputLen, input, inputLen))
144 {
145 EVP_CIPHER_CTX_cleanup(&ctx);
146 throw std::runtime_error("EVP_DecryptUpdate failed");
147 }
148
149 output.resize(outputLen);
150 EVP_CIPHER_CTX_cleanup(&ctx);
151
152 return output;
153}
154
155buffer AlgoAES128::encryptData(const uint8_t* input, const int inputLen) const
156{
157 buffer output(inputLen + AESCBC128BlockSize);
158
159 // Generate the initialization vector
160 if (!RAND_bytes(output.data(), AESCBC128ConfHeader))
161 {
162 throw std::runtime_error("RAND_bytes failed");
163 }
164
165 EVP_CIPHER_CTX ctx;
166
167 // Initializes Cipher context
168 EVP_CIPHER_CTX_init(&ctx);
169
170 /*
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 */
175 if (!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, k2.data(),
176 output.data()))
177 {
178 EVP_CIPHER_CTX_cleanup(&ctx);
179 throw std::runtime_error("EVP_EncryptInit_ex failed for type "
180 "AES-CBC-128");
181 }
182
183 /*
184 * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad
185 * parameter is zero then no padding is performed. This function always
186 * returns 1.
187 */
188 EVP_CIPHER_CTX_set_padding(&ctx, 0);
189
190 int outputLen = 0;
191
192 /*
193 * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any
194 * more data and it will return an error if any data remains in a partial
195 * block: that is if the total data length is not a multiple of the block
196 * size. Since we are adding padding bytes and ensures that payload is a
197 * multiple of block size, we are not making the call to
198 * EVP_DecryptFinal_ex()
199 */
200 if (!EVP_EncryptUpdate(&ctx,
201 output.data() + AESCBC128ConfHeader,
202 &outputLen,
203 input, inputLen))
204 {
205 EVP_CIPHER_CTX_cleanup(&ctx);
206 throw std::runtime_error("EVP_EncryptUpdate failed for type "
207 "AES-CBC-128");
208 }
209
210 output.resize(AESCBC128ConfHeader + outputLen);
211 EVP_CIPHER_CTX_cleanup(&ctx);
212
213 return output;
214}
215
Tom Josephd08b5232017-01-24 18:15:39 +0530216}// namespace crypt
217
218}// namespace cipher
219
220