]> git.lyx.org Git - features.git/blob - src/support/CryptographicEncryption.cpp
add first version of .lyx file encryption. It's disabled by default and could out...
[features.git] / src / support / CryptographicEncryption.cpp
1 // -*- C++ -*-
2 /**
3  * \file CryptographicEncryption.h
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Peter Kümmel
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "CryptographicEncryption.h"
15
16 #include <QDataStream>
17
18
19 #ifdef LYX_ENCRYPTION
20 #include <openssl/aes.h>
21 #include <openssl/evp.h>
22 #endif
23
24
25 namespace lyx {
26 namespace support {
27
28
29
30 CryptographicEncryption::CryptographicEncryption()
31 {
32 }
33
34
35 int CryptographicEncryption::blockAlign(int blockSize, QByteArray& bytes)
36 {
37         int pad = 2 * blockSize - (bytes.size() % blockSize); // pad at least one block
38         bytes.append(QByteArray(pad, (char)pad));
39         return pad;
40 }
41
42
43 int CryptographicEncryption::blockDealign(QByteArray& bytes)
44 {
45         int size = bytes.size();
46         if (size == 0)
47                 return 0;
48         char padded = bytes.at(size - 1);
49         bytes.resize(size - padded);
50         return padded;
51 }
52
53
54 bool CryptographicEncryption::aesEnryption(QByteArray const & in, QByteArray* out, QByteArray const & key, bool encrypt)
55 {
56 #ifndef LYX_ENCRYPTION
57         (void) in;
58         (void) out;
59         (void) key;
60         (void) encrypt;
61         return false;
62 #else
63         if (!out)
64                 return false;
65
66         int keySize = key.size();
67         if (keySize != 16 && keySize != 24 && keySize != 32) {
68                 return false;
69         }
70
71         // AES needs aligned data, but we must not touch already encrypted data
72         QByteArray aligned = in;
73         if (encrypt) {
74                 blockAlign(AES_BLOCK_SIZE, aligned);
75         }
76         if ((aligned.size() % AES_BLOCK_SIZE) != 0) {
77                 return false;
78         }
79
80         *out = QByteArray(aligned.size(), 0);
81         AES_KEY aeskey;
82         if (encrypt)
83                 AES_set_encrypt_key((unsigned char*)key.constData(), keySize * 8, &aeskey);
84         else
85                 AES_set_decrypt_key((unsigned char*)key.constData(), keySize * 8, &aeskey);
86
87         // use some arbitrary start values
88         unsigned char iv[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
89
90         AES_cbc_encrypt((unsigned char*)aligned.constData(),
91                                         (unsigned char*)out->data(),
92                                         aligned.size(), &aeskey, iv,
93                                         (encrypt ? AES_ENCRYPT : AES_DECRYPT));
94
95         if (!encrypt)
96                 blockDealign(*out);
97
98         return true;
99 #endif
100 }
101
102
103 QByteArray CryptographicEncryption::hash(QByteArray const & bytes, QCryptographicHash::Algorithm algorithm)
104 {
105         QCryptographicHash hashAlgo(algorithm);
106         hashAlgo.addData(bytes);
107         return hashAlgo.result();
108 }
109
110
111 bool CryptographicEncryption::encyrpt(QByteArray const & plain, QByteArray* encrypted, QByteArray const & key)
112 {
113         if (!encrypted)
114                 return false;
115
116         QByteArray bytes;
117         QDataStream stream(&bytes, QIODevice::WriteOnly);
118         stream.setVersion(QDataStream::Qt_4_6);
119         stream << plain;
120         stream << hash(plain, QCryptographicHash::Md5);
121
122         if (!aesEnryption(bytes, encrypted, key, true)) {
123                 encrypted->clear();
124                 return false;
125         }
126
127         return true;
128 }
129
130
131 bool CryptographicEncryption::decyrpt(QByteArray const & encrypted, QByteArray* plain, QByteArray const & key)
132 {
133         if (!plain)
134                 return false;
135
136         QByteArray bytes;
137         if (!aesEnryption(encrypted, &bytes, key, false))
138                 return false;
139
140         QByteArray decryptedHash;
141         QDataStream stream(bytes);
142         stream.setVersion(QDataStream::Qt_4_6);
143         stream >> *plain;
144         stream >> decryptedHash;
145
146         if (decryptedHash != hash(*plain, QCryptographicHash::Md5)) {
147                 plain->clear();
148                 return false;
149         }
150
151         return true;
152 }
153
154
155 QByteArray CryptographicEncryption::bytesToKey(QByteArray const & bytes)
156 {
157 #ifndef LYX_ENCRYPTION
158         (void) bytes;
159         return QByteArray();
160 #else
161         const char* in = bytes.constData();
162         int iterations = 10000; // here we could adjust our paranoija
163         unsigned char out[64];
164         PKCS5_PBKDF2_HMAC_SHA1(in, bytes.size(), 0, 0, iterations, 32, out);
165         
166         return QByteArray((const char*) out, 32);
167 #endif
168 }
169
170
171 QByteArray CryptographicEncryption::stringToKey(QString const & str)
172 {
173         QByteArray utf8 = str.toUtf8();
174         return bytesToKey(utf8);
175 }
176
177
178
179
180
181 }
182 }