Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | # SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2022 Softathome # Written by Philippe Reynes <philippe.reynes@softathome.com> # # Entry-type for the global header # import os import struct from dtoc import fdt_util from u_boot_pylib import tools from binman.entry import Entry from binman.etype.collection import Entry_collection from binman.entry import EntryArg from Cryptodome.Hash import SHA256, SHA384, SHA512 from Cryptodome.PublicKey import RSA from Cryptodome.Signature import pkcs1_15 from Cryptodome.Signature import pss PRE_LOAD_MAGIC = b'UBSH' RSAS = { 'rsa1024': 1024 / 8, 'rsa2048': 2048 / 8, 'rsa4096': 4096 / 8 } SHAS = { 'sha256': SHA256, 'sha384': SHA384, 'sha512': SHA512 } class Entry_pre_load(Entry_collection): """Pre load image header Properties / Entry arguments: - pre-load-key-path: Path of the directory that store key (provided by the environment variable PRE_LOAD_KEY_PATH) - content: List of phandles to entries to sign - algo-name: Hash and signature algo to use for the signature - padding-name: Name of the padding (pkcs-1.5 or pss) - key-name: Filename of the private key to sign - header-size: Total size of the header - version: Version of the header This entry creates a pre-load header that contains a global image signature. For example, this creates an image with a pre-load header and a binary:: binman { image2 { filename = "sandbox.bin"; pre-load { content = <&image>; algo-name = "sha256,rsa2048"; padding-name = "pss"; key-name = "private.pem"; header-size = <4096>; version = <1>; }; image: blob-ext { filename = "sandbox.itb"; }; }; }; """ def __init__(self, section, etype, node): super().__init__(section, etype, node) self.algo_name = fdt_util.GetString(self._node, 'algo-name') self.padding_name = fdt_util.GetString(self._node, 'padding-name') self.key_name = fdt_util.GetString(self._node, 'key-name') self.header_size = fdt_util.GetInt(self._node, 'header-size') self.version = fdt_util.GetInt(self._node, 'version') def ReadNode(self): super().ReadNode() self.key_path, = self.GetEntryArgsOrProps( [EntryArg('pre-load-key-path', str)]) if self.key_path is None: self.key_path = '' def _CreateHeader(self): """Create a pre load header""" hash_name, sign_name = self.algo_name.split(',') padding_name = self.padding_name key_name = os.path.join(self.key_path, self.key_name) # Check hash and signature name/type if hash_name not in SHAS: self.Raise(hash_name + " is not supported") if sign_name not in RSAS: self.Raise(sign_name + " is not supported") # Read the key key = RSA.import_key(tools.read_file(key_name)) # Check if the key has the expected size if key.size_in_bytes() != RSAS[sign_name]: self.Raise("The key " + self.key_name + " don't have the expected size") # Compute the hash hash_image = SHAS[hash_name].new() hash_image.update(self.image) # Compute the signature if padding_name is None: padding_name = "pkcs-1.5" padding = None padding_args = None if padding_name == "pss": salt_len = key.size_in_bytes() - hash_image.digest_size - 2 padding = pss padding_args = {'salt_bytes': salt_len} elif padding_name == "pkcs-1.5": padding = pkcs1_15 padding_args = {} else: self.Raise(padding_name + " is not supported") sig = padding.new(key, **padding_args).sign(hash_image) hash_sig = SHA256.new() hash_sig.update(sig) version = self.version header_size = self.header_size image_size = len(self.image) ofs_img_sig = 64 + len(sig) flags = 0 reserved0 = 0 reserved1 = 0 first_header = struct.pack('>4sIIIIIII32s', PRE_LOAD_MAGIC, version, header_size, image_size, ofs_img_sig, flags, reserved0, reserved1, hash_sig.digest()) hash_first_header = SHAS[hash_name].new() hash_first_header.update(first_header) sig_first_header = padding.new(key, **padding_args).sign(hash_first_header) data = first_header + sig_first_header + sig pad = bytearray(self.header_size - len(data)) return data + pad def ObtainContents(self): """Obtain a placeholder for the header contents""" # wait that the image is available self.image = self.GetContents(False) if self.image is None: return False self.SetContents(self._CreateHeader()) return True def ProcessContents(self): data = self._CreateHeader() return self.ProcessContentsUpdate(data) |