273 lines
8.7 KiB
C++
273 lines
8.7 KiB
C++
|
|
/*
|
||
|
|
* Copyright (C) 2022 The Android Open Source Project
|
||
|
|
*
|
||
|
|
* 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 <inttypes.h>
|
||
|
|
#include <cstdint>
|
||
|
|
|
||
|
|
#include "chre/platform/log.h"
|
||
|
|
#include "chre/platform/shared/authentication.h"
|
||
|
|
#include "chre/util/macros.h"
|
||
|
|
|
||
|
|
#include "mbedtls/pk.h"
|
||
|
|
#include "mbedtls/sha256.h"
|
||
|
|
|
||
|
|
namespace chre {
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
// All the size below are in bytes
|
||
|
|
constexpr uint32_t kEcdsaP256SigSize = 64;
|
||
|
|
constexpr uint32_t kEcdsaP256PublicKeySize = 64;
|
||
|
|
constexpr uint32_t kHeaderSize = 0x1000;
|
||
|
|
constexpr uint32_t kSha256HashSize = 32;
|
||
|
|
|
||
|
|
// ASCII of "CHRE", in BE
|
||
|
|
constexpr uint32_t kChreMagicNumber = 0x45524843;
|
||
|
|
|
||
|
|
// Production public key
|
||
|
|
const uint8_t kGooglePublicKey[kEcdsaP256PublicKeySize] = {
|
||
|
|
0x97, 0x66, 0x1f, 0xe7, 0x26, 0xc5, 0xc3, 0x9c, 0xe6, 0x71, 0x59,
|
||
|
|
0x1f, 0x26, 0x3b, 0x1c, 0x87, 0x50, 0x7f, 0xad, 0x4f, 0xeb, 0x4b,
|
||
|
|
0xe5, 0x3b, 0xee, 0x76, 0xff, 0x80, 0x6a, 0x8b, 0x6d, 0xed, 0x58,
|
||
|
|
0xd7, 0xed, 0xf3, 0x18, 0x9e, 0x9a, 0xac, 0xcf, 0xfc, 0xd2, 0x7,
|
||
|
|
0x35, 0x64, 0x54, 0xcc, 0xbc, 0x8b, 0xe0, 0x6c, 0x77, 0xbe, 0xbb,
|
||
|
|
0x1b, 0xdd, 0x18, 0x6d, 0x77, 0xfe, 0xb7, 0x0, 0xd5};
|
||
|
|
|
||
|
|
const uint8_t *const kTrustedPublicKeys[] = {kGooglePublicKey};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A data structure encapsulating metadata necessary for nanoapp binary
|
||
|
|
* signature verification.
|
||
|
|
*
|
||
|
|
* Note that the structure field names that start with 'reserved' are currently
|
||
|
|
* unused.
|
||
|
|
*/
|
||
|
|
struct HeaderInfo {
|
||
|
|
/**
|
||
|
|
* A magic number indicating the start of the header info, ASCII decodes to
|
||
|
|
* 'CHRE'.
|
||
|
|
*/
|
||
|
|
uint32_t magic;
|
||
|
|
|
||
|
|
uint32_t headerVersion;
|
||
|
|
|
||
|
|
// TODO(b/260099197): We should have a hardware backed rollback info check.
|
||
|
|
uint32_t reservedRollbackInfo;
|
||
|
|
|
||
|
|
/** The size in bytes of the actual nanoapp binary. */
|
||
|
|
uint32_t binaryLength;
|
||
|
|
|
||
|
|
/** The flag indicating the public key size. */
|
||
|
|
uint64_t flags[2];
|
||
|
|
|
||
|
|
/** The SHA-256 hash of the actual nanoapp binary. */
|
||
|
|
uint8_t binarySha256[kSha256HashSize];
|
||
|
|
|
||
|
|
uint8_t reservedChipId[32];
|
||
|
|
|
||
|
|
uint8_t reservedAuthConfig[256];
|
||
|
|
|
||
|
|
uint8_t reservedImageConfig[256];
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A header containing information relevant to nanoapp signature authentication
|
||
|
|
* that is tacked onto every signed nanoapp.
|
||
|
|
*/
|
||
|
|
struct ImageHeader {
|
||
|
|
/** The zero-padded signature of the nanoapp binary. */
|
||
|
|
uint8_t signature[512];
|
||
|
|
|
||
|
|
/** The zero-padded public key for the key pair used to sign the hash, which
|
||
|
|
* we use to verify whether we trust the signer or not. */
|
||
|
|
uint8_t publicKey[512];
|
||
|
|
|
||
|
|
/** @see struct HeaderInfo. */
|
||
|
|
HeaderInfo headerInfo;
|
||
|
|
};
|
||
|
|
|
||
|
|
class Authenticator {
|
||
|
|
public:
|
||
|
|
Authenticator() {
|
||
|
|
mbedtls_ecp_group_init(&mGroup);
|
||
|
|
mbedtls_ecp_point_init(&mQ);
|
||
|
|
mbedtls_mpi_init(&mR);
|
||
|
|
mbedtls_mpi_init(&mS);
|
||
|
|
}
|
||
|
|
|
||
|
|
~Authenticator() {
|
||
|
|
mbedtls_mpi_free(&mS);
|
||
|
|
mbedtls_mpi_free(&mR);
|
||
|
|
mbedtls_ecp_point_free(&mQ);
|
||
|
|
mbedtls_ecp_group_free(&mGroup);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool loadEcpGroup() {
|
||
|
|
int result = mbedtls_ecp_group_load(&mGroup, MBEDTLS_ECP_DP_SECP256R1);
|
||
|
|
if (result != 0) {
|
||
|
|
LOGE("Failed to load ecp group. Error code: %d", result);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool loadPublicKey(const uint8_t *publicKey) {
|
||
|
|
// 0x04 prefix is required by mbedtls
|
||
|
|
constexpr uint8_t kPublicKeyPrefix = 0x04;
|
||
|
|
uint8_t buffer[kEcdsaP256PublicKeySize + 1] = {kPublicKeyPrefix};
|
||
|
|
memcpy(buffer + 1, publicKey, kEcdsaP256PublicKeySize);
|
||
|
|
int result =
|
||
|
|
mbedtls_ecp_point_read_binary(&mGroup, &mQ, buffer, ARRAY_SIZE(buffer));
|
||
|
|
if (result != 0) {
|
||
|
|
LOGE("Failed to load the public key. Error code: %d", result);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool loadSignature(const ImageHeader *header) {
|
||
|
|
constexpr uint32_t kRSigSize = kEcdsaP256SigSize / 2;
|
||
|
|
constexpr uint32_t kSSigSize = kEcdsaP256SigSize / 2;
|
||
|
|
int result = mbedtls_mpi_read_binary(&mR, header->signature, kRSigSize);
|
||
|
|
if (result != 0) {
|
||
|
|
LOGE("Failed to read r signature. Error code: %d", result);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
result =
|
||
|
|
mbedtls_mpi_read_binary(&mS, header->signature + kRSigSize, kSSigSize);
|
||
|
|
if (result != 0) {
|
||
|
|
LOGE("Failed to read s signature. Error code: %d", result);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool authenticate(const void *binary) {
|
||
|
|
constexpr size_t kDataOffset = 0x200;
|
||
|
|
constexpr size_t kDataSize = kHeaderSize - kDataOffset;
|
||
|
|
auto data = static_cast<const uint8_t *>(binary) + kDataOffset;
|
||
|
|
unsigned char digest[kSha256HashSize] = {};
|
||
|
|
mbedtls_sha256(data, kDataSize, digest, /* is224= */ 0);
|
||
|
|
int result = mbedtls_ecdsa_verify(&mGroup, digest, ARRAY_SIZE(digest), &mQ,
|
||
|
|
&mR, &mS);
|
||
|
|
if (result != 0) {
|
||
|
|
LOGE("Signature verification failed. Error code: %d", result);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
mbedtls_ecp_group mGroup;
|
||
|
|
mbedtls_ecp_point mQ;
|
||
|
|
mbedtls_mpi mR;
|
||
|
|
mbedtls_mpi mS;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** Retrieves the public key length based on the flag. */
|
||
|
|
uint32_t getPublicKeyLength(const uint64_t *flag) {
|
||
|
|
constexpr int kPkSizeMaskPosition = 9;
|
||
|
|
constexpr uint64_t kPkSizeMask = 0x3;
|
||
|
|
uint8_t keySizeFlag = ((*flag) >> kPkSizeMaskPosition) & kPkSizeMask;
|
||
|
|
switch (keySizeFlag) {
|
||
|
|
case 0:
|
||
|
|
return 64;
|
||
|
|
case 1:
|
||
|
|
return 96;
|
||
|
|
case 2:
|
||
|
|
return 132;
|
||
|
|
default:
|
||
|
|
LOGE("Unsupported flags in nanoapp header!");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Checks if the hash prvided in the header is derived from the image. */
|
||
|
|
bool hasCorrectHash(const void *head, size_t realImageSize,
|
||
|
|
const uint8_t *hashProvided) {
|
||
|
|
auto image = static_cast<const uint8_t *>(head) + kHeaderSize;
|
||
|
|
uint8_t hashCalculated[kSha256HashSize] = {};
|
||
|
|
mbedtls_sha256(image, realImageSize, hashCalculated, /* is224= */ 0);
|
||
|
|
return memcmp(hashCalculated, hashProvided, kSha256HashSize) == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Checks if the public key in the header matches the production public key. */
|
||
|
|
bool isValidProductionPublicKey(const uint8_t *publicKey,
|
||
|
|
size_t publicKeyLength) {
|
||
|
|
if (publicKeyLength != kEcdsaP256PublicKeySize) {
|
||
|
|
LOGE("Public key length %zu is unexpected.", publicKeyLength);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
for (size_t i = 0; i < ARRAY_SIZE(kTrustedPublicKeys); i++) {
|
||
|
|
if (memcmp(kTrustedPublicKeys[i], publicKey, kEcdsaP256PublicKeySize) ==
|
||
|
|
0) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
} // anonymous namespace
|
||
|
|
|
||
|
|
bool authenticateBinary(const void *binary, size_t appBinaryLen,
|
||
|
|
void **realBinaryStart) {
|
||
|
|
#ifndef CHRE_NAPP_AUTHENTICATION_ENABLED
|
||
|
|
UNUSED_VAR(binary);
|
||
|
|
UNUSED_VAR(realBinaryStart);
|
||
|
|
LOGW(
|
||
|
|
"Nanoapp authentication is disabled, which exposes the device to "
|
||
|
|
"security risks!");
|
||
|
|
return true;
|
||
|
|
#endif
|
||
|
|
if (appBinaryLen <= kHeaderSize) {
|
||
|
|
LOGE("Binary size %zu is too short.", appBinaryLen);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
Authenticator authenticator;
|
||
|
|
auto *header = static_cast<const ImageHeader *>(binary);
|
||
|
|
const uint8_t *imageHash = header->headerInfo.binarySha256;
|
||
|
|
const uint8_t *publicKey = header->publicKey;
|
||
|
|
const uint32_t expectedAppBinaryLength =
|
||
|
|
header->headerInfo.binaryLength + kHeaderSize;
|
||
|
|
|
||
|
|
if (header->headerInfo.magic != kChreMagicNumber) {
|
||
|
|
LOGE("Mismatched magic number.");
|
||
|
|
} else if (header->headerInfo.headerVersion != 1) {
|
||
|
|
LOGE("Header version %" PRIu32 " is unsupported.",
|
||
|
|
header->headerInfo.headerVersion);
|
||
|
|
} else if (expectedAppBinaryLength != appBinaryLen) {
|
||
|
|
LOGE("Invalid binary length %zu. Expected %" PRIu32, appBinaryLen,
|
||
|
|
expectedAppBinaryLength);
|
||
|
|
} else if (!isValidProductionPublicKey(
|
||
|
|
publicKey, getPublicKeyLength(header->headerInfo.flags))) {
|
||
|
|
LOGE("Invalid public key attached on the image.");
|
||
|
|
} else if (!hasCorrectHash(binary, header->headerInfo.binaryLength,
|
||
|
|
imageHash)) {
|
||
|
|
LOGE("Hash of the nanoapp image is incorrect.");
|
||
|
|
} else if (!authenticator.loadEcpGroup() ||
|
||
|
|
!authenticator.loadPublicKey(publicKey) ||
|
||
|
|
!authenticator.loadSignature(header)) {
|
||
|
|
LOGE("Failed to load authentication data.");
|
||
|
|
} else if (!authenticator.authenticate(binary)) {
|
||
|
|
LOGE("Failed to authenticate the image.");
|
||
|
|
} else {
|
||
|
|
*realBinaryStart = reinterpret_cast<void *>(
|
||
|
|
reinterpret_cast<uintptr_t>(binary) + kHeaderSize);
|
||
|
|
LOGI("Image is authenticated successfully!");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
} // namespace chre
|