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