blob: 2e4c72359ff3e334d07e077fe2fee1c502a59117 [file] [log] [blame]
# 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"
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)