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 }