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>
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
"""
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";
+ };
+ };
+ };
+};