common/recipes-utils/vboot-utils/files/measure_func.py (144 lines of code) (raw):
# Copyright 2022-present Facebook. All Rights Reserved.
#
# This program file 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.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
import hashlib
from vboot_common import (
get_fdt,
get_fdt_from_file,
read_vbs,
)
class Pcr:
def __init__(self, algo="sha256"):
self.algo = algo
h = hashlib.new(self.algo)
self.value = b"\0" * h.digest_size
self.block_size = h.block_size
self.digest_size = h.digest_size
def extend(self, value):
h = hashlib.new(self.algo, self.value)
h.update(value)
self.value = h.digest()
return self.value
def hash_comp(filename, offset, size, algo="sha256"):
h = hashlib.new(algo)
with open(filename, "rb") as fh:
fh.seek(offset)
remain = size
while remain:
read_size = min(remain, h.block_size)
data = fh.read(read_size)
remain -= read_size
h.update(data)
return h.digest()
def measure_spl(algo, image_meta, rawhash=False):
# PCR0 : hash(spl)
# notice: includes the spl-dtb: keks, signing date, and lock-bit
pcr0 = Pcr(algo)
spl = image_meta.get_part_info("spl")
spl_measure = hash_comp(image_meta.image, spl["offset"], spl["size"], algo)
return spl_measure if rawhash else pcr0.extend(spl_measure)
def measure_keystore(algo, image_meta, rawhash=False):
# PCR1 : hash(key-store)
# notice: the key-store is the first 16K of the u-boot-fit
pcr1 = Pcr(algo)
fit = image_meta.get_part_info("u-boot-fit")
fit_measure = hash_comp(image_meta.image, fit["offset"], 0x4000, algo)
return fit_measure if rawhash else pcr1.extend(fit_measure)
def get_uboot_hash_algo_and_size(filename, offset, size):
with open(filename, "rb") as fh:
fh.seek(offset)
uboot_fit = fh.read(size)
uboot_fdt = get_fdt(uboot_fit)
uboot_size = uboot_fdt.resolve_path("/images/firmware@1/data-size")[0]
uboot_hash = uboot_fdt.resolve_path("/images/firmware@1/hash@1/value").to_raw()
uboot_algo = uboot_fdt.resolve_path("/images/firmware@1/hash@1/algo")[0]
return (uboot_hash, uboot_algo, uboot_size)
def get_os_comps_hash_algo_offset_size(filename, offset, size):
comps = []
with open(filename, "rb") as fh:
fh.seek(offset)
os_fit = get_fdt_from_file(fh)
# kernel
kernel_hash = os_fit.resolve_path("/images/kernel@1/hash@1/value").to_raw()
kernel_algo = os_fit.resolve_path("/images/kernel@1/hash@1/algo")[0]
kernel_data = os_fit.resolve_path("/images/kernel@1/data")
comps.append(
(
kernel_hash,
kernel_algo,
kernel_data.blob_info[0],
kernel_data.blob_info[1],
)
)
# ramdisk
ramdisk_hash = os_fit.resolve_path("/images/ramdisk@1/hash@1/value").to_raw()
ramdisk_algo = os_fit.resolve_path("/images/ramdisk@1/hash@1/algo")[0]
ramdisk_data = os_fit.resolve_path("/images/ramdisk@1/data")
comps.append(
(
ramdisk_hash,
ramdisk_algo,
ramdisk_data.blob_info[0],
ramdisk_data.blob_info[1],
)
)
# fdt
fdt_hash = os_fit.resolve_path("/images/fdt@1/hash@1/value").to_raw()
fdt_algo = os_fit.resolve_path("/images/fdt@1/hash@1/algo")[0]
fdt_data = os_fit.resolve_path("/images/fdt@1/data")
comps.append((fdt_hash, fdt_algo, fdt_data.blob_info[0], fdt_data.blob_info[1]))
return comps
def measure_uboot(algo, image_meta, recal=False, rawhash=False):
# PCR2: hash(sha256(uboot))
# notice: to simplify SPL measure code, spl hash the "hash of uboot" read from fit
pcr2 = Pcr(algo)
fit = image_meta.get_part_info("u-boot-fit")
uboot_hash, uboot_hash_algo, uboot_size = get_uboot_hash_algo_and_size(
image_meta.image, fit["offset"], 0x4000
)
if recal:
# uboot_hash_algo define in FIT signature which is independent with
# measure (input algo of hash_comp) and PCR hash algorithm
uboot_measure = hash_comp(
image_meta.image, fit["offset"] + 0x4000, uboot_size, uboot_hash_algo
)
assert (
uboot_hash == uboot_measure
), """Build, Signing or SPL code BUG!!!.
UBoot size:0x%08X.
Hash:
expected:[%s]
measured:[%s]
""" % (
uboot_size,
uboot_hash.hex(),
uboot_measure.hex(),
)
uboot_measure = hashlib.new(algo, uboot_hash).digest()
return uboot_measure if rawhash else pcr2.extend(uboot_measure)
def measure_uboot_env(algo, image_meta, rawhash=False):
# PCR3 : hash(u-boot-env)
pcr3 = Pcr(algo)
env = image_meta.get_part_info("u-boot-env")
env_measure = hash_comp(image_meta.image, env["offset"], env["size"], algo)
return env_measure if rawhash else pcr3.extend(env_measure)
def measure_rcv_uboot(algo, image_meta, rawhash=False):
# PCR2 : hash(rec-u-boot)
# Notice: will measured when booting into golden image
pcr2 = Pcr(algo)
rec_uboot = image_meta.get_part_info("rec-u-boot")
rec_uboot_measure = hash_comp(
image_meta.image, rec_uboot["offset"], rec_uboot["size"], algo
)
return rec_uboot_measure if rawhash else pcr2.extend(rec_uboot_measure)
def measure_blank_uboot_env(algo, image_meta, rawhash=False):
# PCR3 : hash(64KB zero)
# Notice: in the image the uboot env partition is blank
# during the first time boot up u-boot will save the
# default u-boot environment into this blank env partition.
# But SPL measure the uboot env partition before this
# so SPL will measure a blank uboot env during the first time
# bootup. Add it here to help test cover this special case.
pcr3 = Pcr(algo)
blank_uboot_env_measure = hashlib.new(algo, bytearray(64 * 1024)).digest()
return blank_uboot_env_measure if rawhash else pcr3.extend(blank_uboot_env_measure)
def measure_os(algo, image_meta, recal=False, rawhash=False):
# PCR9: hash(sha256(kernel)), hash(sha256(rootfs)), hash(sha256(fdt))
# notice: to simplify the measure code, we extend
# hash of the sha256(kernel),
# hash of the sha256(rootfs),
# hash of the sha256(fdt)
# in ORDER into PCR9.
pcr9 = Pcr(algo)
fit = image_meta.get_part_info("os-fit")
os_components = get_os_comps_hash_algo_offset_size(
image_meta.image, fit["offset"], fit["size"]
)
raw_hashes = []
for comp_hash, comp_hash_algo, comp_offset, comp_size in os_components:
if recal:
# comp_hash_algo define in FIT signture which is independent with
# measure (input algo of hash_comp) and PCR hash algorithm
comp_measure = hash_comp(
image_meta.image, comp_offset, comp_size, comp_hash_algo
)
assert (
comp_hash == comp_measure
), """Build, Signing or uboot code BUG!!!.
size:0x%08X.
Hash:
expected:[%s]
measured:[%s]
""" % (
comp_size,
comp_hash.hex(),
comp_measure.hex(),
)
comp_measure = hashlib.new(algo, comp_hash).digest()
raw_hashes += [comp_measure]
pcr9.extend(comp_measure)
return raw_hashes if rawhash else pcr9.value
def measure_vbs(algo, rawhash=False):
# PCR5: vbs structure
pcr5 = Pcr(algo)
vbs_measure = hashlib.new(algo, read_vbs()).digest()
return vbs_measure if rawhash else pcr5.extend(vbs_measure)