Modify encryption scheme
[platform/core/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 <utility>
26 #include <climits>
27
28 #include <stdio.h>
29 #include <string.h>
30
31 #include <openssl/evp.h>
32 #include <openssl/rand.h>
33
34 #include <ckm/ckm-error.h>
35
36 #include <dpl/log/log.h>
37
38 #include <base64.h>
39 #include <digest.h>
40 #include <crypto-logic.h>
41
42 #include <generic-backend/exception.h>
43 #include <sw-backend/internals.h>
44
45 namespace CKM {
46
47 namespace {
48
49 const static int AES_CBC_KEY_SIZE = 32;
50 const static int AES_GCM_TAG_SIZE = 16;
51
52 // Encryption scheme flags (enable/disable specific encryption type, multiple choice)
53 const int ENCR_BASE64 =   1 << 0;
54 const int ENCR_APPKEY =   1 << 1;
55 const int ENCR_PASSWORD = 1 << 2;
56
57 // Encryption order flags (single choice)
58 const int ENCR_ORDER_OFFSET = 24;
59 const int ENCR_ORDER_FILTER = INT_MAX << ENCR_ORDER_OFFSET; // 0xff000000
60 const int ENCR_ORDER_CLEAR = ~ENCR_ORDER_FILTER; // 0x00ffffff
61 /*
62  * ENCR_ORDER_V1 - v1 encryption order. Token returned from store is encrypted with app key and
63  * optionally by custom user password. In such form it is stored in db.
64  */
65 const int ENCR_ORDER_V1 = CryptoLogic::ENCRYPTION_V1 << ENCR_ORDER_OFFSET;
66 /*
67  * ENCR_ORDER_V2 - v2 encryption order. Stored data is optionally encrypted by store with
68  * user password. Returned token is encrypted with app key and stored in db.
69  */
70 const int ENCR_ORDER_V2 = CryptoLogic::ENCRYPTION_V2 << ENCR_ORDER_OFFSET;
71
72 } // anonymous namespace
73
74 CryptoLogic::CryptoLogic() {}
75
76 CryptoLogic::CryptoLogic(CryptoLogic &&second) {
77     m_keyMap = std::move(second.m_keyMap);
78 }
79
80 CryptoLogic& CryptoLogic::operator=(CryptoLogic &&second) {
81     if (this == &second)
82         return *this;
83     m_keyMap = std::move(second.m_keyMap);
84     return *this;
85 }
86
87 bool CryptoLogic::haveKey(const Label &smackLabel)
88 {
89     return (m_keyMap.count(smackLabel) > 0);
90 }
91
92 void CryptoLogic::pushKey(const Label &smackLabel,
93                           const RawBuffer &applicationKey)
94 {
95     if (smackLabel.length() == 0) {
96         ThrowErr(Exc::InternalError, "Empty smack label.");
97     }
98     if (applicationKey.size() == 0) {
99         ThrowErr(Exc::InternalError, "Empty application key.");
100     }
101     if (haveKey(smackLabel)) {
102         ThrowErr(Exc::InternalError, "Application key for ", smackLabel,
103             "label already exists.");
104     }
105
106     m_keyMap[smackLabel] = applicationKey;
107 }
108
109 void CryptoLogic::removeKey(const Label &smackLabel)
110 {
111     m_keyMap.erase(smackLabel);
112 }
113
114 RawBuffer CryptoLogic::passwordToKey(
115     const Password &password,
116     const RawBuffer &salt,
117     size_t keySize) const
118 {
119     RawBuffer result(keySize);
120
121     if (1 != PKCS5_PBKDF2_HMAC_SHA1(
122                 password.c_str(),
123                 password.size(),
124                 salt.data(),
125                 salt.size(),
126                 1024,
127                 result.size(),
128                 result.data()))
129     {
130         ThrowErr(Exc::InternalError, "PCKS5_PKKDF_HMAC_SHA1 failed.");
131     }
132
133     return result;
134 }
135
136 RawBuffer CryptoLogic::generateRandIV() const {
137     RawBuffer civ(EVP_MAX_IV_LENGTH);
138
139     if (1 != RAND_bytes(civ.data(), civ.size())) {
140         ThrowErr(Exc::InternalError, "RAND_bytes failed to generate IV.");
141     }
142
143     return civ;
144 }
145
146 void CryptoLogic::encryptRow(DB::Row &row)
147 {
148     try {
149         DB::Row crow = row;
150         RawBuffer key;
151         RawBuffer result1;
152         RawBuffer result2;
153
154         crow.algorithmType = DBCMAlgType::AES_GCM_256;
155         crow.dataSize = crow.data.size();
156
157         if (crow.dataSize <= 0) {
158             ThrowErr(Exc::InternalError, "Invalid dataSize.");
159         }
160
161         if (!haveKey(row.ownerLabel)) {
162             ThrowErr(Exc::InternalError, "Missing application key for ",
163               row.ownerLabel, " label.");
164         }
165
166         if (crow.iv.empty()) {
167             crow.iv = generateRandIV();
168         }
169
170         key = m_keyMap[row.ownerLabel];
171         crow.encryptionScheme = ENCR_APPKEY;
172
173         auto dataPair = Crypto::SW::Internals::encryptDataAesGcm(key, crow.data, crow.iv, AES_GCM_TAG_SIZE);
174         crow.data = dataPair.first;
175
176         crow.tag = dataPair.second;
177
178         encBase64(crow.data);
179         crow.encryptionScheme |= ENCR_BASE64;
180         encBase64(crow.iv);
181
182         crow.encryptionScheme &= ENCR_ORDER_CLEAR;
183         crow.encryptionScheme |= ENCR_ORDER_V2;
184
185         row = std::move(crow);
186     } catch(const CKM::Base64Encoder::Exception::Base &e) {
187         ThrowErr(Exc::InternalError, e.GetMessage());
188     } catch(const CKM::Base64Decoder::Exception::Base &e) {
189         ThrowErr(Exc::InternalError, e.GetMessage());
190     }
191 }
192
193 int CryptoLogic::getSchemeVersion(int encryptionScheme)
194 {
195     return encryptionScheme >> ENCR_ORDER_OFFSET;
196 }
197
198 void CryptoLogic::decryptRow(const Password &password, DB::Row &row)
199 {
200     try {
201         DB::Row crow = row;
202         RawBuffer key;
203         RawBuffer digest, dataDigest;
204
205         if (row.algorithmType != DBCMAlgType::AES_GCM_256) {
206             ThrowErr(Exc::AuthenticationFailed, "Invalid algorithm type.");
207         }
208
209         if ((row.encryptionScheme & ENCR_PASSWORD) && password.empty()) {
210             ThrowErr(Exc::AuthenticationFailed,
211               "DB row is password protected, but given password is "
212               "empty.");
213         }
214
215         if ((row.encryptionScheme & ENCR_APPKEY) && !haveKey(row.ownerLabel)) {
216             ThrowErr(Exc::AuthenticationFailed, "Missing application key for ",
217               row.ownerLabel, " label.");
218         }
219
220         decBase64(crow.iv);
221         if (crow.encryptionScheme & ENCR_BASE64) {
222             decBase64(crow.data);
223         }
224
225         if((crow.encryptionScheme >> ENCR_ORDER_OFFSET) == ENCR_ORDER_V2) {
226             if (crow.encryptionScheme & ENCR_APPKEY) {
227                 key = m_keyMap[crow.ownerLabel];
228                 crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
229             }
230         } else {
231             if (crow.encryptionScheme & ENCR_PASSWORD) {
232                 key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE);
233                 crow.data = Crypto::SW::Internals::decryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv);
234             }
235
236             if (crow.encryptionScheme & ENCR_APPKEY) {
237                 key = m_keyMap[crow.ownerLabel];
238                 crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
239             }
240         }
241         if (static_cast<int>(crow.data.size()) < crow.dataSize) {
242             ThrowErr(Exc::AuthenticationFailed, "Decrypted row size mismatch");
243         }
244
245         if (static_cast<int>(crow.data.size()) > crow.dataSize) {
246             crow.data.resize(crow.dataSize);
247         }
248
249         row = std::move(crow);
250     } catch(const CKM::Base64Encoder::Exception::Base &e) {
251         ThrowErr(Exc::InternalError, e.GetMessage());
252     } catch(const CKM::Base64Decoder::Exception::Base &e) {
253         ThrowErr(Exc::InternalError, e.GetMessage());
254     } catch(const Exc::Exception &e) {
255         ThrowErr(Exc::AuthenticationFailed, e.message());
256     }
257 }
258
259 void CryptoLogic::encBase64(RawBuffer &data)
260 {
261     Base64Encoder benc;
262     RawBuffer encdata;
263
264     benc.append(data);
265     benc.finalize();
266     encdata = benc.get();
267
268     if (encdata.size() == 0) {
269         ThrowErr(Exc::InternalError, "Base64Encoder returned empty data.");
270     }
271
272     data = std::move(encdata);
273 }
274
275 void CryptoLogic::decBase64(RawBuffer &data)
276 {
277     Base64Decoder bdec;
278     RawBuffer decdata;
279
280     bdec.reset();
281     bdec.append(data);
282     if (!bdec.finalize()) {
283         ThrowErr(Exc::InternalError, "Failed in Base64Decoder.finalize.");
284     }
285
286     decdata = bdec.get();
287
288     if (decdata.size() == 0) {
289         ThrowErr(Exc::InternalError, "Base64Decoder returned empty data.");
290     }
291
292     data = std::move(decdata);
293 }
294
295 bool CryptoLogic::equalDigests(RawBuffer &dig1, RawBuffer &dig2)
296 {
297     unsigned int dlen = Digest().length();
298
299     if ((dig1.size() != dlen) || (dig2.size() != dlen))
300         return false;
301     return (dig1 == dig2);
302 }
303
304 } // namespace CKM
305