1 /*
2 * Copyright (C) 2018 Rafael Ostertag
3 *
4 * This file is part of YAPET.
5 *
6 * YAPET is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
10 *
11 * YAPET is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * YAPET. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Additional permission under GNU GPL version 3 section 7
20 *
21 * If you modify this program, or any covered work, by linking or combining it
22 * with the OpenSSL project's OpenSSL library (or a modified version of that
23 * library), containing parts covered by the terms of the OpenSSL or SSLeay
24 * licenses, Rafael Ostertag grants you additional permission to convey the
25 * resulting work. Corresponding Source for a non-source form of such a
26 * combination shall include the source code for the parts of OpenSSL used as
27 * well as that of the covered work.
28 */
29
30 #include <openssl/rand.h>
31 #include <cstdio>
32 #include <cstring>
33
34 #include "aes256.hh"
35 #include "consts.h"
36 #include "cryptoerror.hh"
37 #include "intl.h"
38 #include "logger.hh"
39
40 using namespace yapet;
41
randomIV() const42 SecureArray Aes256::randomIV() const {
43 SecureArray ivec{cipherIvecSize()};
44
45 auto result = RAND_bytes((*ivec), ivec.size());
46 if (result != SSL_SUCCESS) {
47 LOG_MESSAGE(std::string{__func__} +
48 ": Cannot generate random initialization vector");
49 throw CipherError{_("Cannot generate random initialization vector")};
50 }
51
52 return ivec;
53 }
54
extractIVFromRecord(const SecureArray & record) const55 SecureArray Aes256::extractIVFromRecord(const SecureArray& record) const {
56 if (record.size() < cipherIvecSize()) {
57 LOG_MESSAGE(std::string{__func__} +
58 ": Record does not contain initialization vector");
59 throw CipherError{_("Record does not contain initialization vector")};
60 }
61
62 SecureArray ivec{cipherIvecSize()};
63 ivec << record;
64 return ivec;
65 }
66
extractCipherTextFromRecord(const SecureArray & record) const67 SecureArray Aes256::extractCipherTextFromRecord(
68 const SecureArray& record) const {
69 auto ivecSize{cipherIvecSize()};
70 if (record.size() <= ivecSize) {
71 LOG_MESSAGE(std::string{__func__} +
72 ": Record does not contain encrypted data");
73 throw CipherError{_("Record does not contain encrypted data")};
74 }
75
76 auto pointerToCipherText{*record + ivecSize};
77
78 auto cipherTextSize{record.size() - ivecSize};
79
80 return toSecureArray(pointerToCipherText, cipherTextSize);
81 }
82
checkIVSizeOrThrow(const SecureArray & ivec)83 void Aes256::checkIVSizeOrThrow(const SecureArray& ivec) {
84 auto supportedIVSize{cipherIvecSize()};
85 auto expectedIVSize{ivec.size()};
86
87 if (supportedIVSize != expectedIVSize) {
88 LOG_MESSAGE(std::string{__func__} + ": IV size mismatch");
89 char msg[YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE];
90 std::snprintf(msg, YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE,
91 _("Expect cipher to support IV size %d but cipher "
92 "supports only IV size %d"),
93 expectedIVSize, supportedIVSize);
94
95 throw CipherError{msg};
96 }
97 }
98
validateCipherOrThrow(const SecureArray & ivec)99 void Aes256::validateCipherOrThrow(const SecureArray& ivec) {
100 if (getCipher() == nullptr) throw CipherError{_("Unable to get cipher")};
101
102 checkIVSizeOrThrow(ivec);
103 }
104
initializeOrThrow(const SecureArray & ivec,MODE mode)105 EVP_CIPHER_CTX* Aes256::initializeOrThrow(const SecureArray& ivec, MODE mode) {
106 EVP_CIPHER_CTX* context = createContext();
107
108 auto success = EVP_CipherInit_ex(context, getCipher(), nullptr,
109 *getKey()->key(), *ivec, mode);
110 if (success != SSL_SUCCESS) {
111 LOG_MESSAGE(std::string{__func__} + ": Error initializing cipher");
112 destroyContext(context);
113 throw CipherError{_("Error initializing cipher")};
114 }
115
116 success = EVP_CIPHER_CTX_set_key_length(context, getKey()->keySize());
117 if (success != SSL_SUCCESS) {
118 LOG_MESSAGE(std::string{__func__} + ": Error setting key length");
119 destroyContext(context);
120 char msg[YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE];
121 std::snprintf(msg, YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE,
122 _("Cannot set key length on context to %d"),
123 getKey()->keySize());
124 throw CipherError{msg};
125 }
126
127 return context;
128 }
129
Aes256(const std::shared_ptr<Key> & key)130 Aes256::Aes256(const std::shared_ptr<Key>& key) : Crypto(key) {}
131
Aes256(const Aes256 & c)132 Aes256::Aes256(const Aes256& c) : Crypto(c) {}
133
operator =(const Aes256 & c)134 Aes256& Aes256::operator=(const Aes256& c) {
135 if (this == &c) return *this;
136
137 Crypto::operator=(c);
138
139 return *this;
140 }
141
Aes256(Aes256 && c)142 Aes256::Aes256(Aes256&& c) : Crypto{c} {}
143
operator =(Aes256 && c)144 Aes256& Aes256::operator=(Aes256&& c) {
145 if (this == &c) return *this;
146
147 Crypto::operator=(c);
148
149 return *this;
150 }
151
encrypt(const SecureArray & plainText)152 SecureArray Aes256::encrypt(const SecureArray& plainText) {
153 if (plainText.size() == 0) {
154 LOG_MESSAGE(std::string{__func__} +
155 ": Cannot encrypt empty plain text");
156 throw EncryptionError{_("Cannot encrypt empty plain text")};
157 }
158
159 SecureArray ivec{randomIV()};
160
161 validateCipherOrThrow(ivec);
162 EVP_CIPHER_CTX* context = initializeOrThrow(ivec, ENCRYPTION);
163
164 auto blockSize = cipherBlockSize();
165
166 SecureArray temporaryEncryptedData{plainText.size() + (2 * blockSize)};
167 yapet::SecureArray::size_type writtenDataLength;
168
169 auto success =
170 EVP_CipherUpdate(context, *temporaryEncryptedData, &writtenDataLength,
171 *plainText, plainText.size());
172 if (success != SSL_SUCCESS) {
173 LOG_MESSAGE(std::string{__func__} + ": EVP_CipherUpdate failure");
174 destroyContext(context);
175 throw EncryptionError{_("Error encrypting data")};
176 }
177
178 auto effectiveEncryptedDataLength = writtenDataLength;
179 success = EVP_CipherFinal_ex(context,
180 (*temporaryEncryptedData) + writtenDataLength,
181 &writtenDataLength);
182 if (success != SSL_SUCCESS) {
183 LOG_MESSAGE(std::string{__func__} + ": EVP_CipherFinal_ex failure");
184 destroyContext(context);
185 throw EncryptionError{_("Error finalizing encryption")};
186 }
187
188 effectiveEncryptedDataLength += writtenDataLength;
189
190 LOG_MESSAGE(std::string{__func__} + ": " +
191 std::to_string(effectiveEncryptedDataLength) +
192 "bytes encrypted");
193 assert(effectiveEncryptedDataLength <= temporaryEncryptedData.size());
194
195 SecureArray encryptedData{effectiveEncryptedDataLength};
196
197 destroyContext(context);
198
199 return ivec + (encryptedData << temporaryEncryptedData);
200 }
201
decrypt(const SecureArray & cipherText)202 SecureArray Aes256::decrypt(const SecureArray& cipherText) {
203 if (cipherText.size() == 0) {
204 LOG_MESSAGE(std::string{__func__} +
205 ": Cannot decrypt empty cipher text");
206 throw EncryptionError{_("Cannot decrypt empty cipher text")};
207 }
208
209 SecureArray ivec{extractIVFromRecord(cipherText)};
210
211 validateCipherOrThrow(ivec);
212
213 EVP_CIPHER_CTX* context = initializeOrThrow(ivec, DECRYPTION);
214
215 auto blockSize = cipherBlockSize();
216
217 SecureArray cipherTextOnly{extractCipherTextFromRecord(cipherText)};
218
219 SecureArray temporaryDecryptedData{cipherTextOnly.size() + blockSize};
220 int writtenDataLength;
221
222 auto success =
223 EVP_CipherUpdate(context, *temporaryDecryptedData, &writtenDataLength,
224 *cipherTextOnly, cipherTextOnly.size());
225 if (success != SSL_SUCCESS) {
226 LOG_MESSAGE(std::string{__func__} + ": EVP_CipherUpdate failure");
227 destroyContext(context);
228 throw EncryptionError{_("Error decrypting data")};
229 }
230
231 auto effectiveDecryptedDataLength = writtenDataLength;
232 success = EVP_CipherFinal_ex(context,
233 (*temporaryDecryptedData) + writtenDataLength,
234 &writtenDataLength);
235 if (success != SSL_SUCCESS) {
236 LOG_MESSAGE(std::string{__func__} + ": EVP_CipherFinal_ex failure");
237 destroyContext(context);
238 throw EncryptionError{_("Error finalizing decrypting data")};
239 }
240
241 effectiveDecryptedDataLength += writtenDataLength;
242
243 LOG_MESSAGE(std::string{__func__} + ": " +
244 std::to_string(effectiveDecryptedDataLength) +
245 "bytes decrypted");
246
247 assert(effectiveDecryptedDataLength <= temporaryDecryptedData.size());
248
249 SecureArray decryptedData{effectiveDecryptedDataLength};
250
251 destroyContext(context);
252
253 return (decryptedData << temporaryDecryptedData);
254 }