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