blob: ecb30ac526e9d552109d3bef1cff8e371133fea8 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2011 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.
import hashlib
import optparse
import os
import flashrom_util
import fmap
import gft_common
from gft_common import WarningMsg, VerboseMsg, DebugMsg, ErrorMsg, ErrorDie
# Global Variable
_HashAlgorithms = {}
# Utility Function
def HashAlgorithm(func):
"""Decorator for algorithm definition. """
def _wrapped(*arg, **kargs):
result = func(*arg, **kargs)
(target, version) = _wrapped.__name__.split('_')
code = target.lower()[0]
return '%c%s#%s' % (code, version, result)
name = func.__name__
assert name not in _HashAlgorithms
_wrapped.__name__ = name
_HashAlgorithms[name] = _wrapped
return _wrapped
def DefaultHashAlgorithm(func):
"""Decorator of default algorithm. """
name = func.__name__
(target, code) = name.split('_')
new_func = HashAlgorithm(func)
assert target not in _HashAlgorithms
_HashAlgorithms[target] = new_func
return new_func
def FindAlgorithm(target, version):
"""Returns a registered algorithm"""
if version:
name = '%s_%s' % (target, version)
else:
name = target
if name not in _HashAlgorithms:
ErrorDie('Unknown algorithm: %s' % name)
return _HashAlgorithms[name]
def GetGbbKey(image_file, name):
"""
Reads GBB key property from a firmware image.
Args:
image_file: the file name of main firmware image.
name: name of the key to retrieve (see gbb_utility).
"""
filename = gft_common.GetTemporaryFileName('gbb%s' % name)
try:
gft_common.System('gbb_utility -g --%s=%s %s' %
(name, filename, image_file))
return gft_common.ReadBinaryFile(filename)
finally:
os.remove(filename)
# Hash Algorithms
@HashAlgorithm
def Main_v1(image_source):
""" Algorithm: sha256(fmap, RO_SECTION) """
image = flashrom_util.FirmwareImage(image_source)
hash_src = image.get_fmap_blob()
if image.has_section('RO_SECTION'):
hash_src += image.get_section('RO_SECTION')
else:
# legacy section
hash_src += image.get_section('BOOT_STUB')
hash_src += image.get_section('GBB')
hash_src += image.get_section('RECOVERY')
return hashlib.sha256(hash_src).hexdigest()
@DefaultHashAlgorithm
def Main_v2(image_source):
""" Algorithm: sha256(fmap, RO_SECTION[-GBB]) """
image = flashrom_util.FirmwareImage(image_source)
hash_src = image.get_fmap_blob()
gbb = image.get_section('GBB')
zero_gbb = chr(0) * len(gbb)
image.put_section('GBB', zero_gbb)
hash_src += image.get_section('RO_SECTION')
return hashlib.sha256(hash_src).hexdigest()
@HashAlgorithm
def Ec_v1(image_source):
""" Algorithm: sha256(whole_image) """
hash_src = image_source
return hashlib.sha256(hash_src).hexdigest()
@DefaultHashAlgorithm
def Ec_v2(image_source):
""" Algorithm: sha256(fmap, EC_RO) """
image = flashrom_util.FirmwareImage(image_source)
hash_src = image.get_fmap_blob()
hash_src += image.get_section('EC_RO')
return hashlib.sha256(hash_src).hexdigest()
@HashAlgorithm
def Gbb_v1(image_source):
""" Algorithm: sha256(GBB) """
image = flashrom_util.FirmwareImage(image_source)
hash_src = image.get_section('GBB')
return hashlib.sha256(hash_src).hexdigest()
@DefaultHashAlgorithm
def Gbb_v2(image_source):
""" Algorithm: sha256(GBB-HWID) """
image = flashrom_util.FirmwareImage(image_source)
temp_file = gft_common.GetTemporaryFileName()
try:
# HWID can be checked explicitly, so we temporarily overwrite the HWID with
# a fixed string for computing the hash, so the hash doesn't depend on the
# final HWID.
reference_hwid = 'ChromeOS'
gft_common.WriteBinaryFile(temp_file, image.get_section('GBB'))
gft_common.System('gbb_utility -s --hwid="%s" "%s"' %
(reference_hwid, temp_file))
hash_src = gft_common.ReadBinaryFile(temp_file)
finally:
os.remove(temp_file)
return hashlib.sha256(hash_src).hexdigest()
@HashAlgorithm
def Key_v1(key_blob):
""" Algorithm: sha256(key) """
hash_src = key_blob
return hashlib.sha256(hash_src).hexdigest()
@DefaultHashAlgorithm
def Key_v2(key_blob):
""" Algorithm: sha256(key%%[00|FF])) """
# keys may be padded with 0x00 or 0xFF.
hash_src = key_blob
hash_src = hash_src.strip(chr(0)).strip(chr(0xFF))
return hashlib.sha256(hash_src).hexdigest()
# Public API
def GetMainFirmwareReadOnlyHash(file_source=None, algorithm=None):
"""
Returns a hash of main firmware (BIOS) read only parts,
to confirm we have proper keys / boot code / recovery image installed.
Args:
file_source: None to read firmware from system flash chip, otherwise
a file name of firmware image to read.
algorithm: The algorithm to generate hash value
"""
flashrom = flashrom_util.FlashromUtility(exception_type=gft_common.GFTError)
flashrom.initialize(flashrom.TARGET_BIOS, target_file=file_source)
image = flashrom.get_current_image()
algorithm = FindAlgorithm('Main', algorithm)
return algorithm(image)
def GetMainFirmwareGbbHash(file_source=None, algorithm=None):
"""
Returns a hash of main firmware (BIOS) GBB section,
to confirm we have proper keys / images / HWID.
Args:
file_source: None to read firmware from system flash chip, otherwise
a file name of firmware image to read.
algorithm: The algorithm to generate hash value
"""
flashrom = flashrom_util.FlashromUtility(exception_type=gft_common.GFTError)
flashrom.initialize(flashrom.TARGET_BIOS, target_file=file_source)
image = flashrom.get_current_image()
algorithm = FindAlgorithm('Gbb', algorithm)
return algorithm(image)
def GetEcFirmwareReadOnlyHash(file_source=None, algorithm=None):
"""
Returns a hash of Embedded Controller firmware read only parts,
to confirm we have proper updated version of EC firmware.
Args:
file_source: None to read firmware from system flash chip, otherwise
a file name of firmware image to read.
algorithm: The algorithm to generate hash value
"""
flashrom = flashrom_util.FlashromUtility(exception_type=gft_common.GFTError)
flashrom.initialize(flashrom.TARGET_EC, target_file=file_source)
image = flashrom.get_current_image()
algorithm = FindAlgorithm('Ec', algorithm)
return algorithm(image)
def GetKeyHash(key_blob, algorithm=None):
"""
Returns a hash of cryptographic key blob
Args:
key_blob: Cryptographic key blob, with paddings.
algorithm: The algorithm to generate hash value
"""
algorithm = FindAlgorithm('Key', algorithm)
return algorithm(key_blob)
def UpdateGBB(old_image, db_file, in_place=False):
"""
Updates main firmware image GBB data by given components database file.
Returns a new image with updated GBB data.
Args:
old_image: Firmware image file to be updated.
components: Hardware component list to be referred.
"""
base = gft_common.GetComponentsDatabaseBase(db_file)
try:
components = gft_common.LoadComponentsDatabaseFile(db_file)
except:
ErrorDie('UpdateGBB: Invalid components list file: %s' % db_file)
# The are 2 fields in component list related to gbb:
# - part_id_hwqual (HWID, mandatory)
# - data_bitmap_fv (optional because we have universal bitmap now)
# TODO(hungte) check if the keys match "hash_key_*" in components
hwid = components.get('part_id_hwqual', [])
bmpfv = components.get('data_bitmap_fv', [])
if len(hwid) != 1:
ErrorDie('HWID (part_id_hwqual) must be one valid value.')
if len(bmpfv) != 1:
ErrorDie('bitmap')
hwid = hwid[0]
bmpfv = bmpfv[0]
cmd = 'gbb_utility --set --hwid="%s"' % hwid
if bmpfv:
cmd += ' --bmpfv="%s"' % os.path.join(base, bmpfv)
cmd += ' "%s"' % old_image
new_image = old_image
if not in_place:
new_image = gft_common.GetTemporaryFileName()
cmd += ' "%s"' % new_image
VerboseMsg("WriteGBB: invoke command: " + cmd)
gft_common.System(cmd)
return new_image
#############################################################################
# Console main entry
@gft_common.GFTConsole
def _main():
valid_targets = ['main', 'ec', 'gbb', 'gbbkeys']
usage = 'Usage: %prog --target=TARGET --image=IMAGE [--gbb=COMPONENTS]'
parser = optparse.OptionParser(usage=usage)
parser.add_option('--target', dest='target', metavar='|'.join(valid_targets),
help=('type of target: %s' % ', '.join(valid_targets)))
parser.add_option('--image', dest='image',
help='firmware image file, or empty to read from system')
parser.add_option('--algorithm', dest='algorithm',
help='algorithm to generate hash')
parser.add_option('--gbb', dest='gbb', metavar='COMPONENTS',
help='component db file for replacing GBB data in BIOS')
(options, args) = parser.parse_args()
image = options.image
if image is None:
parser.error("Please specify --image to a firmware image file or ''")
if args:
parser.error("Unknown param(s): " + ' '.join(args))
target = options.target and options.target.lower()
# legacy support
if target == 'bios':
target = 'main'
if target not in valid_targets:
#TODO(hungte) Detect type by FMAP section names
parser.error("Please specify --target from: %s" %
(', ').join(valid_targets))
modified_image = None
if options.gbb:
if target != 'main' and target != 'gbb':
parser.error("Please set --target='main' or 'gbb' to update GBB")
if image == '':
parser.error("Please specify --image to a file to update GBB")
modified_image = UpdateGBB(image, options.gbb, in_place=False)
algorithm = options.algorithm
if target == 'main':
print "Main Firmware: %s" % GetMainFirmwareReadOnlyHash(
modified_image or image, algorithm=algorithm)
elif target == 'gbb':
print "GBB Hash: %s" % GetMainFirmwareGbbHash(
image, algorithm=algorithm)
elif target == 'gbbkeys':
print "Recovery Key: %s" % GetKeyHash(
GetGbbKey(image, "recoverykey"), algorithm=algorithm)
print "Root Key: %s" % GetKeyHash(
GetGbbKey(image, "rootkey"), algorithm=algorithm)
elif target == 'ec':
print "EC Firmware: %s" % GetEcFirmwareReadOnlyHash(
image, algorithm=algorithm)
# Remove the temporary GBB-modified file.
if modified_image:
os.remove(modified_image)
if __name__ == "__main__":
_main()