tizen 2.4 release
[framework/security/key-manager.git] / src / manager / service / crypto-logic.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  *
16  * @file        crypto-logic.cpp
17  * @author      Sebastian Grabowski (s.grabowski@samsung.com)
18  * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
19  * @version     1.0
20  * @brief       Crypto module implementation.
21  */
22
23 #include <iostream>
24 #include <fstream>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include <openssl/evp.h>
29 #include <openssl/rand.h>
30
31 #include <ckm/ckm-error.h>
32
33 #include <dpl/log/log.h>
34
35 #include <base64.h>
36 #include <digest.h>
37 #include <crypto.h>
38 #include <crypto-logic.h>
39
40 #define AES_CBC_KEY_SIZE 32
41
42 namespace CKM {
43
44 CryptoLogic::CryptoLogic(){}
45
46 CryptoLogic::CryptoLogic(CryptoLogic &&second) {
47     m_keyMap = std::move(second.m_keyMap);
48 }
49
50 CryptoLogic& CryptoLogic::operator=(CryptoLogic &&second) {
51     if (this == &second)
52         return *this;
53     m_keyMap = std::move(second.m_keyMap);
54     return *this;
55 }
56
57 bool CryptoLogic::haveKey(const Label &smackLabel)
58 {
59     return (m_keyMap.count(smackLabel) > 0);
60 }
61
62 void CryptoLogic::pushKey(const Label &smackLabel,
63                             const RawBuffer &applicationKey)
64 {
65     if (smackLabel.length() == 0) {
66         ThrowMsg(Exception::InternalError, "Empty smack label.");
67     }
68     if (applicationKey.size() == 0) {
69         ThrowMsg(Exception::InternalError, "Empty application key.");
70     }
71     if (haveKey(smackLabel)) {
72         ThrowMsg(Exception::InternalError, "Application key for " << smackLabel
73                  << "label already exists.");
74     }
75     m_keyMap[smackLabel] = applicationKey;
76 }
77
78 void CryptoLogic::removeKey(const Label &smackLabel)
79 {
80     m_keyMap.erase(smackLabel);
81 }
82
83 RawBuffer CryptoLogic::encryptDataAesCbc(
84     const RawBuffer &data,
85     const RawBuffer &key,
86     const RawBuffer &iv) const
87 {
88     Crypto::Cipher::AesCbcEncryption enc(key, iv);
89     RawBuffer result = enc.Append(data);
90     RawBuffer tmp = enc.Finalize();
91     std::copy(tmp.begin(), tmp.end(), std::back_inserter(result));
92     return result;
93 }
94
95 RawBuffer CryptoLogic::decryptDataAesCbc(
96     const RawBuffer &data,
97     const RawBuffer &key,
98     const RawBuffer &iv) const
99 {
100     Crypto::Cipher::AesCbcDecryption dec(key, iv);
101     RawBuffer result = dec.Append(data);
102     RawBuffer tmp = dec.Finalize();
103     std::copy(tmp.begin(), tmp.end(), std::back_inserter(result));
104     return result;
105 }
106
107 std::pair<RawBuffer,RawBuffer> CryptoLogic::encryptDataAesGcm(
108     const RawBuffer &data,
109     const RawBuffer &key,
110     const RawBuffer &iv) const
111 {
112     RawBuffer tag(AES_GCM_TAG_SIZE);
113     Crypto::Cipher::AesGcmEncryption enc(key, iv);
114     RawBuffer result = enc.Append(data);
115     RawBuffer tmp = enc.Finalize();
116     std::copy(tmp.begin(), tmp.end(), std::back_inserter(result));
117     if (0 == enc.Control(EVP_CTRL_GCM_GET_TAG, AES_GCM_TAG_SIZE, tag.data())) {
118         LogError("Error in aes control function. Get tag failed.");
119         ThrowMsg(Exception::EncryptDBRowError, "Error in aes control function. Get tag failed.");
120     }
121     return std::make_pair(result, tag);
122 }
123
124 RawBuffer CryptoLogic::decryptDataAesGcm(
125     const RawBuffer &data,
126     const RawBuffer &key,
127     const RawBuffer &iv,
128     const RawBuffer &tag) const
129 {
130     Crypto::Cipher::AesGcmDecryption dec(key, iv);
131     if (tag.size() < AES_GCM_TAG_SIZE) {
132         LogError("Error in decryptDataAesGcm. Tag is too short.");
133         ThrowMsg(Exception::DecryptDBRowError, "Error in decryptDataAesGcm. Tag is too short");
134     }
135     void *ptr = (void*)tag.data();
136     if (0 == dec.Control(EVP_CTRL_GCM_SET_TAG, AES_GCM_TAG_SIZE, ptr)) {
137         LogError("Error in aes control function. Set tag failed.");
138         ThrowMsg(Exception::DecryptDBRowError, "Error in aes control function. Set tag failed.");
139     }
140     RawBuffer result = dec.Append(data);
141     RawBuffer tmp = dec.Finalize();
142     std::copy(tmp.begin(), tmp.end(), std::back_inserter(result));
143     return result;
144 }
145
146 RawBuffer CryptoLogic::passwordToKey(
147     const Password &password,
148     const RawBuffer &salt,
149     size_t keySize) const
150 {
151     RawBuffer result(keySize);
152
153     if (1 != PKCS5_PBKDF2_HMAC_SHA1(
154                 password.c_str(),
155                 password.size(),
156                 salt.data(),
157                 salt.size(),
158                 1024,
159                 result.size(),
160                 result.data()))
161     {
162         ThrowMsg(Exception::InternalError, "PCKS5_PKKDF_HMAC_SHA1 failed.");
163     }
164     return result;
165 }
166
167 RawBuffer CryptoLogic::generateRandIV() const {
168     RawBuffer civ(EVP_MAX_IV_LENGTH);
169
170     if (1 != RAND_bytes(civ.data(), civ.size())) {
171         ThrowMsg(Exception::InternalError,
172           "RAND_bytes failed to generate IV.");
173     }
174
175     return civ;
176 }
177
178 void CryptoLogic::encryptRow(const Password &password, DB::Row &row)
179 {
180     try {
181         DB::Row crow = row;
182         RawBuffer key;
183         RawBuffer result1;
184         RawBuffer result2;
185
186         crow.algorithmType = DBCMAlgType::AES_GCM_256;
187         crow.dataSize = crow.data.size();
188
189         if (crow.dataSize <= 0) {
190             ThrowMsg(Exception::EncryptDBRowError, "Invalid dataSize.");
191         }
192
193         if (!haveKey(row.ownerLabel)) {
194             ThrowMsg(Exception::EncryptDBRowError, "Missing application key for " <<
195               row.ownerLabel << " label.");
196         }
197
198         if (crow.iv.empty()) {
199             crow.iv = generateRandIV();
200         }
201
202         key = m_keyMap[row.ownerLabel];
203         crow.encryptionScheme = ENCR_APPKEY;
204
205         auto dataPair = encryptDataAesGcm(crow.data, key, crow.iv);
206         crow.data = dataPair.first;
207         crow.tag = dataPair.second;
208
209         if (!password.empty()) {
210             key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE);
211             crow.data = encryptDataAesCbc(crow.data, key, crow.iv);
212             crow.encryptionScheme |= ENCR_PASSWORD;
213         }
214
215         encBase64(crow.data);
216         crow.encryptionScheme |= ENCR_BASE64;
217         encBase64(crow.iv);
218
219         row = crow;
220     } catch(const CKM::Base64Encoder::Exception::Base &e) {
221         LogDebug("Base64Encoder error: " << e.GetMessage());
222         ThrowMsg(Exception::Base64EncoderError, e.GetMessage());
223     } catch(const CKM::Base64Decoder::Exception::Base &e) {
224         LogDebug("Base64Encoder error: " << e.GetMessage());
225         ThrowMsg(Exception::Base64DecoderError, e.GetMessage());
226     } catch(const CKM::Crypto::Exception::Base &e) {
227         LogDebug("Crypto error: " << e.GetMessage());
228         ThrowMsg(Exception::EncryptDBRowError, e.GetMessage());
229     }
230 }
231
232 void CryptoLogic::decryptRow(const Password &password, DB::Row &row)
233 {
234     try {
235         DB::Row crow = row;
236         RawBuffer key;
237         RawBuffer digest, dataDigest;
238
239         if (row.algorithmType != DBCMAlgType::AES_GCM_256) {
240             ThrowMsg(Exception::DecryptDBRowError, "Invalid algorithm type.");
241         }
242
243         if ((row.encryptionScheme & ENCR_PASSWORD) && password.empty()) {
244             ThrowMsg(Exception::DecryptDBRowError,
245               "DB row is password protected, but given password is "
246               "empty.");
247         }
248
249         if ((row.encryptionScheme & ENCR_APPKEY) && !haveKey(row.ownerLabel)) {
250             ThrowMsg(Exception::DecryptDBRowError, "Missing application key for " <<
251               row.ownerLabel << " label.");
252         }
253
254         decBase64(crow.iv);
255         if (crow.encryptionScheme & ENCR_BASE64) {
256             decBase64(crow.data);
257         }
258
259         if (crow.encryptionScheme & ENCR_PASSWORD) {
260             key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE);
261             crow.data = decryptDataAesCbc(crow.data, key, crow.iv);
262         }
263
264         if (crow.encryptionScheme & ENCR_APPKEY) {
265             key = m_keyMap[crow.ownerLabel];
266             crow.data = decryptDataAesGcm(crow.data, key, crow.iv, crow.tag);
267         }
268
269         if (static_cast<int>(crow.data.size()) < crow.dataSize) {
270             ThrowMsg(Exception::DecryptDBRowError,
271                 "Decrypted row size mismatch");
272             LogError("Decryption row size mismatch");
273         }
274
275         if (static_cast<int>(crow.data.size()) > crow.dataSize) {
276             crow.data.resize(crow.dataSize);
277         }
278
279         row = crow;
280     } catch(const CKM::Base64Encoder::Exception::Base &e) {
281         LogDebug("Base64Encoder error: " << e.GetMessage());
282         ThrowMsg(Exception::Base64EncoderError, e.GetMessage());
283     } catch(const CKM::Base64Decoder::Exception::Base &e) {
284         LogDebug("Base64Encoder error: " << e.GetMessage());
285         ThrowMsg(Exception::Base64DecoderError, e.GetMessage());
286     } catch(const CKM::Crypto::Exception::Base &e) {
287         LogDebug("Crypto error: " << e.GetMessage());
288         ThrowMsg(Exception::DecryptDBRowError, e.GetMessage());
289     }
290 }
291
292 void CryptoLogic::encBase64(RawBuffer &data)
293 {
294     Base64Encoder benc;
295     RawBuffer encdata;
296
297     benc.append(data);
298     benc.finalize();
299     encdata = benc.get();
300
301     if (encdata.size() == 0) {
302         ThrowMsg(Exception::Base64EncoderError, "Base64Encoder returned empty data.");
303     }
304
305     data = std::move(encdata);
306 }
307
308 void CryptoLogic::decBase64(RawBuffer &data)
309 {
310     Base64Decoder bdec;
311     RawBuffer decdata;
312
313     bdec.reset();
314     bdec.append(data);
315     if (not bdec.finalize()) {
316         ThrowMsg(Exception::Base64DecoderError,
317           "Failed in Base64Decoder.finalize.");
318     }
319
320     decdata = bdec.get();
321
322     if (decdata.size() == 0) {
323         ThrowMsg(Exception::Base64DecoderError, "Base64Decoder returned empty data.");
324     }
325
326     data = std::move(decdata);
327 }
328
329 bool CryptoLogic::equalDigests(RawBuffer &dig1, RawBuffer &dig2)
330 {
331     unsigned int dlen = Digest().length();
332
333     if ((dig1.size() != dlen) || (dig2.size() != dlen))
334         return false;
335     return (dig1 == dig2);
336 }
337
338 } // namespace CKM
339