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