1 /*
2  * Copyright (c) 2024-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <memory>
17 #include <mutex>
18 #include <fstream>
19 #include <vector>
20 #include <sstream>
21 #include <iomanip>
22 #include <filesystem>
23 #include <climits>
24 #include <cstdlib>
25 
26 #include "openssl/evp.h"
27 #include "openssl/rand.h"
28 
29 #include "aes_gcm_helper.h"
30 #include "ans_log_wrapper.h"
31 
32 namespace OHOS {
33 namespace Notification {
34     static const uint32_t G_AES_GCM_KEY_LEN{32};
35 static const uint32_t G_AES_GCM_IV_LEN{12};
36 static const uint32_t G_AES_GCM_TAG_LEN{16};
37 static const std::string G_DIR_PATH{"/data/service/el1/public/database/notification_service/keyfile"};
38 static const std::string G_KEY_PATH{"/data/service/el1/public/database/notification_service"};
39 static const int STEP = 2;
40 static const int OFFSET = 4;
41 static const int HEX_OF_A = 10;
42 static const int WIDTH_PER_BYTE = 2;
43 static inline std::mutex g_generateKeyMutex{};
44 
Byte2Hex(const std::string & bytes)45 std::string AesGcmHelper::Byte2Hex(const std::string &bytes)
46 {
47     std::ostringstream oss;
48     for (const unsigned char byte : bytes) {
49         oss << std::hex << std::setw(WIDTH_PER_BYTE) << std::setfill('0') << static_cast<int>(byte);
50     }
51     return oss.str();
52 }
53 
HexChar2Byte(const char & hexCh)54 unsigned char AesGcmHelper::HexChar2Byte(const char &hexCh)
55 {
56     if (hexCh >= '0' && hexCh <= '9') {
57         return hexCh - '0';
58     } else if (hexCh >= 'A' && hexCh <= 'F') {
59         return hexCh - 'A' + HEX_OF_A;
60     } else if (hexCh >= 'a' && hexCh <= 'f') {
61         return hexCh - 'a' + HEX_OF_A;
62     } else {
63         ANS_LOGE("Invalid hex char: %{public}c.", hexCh);
64         return 0;
65     }
66 }
67 
Hex2Byte(const std::string & hex)68 std::string AesGcmHelper::Hex2Byte(const std::string &hex)
69 {
70     if (hex.length() % STEP != 0) {
71         ANS_LOGE("Length of hex is not even.");
72         return 0;
73     }
74     std::string bytes;
75     for (int i = 0; i < static_cast<int>(hex.length()); i += STEP) {
76         unsigned char high = HexChar2Byte(hex[i]);
77         unsigned char low = HexChar2Byte(hex[i + 1]);
78         bytes.push_back(static_cast<char>((high << OFFSET) | low));
79     }
80     return bytes;
81 }
82 
GenerateKey(std::string & key)83 bool AesGcmHelper::GenerateKey(std::string &key)
84 {
85     std::lock_guard<std::mutex> lck(g_generateKeyMutex);
86     const char *keyPathPtr = G_KEY_PATH.c_str();
87     char *resolvedPath = (char *)malloc(PATH_MAX);
88     if (resolvedPath != nullptr) {
89         if (realpath(keyPathPtr, resolvedPath) == NULL) {
90             free(resolvedPath);
91             ANS_LOGE("Fail to resolve the key path");
92             return false;
93         }
94         free(resolvedPath);
95     }
96     std::string keyDir = G_DIR_PATH;
97     const char *fileNamePtr = keyDir.c_str();
98     std::filesystem::path keyPath(keyDir);
99     if (std::filesystem::exists(keyPath)) {
100         std::ifstream keyFile(keyDir);
101         if (keyFile.is_open()) {
102             std::string keyHex;
103             std::getline(keyFile, keyHex);
104             key = Hex2Byte(keyHex);
105             keyFile.close();
106             return true;
107         }
108     }
109     unsigned char aes_key[G_AES_GCM_KEY_LEN];
110     if (!RAND_bytes(aes_key, G_AES_GCM_KEY_LEN)) {
111         ANS_LOGE("Fail to randomly generate the key");
112         return false;
113     }
114     key = std::string(reinterpret_cast<const char *>(aes_key), G_AES_GCM_KEY_LEN);
115     std::string keyHex = Byte2Hex(key);
116     if (!std::filesystem::exists(keyPath.parent_path())) {
117         ANS_LOGE("Fail to save the key");
118         return false;
119     }
120     std::ofstream keyFile(keyDir);
121     if (keyFile.is_open()) {
122         keyFile << keyHex;
123         keyFile.close();
124         ANS_LOGI("Generate new key.");
125     } else {
126         ANS_LOGE("Fail to save the key");
127         return false;
128     }
129     return true;
130 }
131 
Encrypt(const std::string & plainText,std::string & cipherText)132 ErrCode AesGcmHelper::Encrypt(const std::string &plainText, std::string &cipherText)
133 {
134     if (plainText.empty()) {
135         ANS_LOGE("Can't encrypt empty plain text.");
136         return ERR_ANS_INVALID_PARAM;
137     }
138     std::string key{""};
139     bool ret = GenerateKey(key);
140     if (!ret) {
141         ANS_LOGE("Fail to get key while encrypting.");
142         return ERR_ANS_ENCRYPT_FAIL;
143     }
144     ret = EncryptAesGcm(plainText, cipherText, key);
145     if (!ret) {
146         ANS_LOGE("Fail to encrypt with AES-GCM.");
147         return ERR_ANS_ENCRYPT_FAIL;
148     }
149     return ERR_OK;
150 }
151 
Decrypt(std::string & plainText,const std::string & cipherText)152 ErrCode AesGcmHelper::Decrypt(std::string &plainText, const std::string &cipherText)
153 {
154     if (cipherText.empty()) {
155         ANS_LOGE("Can't decrypt empty cipher text.");
156         return ERR_ANS_INVALID_PARAM;
157     }
158     std::string key{""};
159     bool ret = GenerateKey(key);
160     if (!ret) {
161         ANS_LOGE("Fail to get key while decrypting");
162         return ERR_ANS_DECRYPT_FAIL;
163     }
164     ret = DecryptAesGcm(plainText, cipherText, key);
165     if (!ret) {
166         ANS_LOGE("Fail to decrypt with AES-GCM.");
167         return ERR_ANS_DECRYPT_FAIL;
168     }
169     return ERR_OK;
170 }
171 
EncryptAesGcm(const std::string & plainText,std::string & cipherText,std::string & key)172 bool AesGcmHelper::EncryptAesGcm(const std::string &plainText, std::string &cipherText, std::string &key)
173 {
174     const unsigned int bufferLen = plainText.size();
175     std::vector<unsigned char> buffer(bufferLen);
176     std::vector<unsigned char> iv(G_AES_GCM_IV_LEN);
177     std::vector<unsigned char> tag(G_AES_GCM_TAG_LEN);
178     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
179     if (!ctx) {
180         ANS_LOGE("EncryptAesGcm ctx error");
181         return false;
182     }
183     bool ret = true;
184     do {
185         if (!RAND_bytes(iv.data(), G_AES_GCM_IV_LEN)) {
186             ANS_LOGE("EncryptAesGcm RAND_bytes error");
187             ret = false;
188             break;
189         }
190         if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr,
191             reinterpret_cast<const unsigned char *>(key.data()), iv.data())) {
192             ANS_LOGE("EncryptAesGcm EVP_EncryptInit_ex error");
193             ret = false;
194             break;
195         }
196         int len;
197         if (!EVP_EncryptUpdate(ctx, buffer.data(), &len,
198             reinterpret_cast<const unsigned char *>(plainText.data()), bufferLen)) {
199             ANS_LOGE("EncryptAesGcm EVP_EncryptUpdate error");
200             ret = false;
201             break;
202         }
203         if (!EVP_EncryptFinal_ex(ctx, buffer.data() + len, &len)) {
204             ANS_LOGE("EncryptAesGcm EVP_EncryptFinal_ex error");
205             ret = false;
206             break;
207         }
208         if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, G_AES_GCM_TAG_LEN, tag.data())) {
209             ANS_LOGE("EncryptAesGcm EVP_CIPHER_CTX_ctrl error");
210             ret = false;
211             break;
212         }
213         cipherText = std::string(iv.begin(), iv.end());
214         cipherText += std::string(buffer.begin(), buffer.end());
215         cipherText += std::string(tag.begin(), tag.end());
216         cipherText = Byte2Hex(cipherText);
217     } while (0);
218     EVP_CIPHER_CTX_free(ctx);
219     return ret;
220 }
221 
DecryptAesGcm(std::string & plainText,const std::string & cipherText,std::string & key)222 bool AesGcmHelper::DecryptAesGcm(std::string &plainText, const std::string &cipherText, std::string &key)
223 {
224     const unsigned int bufferLen = cipherText.size() - G_AES_GCM_IV_LEN - G_AES_GCM_TAG_LEN;
225     std::vector<unsigned char> buffer(bufferLen);
226     std::vector<unsigned char> iv(G_AES_GCM_IV_LEN);
227     std::vector<unsigned char> cipherByte(bufferLen);
228     std::vector<unsigned char> tag(G_AES_GCM_TAG_LEN);
229     std::string cipherBytes = Hex2Byte(cipherText);
230     iv.assign(cipherBytes.begin(), cipherBytes.begin() + G_AES_GCM_IV_LEN);
231     cipherByte.assign(cipherBytes.begin() + G_AES_GCM_IV_LEN, cipherBytes.end() - G_AES_GCM_TAG_LEN);
232     tag.assign(cipherBytes.end() - G_AES_GCM_TAG_LEN, cipherBytes.end());
233     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
234     if (!ctx) {
235         ANS_LOGE("DecryptAesGcm ctx error");
236         return false;
237     }
238     bool ret = true;
239     do {
240         if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr,
241             reinterpret_cast<const unsigned char *>(key.data()), iv.data())) {
242             ANS_LOGE("DecryptAesGcm EVP_DecryptInit_ex error");
243             ret = false;
244             break;
245         }
246         int len;
247         if (!EVP_DecryptUpdate(ctx, buffer.data(), &len, cipherByte.data(), cipherByte.size())) {
248             ANS_LOGE("DecryptAesGcm EVP_DecryptUpdate error");
249             ret = false;
250             break;
251         }
252         if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, G_AES_GCM_TAG_LEN, tag.data())) {
253             ANS_LOGE("DecryptAesGcm EVP_CIPHER_CTX_ctrl error");
254             ret = false;
255             break;
256         }
257         if (EVP_DecryptFinal_ex(ctx, buffer.data() + len, &len) <= 0) {
258             ANS_LOGE("DecryptAesGcm EVP_DecryptFinal_ex error");
259             ret = false;
260             break;
261         }
262         plainText = std::string(buffer.begin(), buffer.end());
263     } while (0);
264     EVP_CIPHER_CTX_free(ctx);
265     return ret;
266 }
267 
268 }  // namespace Notification
269 }  // namespace OHOS