From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EDC40C433F5 for ; Wed, 15 Sep 2021 19:33:15 +0000 (UTC) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 20B5861178 for ; Wed, 15 Sep 2021 19:33:14 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 20B5861178 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=lists.denx.de Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 28BC482C2B; Wed, 15 Sep 2021 21:33:12 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="ciE+qRXg"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 4EAAC82D74; Wed, 15 Sep 2021 21:33:10 +0200 (CEST) Received: from mail-oi1-x22c.google.com (mail-oi1-x22c.google.com [IPv6:2607:f8b0:4864:20::22c]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 819D481D1F for ; Wed, 15 Sep 2021 21:33:06 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=mr.nuke.me@gmail.com Received: by mail-oi1-x22c.google.com with SMTP id w144so5602130oie.13 for ; Wed, 15 Sep 2021 12:33:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=R1kVeFe46wQTEKNheAEEJVXtlcJJzfR52kCPJITQBdw=; b=ciE+qRXgMMfOTOg0OtiEWzCgIfqzE7HTYXG+0xCvEnIRoHiGiS/upZ0whTVovDeS8T s6/8YApGdi9EPcYNDBU92h0ifs6py3GQJE3ijSfY9P9VHPWjqrNDdbzmeycK3wTlRMpR 1jqXZhrBIzLLYvT9DryLmRTUs1g95rMnKdsw/K9KoiQJcXdEj5dXnJpiCtFe7NMSlPb1 ajwQefvUZws4/MvqzbCG7ueTuXURFbRW8ocm6c0MUH1xZALgSB9Okmiblj7KFgHpOzMn HhgeJGrR01Zo9pQChoWgG6WMTfXIKqeaHazku0Kv/9jgvIMU+bZNRSy694gGdweWV1Fl Lmfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=R1kVeFe46wQTEKNheAEEJVXtlcJJzfR52kCPJITQBdw=; b=MsmhkWei+dxcnpTt73SryrtrMUSDTP0HiyX4UVWoy2WDxjojbThAhlM0uYwM7vIhTs JJ9NUt67ooIin3b/kq8AhClPqyRggMnYZP0f+SxbGxyjkfGDuR+CAnEKROwh3WEA+PYS 7hcE0C4rJeD78dlBKuI/xvMiWYLvBJZGMoHbSjDrcGUixC4XqefmxfX/7CH6/X9j1MtZ pQb6VK56H7ADfcVjDd+t7ADUaCDQ5DRbcXJf0nt4mapAHSxf6dhGCkZoye1lPwAvbZ0h MvXBO23TzfG5qLwjKRfvPeMNyEwF2nA5SIEeosm62oRuz3j3Vh2bzBN2tledt+yuJzBK MBFQ== X-Gm-Message-State: AOAM533c4j/0K0k8A0/6it6VfbIMTFE+K90fZzT2miO7FQXU/+RpLWVh IgAzQ/EpkxQjcHFDp18sTqsfOYm4lik= X-Google-Smtp-Source: ABdhPJzx32V5HVHO3OrLDVGjhuuaSWXfpFV+Sa7HxtCZJ1lvr2lKZ1Ogo7JtMluj8uHn7var0bhpag== X-Received: by 2002:aca:d608:: with SMTP id n8mr995280oig.122.1631734384445; Wed, 15 Sep 2021 12:33:04 -0700 (PDT) Received: from nuclearis3.lan (c-98-195-139-126.hsd1.tx.comcast.net. [98.195.139.126]) by smtp.gmail.com with ESMTPSA id u19sm241527oof.30.2021.09.15.12.33.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Sep 2021 12:33:03 -0700 (PDT) From: Alexandru Gagniuc To: u-boot@lists.denx.de, trini@konsulko.com, sjg@chromium.org Cc: Alexandru Gagniuc Subject: [RFC PATCH] test/py: Check hashes produced by mkimage against known values Date: Wed, 15 Sep 2021 14:33:01 -0500 Message-Id: <20210915193301.3687049-1-mr.nuke.me@gmail.com> X-Mailer: git-send-email 2.31.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Target code and mkimage share the same hashing infrastructure. If one is wrong, it's very likely that both are wrong in the same way. Thus testing won't catch hash regressions. This already happened in commit 92055e138f28 ("image: Drop if/elseif hash selection in calculate_hash()"). None of the tests caught that CRC32 was broken. Instead of testing hash_calculate() against itself, create a FIT with containing a kernel with pre-calculated hashes. Then check the hashes produced against the known good hashes. Signed-off-by: Alexandru Gagniuc --- Desired: $ ./test/py/test.py -k hash ... test_mkimage_hashes PASSED Is very cryptic with regards to what is going on. It would be much nicer to have tests named "crc32", "sha256", and so on. But I don't know how to do that without several pyton functions each assembling their own damn FIT. I think it would also be nicer for the test log to show test_sha1 PASSED test_crc32 PASSED test_md5 FAILED test/py/tests/test_fit_hashes.py | 111 ++++++++++++++++++++++++++++ test/py/tests/vboot/hash-images.its | 76 +++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 test/py/tests/test_fit_hashes.py create mode 100644 test/py/tests/vboot/hash-images.its diff --git a/test/py/tests/test_fit_hashes.py b/test/py/tests/test_fit_hashes.py new file mode 100644 index 0000000000..e228ea96d3 --- /dev/null +++ b/test/py/tests/test_fit_hashes.py @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2021 Alexandru Gagniuc + +""" +Check hashes produced by mkimage against known values + +This test checks the correctness of mkimage's hashes. by comparing the mkimage +output of a fixed data block with known good hashes. +This test doesn't run the sandbox. It only checks the host tool 'mkimage' +""" + +import pytest +import u_boot_utils as util + +kernel_hashes = { + "sha512" : "f18c1486a2c29f56360301576cdfce4dfd8e8e932d0ed8e239a1f314b8ae1d77b2a58cd7fe32e4075e69448e623ce53b0b6aa6ce5626d2c189a5beae29a68d93", + "sha384" : "16e28976740048485d08d793d8bf043ebc7826baf2bc15feac72825ad67530ceb3d09e0deb6932c62a5a0e9f3936baf4", + "sha256" : "2955c56bc1e5050c111ba6e089e0f5342bb47dedf77d87e3f429095feb98a7e5", + "sha1" : "652383e1a6d946953e1f65092c9435f6452c2ab7", + "md5" : "4879e5086e4c76128e525b5fe2af55f1", + "crc32" : "32eddfdf", + "crc16-ccitt" : "d4be" +} + +class ReadonlyFitImage(object): + """ Helper to manipulate a FIT image on disk """ + def __init__(self, cons, file_name): + self.fit = file_name + self.cons = cons + self.hashable_nodes = set() + + def __fdt_list(self, path): + return util.run_and_log(self.cons, f'fdtget -l {self.fit} {path}') + + def __fdt_get(self, node, prop): + val = util.run_and_log(self.cons, f'fdtget {self.fit} {node} {prop}') + return val.rstrip('\n') + + def __fdt_get_sexadecimal(self, node, prop): + numbers = util.run_and_log(self.cons, f'fdtget -tbx {self.fit} {node} {prop}') + + sexadecimal = '' + for num in numbers.rstrip('\n').split(' '): + sexadecimal += num.zfill(2) + return sexadecimal + + def find_hashable_image_nodes(self): + for node in self.__fdt_list('/images').split(): + # We only have known hashes for the kernel node + if 'kernel' not in node: + continue + self.hashable_nodes.add(f'/images/{node}') + + return self.hashable_nodes + + def verify_hashes(self): + for image in self.hashable_nodes: + algos = set() + for node in self.__fdt_list(image).split(): + if "hash-" not in node: + continue + + raw_hash = self.__fdt_get_sexadecimal(f'{image}/{node}', 'value') + algo = self.__fdt_get(f'{image}/{node}', 'algo') + algos.add(algo) + + good_hash = kernel_hashes[algo] + if good_hash != raw_hash: + raise ValueError(f'{image} Borked hash: {algo}'); + + # Did we test all the hashes we set out to test? + missing_algos = kernel_hashes.keys() - algos + if (missing_algos): + raise ValueError(f'Missing hashes from FIT: {missing_algos}') + + +@pytest.mark.buildconfigspec('hash') +@pytest.mark.requiredtool('dtc') +@pytest.mark.requiredtool('fdtget') +@pytest.mark.requiredtool('fdtput') +def test_mkimage_hashes(u_boot_console): + """ Test that hashes generated by mkimage are correct. """ + + def assemble_fit_image(dest_fit, its, destdir): + dtc_args = f'-I dts -O dtb -i {destdir}' + util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', its, dest_fit]) + + def dtc(dts): + dtb = dts.replace('.dts', '.dtb') + util.run_and_log(cons, f'dtc {datadir}/{dts} -O dtb -o {tempdir}/{dtb}') + + cons = u_boot_console + mkimage = cons.config.build_dir + '/tools/mkimage' + datadir = cons.config.source_dir + '/test/py/tests/vboot/' + tempdir = cons.config.result_dir + fit_file = f'{tempdir}/test.fit' + dtc('sandbox-kernel.dts') + + # Create a fake kernel image -- Avoid zeroes or crc16 will be zero + with open(f'{tempdir}/test-kernel.bin', 'w') as fd: + fd.write(500 * chr(0xa5)) + + assemble_fit_image(fit_file, f'{datadir}/hash-images.its', tempdir) + + fit = ReadonlyFitImage(cons, fit_file) + nodes = fit.find_hashable_image_nodes() + if len(nodes) == 0: + raise ValueError('FIT image has no "/image" nodes with "hash-..."') + + fit.verify_hashes() diff --git a/test/py/tests/vboot/hash-images.its b/test/py/tests/vboot/hash-images.its new file mode 100644 index 0000000000..3ff797288c --- /dev/null +++ b/test/py/tests/vboot/hash-images.its @@ -0,0 +1,76 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash-0 { + algo = "crc16-ccitt"; + }; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "md5"; + }; + hash-3 { + algo = "sha1"; + }; + hash-4 { + algo = "sha256"; + }; + hash-5 { + algo = "sha384"; + }; + hash-6 { + algo = "sha512"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash-0 { + algo = "crc16-ccitt"; + }; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "md5"; + }; + hash-3 { + algo = "sha1"; + }; + hash-4 { + algo = "sha256"; + }; + hash-5 { + algo = "sha384"; + }; + hash-6 { + algo = "sha512"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; +}; -- 2.31.1