making encoder non-singleton
[accounts-sso:libsignoncrypto-qt.git] / lib / SignOnCrypto / encryptor.cpp
1 /*
2  * This file is part of signoncrypto
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  *
6  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22
23 #include "encryptor.h"
24
25 #include <QDataStream>
26
27 #include <sys/creds.h>
28 #include <aegis_crypto.h>
29
30 #define PRINT_DATA_AFTER_PROCESSING 1
31
32 #define TOKEN_MAXSIZE 256
33 namespace SignOnCrypto {
34
35     class EncryptorPrivate {
36         public:
37             EncryptorPrivate();
38             ~EncryptorPrivate();
39
40             QByteArray fetchToken(const pid_t pid);
41
42             QByteArray encryptData(const QByteArray &clearData,
43                                    const QByteArray &token);
44
45             QByteArray decryptData(const QByteArray &cipherText,
46                                   const QByteArray &token);
47
48             char *self_token;
49             int self_token_len;
50             Encryptor::Status status;
51     };
52 } //namespace
53
54 using namespace SignOnCrypto;
55
56 EncryptorPrivate::EncryptorPrivate()
57 {
58     TRACE();
59
60     self_token = NULL;
61     self_token_len = 0;
62 }
63
64 EncryptorPrivate::~EncryptorPrivate()
65 {
66     if (self_token)
67         free(self_token);
68     TRACE();
69 }
70
71 QByteArray EncryptorPrivate::fetchToken(const pid_t pid)
72 {
73     /*
74      * we cache token for our own process only
75      * */
76     TRACE() << pid << self_token_len;
77     if (pid == 0 && self_token_len != 0)
78         return QByteArray(self_token, self_token_len);
79     TRACE();
80
81     char token[TOKEN_MAXSIZE];
82     memset(token, 0, TOKEN_MAXSIZE);
83     creds_t ccreds = creds_gettask(pid);
84     int res = creds_find(ccreds, "*sso-encryption-token", token, TOKEN_MAXSIZE);
85
86     TRACE() << "creds_find = " << res;
87     creds_free(ccreds);
88
89     if (res == -1) {
90         TRACE() << "No encryption credentials are specified: no need for encryption";
91         return QByteArray();
92     } else if (res >= TOKEN_MAXSIZE) {
93         qCritical() << "Size limit exceeded for aegis token";
94         return QByteArray();
95     }
96
97     TRACE() << token;
98
99     if (pid == 0 &&
100         self_token_len == 0) {
101
102         self_token = (char*)malloc(res);
103         memcpy(self_token, token, res);
104         self_token_len = res;
105     }
106
107     return QByteArray(token, res);
108 }
109
110 QByteArray EncryptorPrivate::encryptData(const QByteArray &clearData, const QByteArray &token)
111 {
112     RAWDATA_PTR cipherData = NULL;
113     size_t cipherLength = 0;
114
115     TRACE() << clearData;
116
117     if (aegis_crypto_encrypt(clearData.data(),
118                              clearData.length(),
119                              token.constData(),
120                              &cipherData,
121                              &cipherLength) != aegis_crypto_ok) {
122
123         aegis_crypto_free(cipherData);
124         TRACE() << QString::fromLatin1("Failed to encrypt data: %1").
125             arg(QLatin1String(aegis_crypto_last_error_str()));
126         return QByteArray();
127     }
128
129     QByteArray encrypted((char *)cipherData, cipherLength);
130     aegis_crypto_free(cipherData);
131
132     if (PRINT_DATA_AFTER_PROCESSING) {
133         QByteArray tmp(encrypted);
134         TRACE() << "Encoded data: " << tmp.toBase64();
135     }
136
137     return encrypted;
138 }
139
140 QByteArray EncryptorPrivate::decryptData(const QByteArray &cipherData, const QByteArray &token)
141 {
142     TRACE();
143     RAWDATA_PTR clearData;
144     size_t length = 0;
145
146     if (aegis_crypto_decrypt(cipherData.data(),
147                              cipherData.length(),
148                              token.constData(),
149                              &clearData,
150                              &length) != aegis_crypto_ok)
151     {
152         aegis_crypto_free(clearData);
153         TRACE() << QString::fromLatin1("Failed to decrypt data: %1.").
154             arg(QLatin1String(aegis_crypto_last_error_str()));
155         return QByteArray();
156     }
157
158     QByteArray decrypted((char *)clearData, length);
159     aegis_crypto_free(clearData);
160
161     if (PRINT_DATA_AFTER_PROCESSING)
162         TRACE() << "Decoded data: " << decrypted;
163
164     return decrypted;
165 }
166
167 const QString encodedBlobKey(QString::fromLatin1(SIGNONC_ENCODED_BLOB_KEY));
168 const QString serializationTypeKey(QString::fromLatin1(SIGNONC_SERIALIZATION_TYPE_KEY));
169 const QString nonDeserializedDataKey(QString::fromLatin1(SIGNONC_NONDESERIALIZED_DATA_KEY));
170
171 Encryptor::Encryptor()
172 {
173     TRACE();
174     priv = new EncryptorPrivate;
175
176     if (!priv)
177         qFatal("Cannot allocate memory for EncryptorPrivate");
178
179     priv->status = Encryptor::Ok;
180 }
181
182 Encryptor::~Encryptor()
183 {
184     TRACE();
185     delete priv;
186 }
187
188 bool Encryptor::isVariantMapEncrypted(const QVariantMap &data) const
189 {
190     return data.contains(encodedBlobKey);
191 }
192
193 QVariantMap Encryptor::encodeVariantMap(const QVariantMap &data, pid_t pid)
194 {
195     priv->status = Encryptor::Ok;
196     if (data.isEmpty())
197         return QVariantMap(data);
198
199     QByteArray securityToken = priv->fetchToken(pid);
200     if (securityToken.isNull())
201         return QVariantMap(data);
202
203     QByteArray serializedData;
204     QDataStream serializer(&serializedData, QIODevice::WriteOnly);
205     serializer.setVersion(QDataStream::Qt_4_7);
206
207     serializer << QVariant(data);
208
209     QByteArray encodedData(priv->encryptData(serializedData, securityToken));
210     if (encodedData.isNull()) {
211         priv->status = Encryptor::AegisCryptoError;
212         return QVariantMap();
213     }
214
215     QVariantMap encodedVariantMap;
216     encodedVariantMap.insert(serializationTypeKey,
217                              (qint32)Encryptor::QtClientType);
218     encodedVariantMap.insert(encodedBlobKey,
219                              encodedData);
220
221     return encodedVariantMap;
222 }
223
224 QVariantMap Encryptor::decodeVariantMap(const QVariantMap &data, pid_t pid)
225 {
226     priv->status = Encryptor::Ok;
227
228     /*
229      * if data does not have encoded
230      * content then it is not encoded
231      * */
232     if (!data.contains(encodedBlobKey)) {
233         priv->status = Encryptor::Ok;
234         return QVariantMap(data);
235     }
236
237     QByteArray securityToken = priv->fetchToken(pid);
238
239     if (securityToken.isNull()) {
240         priv->status = Encryptor::UnableToFetchToken;
241         return QVariantMap();
242     }
243
244     QVariant serializationType = data.value(serializationTypeKey);
245     QByteArray serializedData(priv->decryptData(data.value(encodedBlobKey).toByteArray(),
246                                                 securityToken));
247
248     if (serializedData.isNull()) {
249         priv->status = Encryptor::AegisCryptoError;
250         return QVariantMap();
251     }
252
253     if (serializationType.toInt() != (qint32)Encryptor::QtClientType) {
254         QVariantMap nonDeserializedResult;
255         nonDeserializedResult.insert(nonDeserializedDataKey, serializedData);
256         return nonDeserializedResult;
257     }
258
259     QDataStream deserializer(&serializedData, QIODevice::ReadOnly);
260     QVariant decodedDataVa;
261     deserializer >> decodedDataVa;
262
263     if (deserializer.status() != QDataStream::Ok) {
264         priv->status = Encryptor::CorruptedData;
265         return QVariantMap();
266     }
267
268     return decodedDataVa.toMap();
269 }
270
271 QString Encryptor::encodeString(const QString &data, pid_t pid)
272 {
273     priv->status = Encryptor::Ok;
274     if (data.isNull())
275         return QString(data);
276
277     QByteArray securityToken = priv->fetchToken(pid);
278     if (securityToken.isNull())
279         return QString(data);
280
281     QByteArray encodedData(priv->encryptData(data.toUtf8(), securityToken));
282
283     if (encodedData.isNull()){
284         priv->status = Encryptor::AegisCryptoError;
285         return QString();
286     }
287
288     return QString::fromUtf8(encodedData.toBase64());
289 }
290
291 QString Encryptor::decodeString(const QString &data, pid_t pid)
292 {
293     priv->status = Encryptor::Ok;
294     if (data.isNull())
295         return QString(data);
296
297     QByteArray securityToken = priv->fetchToken(pid);
298     if (securityToken.isNull())
299         return QString(data);
300
301     QByteArray encodedData(QByteArray::fromBase64(data.toUtf8()));
302     QByteArray decodedData(priv->decryptData(encodedData, securityToken));
303
304     if (decodedData.isNull()){
305         priv->status = Encryptor::AegisCryptoError;
306         return QString();
307     }
308
309     return QString::fromUtf8(decodedData.constData());
310 }
311
312 Encryptor::Status Encryptor::status() const
313 {
314     return priv->status;
315 }
316