blob: 6fedfd85f7c67e153688ad2e98ce3ed633ff4beb [file] [log] [blame]
/*
* Copyright (c) 2011,2013-2015 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <x509.h>
#include <err.h>
#include <certificate.h>
#include <crypto_hash.h>
#include <string.h>
#include <platform.h>
#include <openssl/err.h>
#include "image_verify.h"
#include "scm.h"
#include <LEOEMCertificate.h>
const char hash_identifier[] = {
0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
};
/*
* Returns -1 if decryption failed otherwise size of plain_text in bytes
*/
int image_decrypt_signature_rsa(unsigned char *signature_ptr,
unsigned char *plain_text, RSA *rsa_key)
{
int ret = -1;
if (rsa_key == NULL) {
dprintf(CRITICAL, "ERROR: Boot Invalid, RSA_KEY is NULL!\n");
return ret;
}
ret = RSA_public_decrypt(SIGNATURE_SIZE, signature_ptr, plain_text,
rsa_key, RSA_PKCS1_PADDING);
dprintf(SPEW, "DEBUG openssl: Return of RSA_public_decrypt = %d\n",
ret);
return ret;
}
/*
* Returns -1 if decryption failed otherwise size of plain_text in bytes
*/
static int
image_decrypt_signature(unsigned char *signature_ptr, unsigned char *plain_text)
{
/*
* Extract Public Key and Decrypt Signature
*/
int ret = -1;
X509 *x509_certificate = NULL;
const unsigned char *cert_ptr = NULL;
unsigned int cert_size = 0;
if (is_vb_le_enabled()) {
cert_ptr = (const unsigned char *)LE_OEM_CERTIFICATE;
cert_size = sizeof(LE_OEM_CERTIFICATE);
} else {
cert_ptr = (const unsigned char *)certBuffer;
cert_size = sizeof(certBuffer);
}
EVP_PKEY *pub_key = NULL;
RSA *rsa_key = NULL;
/*
* Get Pubkey and Convert the internal EVP_PKEY to RSA internal struct
*/
if ((x509_certificate = d2i_X509(NULL, &cert_ptr, cert_size)) == NULL) {
dprintf(CRITICAL,
"ERROR: Image Invalid, X509_Certificate is NULL!\n");
goto cleanup;
}
pub_key = X509_get_pubkey(x509_certificate);
if (pub_key == NULL) {
dprintf(CRITICAL, "ERROR: Boot Invalid, PUB_KEY is NULL!\n");
goto cleanup;
}
rsa_key = EVP_PKEY_get1_RSA(pub_key);
if (rsa_key == NULL) {
dprintf(CRITICAL, "ERROR: Boot Invalid, RSA_KEY is NULL!\n");
goto cleanup;
}
ret = image_decrypt_signature_rsa(signature_ptr, plain_text, rsa_key);
dprintf(SPEW, "DEBUG openssl: Return of RSA_public_decrypt = %d\n",
ret);
cleanup:
if (rsa_key != NULL)
RSA_free(rsa_key);
if (x509_certificate != NULL)
X509_free(x509_certificate);
if (pub_key != NULL)
EVP_PKEY_free(pub_key);
return ret;
}
/* Calculates digest of an image and save it in digest buffer */
void image_find_digest(unsigned char *image_ptr, unsigned int image_size,
unsigned hash_type, unsigned char *digest)
{
/*
* Calculate hash of image and save calculated hash on TZ.
*/
hash_find(image_ptr, image_size, (unsigned char *)digest, hash_type);
}
#ifdef TZ_SAVE_KERNEL_HASH
void save_kernel_hash(unsigned char *digest, unsigned hash_type)
{
if (hash_type == CRYPTO_AUTH_ALG_SHA256) {
save_kernel_hash_cmd(digest);
dprintf(INFO, "Image hash saved.\n");
} else
dprintf(INFO, "image_verify: hash is not SHA-256.\n");
}
#endif
/*
* Returns 1 when image is signed and authorized.
* Returns 0 when image is unauthorized.
* Expects a pointer to the start of image and pointer to start of sig
*/
int
image_verify(unsigned char *image_ptr,
unsigned char *signature_ptr,
unsigned int image_size, unsigned hash_type)
{
int ret = -1;
int auth = 0;
unsigned char *plain_text = NULL;
unsigned int digest[8];
int hash_size;
plain_text = (unsigned char *)calloc(sizeof(char), SIGNATURE_SIZE);
if (plain_text == NULL) {
dprintf(CRITICAL, "ERROR: Calloc failed during verification\n");
goto cleanup;
}
/*
* Calculate hash of image and save calculated hash on TZ.
*/
hash_size =
(hash_type == CRYPTO_AUTH_ALG_SHA256) ? SHA256_SIZE : SHA1_SIZE;
image_find_digest(image_ptr, image_size, hash_type,
(unsigned char *)&digest);
#ifdef TZ_SAVE_KERNEL_HASH
save_kernel_hash((unsigned char *) &digest, hash_type);
#endif
/*
* Decrypt the pre-calculated expected image hash.
* Return value, ret should be equal to hash_size. Otherwise it means a failure. With this check
* we avoid a potential vulnerability due to trailing data placed at the end of digest.
*/
ret = image_decrypt_signature(signature_ptr, plain_text);
if (is_vb_le_enabled()) {
int sha256_pkcs1_hash_identifier_len = sizeof(hash_identifier);
if (ret != (hash_size + sha256_pkcs1_hash_identifier_len)) {
dprintf(CRITICAL, "ERROR: VB: Image Invalid! signature check failed! ret %d\n", ret);
goto cleanup;
}
/*
* Compare the expected hash with the calculated hash.
*/
if (memcmp(plain_text, hash_identifier, sha256_pkcs1_hash_identifier_len) != 0) {
dprintf(CRITICAL,
"ERROR: VB: Hash identifier is wrong!\n");
goto cleanup;
}
if (memcmp(plain_text + sha256_pkcs1_hash_identifier_len, digest, hash_size) != 0) {
dprintf(CRITICAL,
"ERROR: VB: Image Invalid! Please use another image!\n");
goto cleanup;
}
} else {
if (ret != hash_size) {
dprintf(CRITICAL, "ERROR: Image Invalid! signature check failed! ret %d\n", ret);
goto cleanup;
}
/*
* Compare the expected hash with the calculated hash.
*/
if (memcmp(plain_text, digest, hash_size) != 0) {
dprintf(CRITICAL,
"ERROR: Image Invalid! Please use another image!\n");
ret = -1;
goto cleanup;
}
}
/* Authorized image */
auth = 1;
/* Cleanup after complete usage of openssl - cached data and objects */
cleanup:
if (plain_text != NULL)
free(plain_text);
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
ERR_remove_thread_state(NULL);
return auth;
}