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 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #ifdef HAVE_EVP_CIPHER_CTX_INIT
35 #include <cstdlib>
36 #endif
37 
38 #include <cstdio>
39 
40 #include "consts.h"
41 #include "crypto.hh"
42 #include "cryptoerror.hh"
43 #include "intl.h"
44 
45 using namespace yapet;
46 
checkIVSizeOrThrow()47 void Crypto::checkIVSizeOrThrow() {
48     auto supportedIVSize{cipherIvecSize()};
49     auto expectedIVSize{_key->ivecSize()};
50 
51     if (supportedIVSize != expectedIVSize) {
52         char msg[YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE];
53         std::snprintf(msg, YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE,
54                       _("Expect cipher to support IV size %d but cipher "
55                         "supports only IV size %d"),
56                       expectedIVSize, supportedIVSize);
57         throw CipherError{msg};
58     }
59 }
60 
createContext()61 EVP_CIPHER_CTX* Crypto::createContext() {
62 #ifdef HAVE_EVP_CIPHER_CTX_INIT
63     EVP_CIPHER_CTX* context =
64         (EVP_CIPHER_CTX*)std::malloc(sizeof(EVP_CIPHER_CTX));
65     EVP_CIPHER_CTX_init(context);
66     return context;
67 #elif HAVE_EVP_CIPHER_CTX_NEW
68     return EVP_CIPHER_CTX_new();
69 #else
70 #error "Neither EVP_CIPHER_CTX_init() nor EVP_CIPHER_CTX_new() available"
71 #endif
72 }
73 
destroyContext(EVP_CIPHER_CTX * context)74 void Crypto::destroyContext(EVP_CIPHER_CTX* context) {
75 #ifdef HAVE_EVP_CIPHER_CTX_CLEANUP
76     EVP_CIPHER_CTX_cleanup(context);
77     std::free(context);
78 #elif HAVE_EVP_CIPHER_CTX_FREE
79     EVP_CIPHER_CTX_free(context);
80 #else
81 #error "Neither EVP_CIPHER_CTX_cleanup() nor EVP_CIPHER_CTX_free() available"
82 #endif
83 }
84 
validateCipherOrThrow()85 void Crypto::validateCipherOrThrow() {
86     if (getCipher() == nullptr) throw CipherError{_("Unable to get cipher")};
87 
88     checkIVSizeOrThrow();
89 }
90 
initializeOrThrow(MODE mode)91 EVP_CIPHER_CTX* Crypto::initializeOrThrow(MODE mode) {
92     EVP_CIPHER_CTX* context = createContext();
93 
94     auto success = EVP_CipherInit_ex(context, getCipher(), nullptr,
95                                      *_key->key(), *_key->ivec(), mode);
96     if (success != SSL_SUCCESS) {
97         destroyContext(context);
98         throw CipherError{_("Error initializing cipher")};
99     }
100 
101     success = EVP_CIPHER_CTX_set_key_length(context, _key->keySize());
102     if (success != SSL_SUCCESS) {
103         destroyContext(context);
104         char msg[YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE];
105         std::snprintf(msg, YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE,
106                       _("Cannot set key length on context to %d"),
107                       _key->keySize());
108         throw CipherError{msg};
109     }
110 
111     return context;
112 }
113 
cipherBlockSize() const114 int Crypto::cipherBlockSize() const {
115     return EVP_CIPHER_block_size(getCipher());
116 }
117 
cipherIvecSize() const118 int Crypto::cipherIvecSize() const { return EVP_CIPHER_iv_length(getCipher()); }
119 
Crypto(const std::shared_ptr<yapet::Key> & key)120 Crypto::Crypto(const std::shared_ptr<yapet::Key>& key) : _key{key} {}
121 
Crypto(const Crypto & c)122 Crypto::Crypto(const Crypto& c) : _key{c._key} {}
123 
operator =(const Crypto & c)124 Crypto& Crypto::operator=(const Crypto& c) {
125     if (this == &c) return *this;
126 
127     _key = c._key;
128     return *this;
129 }
130 
Crypto(Crypto && c)131 Crypto::Crypto(Crypto&& c) : _key{std::move(c._key)} {}
132 
operator =(Crypto && c)133 Crypto& Crypto::operator=(Crypto&& c) {
134     if (this == &c) return *this;
135 
136     _key = std::move(c._key);
137     return *this;
138 }
139 
encrypt(const SecureArray & plainText)140 SecureArray Crypto::encrypt(const SecureArray& plainText) {
141     if (plainText.size() == 0) {
142         throw EncryptionError{_("Cannot encrypt empty plain text")};
143     }
144 
145     validateCipherOrThrow();
146     EVP_CIPHER_CTX* context = initializeOrThrow(ENCRYPTION);
147 
148     auto blockSize = cipherBlockSize();
149 
150     SecureArray temporaryEncryptedData{plainText.size() + (2 * blockSize)};
151     yapet::SecureArray::size_type writtenDataLength;
152 
153     auto success =
154         EVP_CipherUpdate(context, *temporaryEncryptedData, &writtenDataLength,
155                          *plainText, plainText.size());
156     if (success != SSL_SUCCESS) {
157         destroyContext(context);
158         throw EncryptionError{_("Error encrypting data")};
159     }
160 
161     auto effectiveEncryptedDataLength = writtenDataLength;
162     success = EVP_CipherFinal_ex(context,
163                                  (*temporaryEncryptedData) + writtenDataLength,
164                                  &writtenDataLength);
165     if (success != SSL_SUCCESS) {
166         destroyContext(context);
167         throw EncryptionError{_("Error finalizing encryption")};
168     }
169 
170     effectiveEncryptedDataLength += writtenDataLength;
171     assert(effectiveEncryptedDataLength <= temporaryEncryptedData.size());
172 
173     SecureArray encryptedData{effectiveEncryptedDataLength};
174 
175     destroyContext(context);
176 
177     return (encryptedData << temporaryEncryptedData);
178 }
179 
decrypt(const SecureArray & cipherText)180 SecureArray Crypto::decrypt(const SecureArray& cipherText) {
181     if (cipherText.size() == 0) {
182         throw EncryptionError{_("Cannot decrypt empty cipher text")};
183     }
184 
185     validateCipherOrThrow();
186 
187     EVP_CIPHER_CTX* context = initializeOrThrow(DECRYPTION);
188 
189     auto blockSize = cipherBlockSize();
190 
191     SecureArray temporaryDecryptedData{cipherText.size() + blockSize};
192     int writtenDataLength;
193 
194     auto success =
195         EVP_CipherUpdate(context, *temporaryDecryptedData, &writtenDataLength,
196                          *cipherText, cipherText.size());
197     if (success != SSL_SUCCESS) {
198         destroyContext(context);
199         throw EncryptionError{_("Error decrypting data")};
200     }
201 
202     auto effectiveDecryptedDataLength = writtenDataLength;
203     success = EVP_CipherFinal_ex(context,
204                                  (*temporaryDecryptedData) + writtenDataLength,
205                                  &writtenDataLength);
206     if (success != SSL_SUCCESS) {
207         destroyContext(context);
208         throw EncryptionError{_("Error finalizing decrypting data")};
209     }
210 
211     effectiveDecryptedDataLength += writtenDataLength;
212     assert(effectiveDecryptedDataLength <= temporaryDecryptedData.size());
213 
214     SecureArray decryptedData{effectiveDecryptedDataLength};
215 
216     destroyContext(context);
217 
218     return (decryptedData << temporaryDecryptedData);
219 }