mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
bdb_get_hash_by_index returns a hash entry from a BDB using an index. bdb_get_hash is also renamed to bdb_get_hash_by_type. bdb_get_hash is deprecated. Callers are expected to call bdb_get_hash_by_index(buf, 0) instead. BUG=none BRANCH=none TEST=make runtests Change-Id: Id99926123c0ac9094574eb057c63f79eceda2867 Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/392947 Reviewed-by: Randall Spangler <rspangler@chromium.org>
451 lines
12 KiB
C
451 lines
12 KiB
C
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*
|
|
* Boot descriptor block firmware functions
|
|
*/
|
|
|
|
#include "2sysincludes.h"
|
|
#include "2common.h"
|
|
#include "2sha.h"
|
|
#include "bdb.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* Check if string contains a null terminator.
|
|
*
|
|
* Bytes after the null terminator do not need to be null.
|
|
*
|
|
* @param s String to check
|
|
* @param size Size of string buffer in characters
|
|
* @return 1 if string has a null terminator, 0 if not
|
|
*/
|
|
int string_has_null(const char *s, size_t size)
|
|
{
|
|
for (; size; size--) {
|
|
if (*s++ == 0)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int bdb_check_header(const struct bdb_header *p, size_t size)
|
|
{
|
|
if (size < sizeof(*p) || size < p->struct_size)
|
|
return BDB_ERROR_BUF_SIZE;
|
|
|
|
if (p->struct_magic != BDB_HEADER_MAGIC)
|
|
return BDB_ERROR_STRUCT_MAGIC;
|
|
|
|
if (p->struct_major_version != BDB_HEADER_VERSION_MAJOR)
|
|
return BDB_ERROR_STRUCT_VERSION;
|
|
|
|
/* Note that minor version doesn't matter yet */
|
|
|
|
if (p->struct_size < sizeof(*p))
|
|
return BDB_ERROR_STRUCT_SIZE;
|
|
|
|
if (p->oem_area_0_size & 3)
|
|
return BDB_ERROR_OEM_AREA_SIZE; /* Not 32-bit aligned */
|
|
|
|
/*
|
|
* Make sure the BDB is at least big enough for us. At this point, all
|
|
* the caller may have loaded is this header We'll check if there's
|
|
* space for everything else after we load it.
|
|
*/
|
|
if (p->bdb_size < sizeof(*p))
|
|
return BDB_ERROR_BDB_SIZE;
|
|
|
|
/*
|
|
* The rest of the fields don't matter yet; we'll check them when we
|
|
* check the BDB itself.
|
|
*/
|
|
return BDB_SUCCESS;
|
|
}
|
|
|
|
int bdb_check_key(const struct bdb_key *p, size_t size)
|
|
{
|
|
size_t expect_key_size = 0;
|
|
|
|
if (size < sizeof(*p) || size < p->struct_size)
|
|
return BDB_ERROR_BUF_SIZE;
|
|
|
|
if (p->struct_magic != BDB_KEY_MAGIC)
|
|
return BDB_ERROR_STRUCT_MAGIC;
|
|
|
|
if (p->struct_major_version != BDB_KEY_VERSION_MAJOR)
|
|
return BDB_ERROR_STRUCT_VERSION;
|
|
|
|
/* Note that minor version doesn't matter yet */
|
|
|
|
if (!string_has_null(p->description, sizeof(p->description)))
|
|
return BDB_ERROR_DESCRIPTION;
|
|
|
|
/* We currently only support SHA-256 */
|
|
if (p->hash_alg != BDB_HASH_ALG_SHA256)
|
|
return BDB_ERROR_HASH_ALG;
|
|
|
|
/* Make sure signature algorithm and size are correct */
|
|
switch (p->sig_alg) {
|
|
case BDB_SIG_ALG_RSA4096:
|
|
expect_key_size = BDB_RSA4096_KEY_DATA_SIZE;
|
|
break;
|
|
case BDB_SIG_ALG_ECSDSA521:
|
|
expect_key_size = BDB_ECDSA521_KEY_DATA_SIZE;
|
|
break;
|
|
case BDB_SIG_ALG_RSA3072B:
|
|
expect_key_size = BDB_RSA3072B_KEY_DATA_SIZE;
|
|
break;
|
|
default:
|
|
return BDB_ERROR_SIG_ALG;
|
|
}
|
|
|
|
if (p->struct_size < sizeof(*p) + expect_key_size)
|
|
return BDB_ERROR_STRUCT_SIZE;
|
|
|
|
return BDB_SUCCESS;
|
|
}
|
|
|
|
int bdb_check_sig(const struct bdb_sig *p, size_t size)
|
|
{
|
|
size_t expect_sig_size = 0;
|
|
|
|
if (size < sizeof(*p) || size < p->struct_size)
|
|
return BDB_ERROR_BUF_SIZE;
|
|
|
|
if (p->struct_magic != BDB_SIG_MAGIC)
|
|
return BDB_ERROR_STRUCT_MAGIC;
|
|
|
|
if (p->struct_major_version != BDB_SIG_VERSION_MAJOR)
|
|
return BDB_ERROR_STRUCT_VERSION;
|
|
|
|
/* Note that minor version doesn't matter yet */
|
|
|
|
if (!string_has_null(p->description, sizeof(p->description)))
|
|
return BDB_ERROR_DESCRIPTION;
|
|
|
|
/* We currently only support SHA-256 */
|
|
if (p->hash_alg != BDB_HASH_ALG_SHA256)
|
|
return BDB_ERROR_HASH_ALG;
|
|
|
|
/* Make sure signature algorithm and size are correct */
|
|
switch (p->sig_alg) {
|
|
case BDB_SIG_ALG_RSA4096:
|
|
expect_sig_size = BDB_RSA4096_SIG_SIZE;
|
|
break;
|
|
case BDB_SIG_ALG_ECSDSA521:
|
|
expect_sig_size = BDB_ECDSA521_SIG_SIZE;
|
|
break;
|
|
case BDB_SIG_ALG_RSA3072B:
|
|
expect_sig_size = BDB_RSA3072B_SIG_SIZE;
|
|
break;
|
|
default:
|
|
return BDB_ERROR_SIG_ALG;
|
|
}
|
|
|
|
if (p->struct_size < sizeof(*p) + expect_sig_size)
|
|
return BDB_ERROR_STRUCT_SIZE;
|
|
|
|
return BDB_SUCCESS;
|
|
}
|
|
|
|
int bdb_check_data(const struct bdb_data *p, size_t size)
|
|
{
|
|
size_t need_size;
|
|
|
|
if (size < sizeof(*p) || size < p->signed_size)
|
|
return BDB_ERROR_BUF_SIZE;
|
|
|
|
if (p->struct_magic != BDB_DATA_MAGIC)
|
|
return BDB_ERROR_STRUCT_MAGIC;
|
|
|
|
if (p->struct_major_version != BDB_DATA_VERSION_MAJOR)
|
|
return BDB_ERROR_STRUCT_VERSION;
|
|
|
|
/* Note that minor version doesn't matter yet */
|
|
|
|
if (!string_has_null(p->description, sizeof(p->description)))
|
|
return BDB_ERROR_DESCRIPTION;
|
|
|
|
if (p->struct_size < sizeof(*p))
|
|
return BDB_ERROR_STRUCT_SIZE;
|
|
|
|
if (p->hash_entry_size < sizeof(struct bdb_hash))
|
|
return BDB_ERROR_HASH_ENTRY_SIZE;
|
|
|
|
/* Calculate expected size */
|
|
need_size = p->struct_size + p->num_hashes * p->hash_entry_size;
|
|
|
|
/* Make sure OEM area size doesn't cause wraparound */
|
|
if (need_size + p->oem_area_1_size < need_size)
|
|
return BDB_ERROR_OEM_AREA_SIZE;
|
|
if (p->oem_area_1_size & 3)
|
|
return BDB_ERROR_OEM_AREA_SIZE; /* Not 32-bit aligned */
|
|
need_size += p->oem_area_1_size;
|
|
|
|
if (p->signed_size < need_size)
|
|
return BDB_ERROR_SIGNED_SIZE;
|
|
|
|
return BDB_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const struct bdb_header *bdb_get_header(const void *buf)
|
|
{
|
|
return buf;
|
|
}
|
|
|
|
uint32_t bdb_size_of(const void *buf)
|
|
{
|
|
return bdb_get_header(buf)->bdb_size;
|
|
}
|
|
|
|
const struct bdb_key *bdb_get_bdbkey(const void *buf)
|
|
{
|
|
const struct bdb_header *h = bdb_get_header(buf);
|
|
const uint8_t *b8 = buf;
|
|
|
|
/* BDB key follows header */
|
|
return (const struct bdb_key *)(b8 + h->struct_size);
|
|
}
|
|
|
|
const void *bdb_get_oem_area_0(const void *buf)
|
|
{
|
|
const struct bdb_key *k = bdb_get_bdbkey(buf);
|
|
const uint8_t *b8 = (const uint8_t *)k;
|
|
|
|
/* OEM area 0 follows BDB key */
|
|
return b8 + k->struct_size;
|
|
}
|
|
|
|
const struct bdb_key *bdb_get_datakey(const void *buf)
|
|
{
|
|
const struct bdb_header *h = bdb_get_header(buf);
|
|
const uint8_t *b8 = bdb_get_oem_area_0(buf);
|
|
|
|
/* datakey follows OEM area 0 */
|
|
return (const struct bdb_key *)(b8 + h->oem_area_0_size);
|
|
}
|
|
|
|
ptrdiff_t bdb_offset_of_datakey(const void *buf)
|
|
{
|
|
return vb2_offset_of(buf, bdb_get_datakey(buf));
|
|
}
|
|
|
|
const struct bdb_sig *bdb_get_header_sig(const void *buf)
|
|
{
|
|
const struct bdb_header *h = bdb_get_header(buf);
|
|
const uint8_t *b8 = bdb_get_oem_area_0(buf);
|
|
|
|
/* Header signature starts after signed data */
|
|
return (const struct bdb_sig *)(b8 + h->signed_size);
|
|
}
|
|
|
|
ptrdiff_t bdb_offset_of_header_sig(const void *buf)
|
|
{
|
|
return vb2_offset_of(buf, bdb_get_header_sig(buf));
|
|
}
|
|
|
|
const struct bdb_data *bdb_get_data(const void *buf)
|
|
{
|
|
const struct bdb_sig *s = bdb_get_header_sig(buf);
|
|
const uint8_t *b8 = (const uint8_t *)s;
|
|
|
|
/* Data follows header signature */
|
|
return (const struct bdb_data *)(b8 + s->struct_size);
|
|
}
|
|
|
|
ptrdiff_t bdb_offset_of_data(const void *buf)
|
|
{
|
|
return vb2_offset_of(buf, bdb_get_data(buf));
|
|
}
|
|
|
|
const void *bdb_get_oem_area_1(const void *buf)
|
|
{
|
|
const struct bdb_data *p = bdb_get_data(buf);
|
|
const uint8_t *b8 = (const uint8_t *)p;
|
|
|
|
/* OEM area 1 follows BDB data */
|
|
return b8 + p->struct_size;
|
|
}
|
|
|
|
static const void *bdb_get_hash(const void *buf)
|
|
{
|
|
const struct bdb_data *data = bdb_get_data(buf);
|
|
const uint8_t *b8 = bdb_get_oem_area_1(buf);
|
|
|
|
/* Hashes follow OEM area 0 */
|
|
return b8 + data->oem_area_1_size;
|
|
}
|
|
|
|
const struct bdb_hash *bdb_get_hash_by_type(const void *buf,
|
|
enum bdb_data_type type)
|
|
{
|
|
const struct bdb_data *data = bdb_get_data(buf);
|
|
const uint8_t *b8 = bdb_get_hash(buf);
|
|
int i;
|
|
|
|
/* Search for a matching hash */
|
|
for (i = 0; i < data->num_hashes; i++, b8 += data->hash_entry_size) {
|
|
const struct bdb_hash *h = (const struct bdb_hash *)b8;
|
|
|
|
if (h->type == type)
|
|
return h;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const struct bdb_hash *bdb_get_hash_by_index(const void *buf, int index)
|
|
{
|
|
const struct bdb_data *data = bdb_get_data(buf);
|
|
const uint8_t *p = bdb_get_hash(buf);
|
|
const struct bdb_hash *h = NULL;
|
|
int i;
|
|
|
|
/* Search for a matching hash */
|
|
for (i = 0; i < data->num_hashes; i++, p += data->hash_entry_size) {
|
|
if (i == index) {
|
|
h = (const struct bdb_hash *)p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
const struct bdb_sig *bdb_get_data_sig(const void *buf)
|
|
{
|
|
const struct bdb_data *data = bdb_get_data(buf);
|
|
const uint8_t *b8 = (const uint8_t *)data;
|
|
|
|
/* Data signature starts after signed data */
|
|
return (const struct bdb_sig *)(b8 + data->signed_size);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int bdb_verify_sig(const struct bdb_key *key,
|
|
const struct bdb_sig *sig,
|
|
const uint8_t *digest)
|
|
{
|
|
/* Key and signature algorithms must match */
|
|
if (key->sig_alg != sig->sig_alg)
|
|
return BDB_ERROR_SIG_ALG;
|
|
|
|
switch (key->sig_alg) {
|
|
case BDB_SIG_ALG_RSA4096:
|
|
if (bdb_rsa4096_verify(key->key_data, sig->sig_data, digest))
|
|
return BDB_ERROR_VERIFY_SIG;
|
|
break;
|
|
case BDB_SIG_ALG_ECSDSA521:
|
|
if (bdb_ecdsa521_verify(key->key_data, sig->sig_data, digest))
|
|
return BDB_ERROR_VERIFY_SIG;
|
|
break;
|
|
case BDB_SIG_ALG_RSA3072B:
|
|
if (bdb_rsa3072b_verify(key->key_data, sig->sig_data, digest))
|
|
return BDB_ERROR_VERIFY_SIG;
|
|
break;
|
|
default:
|
|
return BDB_ERROR_VERIFY_SIG;
|
|
}
|
|
|
|
return BDB_SUCCESS;
|
|
}
|
|
|
|
int bdb_verify(const void *buf, size_t size, const uint8_t *bdb_key_digest)
|
|
{
|
|
const uint8_t *end = (const uint8_t *)buf + size;
|
|
const struct bdb_header *h;
|
|
const struct bdb_key *bdbkey, *datakey;
|
|
const struct bdb_sig *sig;
|
|
const struct bdb_data *data;
|
|
const void *oem;
|
|
uint8_t digest[BDB_SHA256_DIGEST_SIZE];
|
|
int bdb_digest_mismatch = -1;
|
|
|
|
/* Make sure buffer doesn't wrap around address space */
|
|
if (end < (const uint8_t *)buf)
|
|
return BDB_ERROR_BUF_SIZE;
|
|
|
|
/*
|
|
* Check header now that we've actually loaded it. We can't guarantee
|
|
* this is the same header which was checked before.
|
|
*/
|
|
h = bdb_get_header(buf);
|
|
if (bdb_check_header(h, size))
|
|
return BDB_ERROR_HEADER;
|
|
|
|
/* Sanity-check BDB key */
|
|
bdbkey = bdb_get_bdbkey(buf);
|
|
if (bdb_check_key(bdbkey, end - (const uint8_t *)bdbkey))
|
|
return BDB_ERROR_BDBKEY;
|
|
|
|
/* Calculate BDB key digest and compare with expected */
|
|
if (vb2_digest_buffer((uint8_t *)bdbkey, bdbkey->struct_size,
|
|
VB2_HASH_SHA256, digest, BDB_SHA256_DIGEST_SIZE))
|
|
return BDB_ERROR_DIGEST;
|
|
|
|
if (bdb_key_digest)
|
|
bdb_digest_mismatch = memcmp(digest,
|
|
bdb_key_digest, sizeof(digest));
|
|
|
|
/* Make sure OEM area 0 fits */
|
|
oem = bdb_get_oem_area_0(buf);
|
|
if (h->oem_area_0_size > end - (const uint8_t *)oem)
|
|
return BDB_ERROR_OEM_AREA_0;
|
|
|
|
/* Sanity-check datakey */
|
|
datakey = bdb_get_datakey(buf);
|
|
if (bdb_check_key(datakey, end - (const uint8_t *)datakey))
|
|
return BDB_ERROR_DATAKEY;
|
|
|
|
/* Make sure enough data was signed, and the signed data fits */
|
|
if (h->oem_area_0_size + datakey->struct_size > h->signed_size ||
|
|
h->signed_size > end - (const uint8_t *)oem)
|
|
return BDB_ERROR_BDB_SIGNED_SIZE;
|
|
|
|
/* Sanity-check header signature */
|
|
sig = bdb_get_header_sig(buf);
|
|
if (bdb_check_sig(sig, end - (const uint8_t *)sig))
|
|
return BDB_ERROR_HEADER_SIG;
|
|
|
|
/* Make sure it signed the right amount of data */
|
|
if (sig->signed_size != h->signed_size)
|
|
return BDB_ERROR_HEADER_SIG;
|
|
|
|
/* Calculate header digest and compare with expected signature */
|
|
if (vb2_digest_buffer((uint8_t *)oem, h->signed_size,
|
|
VB2_HASH_SHA256, digest, BDB_SHA256_DIGEST_SIZE))
|
|
return BDB_ERROR_DIGEST;
|
|
if (bdb_verify_sig(bdbkey, sig, digest))
|
|
return BDB_ERROR_HEADER_SIG;
|
|
|
|
/*
|
|
* Sanity-check data struct. This also checks that OEM area 1 and the
|
|
* hashes fit in the remaining buffer.
|
|
*/
|
|
data = bdb_get_data(buf);
|
|
if (bdb_check_data(data, end - (const uint8_t *)data))
|
|
return BDB_ERROR_DATA;
|
|
|
|
/* Sanity-check data signature */
|
|
sig = bdb_get_data_sig(buf);
|
|
if (bdb_check_sig(sig, end - (const uint8_t *)sig))
|
|
return BDB_ERROR_DATA_CHECK_SIG;
|
|
if (sig->signed_size != data->signed_size)
|
|
return BDB_ERROR_DATA_SIGNED_SIZE;
|
|
|
|
/* Calculate data digest and compare with expected signature */
|
|
if (vb2_digest_buffer((uint8_t *)data, data->signed_size,
|
|
VB2_HASH_SHA256, digest, BDB_SHA256_DIGEST_SIZE))
|
|
return BDB_ERROR_DIGEST;
|
|
if (bdb_verify_sig(datakey, sig, digest))
|
|
return BDB_ERROR_DATA_SIG;
|
|
|
|
/* Return success or success-other-than-BDB-key-mismatch */
|
|
return bdb_digest_mismatch ? BDB_GOOD_OTHER_THAN_KEY : BDB_SUCCESS;
|
|
}
|