From e0e5df9310d3a0e1fc0eda86ff43fd3e782e61f1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 14 Sep 2018 04:57:31 -0600 Subject: [PATCH] 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 --- tools/binman/README | 22 +++++++++++++++++++++ tools/binman/bsection.py | 3 +++ tools/binman/entry.py | 4 ++++ tools/binman/ftest.py | 36 ++++++++++++++++++++++++++++++++++ tools/binman/state.py | 25 +++++++++++++++++++++++ tools/binman/test/90_hash.dts | 12 ++++++++++++ tools/binman/test/91_hash_no_algo.dts | 11 +++++++++++ tools/binman/test/92_hash_bad_algo.dts | 12 ++++++++++++ tools/binman/test/99_hash_section.dts | 18 +++++++++++++++++ 9 files changed, 143 insertions(+) create mode 100644 tools/binman/test/90_hash.dts create mode 100644 tools/binman/test/91_hash_no_algo.dts create mode 100644 tools/binman/test/92_hash_bad_algo.dts create mode 100644 tools/binman/test/99_hash_section.dts diff --git a/tools/binman/README b/tools/binman/README index cf1a06d..088f3a6 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -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 ----------------------- diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py index 52ac31a..650e9ba 100644 --- a/tools/binman/bsection.py +++ b/tools/binman/bsection.py @@ -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() diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 0915b47..fd72234 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -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 diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index b156943..c46a065 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -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() diff --git a/tools/binman/state.py b/tools/binman/state.py index 2f8c086..d945e4b 100644 --- a/tools/binman/state.py +++ b/tools/binman/state.py @@ -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) diff --git a/tools/binman/test/90_hash.dts b/tools/binman/test/90_hash.dts new file mode 100644 index 0000000..2003045 --- /dev/null +++ b/tools/binman/test/90_hash.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + hash { + algo = "sha256"; + }; + }; + }; +}; diff --git a/tools/binman/test/91_hash_no_algo.dts b/tools/binman/test/91_hash_no_algo.dts new file mode 100644 index 0000000..b64df20 --- /dev/null +++ b/tools/binman/test/91_hash_no_algo.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + hash { + }; + }; + }; +}; diff --git a/tools/binman/test/92_hash_bad_algo.dts b/tools/binman/test/92_hash_bad_algo.dts new file mode 100644 index 0000000..d240200 --- /dev/null +++ b/tools/binman/test/92_hash_bad_algo.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + hash { + algo = "invalid"; + }; + }; + }; +}; diff --git a/tools/binman/test/99_hash_section.dts b/tools/binman/test/99_hash_section.dts new file mode 100644 index 0000000..dcd8683 --- /dev/null +++ b/tools/binman/test/99_hash_section.dts @@ -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"; + }; + }; + }; +};