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 (realpath(keyPathPtr, resolvedPath) == NULL) {
89         free(resolvedPath);
90         ANS_LOGE("Fail to resolve the key path");
91         return false;
92     }
93     std::string keyDir = G_DIR_PATH;
94     const char *fileNamePtr = keyDir.c_str();
95     std::filesystem::path keyPath(keyDir);
96     if (std::filesystem::exists(keyPath)) {
97         std::ifstream keyFile(keyDir);
98         if (keyFile.is_open()) {
99             std::string keyHex;
100             std::getline(keyFile, keyHex);
101             key = Hex2Byte(keyHex);
102             keyFile.close();
103             return true;
104         }
105     }
106     unsigned char aes_key[G_AES_GCM_KEY_LEN];
107     if (!RAND_bytes(aes_key, G_AES_GCM_KEY_LEN)) {
108         ANS_LOGE("Fail to randomly generate the key");
109         return false;
110     }
111     key = std::string(reinterpret_cast<const char *>(aes_key), G_AES_GCM_KEY_LEN);
112     std::string keyHex = Byte2Hex(key);
113     if (!std::filesystem::exists(keyPath.parent_path())) {
114         ANS_LOGE("Fail to save the key");
115         return false;
116     }
117     std::ofstream keyFile(keyDir);
118     if (keyFile.is_open()) {
119         keyFile << keyHex;
120         keyFile.close();
121         ANS_LOGI("Generate new key.");
122     } else {
123         ANS_LOGE("Fail to save the key");
124         return false;
125     }
126     return true;
127 }
128 
Encrypt(const std::string &plainText, std::string &cipherText)129 ErrCode AesGcmHelper::Encrypt(const std::string &plainText, std::string &cipherText)
130 {
131     if (plainText.empty()) {
132         ANS_LOGE("Can't encrypt empty plain text.");
133         return ERR_ANS_INVALID_PARAM;
134     }
135     std::string key{""};
136     bool ret = GenerateKey(key);
137     if (!ret) {
138         ANS_LOGE("Fail to get key while encrypting.");
139         return ERR_ANS_ENCRYPT_FAIL;
140     }
141     ret = EncryptAesGcm(plainText, cipherText, key);
142     if (!ret) {
143         ANS_LOGE("Fail to encrypt with AES-GCM.");
144         return ERR_ANS_ENCRYPT_FAIL;
145     }
146     return ERR_OK;
147 }
148 
Decrypt(std::string &plainText, const std::string &cipherText)149 ErrCode AesGcmHelper::Decrypt(std::string &plainText, const std::string &cipherText)
150 {
151     if (cipherText.empty()) {
152         ANS_LOGE("Can't decrypt empty cipher text.");
153         return ERR_ANS_INVALID_PARAM;
154     }
155     std::string key{""};
156     bool ret = GenerateKey(key);
157     if (!ret) {
158         ANS_LOGE("Fail to get key while decrypting");
159         return ERR_ANS_DECRYPT_FAIL;
160     }
161     ret = DecryptAesGcm(plainText, cipherText, key);
162     if (!ret) {
163         ANS_LOGE("Fail to decrypt with AES-GCM.");
164         return ERR_ANS_DECRYPT_FAIL;
165     }
166     return ERR_OK;
167 }
168 
EncryptAesGcm(const std::string &plainText, std::string &cipherText, std::string &key)169 bool AesGcmHelper::EncryptAesGcm(const std::string &plainText, std::string &cipherText, std::string &key)
170 {
171     const unsigned int bufferLen = plainText.size();
172     std::vector<unsigned char> buffer(bufferLen);
173     std::vector<unsigned char> iv(G_AES_GCM_IV_LEN);
174     std::vector<unsigned char> tag(G_AES_GCM_TAG_LEN);
175     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
176     if (!ctx) {
177         ANS_LOGE("EncryptAesGcm ctx error");
178         return false;
179     }
180     bool ret = true;
181     do {
182         if (!RAND_bytes(iv.data(), G_AES_GCM_IV_LEN)) {
183             ANS_LOGE("EncryptAesGcm RAND_bytes error");
184             ret = false;
185             break;
186         }
187         if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr,
188             reinterpret_cast<const unsigned char *>(key.data()), iv.data())) {
189             ANS_LOGE("EncryptAesGcm EVP_EncryptInit_ex error");
190             ret = false;
191             break;
192         }
193         int len;
194         if (!EVP_EncryptUpdate(ctx, buffer.data(), &len,
195             reinterpret_cast<const unsigned char *>(plainText.data()), bufferLen)) {
196             ANS_LOGE("EncryptAesGcm EVP_EncryptUpdate error");
197             ret = false;
198             break;
199         }
200         if (!EVP_EncryptFinal_ex(ctx, buffer.data() + len, &len)) {
201             ANS_LOGE("EncryptAesGcm EVP_EncryptFinal_ex error");
202             ret = false;
203             break;
204         }
205         if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, G_AES_GCM_TAG_LEN, tag.data())) {
206             ANS_LOGE("EncryptAesGcm EVP_CIPHER_CTX_ctrl error");
207             ret = false;
208             break;
209         }
210         cipherText = std::string(iv.begin(), iv.end());
211         cipherText += std::string(buffer.begin(), buffer.end());
212         cipherText += std::string(tag.begin(), tag.end());
213         cipherText = Byte2Hex(cipherText);
214     } while (0);
215     EVP_CIPHER_CTX_free(ctx);
216     return ret;
217 }
218 
DecryptAesGcm(std::string &plainText, const std::string &cipherText, std::string &key)219 bool AesGcmHelper::DecryptAesGcm(std::string &plainText, const std::string &cipherText, std::string &key)
220 {
221     const unsigned int bufferLen = cipherText.size() - G_AES_GCM_IV_LEN - G_AES_GCM_TAG_LEN;
222     std::vector<unsigned char> buffer(bufferLen);
223     std::vector<unsigned char> iv(G_AES_GCM_IV_LEN);
224     std::vector<unsigned char> cipherByte(bufferLen);
225     std::vector<unsigned char> tag(G_AES_GCM_TAG_LEN);
226     std::string cipherBytes = Hex2Byte(cipherText);
227     iv.assign(cipherBytes.begin(), cipherBytes.begin() + G_AES_GCM_IV_LEN);
228     cipherByte.assign(cipherBytes.begin() + G_AES_GCM_IV_LEN, cipherBytes.end() - G_AES_GCM_TAG_LEN);
229     tag.assign(cipherBytes.end() - G_AES_GCM_TAG_LEN, cipherBytes.end());
230     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
231     if (!ctx) {
232         ANS_LOGE("DecryptAesGcm ctx error");
233         return false;
234     }
235     bool ret = true;
236     do {
237         if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr,
238             reinterpret_cast<const unsigned char *>(key.data()), iv.data())) {
239             ANS_LOGE("DecryptAesGcm EVP_DecryptInit_ex error");
240             ret = false;
241             break;
242         }
243         int len;
244         if (!EVP_DecryptUpdate(ctx, buffer.data(), &len, cipherByte.data(), cipherByte.size())) {
245             ANS_LOGE("DecryptAesGcm EVP_DecryptUpdate error");
246             ret = false;
247             break;
248         }
249         if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, G_AES_GCM_TAG_LEN, tag.data())) {
250             ANS_LOGE("DecryptAesGcm EVP_CIPHER_CTX_ctrl error");
251             ret = false;
252             break;
253         }
254         if (EVP_DecryptFinal_ex(ctx, buffer.data() + len, &len) <= 0) {
255             ANS_LOGE("DecryptAesGcm EVP_DecryptFinal_ex error");
256             ret = false;
257             break;
258         }
259         plainText = std::string(buffer.begin(), buffer.end());
260     } while (0);
261     EVP_CIPHER_CTX_free(ctx);
262     return ret;
263 }
264 
265 }  // namespace Notification
266 }  // namespace OHOS
267