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>
This commit is contained in:
parent
9c888cca5e
commit
e0e5df9310
|
|
@ -466,6 +466,28 @@ see README.entries. This is generated from the source code using:
|
||||||
binman -E >tools/binman/README.entries
|
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
|
Order of image creation
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,8 @@ class Section(object):
|
||||||
|
|
||||||
def _ReadEntries(self):
|
def _ReadEntries(self):
|
||||||
for node in self._node.subnodes:
|
for node in self._node.subnodes:
|
||||||
|
if node.name == 'hash':
|
||||||
|
continue
|
||||||
entry = Entry.Create(self, node)
|
entry = Entry.Create(self, node)
|
||||||
entry.SetPrefix(self._name_prefix)
|
entry.SetPrefix(self._name_prefix)
|
||||||
self._entries[node.name] = entry
|
self._entries[node.name] = entry
|
||||||
|
|
@ -112,6 +114,7 @@ class Section(object):
|
||||||
for prop in ['offset', 'size', 'image-pos']:
|
for prop in ['offset', 'size', 'image-pos']:
|
||||||
if not prop in self._node.props:
|
if not prop in self._node.props:
|
||||||
state.AddZeroProp(self._node, prop)
|
state.AddZeroProp(self._node, prop)
|
||||||
|
state.CheckAddHashProp(self._node)
|
||||||
for entry in self._entries.values():
|
for entry in self._entries.values():
|
||||||
entry.AddMissingProperties()
|
entry.AddMissingProperties()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,12 +189,16 @@ class Entry(object):
|
||||||
for prop in ['offset', 'size', 'image-pos']:
|
for prop in ['offset', 'size', 'image-pos']:
|
||||||
if not prop in self._node.props:
|
if not prop in self._node.props:
|
||||||
state.AddZeroProp(self._node, prop)
|
state.AddZeroProp(self._node, prop)
|
||||||
|
err = state.CheckAddHashProp(self._node)
|
||||||
|
if err:
|
||||||
|
self.Raise(err)
|
||||||
|
|
||||||
def SetCalculatedProperties(self):
|
def SetCalculatedProperties(self):
|
||||||
"""Set the value of device-tree properties calculated by binman"""
|
"""Set the value of device-tree properties calculated by binman"""
|
||||||
state.SetInt(self._node, 'offset', self.offset)
|
state.SetInt(self._node, 'offset', self.offset)
|
||||||
state.SetInt(self._node, 'size', self.size)
|
state.SetInt(self._node, 'size', self.size)
|
||||||
state.SetInt(self._node, 'image-pos', self.image_pos)
|
state.SetInt(self._node, 'image-pos', self.image_pos)
|
||||||
|
state.CheckSetHashValue(self._node, self.GetData)
|
||||||
|
|
||||||
def ProcessFdt(self, fdt):
|
def ProcessFdt(self, fdt):
|
||||||
"""Allow entries to adjust the device tree
|
"""Allow entries to adjust the device tree
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#
|
#
|
||||||
# python -m unittest func_test.TestFunctional.testHelp
|
# python -m unittest func_test.TestFunctional.testHelp
|
||||||
|
|
||||||
|
import hashlib
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
@ -1608,6 +1609,41 @@ class TestFunctional(unittest.TestCase):
|
||||||
self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
|
self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
|
||||||
'expanding entry', str(e.exception))
|
'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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
# Holds and modifies the state information held by binman
|
# Holds and modifies the state information held by binman
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import hashlib
|
||||||
import re
|
import re
|
||||||
from sets import Set
|
from sets import Set
|
||||||
|
|
||||||
|
|
@ -226,3 +227,27 @@ def SetInt(node, prop, value):
|
||||||
"""
|
"""
|
||||||
for n in GetUpdateNodes(node):
|
for n in GetUpdateNodes(node):
|
||||||
n.SetInt(prop, value)
|
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…
Reference in New Issue