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 }