Joel Kitching has uploaded this change for review.

View Change

eltan/verified_boot: include vboot21 code directly

vboot 2.1 data structures and code, as well as the fwlib21 build
target, are all being deprecated. Any use of these data
structures and code should be forked and handled separately.
The data structures and functions used by eltan verified boot
were never part of the vboot API, and were never intended to be
used by external parties.

Instead of building fwlib21 target, build fwlib instead, which
will be the only available library going forward. This can
provide access to the vb2_sha.h API.

Note that vboot 2.1 error codes will also be deprecated in the
future. At this point, vb2_error_t may also be forked and
included in eltan_vb21.h.

BUG=b:124141368, chromium:968464
TEST=util/abuild/abuild -v -B -e -p none -t FACEBOOK_FBG1701
BRANCH=none

Change-Id: Iae58a0c42161ddfec9908f9867286e3dc1f055a2
Signed-off-by: Joel Kitching <kitching@google.com>
---
M src/vendorcode/eltan/security/include/cb_sha.h
M src/vendorcode/eltan/security/lib/Makefile.inc
M src/vendorcode/eltan/security/verified_boot/Makefile.inc
A src/vendorcode/eltan/security/verified_boot/eltan_vb21.c
A src/vendorcode/eltan/security/verified_boot/eltan_vb21.h
M src/vendorcode/eltan/security/verified_boot/vboot_check.c
6 files changed, 1,114 insertions(+), 32 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/54/37654/1
diff --git a/src/vendorcode/eltan/security/include/cb_sha.h b/src/vendorcode/eltan/security/include/cb_sha.h
index 9a231d8..eea9f40 100644
--- a/src/vendorcode/eltan/security/include/cb_sha.h
+++ b/src/vendorcode/eltan/security/include/cb_sha.h
@@ -16,9 +16,7 @@
#ifndef __SECURITY_CB_SHA_H__
#define __SECURITY_CB_SHA_H__

-#include <2rsa.h>
-#include <vb21_common.h>
-#include <vb2_api.h>
+#include <vb2_sha.h>

vb2_error_t cb_sha_little_endian(enum vb2_hash_algorithm hash_alg, const uint8_t *data,
uint32_t len, uint8_t *digest);
diff --git a/src/vendorcode/eltan/security/lib/Makefile.inc b/src/vendorcode/eltan/security/lib/Makefile.inc
index 2e11fb5..d79d70b 100644
--- a/src/vendorcode/eltan/security/lib/Makefile.inc
+++ b/src/vendorcode/eltan/security/lib/Makefile.inc
@@ -16,7 +16,9 @@
# call with $1 = stage name to create rules for building the library
# for the stage and adding it to the stage's set of object files.
define vendor-security-lib
-VEN_SEC_LIB_$(1) = $(obj)/external/ven_sec_lib-$(1)/vboot_fw21.a
+VEN_SEC_LIB_$(1) = \
+ $(obj)/external/ven_sec_lib-$(1)/vboot_fw.a
+
VEN_SEC_CFLAGS_$(1) += $$(patsubst -I%,-I$(top)/%,\
$$(patsubst $(src)/%.h,$(top)/$(src)/%.h,\
$$(filter-out -I$(obj), $$(CPPFLAGS_$(1)))))
@@ -32,29 +34,25 @@
$(MAKE) -C $(VBOOT_SOURCE) \
BUILD=$$(abspath $$(dir $$(VEN_SEC_LIB_$(1)))) \
V=$(V) \
- fwlib21
+ fwlib
endef # vendor-security-for-stage

-CFLAGS_common += -I3rdparty/vboot/firmware/2lib/include
-CFLAGS_common += -I3rdparty/vboot/firmware/lib21/include
-
ifneq ($(filter y,$(CONFIG_VENDORCODE_ELTAN_VBOOT) $(CONFIG_VENDORCODE_ELTAN_MBOOT)),)

bootblock-y += cb_sha.c
-bootblock-y += ../../../../security/vboot/vboot_logic.c
$(eval $(call vendor-security-lib,bootblock))
-bootblock-srcs += $(obj)/external/ven_sec_lib-bootblock/vboot_fw21.a
+bootblock-srcs += $(obj)/external/ven_sec_lib-bootblock/vboot_fw.a

postcar-y += cb_sha.c
$(eval $(call vendor-security-lib,postcar))
-postcar-srcs += $(obj)/external/ven_sec_lib-postcar/vboot_fw21.a
+postcar-srcs += $(obj)/external/ven_sec_lib-postcar/vboot_fw.a

ramstage-y += cb_sha.c
$(eval $(call vendor-security-lib,ramstage))
-ramstage-srcs += $(obj)/external/ven_sec_lib-ramstage/vboot_fw21.a
+ramstage-srcs += $(obj)/external/ven_sec_lib-ramstage/vboot_fw.a

romstage-y += cb_sha.c
$(eval $(call vendor-security-lib,romstage))
-romstage-srcs += $(obj)/external/ven_sec_lib-romstage/vboot_fw21.a
+romstage-srcs += $(obj)/external/ven_sec_lib-romstage/vboot_fw.a

-endif
\ No newline at end of file
+endif
diff --git a/src/vendorcode/eltan/security/verified_boot/Makefile.inc b/src/vendorcode/eltan/security/verified_boot/Makefile.inc
index 97d8f81..1663fa5 100644
--- a/src/vendorcode/eltan/security/verified_boot/Makefile.inc
+++ b/src/vendorcode/eltan/security/verified_boot/Makefile.inc
@@ -15,7 +15,10 @@

ifneq ($(filter y,$(CONFIG_VENDORCODE_ELTAN_VBOOT) $(CONFIG_VENDORCODE_ELTAN_MBOOT)),)

-CPPFLAGS_common += -I$(src)/security/vboot
+bootblock-y += eltan_vb21.c
+postcar-y += eltan_vb21.c
+romstage-y += eltan_vb21.c
+ramstage-y += eltan_vb21.c

bootblock-y += vboot_check.c
postcar-y += vboot_check.c
diff --git a/src/vendorcode/eltan/security/verified_boot/eltan_vb21.c b/src/vendorcode/eltan/security/verified_boot/eltan_vb21.c
new file mode 100644
index 0000000..277b55a
--- /dev/null
+++ b/src/vendorcode/eltan/security/verified_boot/eltan_vb21.c
@@ -0,0 +1,840 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2019 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <console/console.h>
+#include <vb2_sha.h>
+#include <vendorcode/eltan/security/verified_boot/eltan_vb21.h>
+
+/**
+ * a[] -= mod
+ */
+static void subM(const struct evb2_public_key *key, uint32_t *a)
+{
+ int64_t A = 0;
+ uint32_t i;
+ for (i = 0; i < key->arrsize; ++i) {
+ A += (uint64_t)a[i] - key->n[i];
+ a[i] = (uint32_t)A;
+ A >>= 32;
+ }
+}
+
+/**
+ * Return a[] >= mod
+ */
+static int evb2_mont_ge(const struct evb2_public_key *key, uint32_t *a)
+{
+ uint32_t i;
+ for (i = key->arrsize; i;) {
+ --i;
+ if (a[i] < key->n[i])
+ return 0;
+ if (a[i] > key->n[i])
+ return 1;
+ }
+ return 1; /* equal */
+}
+
+/**
+ * Montgomery c[] += a * b[] / R % mod
+ */
+static void montMulAdd(const struct evb2_public_key *key,
+ uint32_t *c,
+ const uint32_t a,
+ const uint32_t *b)
+{
+ uint64_t A = (uint64_t)a * b[0] + c[0];
+ uint32_t d0 = (uint32_t)A * key->n0inv;
+ uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
+ uint32_t i;
+
+ for (i = 1; i < key->arrsize; ++i) {
+ A = (A >> 32) + (uint64_t)a * b[i] + c[i];
+ B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
+ c[i - 1] = (uint32_t)B;
+ }
+
+ A = (A >> 32) + (B >> 32);
+
+ c[i - 1] = (uint32_t)A;
+
+ if (A >> 32) {
+ subM(key, c);
+ }
+}
+
+/**
+ * Montgomery c[] += 0 * b[] / R % mod
+ */
+static void montMulAdd0(const struct evb2_public_key *key,
+ uint32_t *c,
+ const uint32_t *b)
+{
+ uint32_t d0 = c[0] * key->n0inv;
+ uint64_t B = (uint64_t)d0 * key->n[0] + c[0];
+ uint32_t i;
+
+ for (i = 1; i < key->arrsize; ++i) {
+ B = (B >> 32) + (uint64_t)d0 * key->n[i] + c[i];
+ c[i - 1] = (uint32_t)B;
+ }
+
+ c[i - 1] = B >> 32;
+}
+
+/**
+ * Montgomery c[] = a[] * b[] / R % mod
+ */
+static void montMul(const struct evb2_public_key *key,
+ uint32_t *c,
+ const uint32_t *a,
+ const uint32_t *b)
+{
+ uint32_t i;
+ for (i = 0; i < key->arrsize; ++i) {
+ c[i] = 0;
+ }
+ for (i = 0; i < key->arrsize; ++i) {
+ montMulAdd(key, c, a[i], b);
+ }
+}
+
+/* Montgomery c[] = a[] * 1 / R % key. */
+static void montMul1(const struct evb2_public_key *key,
+ uint32_t *c,
+ const uint32_t *a)
+{
+ int i;
+
+ for (i = 0; i < key->arrsize; ++i)
+ c[i] = 0;
+
+ montMulAdd(key, c, 1, a);
+ for (i = 1; i < key->arrsize; ++i)
+ montMulAdd0(key, c, a);
+}
+
+/**
+ * In-place public exponentiation.
+ *
+ * @param key Key to use in signing
+ * @param inout Input and output big-endian byte array
+ * @param workbuf32 Work buffer; caller must verify this is
+ * (3 * key->arrsize) elements long.
+ * @param exp RSA public exponent: either 65537 (F4) or 3
+ */
+static void modpow(const struct evb2_public_key *key, uint8_t *inout,
+ uint32_t *workbuf32, int exp)
+{
+ uint32_t *a = workbuf32;
+ uint32_t *aR = a + key->arrsize;
+ uint32_t *aaR = aR + key->arrsize;
+ uint32_t *aaa = aaR; /* Re-use location. */
+ int i;
+
+ /* Convert from big endian byte array to little endian word array. */
+ for (i = 0; i < (int)key->arrsize; ++i) {
+ uint32_t tmp =
+ ((uint32_t)inout[((key->arrsize - 1 - i) * 4) + 0]
+ << 24) |
+ (inout[((key->arrsize - 1 - i) * 4) + 1] << 16) |
+ (inout[((key->arrsize - 1 - i) * 4) + 2] << 8) |
+ (inout[((key->arrsize - 1 - i) * 4) + 3] << 0);
+ a[i] = tmp;
+ }
+
+ montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
+ if (exp == 3) {
+ montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
+ montMul(key, a, aaR, aR); /* a = aaR * aR / R mod M */
+ montMul1(key, aaa, a); /* aaa = a * 1 / R mod M */
+ } else {
+ /* Exponent 65537 */
+ for (i = 0; i < 16; i+=2) {
+ montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
+ montMul(key, aR, aaR, aaR); /* aR = aaR * aaR / R mod M */
+ }
+ montMul(key, aaa, aR, a); /* aaa = aR * a / R mod M */
+ }
+
+ /* Make sure aaa < mod; aaa is at most 1x mod too large. */
+ if (evb2_mont_ge(key, aaa)) {
+ subM(key, aaa);
+ }
+
+ /* Convert to bigendian byte array */
+ for (i = (int)key->arrsize - 1; i >= 0; --i) {
+ uint32_t tmp = aaa[i];
+ *inout++ = (uint8_t)(tmp >> 24);
+ *inout++ = (uint8_t)(tmp >> 16);
+ *inout++ = (uint8_t)(tmp >> 8);
+ *inout++ = (uint8_t)(tmp >> 0);
+ }
+}
+
+/**
+ * Safer memcmp() for use in crypto.
+ *
+ * Compares the buffers to see if they are equal. Time taken to perform
+ * the comparison is dependent only on the size, not the relationship of
+ * the match between the buffers. Note that unlike memcmp(), this only
+ * indicates inequality, not which buffer is lesser.
+ *
+ * @param s1 First buffer
+ * @param s2 Second buffer
+ * @param size Number of bytes to compare
+ * @return 0 if match or size=0, non-zero if at least one byte mismatched.
+ */
+static vb2_error_t evb2_safe_memcmp(const void *s1, const void *s2, size_t size)
+{
+ const unsigned char *us1 = s1;
+ const unsigned char *us2 = s2;
+ int result = 0;
+
+ if (0 == size)
+ return 0;
+
+ /*
+ * Code snippet without data-dependent branch due to Nate Lawson
+ * (nate@root.org) of Root Labs.
+ */
+ while (size--)
+ result |= *us1++ ^ *us2++;
+
+ return result != 0;
+}
+
+/**
+ * Round up a number to a multiple of VB2_WORKBUF_ALIGN
+ *
+ * @param v Number to round up
+ * @return The number, rounded up.
+ */
+static inline uint32_t evb2_wb_round_up(uint32_t v)
+{
+ return (v + VB2_WORKBUF_ALIGN - 1) & ~(VB2_WORKBUF_ALIGN - 1);
+}
+
+/**
+ * Allocate space in a work buffer.
+ *
+ * Note that the returned buffer will always be aligned to VB2_WORKBUF_ALIGN.
+ *
+ * The work buffer acts like a stack, and detailed tracking of allocs and frees
+ * is not done. The caller must track the size of each allocation and free via
+ * evb2_workbuf_free() in the reverse order they were allocated.
+ *
+ * An acceptable alternate workflow inside a function is to pass in a const
+ * work buffer, then make a local copy. Allocations done to the local copy
+ * then don't change the passed-in work buffer, and will effectively be freed
+ * when the local copy goes out of scope.
+ *
+ * @param wb Work buffer
+ * @param size Requested size in bytes
+ * @return A pointer to the allocated space, or NULL if error.
+ */
+static void *evb2_workbuf_alloc(struct evb2_workbuf *wb, uint32_t size)
+{
+ uint8_t *ptr = wb->buf;
+
+ /* Round up size to work buffer alignment */
+ size = evb2_wb_round_up(size);
+
+ if (size > wb->size)
+ return NULL;
+
+ wb->buf += size;
+ wb->size -= size;
+
+ return ptr;
+}
+
+/**
+ * Free the preceding allocation.
+ *
+ * Note that the work buffer acts like a stack, and detailed tracking of
+ * allocs and frees is not done. The caller must track the size of each
+ * allocation and free them in reverse order.
+ *
+ * @param wb Work buffer
+ * @param size Size of data to free
+ */
+static void evb2_workbuf_free(struct evb2_workbuf *wb, uint32_t size)
+{
+ /* Round up size to work buffer alignment */
+ size = evb2_wb_round_up(size);
+
+ wb->buf -= size;
+ wb->size += size;
+}
+
+/*
+ * PKCS 1.5 padding (from the RSA PKCS#1 v2.1 standard)
+ *
+ * Depending on the RSA key size and hash function, the padding is calculated
+ * as follows:
+ *
+ * 0x00 || 0x01 || PS || 0x00 || T
+ *
+ * T: DER Encoded DigestInfo value which depends on the hash function used.
+ *
+ * SHA-1: (0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H.
+ * SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H.
+ * SHA-512: (0x)30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 || H.
+ *
+ * Length(T) = 35 octets for SHA-1
+ * Length(T) = 51 octets for SHA-256
+ * Length(T) = 83 octets for SHA-512
+ *
+ * PS: octet string consisting of {Length(RSA Key) - Length(T) - 3} 0xFF
+ */
+static const uint8_t sha1_tail[] = {
+ 0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,
+ 0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14
+};
+
+static const uint8_t sha256_tail[] = {
+ 0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,
+ 0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,
+ 0x05,0x00,0x04,0x20
+};
+
+static const uint8_t sha512_tail[] = {
+ 0x00,0x30,0x51,0x30,0x0d,0x06,0x09,0x60,
+ 0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,
+ 0x05,0x00,0x04,0x40
+};
+
+/**
+ * Check pkcs 1.5 padding bytes
+ *
+ * @param sig Signature to verify
+ * @param key Key to take signature and hash algorithms from
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+static vb2_error_t evb2_check_padding(const uint8_t *sig,
+ const struct evb2_public_key *key)
+{
+ /* Determine padding to use depending on the signature type */
+ uint32_t sig_size = evb2_rsa_sig_size(key->sig_alg);
+ uint32_t hash_size = vb2_digest_size(key->hash_alg);
+ uint32_t pad_size = sig_size - hash_size;
+ const uint8_t *tail;
+ uint32_t tail_size;
+ int result = 0;
+ int i;
+
+ if (!sig_size || !hash_size || hash_size > sig_size)
+ return VB2_ERROR_RSA_PADDING_SIZE;
+
+ switch (key->hash_alg) {
+ case VB2_HASH_SHA1:
+ tail = sha1_tail;
+ tail_size = sizeof(sha1_tail);
+ break;
+ case VB2_HASH_SHA256:
+ tail = sha256_tail;
+ tail_size = sizeof(sha256_tail);
+ break;
+ case VB2_HASH_SHA512:
+ tail = sha512_tail;
+ tail_size = sizeof(sha512_tail);
+ break;
+ default:
+ return VB2_ERROR_RSA_PADDING_ALGORITHM;
+ }
+
+ /* First 2 bytes are always 0x00 0x01 */
+ result |= *sig++ ^ 0x00;
+ result |= *sig++ ^ 0x01;
+
+ /* Then 0xff bytes until the tail */
+ for (i = 0; i < pad_size - tail_size - 2; i++)
+ result |= *sig++ ^ 0xff;
+
+ /*
+ * Then the tail. Even though there are probably no timing issues
+ * here, we use evb2_safe_memcmp() just to be on the safe side.
+ */
+ result |= evb2_safe_memcmp(sig, tail, tail_size);
+
+ return result ? VB2_ERROR_RSA_PADDING : VB2_SUCCESS;
+}
+
+/**
+ * Return the exponent used by an RSA algorithm
+ *
+ * @param sig_alg Signature algorithm
+ * @return The exponent to use (3 or 65537(F4)), or 0 if error.
+ */
+static uint32_t evb2_rsa_exponent(enum vb2_signature_algorithm sig_alg)
+{
+ switch (sig_alg) {
+ case VB2_SIG_RSA1024:
+ case VB2_SIG_RSA2048:
+ case VB2_SIG_RSA4096:
+ case VB2_SIG_RSA8192:
+ return 65537;
+ case VB2_SIG_RSA2048_EXP3:
+ case VB2_SIG_RSA3072_EXP3:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Verify a RSA PKCS1.5 signature against an expected hash digest.
+ *
+ * @param key Key to use in signature verification
+ * @param sig Signature to verify (destroyed in process)
+ * @param digest Digest of signed data
+ * @param wb Work buffer
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+static vb2_error_t evb2_rsa_verify_digest(const struct evb2_public_key *key,
+ uint8_t *sig, const uint8_t *digest,
+ const struct evb2_workbuf *wb)
+{
+ struct evb2_workbuf wblocal = *wb;
+ uint32_t *workbuf32;
+ uint32_t key_bytes;
+ int sig_size;
+ int pad_size;
+ int exp;
+ vb2_error_t rv;
+
+ if (!key || !sig || !digest)
+ return VB2_ERROR_RSA_VERIFY_PARAM;
+
+ sig_size = evb2_rsa_sig_size(key->sig_alg);
+ exp = evb2_rsa_exponent(key->sig_alg);
+ if (!sig_size || !exp) {
+ EVB2_DEBUG("Invalid signature type!\n");
+ return VB2_ERROR_RSA_VERIFY_ALGORITHM;
+ }
+
+ /* Signature length should be same as key length */
+ key_bytes = key->arrsize * sizeof(uint32_t);
+ if (key_bytes != sig_size) {
+ EVB2_DEBUG("Signature is of incorrect length!\n");
+ return VB2_ERROR_RSA_VERIFY_SIG_LEN;
+ }
+
+ workbuf32 = evb2_workbuf_alloc(&wblocal, 3 * key_bytes);
+ if (!workbuf32) {
+ EVB2_DEBUG("ERROR - vboot2 work buffer too small!\n");
+ return VB2_ERROR_RSA_VERIFY_WORKBUF;
+ }
+
+ modpow(key, sig, workbuf32, exp);
+
+ evb2_workbuf_free(&wblocal, 3 * key_bytes);
+
+ /*
+ * Check padding. Only fail immediately if the padding size is bad.
+ * Otherwise, continue on to check the digest to reduce the risk of
+ * timing based attacks.
+ */
+ rv = evb2_check_padding(sig, key);
+ if (rv == VB2_ERROR_RSA_PADDING_SIZE)
+ return rv;
+
+ /*
+ * Check digest. Even though there are probably no timing issues here,
+ * use evb2_safe_memcmp() just to be on the safe side. (That's also why
+ * we don't return before this check if the padding check failed.)
+ */
+ pad_size = sig_size - vb2_digest_size(key->hash_alg);
+ if (evb2_safe_memcmp(sig + pad_size, digest, key_bytes - pad_size)) {
+ EVB2_DEBUG("Digest check failed!\n");
+ if (!rv)
+ rv = VB2_ERROR_RSA_VERIFY_DIGEST;
+ }
+
+ return rv;
+}
+
+/**
+ * Return the description of an object starting with a evb21_struct_common
+ * header.
+ *
+ * Does not sanity-check the buffer; merely returns the pointer.
+ *
+ * @param buf Pointer to common object
+ * @return A pointer to description or an empty string if none.
+ */
+static const char *evb21_common_desc(const void *buf)
+{
+ const struct evb21_struct_common *c = buf;
+
+ return c->desc_size ? (const char *)c + c->fixed_size : "";
+}
+
+/**
+ * Verify the common struct header is fully contained in its parent data
+ *
+ * Also verifies the description is either zero-length or null-terminated.
+ *
+ * @param parent Parent data
+ * @param parent_size Parent size in bytes
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+static vb2_error_t evb21_verify_common_header(const void *parent,
+ uint32_t parent_size)
+{
+ const struct evb21_struct_common *c = parent;
+
+ /* Parent buffer size must be at least the claimed total size */
+ if (parent_size < c->total_size)
+ return VB2_ERROR_COMMON_TOTAL_SIZE;
+
+ /*
+ * And big enough for the fixed size, which itself must be at least as
+ * big as the common struct header.
+ */
+ if (c->total_size < c->fixed_size || c->fixed_size < sizeof(*c))
+ return VB2_ERROR_COMMON_FIXED_SIZE;
+
+ /* Make sure sizes are all multiples of 32 bits */
+ if (!evb2_aligned(c->total_size, sizeof(uint32_t)))
+ return VB2_ERROR_COMMON_TOTAL_UNALIGNED;
+ if (!evb2_aligned(c->fixed_size, sizeof(uint32_t)))
+ return VB2_ERROR_COMMON_FIXED_UNALIGNED;
+ if (!evb2_aligned(c->desc_size, sizeof(uint32_t)))
+ return VB2_ERROR_COMMON_DESC_UNALIGNED;
+
+ /* Check description */
+ if (c->desc_size > 0) {
+ /* Make sure description fits and doesn't wrap */
+ if (c->fixed_size + c->desc_size < c->fixed_size)
+ return VB2_ERROR_COMMON_DESC_WRAPS;
+ if (c->fixed_size + c->desc_size > c->total_size)
+ return VB2_ERROR_COMMON_DESC_SIZE;
+
+ /* Description must be null-terminated */
+ if (evb21_common_desc(c)[c->desc_size - 1] != 0)
+ return VB2_ERROR_COMMON_DESC_TERMINATOR;
+ }
+
+ return VB2_SUCCESS;
+}
+
+/**
+ * Verify a member is within the data for a parent object
+ *
+ * @param parent Parent data (starts with struct evb21_struct_common)
+ * @param min_offset Pointer to minimum offset where member can be located.
+ * If this offset is 0 on input, uses the size of the
+ * fixed header (and description, if any). This will be
+ * updated on return to the end of the passed member. On
+ * error, the value of min_offset is undefined.
+ * @param member_offset Offset of member data from start of parent, in bytes
+ * @param member_size Size of member data, in bytes
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+static vb2_error_t evb21_verify_common_member(const void *parent,
+ uint32_t *min_offset,
+ uint32_t member_offset,
+ uint32_t member_size)
+{
+ const struct evb21_struct_common *c = parent;
+ uint32_t member_end = member_offset + member_size;
+
+ /* Make sure member doesn't wrap */
+ if (member_end < member_offset)
+ return VB2_ERROR_COMMON_MEMBER_WRAPS;
+
+ /* Member offset and size must be 32-bit aligned */
+ if (!evb2_aligned(member_offset, sizeof(uint32_t)) ||
+ !evb2_aligned(member_size, sizeof(uint32_t)))
+ return VB2_ERROR_COMMON_MEMBER_UNALIGNED;
+
+ /* Initialize minimum offset if necessary */
+ if (!*min_offset)
+ *min_offset = c->fixed_size + c->desc_size;
+
+ /* Member must be after minimum offset */
+ if (member_offset < *min_offset)
+ return VB2_ERROR_COMMON_MEMBER_OVERLAP;
+
+ /* Member must end before total size */
+ if (member_end > c->total_size)
+ return VB2_ERROR_COMMON_MEMBER_SIZE;
+
+ /* Update minimum offset for subsequent checks */
+ *min_offset = member_end;
+
+ return VB2_SUCCESS;
+}
+
+/**
+ * Return expected signature size for a signature/hash algorithm pair
+ *
+ * @param sig_alg Signature algorithm
+ * @param hash_alg Hash algorithm
+ * @return The signature size, or zero if error / unsupported algorithm.
+ */
+static uint32_t evb2_sig_size(enum vb2_signature_algorithm sig_alg,
+ enum vb2_hash_algorithm hash_alg)
+{
+ uint32_t digest_size = vb2_digest_size(hash_alg);
+
+ /* Fail if we don't support the hash algorithm */
+ if (!digest_size)
+ return 0;
+
+ /* Handle unsigned hashes */
+ if (sig_alg == VB2_SIG_NONE)
+ return digest_size;
+
+ return evb2_rsa_sig_size(sig_alg);
+}
+
+/**
+ * Return the signature data for a signature
+ */
+static uint8_t *evb21_signature_data(struct evb21_signature *sig)
+{
+ return (uint8_t *)sig + sig->sig_offset;
+}
+
+/**
+ * Verify a signature against an expected hash digest.
+ *
+ * @param key Key to use in signature verification
+ * @param sig Signature to verify (may be destroyed in process)
+ * @param digest Digest of signed data
+ * @param wb Work buffer
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+static vb2_error_t evb21_verify_digest(const struct evb2_public_key *key,
+ struct evb21_signature *sig,
+ const uint8_t *digest,
+ const struct evb2_workbuf *wb)
+{
+ uint32_t key_sig_size = evb2_sig_size(key->sig_alg, key->hash_alg);
+
+ /* If we can't figure out the signature size, key algorithm was bad */
+ if (!key_sig_size)
+ return VB2_ERROR_VDATA_ALGORITHM;
+
+ /* Make sure the signature and key algorithms match */
+ if (key->sig_alg != sig->sig_alg || key->hash_alg != sig->hash_alg)
+ return VB2_ERROR_VDATA_ALGORITHM_MISMATCH;
+
+ if (sig->sig_size != key_sig_size)
+ return VB2_ERROR_VDATA_SIG_SIZE;
+
+ if (key->sig_alg == VB2_SIG_NONE) {
+ /* Bare hash */
+ if (evb2_safe_memcmp(evb21_signature_data(sig),
+ digest, key_sig_size))
+ return VB2_ERROR_VDATA_VERIFY_DIGEST;
+
+ return VB2_SUCCESS;
+ } else {
+ /* RSA-signed digest */
+ return evb2_rsa_verify_digest(key,
+ evb21_signature_data(sig),
+ digest, wb);
+ }
+}
+
+/**
+ * Return the size of a pre-processed RSA public key.
+ *
+ * @param sig_alg Signature algorithm
+ * @return The size of the preprocessed key in bytes, or 0 if error.
+ */
+static uint32_t evb2_packed_key_size(enum vb2_signature_algorithm sig_alg)
+{
+ uint32_t sig_size = evb2_rsa_sig_size(sig_alg);
+
+ if (!sig_size)
+ return 0;
+
+ /*
+ * Total size needed by a RSAPublicKey buffer is =
+ * 2 * key_len bytes for the n and rr arrays
+ * + sizeof len + sizeof n0inv.
+ */
+ return 2 * sig_size + 2 * sizeof(uint32_t);
+}
+
+/**
+ * Unpack the RSA data fields for a public key
+ *
+ * This is called by evb21_unpack_key() to extract the arrays from a packed key.
+ * These elements of *key will point inside the key_data buffer.
+ *
+ * @param key Destination key for RSA data fields
+ * @param key_data Packed key data (from inside a packed key buffer)
+ * @param key_size Size of packed key data in bytes
+ */
+static vb2_error_t evb2_unpack_key_data(struct evb2_public_key *key,
+ const uint8_t *key_data,
+ uint32_t key_size)
+{
+ const uint32_t *buf32 = (const uint32_t *)key_data;
+ uint32_t expected_key_size = evb2_packed_key_size(key->sig_alg);
+
+ /* Make sure buffer is the correct length */
+ if (!expected_key_size || expected_key_size != key_size) {
+ EVB2_DEBUG("Wrong key size for algorithm\n");
+ return VB2_ERROR_UNPACK_KEY_SIZE;
+ }
+
+ /* Check for alignment */
+ if (!evb2_aligned(buf32, sizeof(uint32_t)))
+ return VB2_ERROR_UNPACK_KEY_ALIGN;
+
+ key->arrsize = buf32[0];
+
+ /* Sanity check key array size */
+ if (key->arrsize * sizeof(uint32_t) != evb2_rsa_sig_size(key->sig_alg))
+ return VB2_ERROR_UNPACK_KEY_ARRAY_SIZE;
+
+ key->n0inv = buf32[1];
+
+ /* Arrays point inside the key data */
+ key->n = buf32 + 2;
+ key->rr = buf32 + 2 + key->arrsize;
+
+ return VB2_SUCCESS;
+}
+
+vb2_error_t evb21_unpack_key(struct evb2_public_key *key, const uint8_t *buf,
+ uint32_t size)
+{
+ const struct evb21_packed_key *pkey =
+ (const struct evb21_packed_key *)buf;
+ uint32_t sig_size;
+ uint32_t min_offset = 0;
+ vb2_error_t rv;
+
+ /* Check magic number */
+ if (pkey->c.magic != VB21_MAGIC_PACKED_KEY)
+ return VB2_ERROR_UNPACK_KEY_MAGIC;
+
+ rv = evb21_verify_common_header(buf, size);
+ if (rv)
+ return rv;
+
+ /* Make sure key data is inside */
+ rv = evb21_verify_common_member(pkey, &min_offset,
+ pkey->key_offset, pkey->key_size);
+ if (rv)
+ return rv;
+
+ /*
+ * Check for compatible version. No need to check minor version, since
+ * that's compatible across readers matching the major version, and we
+ * haven't added any new fields.
+ */
+ if (pkey->c.struct_version_major != VB21_PACKED_KEY_VERSION_MAJOR)
+ return VB2_ERROR_UNPACK_KEY_STRUCT_VERSION;
+
+ /* Copy key algorithms */
+ key->hash_alg = pkey->hash_alg;
+ if (!vb2_digest_size(key->hash_alg))
+ return VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM;
+
+ key->sig_alg = pkey->sig_alg;
+ if (key->sig_alg != VB2_SIG_NONE) {
+ sig_size = evb2_rsa_sig_size(key->sig_alg);
+ if (!sig_size)
+ return VB2_ERROR_UNPACK_KEY_SIG_ALGORITHM;
+ rv = evb2_unpack_key_data(
+ key,
+ (const uint8_t *)pkey + pkey->key_offset,
+ pkey->key_size);
+ if (rv)
+ return rv;
+ }
+
+ /* Key description */
+ key->desc = evb21_common_desc(pkey);
+ key->version = pkey->key_version;
+ key->id = &pkey->id;
+
+ return VB2_SUCCESS;
+}
+
+uint32_t evb2_rsa_sig_size(enum vb2_signature_algorithm sig_alg)
+{
+ switch (sig_alg) {
+ case VB2_SIG_RSA1024:
+ return 1024 / 8;
+ case VB2_SIG_RSA2048:
+ case VB2_SIG_RSA2048_EXP3:
+ return 2048 / 8;
+ case VB2_SIG_RSA3072_EXP3:
+ return 3072 / 8;
+ case VB2_SIG_RSA4096:
+ return 4096 / 8;
+ case VB2_SIG_RSA8192:
+ return 8192 / 8;
+ default:
+ return 0;
+ }
+}
+
+vb2_error_t evb21_verify_data(const void *data, uint32_t size,
+ struct evb21_signature *sig,
+ const struct evb2_public_key *key,
+ const struct evb2_workbuf *wb)
+{
+ struct evb2_workbuf wblocal = *wb;
+ struct vb2_digest_context *dc;
+ uint8_t *digest;
+ uint32_t digest_size;
+ vb2_error_t rv;
+
+ if (sig->data_size != size) {
+ EVB2_DEBUG("Wrong amount of data signed.\n");
+ return VB2_ERROR_VDATA_SIZE;
+ }
+
+ /* Digest goes at start of work buffer */
+ digest_size = vb2_digest_size(key->hash_alg);
+ if (!digest_size)
+ return VB2_ERROR_VDATA_DIGEST_SIZE;
+
+ digest = evb2_workbuf_alloc(&wblocal, digest_size);
+ if (!digest)
+ return VB2_ERROR_VDATA_WORKBUF_DIGEST;
+
+ /* Hashing requires temp space for the context */
+ dc = evb2_workbuf_alloc(&wblocal, sizeof(*dc));
+ if (!dc)
+ return VB2_ERROR_VDATA_WORKBUF_HASHING;
+
+ rv = vb2_digest_init(dc, key->hash_alg);
+ if (rv)
+ return rv;
+
+ rv = vb2_digest_extend(dc, data, size);
+ if (rv)
+ return rv;
+
+ rv = vb2_digest_finalize(dc, digest, digest_size);
+ if (rv)
+ return rv;
+
+ evb2_workbuf_free(&wblocal, sizeof(*dc));
+
+ return evb21_verify_digest(key, sig, digest, &wblocal);
+}
diff --git a/src/vendorcode/eltan/security/verified_boot/eltan_vb21.h b/src/vendorcode/eltan/security/verified_boot/eltan_vb21.h
new file mode 100644
index 0000000..c2f7982
--- /dev/null
+++ b/src/vendorcode/eltan/security/verified_boot/eltan_vb21.h
@@ -0,0 +1,241 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2019 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef ELTAN_VB21_H
+#define ELTAN_VB21_H
+
+#include <vb2_api.h>
+#include <vb2_sha.h>
+
+#define EVB2_DEBUG(format, args...) \
+ printk(BIOS_INFO, format, ## args)
+
+/* Check if a pointer is aligned on an align-byte boundary */
+#define evb2_aligned(ptr, align) (!(((uintptr_t)(ptr)) & ((align) - 1)))
+
+/*
+ * Magic numbers used by evb21_struct_common.magic.
+ *
+ * All valid numbers should be listed here to avoid accidental overlap.
+ * Numbers start at a large value, so that previous parsers (which stored
+ * things like lengths and offsets at that field) will detect and reject new
+ * structs as invalid.
+ */
+enum evb21_struct_common_magic {
+ /* "Vb2P" = evb21_packed_key.c.magic */
+ VB21_MAGIC_PACKED_KEY = 0x50326256,
+};
+
+/* Work buffer */
+struct evb2_workbuf {
+ uint8_t *buf;
+ uint32_t size;
+};
+
+#define EVB2_ID_NUM_BYTES 20
+
+struct evb2_id {
+ uint8_t raw[EVB2_ID_NUM_BYTES];
+} __attribute__((packed));
+
+/* Public key structure in RAM */
+struct evb2_public_key {
+ uint32_t arrsize; /* Length of n[] and rr[] in number of uint32_t */
+ uint32_t n0inv; /* -1 / n[0] mod 2^32 */
+ const uint32_t *n; /* Modulus as little endian array */
+ const uint32_t *rr; /* R^2 as little endian array */
+ enum vb2_signature_algorithm sig_alg; /* Signature algorithm */
+ enum vb2_hash_algorithm hash_alg; /* Hash algorithm */
+ const char *desc; /* Description */
+ uint32_t version; /* Key version */
+ const struct evb2_id *id; /* Key ID */
+};
+
+/*
+ * Generic struct header for all vboot2.1 structs. This makes it easy to
+ * automatically parse and identify vboot structs (e.g., in futility). This
+ * must be the first member of the parent vboot2.1 struct.
+ */
+struct evb21_struct_common {
+ /* Magic number; see evb21_struct_common_magic for expected values */
+ uint32_t magic;
+
+ /*
+ * Parent struct version; see each struct for the expected value.
+ *
+ * How to handle struct version mismatches, if the parser is version
+ * A.b and the data is version C.d:
+ * 1) If A.b == C.d, we're good.
+ * 2) If A != C, the data cannot be parsed at all.
+ * 3) If b < d, C.d is a newer version of data which is backwards-
+ * compatible to old parsers. We're good.
+ * 4) If b > d, C.d is an older version of data. The parser should
+ * use default values for fields added after version d. We're
+ * good.
+ *
+ * Struct versions start at 3.0, since the highest version of the old
+ * structures was 2.1. This way, there is no possibility of collision
+ * for old code which depends on the version number.
+ */
+ uint16_t struct_version_major;
+ uint16_t struct_version_minor;
+
+ /*
+ * Size of the parent structure and all its data, including the
+ * description and any necessary padding. That is, all data must lie
+ * in a contiguous region of <total_size> bytes starting at the first
+ * byte of this header.
+ */
+ uint32_t total_size;
+
+ /*
+ * Size of the fixed portion of the parent structure. If a description
+ * is present, it must start at this offset.
+ */
+ uint32_t fixed_size;
+
+ /*
+ * The object may contain an ASCII description following the fixed
+ * portion of the structure. If it is present, it must be
+ * null-terminated, and padded with 0 (null) bytes to a multiple of 32
+ * bits.
+ *
+ * Size of ASCII description in bytes, counting null terminator and
+ * padding (if any). Set 0 if no description is present. If non-zero,
+ * there must be a null terminator (0) at offset (fixed_size +
+ * desc_size - 1).
+ */
+ uint32_t desc_size;
+} __attribute__((packed));
+
+/*
+ * Signature data
+ *
+ * The signature data must be arranged like this:
+ * 1) evb21_signature header struct h
+ * 2) Signature description (pointed to by h.c.fixed_size)
+ * 3) Signature data (pointed to by h.sig_offset)
+ */
+struct evb21_signature {
+ /* Common header fields */
+ struct evb21_struct_common c;
+
+ /* Offset of signature data from start of this struct */
+ uint32_t sig_offset;
+
+ /* Size of signature data in bytes */
+ uint32_t sig_size;
+
+ /* Size of the data block which was signed in bytes */
+ uint32_t data_size;
+
+ /* Signature algorithm used (enum vb2_signature_algorithm) */
+ uint16_t sig_alg;
+
+ /* Hash digest algorithm used (enum vb2_hash_algorithm) */
+ uint16_t hash_alg;
+
+ /*
+ * ID for the signature.
+ *
+ * If this is a keyblock signature entry, this is the ID of the key
+ * used to generate this signature. This allows the firmware to
+ * quickly determine which signature block (if any) goes with the key
+ * being used by the firmware.
+ *
+ * If this is a preamble hash entry, this is the ID of the data type
+ * being hashed. There is no key ID, because sig_alg=VB2_ALG_NONE.
+ */
+ struct evb2_id id;
+} __attribute__((packed));
+
+/* Current version of evb21_packed_key struct */
+#define VB21_PACKED_KEY_VERSION_MAJOR 3
+#define VB21_PACKED_KEY_VERSION_MINOR 0
+
+/*
+ * Packed public key data
+ *
+ * The key data must be arranged like this:
+ * 1) evb21_packed_key header struct h
+ * 2) Key description (pointed to by h.c.fixed_size)
+ * 3) Key data key (pointed to by h.key_offset)
+ */
+struct evb21_packed_key {
+ /* Common header fields */
+ struct evb21_struct_common c;
+
+ /* Offset of key data from start of this struct */
+ uint32_t key_offset;
+
+ /* Size of key data in bytes (NOT strength of key in bits) */
+ uint32_t key_size;
+
+ /* Signature algorithm used by the key (enum vb2_signature_algorithm) */
+ uint16_t sig_alg;
+
+ /*
+ * Hash digest algorithm used with the key (enum vb2_hash_algorithm).
+ * This is explicitly specified as part of the key to prevent use of a
+ * strong key with a weak hash.
+ */
+ uint16_t hash_alg;
+
+ /* Key version */
+ uint32_t key_version;
+
+ /* Key ID */
+ struct evb2_id id;
+} __attribute__((packed));
+
+/**
+ * Unpack a key for use in verification
+ *
+ * The elements of the unpacked key will point into the source buffer, so don't
+ * free the source buffer until you're done with the key.
+ *
+ * @param key Destintion for unpacked key
+ * @param buf Source buffer containing packed key
+ * @param size Size of buffer in bytes
+ * @return VB2_SUCCESS, or non-zero error code if error.
+ */
+vb2_error_t evb21_unpack_key(struct evb2_public_key *key, const uint8_t *buf,
+ uint32_t size);
+
+/**
+ * Verify data matches signature.
+ *
+ * @param data Data to verify
+ * @param size Size of data buffer. Note that amount of data to
+ * actually validate is contained in sig->data_size.
+ * @param sig Signature of data (destroyed in process)
+ * @param key Key to use to validate signature
+ * @param wb Work buffer
+ * @return VB2_SUCCESS, or non-zero error code if error.
+ */
+vb2_error_t evb21_verify_data(const void *data, uint32_t size,
+ struct evb21_signature *sig,
+ const struct evb2_public_key *key,
+ const struct evb2_workbuf *wb);
+
+/**
+ * Return the size of a RSA signature
+ *
+ * @param sig_alg Signature algorithm
+ * @return The size of the signature in bytes, or 0 if error.
+ */
+uint32_t evb2_rsa_sig_size(enum vb2_signature_algorithm sig_alg);
+
+#endif //ELTAN_VB21_H
diff --git a/src/vendorcode/eltan/security/verified_boot/vboot_check.c b/src/vendorcode/eltan/security/verified_boot/vboot_check.c
index bc502c9..7d39a44 100644
--- a/src/vendorcode/eltan/security/verified_boot/vboot_check.c
+++ b/src/vendorcode/eltan/security/verified_boot/vboot_check.c
@@ -16,8 +16,10 @@
#include <boot_device.h>
#include <bootmem.h>
#include <cbfs.h>
-#include <vboot_check.h>
-#include <vboot_common.h>
+#include <security/vboot/vboot_common.h>
+#include <vb2_sha.h>
+#include <vendorcode/eltan/security/verified_boot/vboot_check.h>
+#include <vendorcode/eltan/security/verified_boot/eltan_vb21.h>

#define RSA_PUBLICKEY_FILE_NAME "vboot_public_key.bin"

@@ -32,11 +34,11 @@
int verified_boot_check_manifest(void)
{
uint8_t *buffer;
- uint8_t sig_buffer[1024]; /* used to build vb21_signature */
+ uint8_t sig_buffer[1024]; /* used to build evb21_signature */
size_t size = 0;
- struct vb2_public_key key;
- struct vb2_workbuf wb;
- struct vb21_signature *vb2_sig_hdr = (struct vb21_signature *)sig_buffer;
+ struct evb2_public_key key;
+ struct evb2_workbuf wb;
+ struct evb21_signature *evb2_sig_hdr = (struct evb21_signature *)sig_buffer;
uint8_t wb_buffer[1024];

buffer = cbfs_boot_map_with_leak(RSA_PUBLICKEY_FILE_NAME, CBFS_TYPE_RAW, &size);
@@ -51,14 +53,14 @@
goto fail;
}

- if (vb21_unpack_key(&key, buffer, size)) {
+ if (evb21_unpack_key(&key, buffer, size)) {
printk(BIOS_ERR, "ERROR: Invalid public key!\n");
goto fail;
}

cbfs_boot_map_with_leak("oemmanifest.bin", CBFS_TYPE_RAW, &size);
if (size != (CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE) +
- vb2_rsa_sig_size(VB2_SIG_RSA2048)) {
+ evb2_rsa_sig_size(VB2_SIG_RSA2048)) {
printk(BIOS_ERR, "ERROR: Incorrect manifest size!\n");
goto fail;
}
@@ -67,18 +69,18 @@
wb.buf = (uint8_t *)&wb_buffer;
wb.size = sizeof(wb_buffer);

- /* Build vb2_sig_hdr buffer */
- vb2_sig_hdr->sig_offset = sizeof(struct vb21_signature) +
+ /* Build evb2_sig_hdr buffer */
+ evb2_sig_hdr->sig_offset = sizeof(struct evb21_signature) +
(CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE);
- vb2_sig_hdr->sig_alg = VB2_SIG_RSA2048;
- vb2_sig_hdr->sig_size = vb2_rsa_sig_size(VB2_SIG_RSA2048);
- vb2_sig_hdr->hash_alg = HASH_ALG;
- vb2_sig_hdr->data_size = CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE;
- memcpy(&sig_buffer[sizeof(struct vb21_signature)],
+ evb2_sig_hdr->sig_alg = VB2_SIG_RSA2048;
+ evb2_sig_hdr->sig_size = evb2_rsa_sig_size(VB2_SIG_RSA2048);
+ evb2_sig_hdr->hash_alg = HASH_ALG;
+ evb2_sig_hdr->data_size = CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_ITEMS * DIGEST_SIZE;
+ memcpy(&sig_buffer[sizeof(struct evb21_signature)],
(uint8_t *)CONFIG_VENDORCODE_ELTAN_OEM_MANIFEST_LOC, size);

- if (vb21_verify_data(&sig_buffer[sizeof(struct vb21_signature)], vb2_sig_hdr->data_size,
- (struct vb21_signature *)&sig_buffer, &key, &wb)) {
+ if (evb21_verify_data(&sig_buffer[sizeof(struct evb21_signature)], evb2_sig_hdr->data_size,
+ (struct evb21_signature *)&sig_buffer, &key, &wb)) {
printk(BIOS_ERR, "ERROR: Signature verification failed for hash table\n");
goto fail;
}

To view, visit change 37654. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: Iae58a0c42161ddfec9908f9867286e3dc1f055a2
Gerrit-Change-Number: 37654
Gerrit-PatchSet: 1
Gerrit-Owner: Joel Kitching <kitching@google.com>
Gerrit-MessageType: newchange