binman: Support hashing entries

Sometimesi it us useful to be able to verify the content of entries with
a hash. Add an easy way to do this in binman. The hash information can be
retrieved from the device tree at run time.

Signed-off-by: Simon Glass <sjg@chromium.org>
lime2-spi
Simon Glass 6 years ago
parent 9c888cca5e
commit e0e5df9310
  1. 22
      tools/binman/README
  2. 3
      tools/binman/bsection.py
  3. 4
      tools/binman/entry.py
  4. 36
      tools/binman/ftest.py
  5. 25
      tools/binman/state.py
  6. 12
      tools/binman/test/90_hash.dts
  7. 11
      tools/binman/test/91_hash_no_algo.dts
  8. 12
      tools/binman/test/92_hash_bad_algo.dts
  9. 18
      tools/binman/test/99_hash_section.dts

@ -466,6 +466,28 @@ see README.entries. This is generated from the source code using:
binman -E >tools/binman/README.entries
Hashing Entries
---------------
It is possible to ask binman to hash the contents of an entry and write that
value back to the device-tree node. For example:
binman {
u-boot {
hash {
algo = "sha256";
};
};
};
Here, a new 'value' property will be written to the 'hash' node containing
the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole
sections can be hased if desired, by adding the 'hash' node to the section.
The has value can be chcked at runtime by hashing the data actually read and
comparing this has to the value in the device tree.
Order of image creation
-----------------------

@ -89,6 +89,8 @@ class Section(object):
def _ReadEntries(self):
for node in self._node.subnodes:
if node.name == 'hash':
continue
entry = Entry.Create(self, node)
entry.SetPrefix(self._name_prefix)
self._entries[node.name] = entry
@ -112,6 +114,7 @@ class Section(object):
for prop in ['offset', 'size', 'image-pos']:
if not prop in self._node.props:
state.AddZeroProp(self._node, prop)
state.CheckAddHashProp(self._node)
for entry in self._entries.values():
entry.AddMissingProperties()

@ -189,12 +189,16 @@ class Entry(object):
for prop in ['offset', 'size', 'image-pos']:
if not prop in self._node.props:
state.AddZeroProp(self._node, prop)
err = state.CheckAddHashProp(self._node)
if err:
self.Raise(err)
def SetCalculatedProperties(self):
"""Set the value of device-tree properties calculated by binman"""
state.SetInt(self._node, 'offset', self.offset)
state.SetInt(self._node, 'size', self.size)
state.SetInt(self._node, 'image-pos', self.image_pos)
state.CheckSetHashValue(self._node, self.GetData)
def ProcessFdt(self, fdt):
"""Allow entries to adjust the device tree

@ -6,6 +6,7 @@
#
# python -m unittest func_test.TestFunctional.testHelp
import hashlib
from optparse import OptionParser
import os
import shutil
@ -1608,6 +1609,41 @@ class TestFunctional(unittest.TestCase):
self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
'expanding entry', str(e.exception))
def testHash(self):
"""Test hashing of the contents of an entry"""
_, _, _, out_dtb_fname = self._DoReadFileDtb('90_hash.dts',
use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
m = hashlib.sha256()
m.update(U_BOOT_DATA)
self.assertEqual(m.digest(), ''.join(hash_node.value))
def testHashNoAlgo(self):
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('91_hash_no_algo.dts', update_dtb=True)
self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
'hash node', str(e.exception))
def testHashBadAlgo(self):
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('92_hash_bad_algo.dts', update_dtb=True)
self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
str(e.exception))
def testHashSection(self):
"""Test hashing of the contents of an entry"""
_, _, _, out_dtb_fname = self._DoReadFileDtb('99_hash_section.dts',
use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
hash_node = dtb.GetNode('/binman/section/hash').props['value']
m = hashlib.sha256()
m.update(U_BOOT_DATA)
m.update(16 * 'a')
self.assertEqual(m.digest(), ''.join(hash_node.value))
if __name__ == "__main__":
unittest.main()

@ -5,6 +5,7 @@
# Holds and modifies the state information held by binman
#
import hashlib
import re
from sets import Set
@ -226,3 +227,27 @@ def SetInt(node, prop, value):
"""
for n in GetUpdateNodes(node):
n.SetInt(prop, value)
def CheckAddHashProp(node):
hash_node = node.FindNode('hash')
if hash_node:
algo = hash_node.props.get('algo')
if not algo:
return "Missing 'algo' property for hash node"
if algo.value == 'sha256':
size = 32
else:
return "Unknown hash algorithm '%s'" % algo
for n in GetUpdateNodes(hash_node):
n.AddEmptyProp('value', size)
def CheckSetHashValue(node, get_data_func):
hash_node = node.FindNode('hash')
if hash_node:
algo = hash_node.props.get('algo').value
if algo == 'sha256':
m = hashlib.sha256()
m.update(get_data_func())
data = m.digest()
for n in GetUpdateNodes(hash_node):
n.SetData('value', data)

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot {
hash {
algo = "sha256";
};
};
};
};

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot {
hash {
};
};
};
};

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot {
hash {
algo = "invalid";
};
};
};
};

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
section {
u-boot {
};
fill {
size = <0x10>;
fill-byte = [61];
};
hash {
algo = "sha256";
};
};
};
};
Loading…
Cancel
Save