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