* Copyright (c) 2023-2024 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 "cert_utils.h"
#include <chrono>
#include <cstring>
#include <memory>
#include <openssl/rand.h>
#include <securec.h>
#include <string>
#include <thread>
#include <vector>
#include "byte_buffer.h"
#include "cs_hisysevent.h"
#include "errcode.h"
#include "huks_param_set.h"
#include "log.h"
namespace OHOS {
namespace Security {
namespace CodeSign {
static const uint32_t CERT_DATA_SIZE = 8192;
static const uint32_t CHALLENGE_LEN = 32;
static inline uint8_t *CastToUint8Ptr(uint32_t *ptr)
{
return reinterpret_cast<uint8_t *>(ptr);
}
bool ConstructDataToCertChain(struct HksCertChain **certChain, int certsCount)
{
*certChain = static_cast<struct HksCertChain *>(malloc(sizeof(struct HksCertChain)));
if (*certChain == nullptr) {
LOG_ERROR("malloc fail");
return false;
}
(*certChain)->certsCount = CERT_COUNT;
(*certChain)->certs = static_cast<struct HksBlob *>(malloc(sizeof(struct HksBlob) *
((*certChain)->certsCount)));
if ((*certChain)->certs == nullptr) {
free(*certChain);
*certChain = nullptr;
return false;
}
for (uint32_t i = 0; i < (*certChain)->certsCount; i++) {
(*certChain)->certs[i].size = CERT_DATA_SIZE;
(*certChain)->certs[i].data = static_cast<uint8_t *>(malloc((*certChain)->certs[i].size));
if ((*certChain)->certs[i].data == nullptr) {
LOG_ERROR("malloc fail");
FreeCertChain(certChain, i);
return false;
}
}
return true;
}
void FreeCertChain(struct HksCertChain **certChain, const uint32_t pos)
{
if (*certChain == nullptr) {
return;
}
if ((*certChain)->certs == nullptr) {
free(*certChain);
*certChain = nullptr;
return;
}
for (uint32_t j = 0; j < pos; j++) {
if ((*certChain)->certs[j].data != nullptr) {
free((*certChain)->certs[j].data);
(*certChain)->certs[j].data = nullptr;
}
}
free((*certChain)->certs);
(*certChain)->certs = nullptr;
free(*certChain);
*certChain = nullptr;
}
bool FormattedCertChain(const HksCertChain *certChain, ByteBuffer &buffer)
{
uint32_t certsCount = certChain->certsCount;
uint32_t totalLen = sizeof(uint32_t);
for (uint32_t i = 0; i < certsCount; i++) {
totalLen += sizeof(uint32_t) + certChain->certs[i].size;
}
buffer.Resize(totalLen);
if (!buffer.PutData(0, CastToUint8Ptr(&certsCount), sizeof(uint32_t))) {
return false;
}
uint32_t pos = sizeof(uint32_t);
for (uint32_t i = 0; i < certsCount; i++) {
if (!buffer.PutData(pos, CastToUint8Ptr(&certChain->certs[i].size), sizeof(uint32_t))) {
return false;
}
pos += sizeof(uint32_t);
if (!buffer.PutData(pos, certChain->certs[i].data, certChain->certs[i].size)) {
return false;
}
pos += certChain->certs[i].size;
}
return true;
}
static inline bool CheckSizeAndAssign(uint8_t *&bufferPtr, uint32_t &restSize, uint32_t &retSize)
{
if (restSize < sizeof(uint32_t)) {
return false;
}
if (memcpy_s(&retSize, sizeof(uint32_t), bufferPtr, sizeof(uint32_t)) != EOK) {
return false;
}
bufferPtr += sizeof(uint32_t);
restSize -= sizeof(uint32_t);
return true;
}
static inline bool CheckSizeAndCopy(uint8_t *&bufferPtr, uint32_t &restSize, const uint32_t size,
ByteBuffer &ret)
{
if (restSize < size) {
return false;
}
if (!ret.CopyFrom(bufferPtr, size)) {
return false;
}
bufferPtr += size;
restSize -= size;
return true;
}
bool GetCertChainFormBuffer(const ByteBuffer &certChainBuffer,
ByteBuffer &signCert, ByteBuffer &issuer, std::vector<ByteBuffer> &chain)
{
uint8_t *rawPtr = certChainBuffer.GetBuffer();
if (rawPtr == nullptr || certChainBuffer.GetSize() < sizeof(uint32_t)) {
LOG_ERROR("empty cert chain buffer.");
return false;
}
uint32_t certsCount;
if (memcpy_s(&certsCount, sizeof(uint32_t), rawPtr, sizeof(uint32_t)) != EOK) {
return false;
}
rawPtr += sizeof(uint32_t);
if (certsCount == 0) {
return false;
}
uint32_t certSize;
bool ret = true;
uint32_t restSize = certChainBuffer.GetSize() - sizeof(uint32_t);
for (uint32_t i = 0; i < certsCount - 1; i++) {
if (!CheckSizeAndAssign(rawPtr, restSize, certSize)) {
return false;
}
if (i == 0) {
ret = CheckSizeAndCopy(rawPtr, restSize, certSize, signCert);
} else if (i == 1) {
ret = CheckSizeAndCopy(rawPtr, restSize, certSize, issuer);
} else {
ByteBuffer cert;
ret = CheckSizeAndCopy(rawPtr, restSize, certSize, cert);
chain.emplace_back(cert);
}
if (!ret) {
LOG_ERROR("failed at index = %{public}u", i);
break;
}
}
return ret;
}
std::unique_ptr<ByteBuffer> GetRandomChallenge()
{
std::unique_ptr<ByteBuffer> challenge = std::make_unique<ByteBuffer>(CHALLENGE_LEN);
if (challenge == nullptr) {
LOG_ERROR("Failed to allocate ByteBuffer for challenge");
return nullptr;
}
const int maxRetries = 4;
int delayMs = 50;
for (int i = 1; i <= maxRetries; i++) {
if (RAND_bytes(challenge->GetBuffer(), CHALLENGE_LEN) == 1) {
return challenge;
}
if (i < maxRetries) {
LOG_WARN("RAND_bytes failed, retry %{public}d/%{public}d after %{public}dms",
i, maxRetries - 1, delayMs);
std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
delayMs *= 2;
}
}
LOG_ERROR("RAND_bytes failed after %{public}d retries", maxRetries);
ReportLoadSAError(CS_ERR_GENERATE_CHALLENGE);
return nullptr;
}
bool CheckChallengeSize(uint32_t size)
{
if (size > CHALLENGE_LEN) {
return false;
}
return true;
}
}
}
}