* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "auth.h"
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/err.h>
#include <fstream>
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
#include "password.h"
#endif
using namespace Hdc;
#define BIGNUMTOBIT 32
namespace HdcAuth {
#ifdef HDC_HOST
bool AuthVerify(uint8_t* , uint8_t* , int )
{
return false;
};
bool PostUIConfirm(string )
{
return false;
}
#else
bool GenerateKey(const char* )
{
return false;
};
#endif
const uint32_t RSANUMBYTES = 512;
const uint32_t RSANUMWORDS = (RSANUMBYTES / sizeof(uint32_t));
struct RSAPublicKey {
int wordModulusSize;
uint32_t rsaN0inv;
uint32_t modulus[RSANUMWORDS];
uint32_t rr[RSANUMWORDS];
BN_ULONG exponent;
};
#ifdef HDC_HOST
int RSA2RSAPublicKey(RSA *rsa, RSAPublicKey *publicKey)
{
int result = 1;
unsigned int i;
BN_CTX *ctx = BN_CTX_new();
BIGNUM *r32 = BN_new();
BIGNUM *rsaRR = BN_new();
BIGNUM *rsaR = BN_new();
BIGNUM *rsaRem = BN_new();
BIGNUM *rsaN0inv = BN_new();
#ifdef OPENSSL_IS_BORINGSSL
BIGNUM *n = BN_new();
BN_copy(n, rsa->n);
publicKey->exponent = BN_get_word(rsa->e);
#else
#if OPENSSL_VERSION_NUMBER >= 0x10100005L
BIGNUM *n = (BIGNUM *)RSA_get0_n(rsa);
publicKey->exponent = BN_get_word(RSA_get0_e(rsa));
#else
BIGNUM *n = BN_new();
BN_copy(n, rsa->n);
publicKey->exponent = BN_get_word(rsa->e);
#endif
#endif
while (true) {
if (RSA_size(rsa) != RSANUMBYTES) {
result = 0;
break;
}
BN_set_bit(r32, BIGNUMTOBIT);
BN_set_bit(rsaR, RSANUMWORDS * BIGNUMTOBIT);
BN_mod_sqr(rsaRR, rsaR, n, ctx);
BN_div(nullptr, rsaRem, n, r32, ctx);
BN_mod_inverse(rsaN0inv, rsaRem, r32, ctx);
publicKey->wordModulusSize = RSANUMWORDS;
publicKey->rsaN0inv = 0 - BN_get_word(rsaN0inv);
for (i = 0; i < RSANUMWORDS; ++i) {
BN_div(rsaRR, rsaRem, rsaRR, r32, ctx);
publicKey->rr[i] = BN_get_word(rsaRem);
BN_div(n, rsaRem, n, r32, ctx);
publicKey->modulus[i] = BN_get_word(rsaRem);
}
break;
}
BN_free(rsaR);
BN_free(rsaRR);
BN_free(n);
BN_free(r32);
BN_free(rsaN0inv);
BN_free(rsaRem);
BN_CTX_free(ctx);
return result;
}
int GetUserInfo(char *buf, size_t len)
{
char hostname[BUF_SIZE_DEFAULT];
char username[BUF_SIZE_DEFAULT];
uv_passwd_t pwd;
int ret = -1;
size_t bufSize = sizeof(hostname);
if (uv_os_gethostname(hostname, &bufSize) < 0 && EOK != strcpy_s(hostname, sizeof(hostname), "unknown")) {
return ERR_API_FAIL;
}
if (!uv_os_get_passwd(&pwd) && !strcpy_s(username, sizeof(username), pwd.username)) {
ret = 0;
}
uv_os_free_passwd(&pwd);
if (ret < 0 && EOK != strcpy_s(username, sizeof(username), "unknown")) {
return ERR_API_FAIL;
}
if (snprintf_s(buf, len, len - 1, " %s@%s", username, hostname) < 0) {
buf[len-1] = '\0';
return ERR_BUF_OVERFLOW;
}
return RET_SUCCESS;
}
int WritePublicKeyfile(RSA *private_key, const char *private_key_path)
{
RSAPublicKey publicKey;
char info[BUF_SIZE_DEFAULT];
int ret = 0;
string path = private_key_path + string(".pub");
ret = RSA2RSAPublicKey(private_key, &publicKey);
if (!ret) {
WRITE_LOG(LOG_DEBUG, "Failed to convert to publickey\n");
return 0;
}
vector<uint8_t> vec = Base::Base64Encode((const uint8_t *)&publicKey, sizeof(RSAPublicKey));
if (!vec.size()) {
return 0;
}
GetUserInfo(info, sizeof(info));
vec.insert(vec.end(), (uint8_t *)info, (uint8_t *)info + strlen(info));
ret = Base::WriteBinFile(path.c_str(), vec.data(), vec.size(), true);
return ret >= 0 ? 1 : 0;
}
bool GenerateKey(const char *file)
{
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
Base::PrintMessage("[E002105] Unsupport command]");
return false;
#endif
EVP_PKEY *publicKey = EVP_PKEY_new();
BIGNUM *exponent = BN_new();
RSA *rsa = RSA_new();
int bits = 4096;
mode_t old_mask;
FILE *fKey = nullptr;
bool ret = false;
while (true) {
WRITE_LOG(LOG_DEBUG, "generate_key '%s'\n", Hdc::MaskString(string(file)).c_str());
if (!publicKey || !exponent || !rsa) {
WRITE_LOG(LOG_DEBUG, "Failed to allocate key");
break;
}
BN_set_word(exponent, RSA_F4);
if (!RSA_generate_key_ex(rsa, bits, exponent, nullptr)) {
WRITE_LOG(LOG_DEBUG, "Failed to generate RSA key");
break;
}
EVP_PKEY_set1_RSA(publicKey, rsa);
old_mask = umask(077);
fKey = Base::Fopen(file, "w");
if (!fKey) {
WRITE_LOG(LOG_DEBUG, "Failed to open '%s'\n", Hdc::MaskString(string(file)).c_str());
umask(old_mask);
break;
}
umask(old_mask);
if (!PEM_write_PrivateKey(fKey, publicKey, nullptr, nullptr, 0, nullptr, nullptr)) {
WRITE_LOG(LOG_DEBUG, "Failed to write key");
break;
}
if (!WritePublicKeyfile(rsa, file)) {
WRITE_LOG(LOG_DEBUG, "Failed to write public key");
break;
}
ret = true;
break;
}
EVP_PKEY_free(publicKey);
BN_free(exponent);
RSA_free(rsa);
if (fKey)
fclose(fKey);
return ret;
}
bool ReadKey(const char *file, list<void *> *listPrivateKey)
{
FILE *f = nullptr;
bool ret = false;
if (file == nullptr || listPrivateKey == nullptr) {
WRITE_LOG(LOG_FATAL, "file or listPrivateKey is null");
return ret;
}
while (true) {
if (!(f = fopen(file, "r"))) {
break;
}
RSA *rsa = RSA_new();
if (!rsa) {
break;
}
if (!PEM_read_RSAPrivateKey(f, &rsa, nullptr, nullptr)) {
RSA_free(rsa);
break;
}
listPrivateKey->push_back((void *)rsa);
ret = true;
break;
}
if (f) {
fclose(f);
}
return ret;
}
bool GetUserKeyPath(string &path)
{
struct stat status;
const char harmoneyPath[] = ".harmony";
const char hdcKeyFile[] = "hdckey";
char buf[BUF_SIZE_DEFAULT];
size_t len = BUF_SIZE_DEFAULT;
if (uv_os_homedir(buf, &len) < 0)
return false;
string dir = string(buf) + Base::GetPathSep() + string(harmoneyPath) + Base::GetPathSep();
path = Base::CanonicalizeSpecPath(dir);
if (path.empty()) {
path = dir;
} else {
path += Base::GetPathSep();
}
if (stat(path.c_str(), &status)) {
uv_fs_t req;
uv_fs_mkdir(nullptr, &req, path.c_str(), 0750, nullptr);
uv_fs_req_cleanup(&req);
uv_fs_stat(nullptr, &req, path.c_str(), nullptr);
uv_fs_req_cleanup(&req);
if (req.result < 0) {
WRITE_LOG(LOG_FATAL, "Cannot mkdir '%s'", Hdc::MaskString(path).c_str());
return false;
}
}
path += hdcKeyFile;
return true;
}
bool LoadHostUserKey(list<void *> *listPrivateKey)
{
struct stat status;
string path;
if (!GetUserKeyPath(path)) {
return false;
}
if (stat(path.c_str(), &status) == -1) {
if (!GenerateKey(path.c_str())) {
WRITE_LOG(LOG_DEBUG, "Failed to generate new key");
return false;
}
}
if (!ReadKey(path.c_str(), listPrivateKey)) {
return false;
}
return true;
}
#else
bool RSAPublicKey2RSA(const uint8_t *keyBuf, RSA **key)
{
const int pubKeyModulusSize = 256;
const int pubKeyModulusSizeWords = pubKeyModulusSize / 4;
const RSAPublicKey *keyStruct = reinterpret_cast<const RSAPublicKey *>(keyBuf);
bool ret = false;
uint8_t modulusBuffer[pubKeyModulusSize];
RSA *newKey = RSA_new();
if (!newKey) {
goto cleanup;
}
if (keyStruct->wordModulusSize != pubKeyModulusSizeWords) {
goto cleanup;
}
if (memcpy_s(modulusBuffer, sizeof(modulusBuffer), keyStruct->modulus, sizeof(modulusBuffer)) != EOK) {
goto cleanup;
}
Base::ReverseBytes(modulusBuffer, sizeof(modulusBuffer));
#ifdef OPENSSL_IS_BORINGSSL
newKey->n = BN_bin2bn(modulusBuffer, sizeof(modulusBuffer), nullptr);
newKey->e = BN_new();
if (!newKey->e || !BN_set_word(newKey->e, keyStruct->exponent) || !newKey->n) {
goto cleanup;
}
#else
#if OPENSSL_VERSION_NUMBER >= 0x10100005L
RSA_set0_key(newKey, BN_bin2bn(modulusBuffer, sizeof(modulusBuffer), nullptr), BN_new(), BN_new());
#else
newKey->n = BN_bin2bn(modulusBuffer, sizeof(modulusBuffer), nullptr);
newKey->e = BN_new();
if (!newKey->e || !BN_set_word(newKey->e, keyStruct->exponent) || !newKey->n) {
goto cleanup;
}
#endif
#endif
*key = newKey;
ret = true;
cleanup:
if (!ret && newKey) {
RSA_free(newKey);
}
return ret;
}
void ReadDaemonKeys(const char *file, list<void *> *listPublicKey)
{
char buf[BUF_SIZE_DEFAULT2] = { 0 };
char *sep = nullptr;
int ret;
FILE *f = Base::Fopen(file, "re");
if (!f) {
WRITE_LOG(LOG_DEBUG, "Can't open '%s'", file);
return;
}
while (fgets(buf, sizeof(buf), f)) {
RSAPublicKey *key = new RSAPublicKey();
if (!key) {
break;
}
sep = strpbrk(buf, " \t");
if (sep) {
*sep = '\0';
}
ret = Base::Base64DecodeBuf(reinterpret_cast<uint8_t *>(buf), strlen(buf), reinterpret_cast<uint8_t *>(key));
if (ret != sizeof(RSAPublicKey)) {
WRITE_LOG(LOG_DEBUG, "%s: Invalid base64 data ret=%d", file, ret);
delete key;
continue;
}
if (key->wordModulusSize != RSANUMWORDS) {
WRITE_LOG(LOG_DEBUG, "%s: Invalid key len %d\n", file, key->wordModulusSize);
delete key;
continue;
}
listPublicKey->push_back(key);
}
fclose(f);
}
bool AuthVerify(uint8_t *token, uint8_t *sig, int siglen)
{
list<void *> listPublicKey;
uint8_t authKeyIndex = 0;
void *ptr = nullptr;
int ret = 0;
int childRet = 0;
while (KeylistIncrement(&listPublicKey, authKeyIndex, &ptr)) {
RSA *rsa = nullptr;
if (!RSAPublicKey2RSA((const uint8_t *)ptr, &rsa)) {
break;
}
childRet = RSA_verify(NID_sha256, reinterpret_cast<const unsigned char *>(token),
RSA_TOKEN_SIZE, reinterpret_cast<const unsigned char *>(sig),
siglen, rsa);
RSA_free(rsa);
if (childRet == 1) {
ret = 1;
break;
}
}
FreeKey(true, &listPublicKey);
return ret;
}
void LoadDaemonKey(list<void *> *listPublicKey)
{
#ifdef HDC_PCDEBUG
char keyPaths[][BUF_SIZE_SMALL] = { "/root/.harmony/hdckey.pub" };
#else
char keyPaths[][BUF_SIZE_SMALL] = { "/data/service/el1/public/hdc/hdc_keys" };
#endif
int num = sizeof(keyPaths) / sizeof(keyPaths[0]);
struct stat buf;
for (int i = 0; i < num; ++i) {
char *p = keyPaths[i];
if (!stat(p, &buf)) {
WRITE_LOG(LOG_DEBUG, "Loading keys from '%s'", p);
ReadDaemonKeys(p, listPublicKey);
}
}
}
bool PostUIConfirm(string )
{
return true;
}
#endif
bool KeylistIncrement(list<void *> *listKey, uint8_t &authKeyIndex, void **out)
{
if (!listKey->size()) {
#ifdef HDC_HOST
LoadHostUserKey(listKey);
#else
LoadDaemonKey(listKey);
#endif
}
if (authKeyIndex == listKey->size()) {
return false;
}
auto listIndex = listKey->begin();
std::advance(listIndex, ++authKeyIndex);
*out = *listIndex;
if (!*out) {
return false;
}
return true;
}
void FreeKey(bool publicOrPrivate, list<void *> *listKey)
{
for (auto &&v : *listKey) {
if (publicOrPrivate) {
delete (RSAPublicKey *)v;
v = nullptr;
} else {
RSA_free((RSA *)v);
v = nullptr;
}
}
listKey->clear();
}
#ifdef HDC_HOST
EVP_PKEY *GenerateNewKey(void)
{
bool success = false;
int bits = RSA_KEY_BITS;
RSA *rsa = RSA_new();
BIGNUM *e = BN_new();
EVP_PKEY *evp = EVP_PKEY_new();
while (true) {
if (!evp || !e || !rsa) {
WRITE_LOG(LOG_FATAL, "allocate key failed");
break;
}
BN_set_word(e, RSA_F4);
if (!RSA_generate_key_ex(rsa, bits, e, nullptr)) {
WRITE_LOG(LOG_FATAL, "generate rsa key failed");
break;
}
if (!EVP_PKEY_set1_RSA(evp, rsa)) {
WRITE_LOG(LOG_FATAL, "evp set rsa failed");
break;
}
WRITE_LOG(LOG_INFO, "generate key pair success");
success = true;
break;
}
if (e)
BN_free(e);
if (success) {
return evp;
}
if (rsa)
RSA_free(rsa);
if (evp)
EVP_PKEY_free(evp);
return nullptr;
}
static bool WritePublicFile(const std::string& fileName, EVP_PKEY *evp)
{
FILE *fp = nullptr;
fp = Base::Fopen(fileName.c_str(), "w");
if (fp == nullptr) {
if (Base::GetCaller() == Base::Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "open %s failed", Hdc::MaskString(fileName).c_str());
} else {
WRITE_LOG(LOG_FATAL, "open %s failed", fileName.c_str());
}
return false;
}
if (!PEM_write_PUBKEY(fp, evp)) {
if (Base::GetCaller() == Base::Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "write public key file %s failed", Hdc::MaskString(fileName).c_str());
} else {
WRITE_LOG(LOG_FATAL, "write public key file %s failed", fileName.c_str());
}
(void)fclose(fp);
return false;
}
(void)fclose(fp);
return true;
}
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
static bool WritePrivatePwdFile(const std::string& fileName, const std::string& encryptPwd)
{
return Base::WriteToFile(fileName, encryptPwd, std::ios::out | std::ios::trunc);
}
#endif
static bool WritePrivateFile(const std::string& fileName, EVP_PKEY *evp)
{
FILE *fp = nullptr;
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
Hdc::HdcPassword pwd(HDC_PRIVATE_KEY_FILE_PWD_KEY_ALIAS);
pwd.GeneratePassword();
if (!pwd.EncryptPwd()) {
WRITE_LOG(LOG_FATAL, "encrypt pwd failed");
return false;
}
std::pair<uint8_t*, int> pwdValue = pwd.GetPassword();
#endif
fp = Base::Fopen(fileName.c_str(), "w");
if (fp == nullptr) {
if (Base::GetCaller() == Base::Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "open %s failed", Hdc::MaskString(fileName).c_str());
} else {
WRITE_LOG(LOG_FATAL, "open %s failed", fileName.c_str());
}
return false;
}
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
if (!PEM_write_PrivateKey(fp, evp, EVP_aes_256_cbc(), pwdValue.first, pwdValue.second, nullptr, nullptr)) {
#else
if (!PEM_write_PrivateKey(fp, evp, nullptr, nullptr, 0, nullptr, nullptr)) {
#endif
WRITE_LOG(LOG_FATAL, "write private key failed");
(void)fclose(fp);
return false;
}
(void)fclose(fp);
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
if (!WritePrivatePwdFile(fileName + ".key", pwd.GetEncryptPassword())) {
WRITE_LOG(LOG_FATAL, "write private key pwd failed");
return false;
}
#endif
return true;
}
bool GenerateKeyPair(const string& prikey_filename, const string& pubkey_filename)
{
bool ret = false;
EVP_PKEY *evp = nullptr;
#ifdef __OHOS__
mode_t old_mask = umask(027);
#else
mode_t old_mask = umask(077);
#endif
while (true) {
evp = GenerateNewKey();
if (!evp) {
WRITE_LOG(LOG_FATAL, "generate new key failed");
break;
}
if (!WritePublicFile(pubkey_filename, evp)) {
break;
}
if (!WritePrivateFile(prikey_filename, evp)) {
break;
}
WRITE_LOG(LOG_INFO, "generate key pair success");
ret = true;
break;
}
if (evp) {
EVP_PKEY_free(evp);
}
umask(old_mask);
return ret;
}
bool LoadPublicKey(const string& pubkey_filename, string &pubkey)
{
bool ret = false;
BIO *bio = nullptr;
EVP_PKEY *evp = nullptr;
FILE *file_pubkey = nullptr;
do {
file_pubkey = Base::Fopen(pubkey_filename.c_str(), "r");
if (!file_pubkey) {
WRITE_LOG(LOG_FATAL, "open file %s failed", Hdc::MaskString(pubkey_filename).c_str());
break;
}
evp = PEM_read_PUBKEY(file_pubkey, NULL, NULL, NULL);
if (!evp) {
WRITE_LOG(LOG_FATAL, "read pubkey from %s failed", Hdc::MaskString(pubkey_filename).c_str());
break;
}
bio = BIO_new(BIO_s_mem());
if (!bio) {
WRITE_LOG(LOG_FATAL, "alloc bio mem failed");
break;
}
if (!PEM_write_bio_PUBKEY(bio, evp)) {
WRITE_LOG(LOG_FATAL, "write bio failed");
break;
}
size_t len = 0;
char buf[RSA_KEY_BITS] = {0};
if (BIO_read_ex(bio, buf, sizeof(buf), &len) <= 0) {
WRITE_LOG(LOG_FATAL, "read bio failed");
break;
}
pubkey = string(buf, len);
ret = true;
WRITE_LOG(LOG_INFO, "load pubkey from file(%s) success", Hdc::MaskString(pubkey_filename).c_str());
} while (0);
if (evp) {
EVP_PKEY_free(evp);
evp = nullptr;
}
if (bio) {
BIO_free(bio);
bio = nullptr;
}
if (file_pubkey) {
fclose(file_pubkey);
file_pubkey = nullptr;
}
return ret;
}
bool TryLoadPublicKey(string &pubkey)
{
string prikey_filename;
struct stat status;
if (!GetUserKeyPath(prikey_filename)) {
WRITE_LOG(LOG_FATAL, "get key path failed");
return false;
}
string pubkey_filename = prikey_filename + ".pub";
#ifdef _WIN32
string prikeyFilenameWin = Base::UnicodeToUtf8(prikey_filename.c_str(), true);
if (stat(prikeyFilenameWin.c_str(), &status) == -1) {
#else
if (stat(prikey_filename.c_str(), &status) == -1) {
#endif
if (!GenerateKeyPair(prikey_filename, pubkey_filename)) {
WRITE_LOG(LOG_FATAL, "generate new key failed");
return false;
}
}
if (!LoadPublicKey(pubkey_filename, pubkey)) {
WRITE_LOG(LOG_FATAL, "load key failed");
return false;
}
WRITE_LOG(LOG_INFO, "load pubkey success");
return true;
}
bool GetHostName(string &hostname)
{
int ret;
char buf[BUF_SIZE_DEFAULT] = {0};
size_t bufsize = sizeof(buf);
ret = uv_os_gethostname(buf, &bufsize);
if (ret != 0) {
WRITE_LOG(LOG_FATAL, "get hostname failed: %d", ret);
return false;
}
hostname = string(buf, bufsize);
WRITE_LOG(LOG_INFO, "hostname: %s", Hdc::MaskString(hostname).c_str());
return true;
}
bool GetPublicKeyinfo(string &pubkey_info)
{
string hostname;
if (!GetHostName(hostname)) {
WRITE_LOG(LOG_FATAL, "gethostname failed");
return false;
}
string pubkey;
if (!HdcAuth::TryLoadPublicKey(pubkey)) {
WRITE_LOG(LOG_FATAL, "load public key failed");
return false;
}
pubkey_info = hostname;
pubkey_info.append(HDC_HOST_DAEMON_BUF_SEPARATOR);
pubkey_info.append(pubkey);
WRITE_LOG(LOG_INFO, "Get pubkey info success");
return true;
}
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
bool ReadEncryptKeyFile(const std::string& privateKeyFile, std::vector<uint8_t>& fileData, int needFileLength)
{
std::string privateKeyKeyFile = privateKeyFile + ".key";
std::ifstream inFile(privateKeyKeyFile.c_str(), std::ios::binary);
if (!inFile) {
WRITE_LOG(LOG_FATAL, "open file %s failed", Hdc::MaskString(privateKeyKeyFile).c_str());
return false;
}
fileData.resize(needFileLength);
inFile.read(reinterpret_cast<char*>(fileData.data()), needFileLength);
if (inFile.eof() || inFile.fail()) {
WRITE_LOG(LOG_FATAL, "read file %s failed", Hdc::MaskString(privateKeyKeyFile).c_str());
inFile.close();
return false;
}
inFile.close();
return true;
}
static uint8_t* GetPlainPwd(const std::string& privateKeyFile)
{
std::vector<uint8_t> encryptPwd;
Hdc::HdcPassword pwd(HDC_PRIVATE_KEY_FILE_PWD_KEY_ALIAS);
if (!ReadEncryptKeyFile(privateKeyFile, encryptPwd, pwd.GetEncryptPwdLength())) {
return nullptr;
}
if (!pwd.DecryptPwd(encryptPwd)) {
return nullptr;
}
std::pair<uint8_t*, int> plainPwd = pwd.GetPassword();
if (plainPwd.first == nullptr) {
return nullptr;
}
uint8_t *localPwd = new(std::nothrow)uint8_t[plainPwd.second + 1];
if (localPwd == nullptr) {
WRITE_LOG(LOG_FATAL, "out of mem %d", plainPwd.second);
return nullptr;
}
if (memcpy_s(localPwd, plainPwd.second, plainPwd.first, plainPwd.second) != EOK) {
delete []localPwd;
localPwd = nullptr;
return nullptr;
}
localPwd[plainPwd.second] = '\0';
return localPwd;
}
#endif
static bool LoadPrivateKey(const string& prikey_filename, RSA **rsa, EVP_PKEY **evp)
{
FILE *file_prikey = nullptr;
bool ret = false;
*rsa = nullptr;
*evp = nullptr;
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
char *pwd = reinterpret_cast<char*>(GetPlainPwd(prikey_filename));
if (pwd == nullptr) {
return false;
}
#endif
do {
file_prikey = Base::Fopen(prikey_filename.c_str(), "r");
if (!file_prikey) {
WRITE_LOG(LOG_FATAL, "open file %s failed", Hdc::MaskString(prikey_filename).c_str());
break;
}
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
*evp = PEM_read_PrivateKey(file_prikey, NULL, NULL, pwd);
#else
*evp = PEM_read_PrivateKey(file_prikey, NULL, NULL, NULL);
#endif
if (*evp == nullptr) {
WRITE_LOG(LOG_FATAL, "read prikey from %s failed", Hdc::MaskString(prikey_filename).c_str());
break;
}
*rsa = EVP_PKEY_get1_RSA(*evp);
ret = true;
WRITE_LOG(LOG_FATAL, "load prikey success");
} while (0);
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
memset_s(pwd, strlen(pwd), 0, strlen(pwd));
delete[] pwd;
#endif
if (file_prikey) {
fclose(file_prikey);
file_prikey = nullptr;
}
return ret;
}
static bool MakeRsaSign(EVP_PKEY_CTX *ctx, string &result, unsigned char *digest, int digestLen)
{
size_t signResultLen = 0;
int signOutLen = 0;
if (EVP_PKEY_sign(ctx, nullptr, &signResultLen, digest, digestLen) <= 0) {
WRITE_LOG(LOG_FATAL, "get sign result length failed");
return false;
}
try {
std::unique_ptr<unsigned char[]> signResult = std::make_unique<unsigned char[]>(signResultLen);
std::unique_ptr<unsigned char[]> signOut = std::make_unique<unsigned char[]>(signResultLen * 2);
if (EVP_PKEY_sign(ctx, signResult.get(), &signResultLen, digest, digestLen) <=0) {
unsigned long err = ERR_get_error();
if (err != 0) {
char *err_str = ERR_error_string(err, NULL);
WRITE_LOG(LOG_FATAL, "sign failed: %s", err_str);
}
return false;
}
signOutLen = EVP_EncodeBlock(signOut.get(), signResult.get(), signResultLen);
result = string(reinterpret_cast<char *>(signOut.get()), signOutLen);
} catch (std::exception &e) {
WRITE_LOG(LOG_FATAL, "sign failed for exception %s", e.what());
return false;
}
WRITE_LOG(LOG_INFO, "sign success");
return true;
}
bool RsaSign(string &buf, EVP_PKEY *signKey)
{
unsigned char sha512Hash[SHA512_DIGEST_LENGTH];
EVP_PKEY_CTX *ctx = nullptr;
bool signRet = false;
do {
ctx = EVP_PKEY_CTX_new(signKey, nullptr);
if (ctx == nullptr) {
WRITE_LOG(LOG_FATAL, "EVP_PKEY_CTX_new failed");
break;
}
if (EVP_PKEY_sign_init(ctx) <= 0) {
WRITE_LOG(LOG_FATAL, "EVP_PKEY_CTX_new failed");
break;
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0 ||
EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, RSA_PSS_SALTLEN_DIGEST) <= 0) {
WRITE_LOG(LOG_FATAL, "set saltlen or padding failed");
break;
}
if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha512()) <= 0) {
WRITE_LOG(LOG_FATAL, "EVP_PKEY_CTX_set_signature_md failed");
break;
}
SHA512(reinterpret_cast<const unsigned char *>(buf.c_str()), buf.size(), sha512Hash);
signRet = MakeRsaSign(ctx, buf, sha512Hash, sizeof(sha512Hash));
} while (0);
if (ctx != nullptr) {
EVP_PKEY_CTX_free(ctx);
}
return signRet;
}
bool RsaEncrypt(string &buf, RSA *rsa)
{
int signOriSize;
int signOutSize;
unsigned char signOri[BUF_SIZE_DEFAULT2] = { 0 };
unsigned char *signOut = nullptr;
int inSize = buf.size();
const unsigned char *in = reinterpret_cast<const unsigned char *>(buf.c_str());
signOriSize = RSA_private_encrypt(inSize, in, signOri, rsa, RSA_PKCS1_PADDING);
if (signOriSize <= 0) {
WRITE_LOG(LOG_FATAL, "encrypt failed");
return false;
}
signOut = new(std::nothrow) unsigned char[signOriSize * 2];
if (signOut == nullptr) {
WRITE_LOG(LOG_FATAL, "alloc mem failed");
return false;
}
signOutSize = EVP_EncodeBlock(signOut, signOri, signOriSize);
if (signOutSize <= 0) {
WRITE_LOG(LOG_FATAL, "encode buf failed");
delete[] signOut;
signOut = nullptr;
return false;
}
buf = string(reinterpret_cast<char *>(signOut), signOutSize);
WRITE_LOG(LOG_INFO, "sign success");
delete[] signOut;
signOut = nullptr;
return true;
}
bool RsaSignAndBase64(string &buf, AuthVerifyType type)
{
RSA *rsa = nullptr;
EVP_PKEY *evp = nullptr;
string prikeyFileName;
bool signResult = false;
if (!GetUserKeyPath(prikeyFileName)) {
WRITE_LOG(LOG_FATAL, "get key path failed");
return false;
}
if (!LoadPrivateKey(prikeyFileName, &rsa, &evp)) {
WRITE_LOG(LOG_FATAL, "load prikey from file(%s) failed", Hdc::MaskString(prikeyFileName).c_str());
return false;
}
if (type == AuthVerifyType::RSA_3072_SHA512) {
signResult = RsaSign(buf, evp);
} else {
signResult = RsaEncrypt(buf, rsa);
}
if (rsa != nullptr) {
RSA_free(rsa);
}
if (evp != nullptr) {
EVP_PKEY_free(evp);
}
return signResult;
}
int RsaPrikeyDecryptPsk(const unsigned char* in, int inLen, unsigned char* out, int outBufSize)
{
if (in == nullptr || out == nullptr) {
WRITE_LOG(LOG_FATAL, "RsaPrikeyDecryptPsk IO buf is alloc failed");
return -1;
}
if (inLen > BUF_SIZE_DEFAULT2 || inLen <= 0) {
WRITE_LOG(LOG_FATAL, "invalid encryptToken, length is %d", inLen);
return -1;
}
if (outBufSize < PSK_MAX_PSK_LEN) {
WRITE_LOG(LOG_FATAL, "out buffer is too small");
return -1;
}
RSA *rsa = nullptr;
EVP_PKEY *evp = nullptr;
string prikeyFileName;
int outLen = -1;
do {
if (!GetUserKeyPath(prikeyFileName)) {
WRITE_LOG(LOG_FATAL, "get key path failed");
break;
}
if (!LoadPrivateKey(prikeyFileName, &rsa, &evp)) {
WRITE_LOG(LOG_FATAL, "load prikey from file(%s) failed", Hdc::MaskString(prikeyFileName).c_str());
break;
}
unsigned char tokenDecode[BUF_SIZE_DEFAULT] = { 0 };
int tbytes = EVP_DecodeBlock(tokenDecode, in, inLen);
if (tbytes <= 0) {
WRITE_LOG(LOG_FATAL, "base64 decode PreShared Key failed");
break;
}
outLen = RSA_private_decrypt(tbytes, tokenDecode, out, rsa, RSA_PKCS1_OAEP_PADDING);
if (outLen < 0) {
WRITE_LOG(LOG_FATAL, "RSA_private_decrypt failed(%lu)", ERR_get_error());
break;
}
} while (0);
if (rsa != nullptr) {
RSA_free(rsa);
}
if (evp != nullptr) {
EVP_PKEY_free(evp);
}
return outLen;
}
#ifdef HDC_SUPPORT_ENCRYPT_PRIVATE_KEY
int IsEncryptedPEM(const std::string& prikeyFileName)
{
if (prikeyFileName.empty()) {
WRITE_LOG(LOG_FATAL, "private key file name is empty");
return -1;
}
FILE* filePrikey = Base::Fopen(prikeyFileName.c_str(), "r");
if (filePrikey == nullptr) {
WRITE_LOG(LOG_FATAL, "open private key file failed!error code: %d", strerror(errno));
return 1;
}
char buf[BUF_SIZE_SMALL] = {0};
int ret = 1;
if (fgets(buf, sizeof(buf), filePrikey) != nullptr) {
if (strncmp(buf, HDC_PRIVATE_KEY_FILE_FIRST_LINE_STR.c_str(),
HDC_PRIVATE_KEY_FILE_FIRST_LINE_STR.size()) == 0) {
WRITE_LOG(LOG_INFO, "private key is not encrypted, Re encrypt!");
ret = 0;
} else {
WRITE_LOG(LOG_INFO, "private key is encrypted, not re encrypt!");
ret = 1;
}
}
if (fclose(filePrikey) != 0) {
WRITE_LOG(LOG_FATAL, "fclose error:%d", strerror(errno));
return -1;
}
return ret;
}
bool CheckPrivateKeyFile()
{
std::string prikeyFileName;
if (!GetUserKeyPath(prikeyFileName)) {
WRITE_LOG(LOG_FATAL, "get key path failed");
return false;
}
int ret = IsEncryptedPEM(prikeyFileName);
if (ret == 0) {
EVP_PKEY* evp = GenerateNewKey();
if (evp == nullptr) {
WRITE_LOG(LOG_FATAL, "generate new key failed");
return false;
}
if (!WritePrivateFile(prikeyFileName, evp)) {
WRITE_LOG(LOG_FATAL, "write private key failed");
EVP_PKEY_free(evp);
return false;
}
} else if (ret < 0) {
WRITE_LOG(LOG_FATAL, "check private key file failed");
return false;
}
return true;
}
#endif
#else
int RsaPubkeyEncryptPsk(const unsigned char* in, int inLen, unsigned char* out, int outBufSize, const string& pubkey)
{
if (in == nullptr || out == nullptr) {
WRITE_LOG(LOG_FATAL, "RsaPubkeyEncryptPsk IO buf is alloc failed");
return -1;
}
BIO *bio = nullptr;
RSA *rsa = nullptr;
int outLen = -1;
do {
bio = BIO_new(BIO_s_mem());
if (bio == nullptr) {
WRITE_LOG(LOG_FATAL, "RsaPubkeyEncryptPsk create bio failed");
break;
}
int wbytes = BIO_write(bio, reinterpret_cast<const unsigned char *>(pubkey.c_str()), pubkey.length());
if (wbytes != static_cast<int>(pubkey.length())) {
WRITE_LOG(LOG_FATAL, "RsaPubkeyEncryptPsk bio write failed %d ", wbytes);
break;
}
rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr);
if (rsa == nullptr) {
WRITE_LOG(LOG_FATAL, "RsaPubkeyEncryptPsk rsa failed");
break;
}
unsigned char encryptedBuf[BUF_SIZE_DEFAULT2] = { 0 };
int encryptedBufSize = RSA_public_encrypt(inLen, in, encryptedBuf, rsa, RSA_PKCS1_OAEP_PADDING);
if (encryptedBufSize <= 0 || (outBufSize < ((encryptedBufSize + 2) / 3 * 4))) {
WRITE_LOG(LOG_FATAL, "encrypt PreShared Key failed, encryptedBufSize: %d", encryptedBufSize);
break;
}
outLen = EVP_EncodeBlock(out, encryptedBuf, encryptedBufSize);
if (outLen <= 0) {
WRITE_LOG(LOG_FATAL, "base64 encode PreShared Key failed");
break;
}
} while (0);
if (bio != nullptr) {
BIO_free(bio);
}
if (rsa != nullptr) {
RSA_free(rsa);
}
return outLen;
}
#endif
}