Subversion Repositories tpanel

Rev

Rev 476 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
475 andreas 1
/*
2
 * Copyright (C) 2024 by Andreas Theofilu <andreas@theosys.at>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
19
#include <iostream>
20
#include <fstream>
21
#include <cstring>
22
 
23
#include <openssl/err.h>
24
#include <openssl/evp.h>
25
 
26
#include "tscramble.h"
27
#include "terror.h"
28
 
29
using std::string;
30
using std::ifstream;
31
using std::ios_base;
32
using std::ios;
33
using std::exception;
34
using std::min;
35
using std::cout;
36
using std::cerr;
37
using std::endl;
38
 
39
#define CHUNK_SIZE          1024
40
#define OSSL_SUCCESS        1
41
#define OSSL_ERROR          0
42
static unsigned long ssl_errno;
43
 
476 andreas 44
/*
45
 * Internally used helper functions
46
 */
475 andreas 47
void _print_ssl_error(unsigned long eno)
48
{
49
    char msg[1024];
50
 
51
    ERR_error_string_n(eno, msg, sizeof(msg));
52
    MSG_ERROR(msg);
53
}
54
 
55
string _get_ssl_error()
56
{
57
    char msg[1024];
58
 
59
    ERR_error_string_n(ssl_errno, msg, sizeof(msg));
60
    return string(msg);
61
}
62
 
476 andreas 63
/* -------------------------------------------------------------------------
64
 * Implementaion of methods from class TScramble
65
 * -------------------------------------------------------------------------*/
475 andreas 66
TScramble::TScramble()
67
{
68
    DECL_TRACER("TScramble::TScramble()");
69
 
70
    mCtx = EVP_CIPHER_CTX_new();
71
 
72
    if (!mCtx)
73
        MSG_ERROR("Error getting new context!");
74
}
75
 
76
TScramble::~TScramble()
77
{
78
    DECL_TRACER("TScramble::~TScramble()");
79
 
80
    if (mCtx)
81
    {
82
        EVP_CIPHER_CTX_cleanup(mCtx);
83
        EVP_CIPHER_CTX_free(mCtx);
84
    }
85
}
86
 
476 andreas 87
bool TScramble::aesInit(const string& key, const string& salt)
475 andreas 88
{
476 andreas 89
    DECL_TRACER("TScramble::aesInit(const string& key, const string& salt)");
475 andreas 90
 
91
    if (mAesInitialized)
92
        return true;
93
 
94
    if (!mCtx)
95
    {
96
        MSG_ERROR("ERROR: No context available! Initialisation failed!");
97
        return false;
98
    }
99
 
100
    int keySize;
101
    int count = 5;      // Number iterations
102
 
103
    const EVP_MD *md = EVP_sha1();
104
    const EVP_CIPHER *pCipher = EVP_aes_128_cbc();
105
 
106
    if (!md)
107
    {
108
        MSG_ERROR("Error getting SHA1 hash function!");
109
        return false;
110
    }
111
 
112
    if (!pCipher)
113
    {
114
        MSG_ERROR("Error getting the AES128-CBC cipher algorithm!");
115
        return false;
116
    }
117
 
118
    // If the given salt is less then AES128_SALT_SIZE bytes, we first initialize
119
    // the buffer with 0 bytes. Then the salt is copied into the buffer. This
120
    // guaranties that we have a padding.
121
    memset(mAesSalt, 0, AES128_SALT_SIZE);
122
    memcpy(mAesSalt, salt.c_str(), min((size_t)AES128_SALT_SIZE, salt.length()));
123
    // Initialize the key and IV with 0
124
    memset(mAesKey, 0, AES128_KEY_SIZE);
125
    memset(mAesIV, 0, AES128_KEY_SIZE);
126
 
482 andreas 127
    keySize = EVP_BytesToKey(pCipher, md, mAesSalt, (unsigned char *)key.c_str(), static_cast<int>(key.length()), count, mAesKey, mAesIV);
475 andreas 128
 
129
    if (keySize == AES128_KEY_SIZE)
130
    {
131
        EVP_CIPHER_CTX_init(mCtx);
132
 
476 andreas 133
        if ((ssl_errno = EVP_DecryptInit_ex(mCtx, pCipher, nullptr, mAesKey, mAesIV)) != OSSL_SUCCESS)
475 andreas 134
        {
476 andreas 135
            MSG_ERROR("Error initializing: " << _get_ssl_error());
136
            return false;
475 andreas 137
        }
482 andreas 138
 
475 andreas 139
        EVP_CIPHER_CTX_set_key_length(mCtx, AES128_KEY_SIZE);
140
    }
141
    else
142
    {
143
        MSG_ERROR("Key size is " << (keySize * 8) << " bits - should be 128 bits");
144
        return false;
145
    }
146
 
147
    mAesInitialized = true;
148
    return mAesInitialized;
149
}
150
 
151
bool TScramble::aesDecodeFile(const string& fname)
152
{
153
    DECL_TRACER("TScramble::aesDecodeFile(const string& fname)");
154
 
155
    if (fname.empty())
156
    {
157
        MSG_ERROR("Got no file name to open a file!");
158
        return false;
159
    }
160
 
161
    ifstream ifile;
162
 
163
    try
164
    {
165
        ifile.open(fname, ios::binary);
166
        bool state = aesDecodeFile(ifile);
167
        ifile.close();
168
        return state;
169
    }
170
    catch(exception& e)
171
    {
172
        MSG_ERROR("Error opening file \"" << fname << "\": " << e.what());
173
 
174
        if (ifile.is_open())
175
            ifile.close();
176
    }
177
 
178
    return false;
179
}
180
 
181
bool TScramble::aesDecodeFile(ifstream& is)
182
{
183
    DECL_TRACER("TScramble::aesDecodeFile(ifstream& is)");
184
 
185
    if (!is.is_open())
186
    {
187
        MSG_ERROR("No open file!");
188
        return false;
189
    }
190
 
191
    if (!mAesInitialized || !mCtx)
192
    {
193
        MSG_ERROR("Class was not initialized!");
194
        return false;
195
    }
196
 
197
    mDecrypted.clear();
198
    // Find file size
199
    is.seekg(0, ios_base::end);         // Seek to end of file
200
    size_t fileSize = is.tellg();       // Get current position (file size)
201
    is.seekg(0);                        // Seek to first byte of file
202
    size_t pos = 0;                     // Position (offset) in input and output buffer
203
    // Allocate space for input and output buffers
204
    unsigned char *buffer = new unsigned char[fileSize];
205
    unsigned char *outBuffer = new unsigned char[fileSize];
206
    unsigned char decBuffer[CHUNK_SIZE];    // Buffer for decoding a chunk.
207
    unsigned char encBuffer[CHUNK_SIZE];    // Buffer for decoding a chunk.
208
    int len = 0;
209
    // Not really necessary, but initialize output buffer with zeros
210
    memset(outBuffer, 0, fileSize);
211
    // Read whole file
212
    is.read(reinterpret_cast<char *>(buffer), fileSize);
213
    // decode
214
    if (fileSize > CHUNK_SIZE)     // Is the file size greater then a chunk?
215
    {                               // Yes, then start reading the file in chunks
216
        size_t numBlocks = fileSize / CHUNK_SIZE;
217
 
218
        for (size_t i = 0; i < numBlocks; ++i)
219
        {
220
            memcpy(encBuffer, buffer + i * CHUNK_SIZE, CHUNK_SIZE);
221
 
222
            if ((ssl_errno = EVP_DecryptUpdate(mCtx, decBuffer, &len, encBuffer, CHUNK_SIZE)) != OSSL_SUCCESS)
223
            {
224
                MSG_ERROR("Loop " << i << ": Error updating");
225
                _print_ssl_error(ssl_errno);
226
                delete[] buffer;
227
                delete[] outBuffer;
228
                return false;
229
            }
230
 
231
            memcpy(outBuffer+pos, decBuffer, len);
232
            pos += len;
233
        }
234
 
482 andreas 235
        size_t size2 = fileSize - (numBlocks * CHUNK_SIZE);
475 andreas 236
 
237
        if (size2 > 0)      // Is there something left of the file less then CHUNK_SIZE?
238
        {                   // Yes, then decrypt it
239
            memcpy(encBuffer, buffer + numBlocks * CHUNK_SIZE, size2);
240
 
482 andreas 241
            if ((ssl_errno = EVP_DecryptUpdate(mCtx, decBuffer, &len, encBuffer, static_cast<int>(size2))) != OSSL_SUCCESS)
475 andreas 242
            {
243
                MSG_ERROR("Error updating");
244
                _print_ssl_error(ssl_errno);
245
                delete[] buffer;
246
                delete[] outBuffer;
247
                return false;
248
            }
249
 
250
            memcpy(outBuffer+pos, decBuffer, len);
251
            pos += len;
252
        }
253
    }
254
    else
255
    {
482 andreas 256
        if ((ssl_errno = EVP_DecryptUpdate(mCtx, outBuffer, &len, buffer, static_cast<int>(fileSize))) != OSSL_SUCCESS)
475 andreas 257
        {
258
            _print_ssl_error(ssl_errno);
259
            delete[] buffer;
260
            delete[] outBuffer;
261
            return false;
262
        }
263
 
264
        pos = len;
265
    }
266
 
267
    if ((ssl_errno = EVP_DecryptFinal_ex(mCtx, outBuffer + pos, &len)) != OSSL_SUCCESS)
268
    {
269
        MSG_ERROR("Error finalizing: " << _get_ssl_error());
270
        delete[] buffer;
271
        delete[] outBuffer;
272
        return false;
273
    }
274
 
275
    pos += len;
276
    mDecrypted.assign(reinterpret_cast<char *>(outBuffer), pos);
277
    delete[] buffer;
278
    delete[] outBuffer;
279
 
280
    return true;
281
}