From 47c14fe29596948fecdc958f5c3f36f3250c131c Mon Sep 17 00:00:00 2001 From: Chintankumar Shah <quic_chishah@quicinc.com> Date: Tue, 2 Jul 2024 15:19:29 +0530 Subject: [PATCH] Adding support for signing separate efi.bin & dtb.bin o Support for signing efi.bin updated - efi.bin can be signed with/without dtb directory - Make sure there are not many dtbs files in dtb directory due to size problem o Support for signing dtb.bin added o Updated README.md file o Updated typos in config.ini file Change-Id: I179706a0f44b0a57d09a972f7e5eb019333aa583 --- README.md | 412 ++++++++++++++++++++++++----------------------- config.ini | 6 +- signing_tool.py | 419 ++++++++++++++++++++++++++++++------------------ 3 files changed, 480 insertions(+), 357 deletions(-) diff --git a/README.md b/README.md index bd3fc5a..05aa41e 100644 --- a/README.md +++ b/README.md @@ -1,281 +1,291 @@ -Host Tool Description ---------------------- +Host Signing Tool - To Sign HLOS Images +======================================== -- This host tool shall run on any Linux machine(Ubuntu - 18.04 or higher) +1 Introduction to the Host Signing Tool +------------------------------------- -- The host tool shall be a python script which would have to be used - by target team to sign the images. +If UEFI secure boot is enabled, then EFI & DTB images should be +signed. Signing these images manually is very lengthy and error prune +process. To automate this process - the Host signing tool should be +used. -- The host tool shall sign two different kinds of binaries/files. +The signing tool runs on a Linux host machine(Preferred Ubuntu 18.04 or higher). +This is a command line-based python script. Using this tool, we can sign +EFI & DTB image in two separate operations. - - EFI - - DTB +This script shall be invoked after the completion of the build process to sign +the compiled unsigned EFI & DTB images. -- Host tool shall be used to sign the images independent of the build - process. +2 Overview of the Host Signing Tool +--------------------------------- -- The host tool shall be used to sign bootloader, Linux kernel and - DTB. It shall be offline process carried out by target team on host - machine. +This tool will run on a Linux machine with Pyhton3 installed. As +discussed earlier, this tool can either sign EFI image or DTB image in +a single operation. So, to sign both EFI & DTB images user must invoke +this tool two times with different inputs. -Host Tool - Pre-requisite -------------------------- -1. The host tool shall be a python script +The host tool requires unsigned EFI or DTB +files, certificate & keys as an input. Once invoked, host tool unpacks +the unsigned image and sign items available in the package using +available key & certificate. Once signed, host tool will pack the +images again with updated items - replacing unsigned image with a +signed one. -2. This script shall run on a Linux Machine – preferable Ubuntu 18.04 or - above +3 Working of the Host Signing Tool +-------------------------------- -3. This script shall run with Python3 +### 3.1 Pre-requisite to run the tool -4. Host machine running this script must have – openssl, sbsign - installed +To run this tool the host Linux machine should comply with below pre-requisites: -5. Host machine running this script shall have below python module - installed + 1. Install OpenSSL & sbsign utilities + 2. Install Python3 + 3. Install these python modules + a. pip, subprocess, shlex, socket & shutil - a. Pip, subprocess, shlex, socket, shutil -Host Tool – How To Run ----------------------- +### 3.2 Configuration of the Host Signing Tool -1. User shall invoke the host tool after the build process. +User is required to configure the Host Signing Tool before starting the operation. -2. Host tool shall be a command line tool. +#### 3.2.1 Config.ini file -3. Command to invoke the host tool – ****python3 signing\_tool.py**** +User can provide all the required information in ***config.ini*** +configuration file and the script will read the file at the boot-up +and sign the image accordingly. Below is the list of config file +variables. -4. This script shall sign the uki.efi, bootaa64.efi and all dtb files - under efi.bin in a single run +- ***image\_type = efi or dtb***(Use this config to select + efi or dtb to sign separately) -5. Once the host tool finishes the signing process – it stores the - updated efi.bin under ***signed\_binaries*** directory on the same - path as the tool +- ***file\_path = local or remote*** (local = keys & efi.bin/dtb.bin + are present on the same path as the script. Remote = copy + efi.bin/dtb.bin and keys from remote Linux machine to current path) -Host tool requirement to sign EFI file --------------------------------------- - -1. The script shall require ***efi.bin*** under - ***“unsigned\_binariesâ€*** directory on the same path as the script +- ***local\_machine\_private\_key\_path = < path of id\_rsa file in + local machine >*** (This file shall be used to establish SSH + connection with remote machine if ***file\_path = remote*** is + selected) -2. The script shall require below files under ***“keysâ€*** directory on - the same path as the script to sign the ***efi.bin*** +- ***loader\_conf\_timeout = < timeout in seconds >***(systemdboot + wait-time to let user chose to authenticate the binaries. This + option is required to sign efi.bin) - - db.auth, db.crt, db.key, KEK.auth, PK.auth +- ***efi/keys/dtb\_remote\_hostname = < ip or hostname of the remote + linux machine >***(if ***file\_path = remote***, then this + configuration is used by host tool to select the hostname of the + remote machine to copy the efi/keys/dtb file from the remote machine + using scp. -3. If user want script to copy above files from remote machine, then - user shall provide all the required information in one configuration - file named ***config.ini.*** Please note that this script shall - only support from another linux machine over SCP +- ***efi/keys/dtb\_remote\_username = < username\_on\_remote\_machine >*** (if ***file\_path = remote***, then this configuration is used by host tool to select the username of the remote machine to copy the efi/keys/dtb file from the remote machine using scp - GIVEN THAT THE USERNAME IS CREATED ON THE LOCAL MACHINE. -4. If user choose to provide the configuration file but miss some of - the configuration – the script shall still run and ask for those - missing information from the user from the command line. +- ***efi/keys/dtb\_remote\_filepath = < full\_path\_of\_file\_on\_remote\_machine >*** (if ***file\_path = remote***, then this configuration is used by host tool to select the path of the efi/key/dtb file on the remote machine to copy that file from remote machine using scp. -#### The configuration file – config.ini +#### 3.2.2 How to Configure using config.ini file -User can provide all the required information in one configuration file and the -script shall read the file at the boot-up and sign the image accordingly. +- User should select which image is to be signed using -\> ***image\_type*** variable. Choose either ***efi*** or ***dtb***. -##### Config file variables +- User should select the location where the unsigned EFI/DTB image and + keys-certificates are stored using ***file\_path*** variable. -- ***image\_type* = *efi*** (In future if dtb file is separate from - efi.bin then we shall use this config to select efi or dtb to sign - separately) + - If user choses ***local*** in the configuration file, then user will have to manually copy the EFI/DTB image and keys-certificate file in the local working directory -- ***file\_path = local or remote*** (local = keys & efi.bin are - present on the same path as the script. Remote = copy efi.bin and - keys from remote linux machine to current path) + - Create ***"unsigned_binaries"*** directory on the same path as the script and copy efi.bin/dtb.bin image under that directory + - Create ***"keys"*** directory on the same path as the script and copy below files in that directory + - db.auth, db.crt, db.key, KEK.auth, PK.auth -- ***local\_machine\_private\_key\_path = <path of id\_rsa file in - local machine>*** (This file shall be used to establish SSH - connection with remote machine if ***file\_path = remote*** is - selected) + - If user want script to automatically copy the required files from remote Linux machine in the same network, then user should choose ***remote*** in the configuration file. -- ***loader\_conf\_timeout = <timeout in seconds>*** - (systemdboot wait-time to let user chose to authenticate the - binaries) + - User must provide information for below variables in the configuration file -- ***efi/keys/dtb\_remote\_hostname = <ip or hostname of the remote - linux machine>*** (if ***file\_path = remote***, then this - configuration is used by host tool to select the hostname of the - remote machine to copy the efi/keys/dtb file from the remote machine - using scp. Dtb related config is for future expansion when the dtb - file shall be separate from efi.bin) + 1. ***local\_machine\_private\_key\_path*** -\> mandatory + 2. ***\[efi\_config\]*** section -\> if ***image\_type = efi*** + 3. ***\[keys\_config\]*** section -\> mandatory + 4. ***\[dtb\_config\]*** section -\> if ***image\_type = dtb*** -- ***efi/keys/dtb\_remote\_username = - <username\_on\_remote\_machine>*** (if ***file\_path = - remote***, then this configuration is used by host tool to select - the username of the remote machine to copy the efi/keys/dtb file - from the remote machine using scp – GIVEN THAT THE USERNAME IS - CREATED ON THE LOCAL MACHINE. Dtb related config is for future - expansion when the dtb file shall be separate from efi.bin) + - Please note that this script shall support copy from another linux machine only over SCP in the same network -- ***efi/keys/dtb\_remote\_filepath = - <full\_path\_of\_file\_on\_remote\_machine>*** (if - ***file\_path = remote***, then this configuration is used by host - tool to select the path of the efi/key/dtb file on the remote - machine to copy that file from remote machine using scp. Dtb related - config is for future expansion when the dtb file shall be separate - from efi.bin) +- Update ***loader\_conf\_timeout*** variable in the configuration + file if ***image\_type = efi*** is selected in the configuration + file -Host Tool - Working -------------------- +- If user miss any of the configuration - the script shall still run + and ask for those missing information from the user from the command + line. -1. Host tool shall require the path of ***efi.bin***(absolute path or - network path) +### 3.3 How to run the Host Signing Tool - a. ***efi.bin*** contains ***uki.efi***(Linux Kernel image) & ***bootaa64.efi***(Bootloader image) +1. User should invoke the host tool after the code build process is completed and newly compiled unsigned efi.bin and dtb.bin images are available -2. Host tool shall require the path of ***certificate*** and - ***key***(absolute path or network path) - which would be used to - sign the images +2. Store the Host Signing Tool files in a Host Linux machine before starting the operation. ***"signing\_tool.py"*** and ***config.ini*** files make the Host Signing Tool. Both files should be in the same working directory. -3. Host tool shall first mount the ***efi.bin*** on FAT partition - - which would provide below directory structure +3. Configure the Host Signing Tool as per above section. -> ├── dtb -> -> │  ├── qcm6490-idp.dtb -> -> │  ├── qcm6490-rb3.dtb -> -> │  ├── qcm6490-rb3gen2-ia-mezz.dtb -> -> │  ├── qcm6490-rb3gen2-ptz-mezz.dtb -> -> │  ├── qcm6490-rb3gen2-video-mezz.dtb -> -> │  └── qcm6490-rb3gen2-vision-mezz.dtb -> -> ├── EFI -> -> │  ├── BOOT -> -> │  │  └── bootaa64.efi -> -> │  └── Linux -> -> └── uki.efi +4. Invoke the host tool from command line using command -\> ***$python3 signing\_tool.py*** -1. Host tool shall use ***sbsign*** utility to sign ***uki.efi*** & - ***bootaa64.efi*** images separately. +5. Host Signing Tool will show all the user selection and operational commands on the screen while it is trying to sign the image. If something goes wrong - the tool will throw appropriate error on the command line -2. ***sbsign*** requires ***certificate*** and ***key*** for the - signing process. Check below example where ***dsk1.key*** is key & - ***dsk1.crt*** is certificate. Output file name is same as input - file. +6. Once the Host tool completes its process - it will store the newly signed ***efi.bin/dtb.bin*** image in a new directory called -\> ***signed\_binaries***. This will be created by the tool on the same working directory. Other directories which may be created by user during configuration will be deleted by the Host Signing Tool at the end of the signing process. - i. **sbsign --key <key file> --cert <cert file> <efi file> <o/p file location>** +7. Please note that this process should be followed two times to sign ***efi.bin*** and ***dtb.bin*** separately. After each process, please delete **"signed\_binaries"** directly after copying signed image before starting new operation. - ii. *sbsign --key **dsk1.key** --cert **dsk1.crt** bootaa64.efi bootaa64.efi * +### 3.4 Workflow of the Host Signing Tool - iii. *sbsign --key **dsk1.key** --cert **dsk1.crt** uki.efi uki.efi * +The Host Signing Tool takes either efi.bin or dtb.bin image & +keys-certificate as an input and signs them using a separate signing +process. -3. After signing the images, host tool shall copy the AUTH files under - ***/loader/keys/*** directory structure. AUTH files MUST be - available with keys & certificates on a path hosted by Target or - Security team +#### 3.4.1 Host Signing Tool Workflow -4. Host tool shall configure the wait-time in systemdboot loader - configuration. This wait-time stops the kernel loading and allows - user to review & select systemdboot menu options. +- Host tool shall require the path of ***efi.bin & dtb.bin***(absolute + path or network path) - i. Host tool shall configure ***/loader/loader.conf*** + - ***efi.bin*** contains ***uki.efi***(Linux Kernel image) + & ***bootaa64.efi***(Bootloader image) - ii. Syntax for ***loader.conf*** + - ***dtb.bin*** contains ***combined-dtb.dtb*** - ***timeout x*** - x = timeout in seconds +- Host tool shall require the path + of ***certificate*** and ***key***(absolute path or network path) - + which would be used to sign the images -5. Host tool shall require the path of the ***dtb*** files in efi.bin +- Host tool shall first mount the ***efi.bin/dtb.bin*** on FAT + partition - which would provide below directory structure - and + follow their separate signing process -6. Host tool shall require the path of the ***key*** & - ***certificate***(absolute path or network path) – which would be - used to sign the images +***efi.bin*** +> ├── EFI +> +> │  ├── BOOT +> +> │  │  └── bootaa64.efi +> +> │  ├── Linux +> +> │  │  └── uki.efi +> +> ├── loader +> +> │  └── loader.conf -7. UEFI secure boot requires PE format file for verification but non-PE - files, like dtb, cannot be signed using ***sbsign*** as this signing - tool also requires PE format files as input +***dtb.bin*** +> └── combined-dtb.dtb +> -8. So, Host tool shall use ***openssl*** utility to sign the dtb file. - Check below example where ***dsk1.key*** is key and ***dsk1.crt*** - is certificate. +- After signing the images, host tool shall copy AUTH files + under ***/loader/keys/authkeys*** directory structure for + both ***efi.bin*** & ***dtb.bin***. AUTH files MUST be maintained by + user with keys & certificates and should be available for signing + process - - **openssl cms -sign -inkey <.key file> -signer <.crt - file> -binary -in <dtb file> --out <O/P .dtb.sig - file> -outform DER** +- Host tool shall configure the wait-time in systemdboot loader + configuration. This wait-time stops the kernel loading and allows + user to review & select systemdboot menu options. This + ***loader.conf*** file shall only be available in + updated ***efi.bin*** file. For ***dtb.bin*** file this process + shall not be followed - - *openssl cms -sign -inkey dsk1.key -signer dsk1.crt -binary -in - <foo.dtb file> --out <foo.dtb.sig file > -outform - DER* + - Host tool shall configure ***/loader/loader.conf*** -<!-- --> + - Syntax for ***loader.conf*** -12. Above command adds signature for dtb file in separate file (i.e., - foo.dtb.sig) and doesn’t disturb original file(i.e., foo.dtb file). - That’s why host tool shall keep both the files where - ***\*.dtb.sign*** file will be used during verification of UEFI - secure boot process + - ***timeout x*** -<!-- --> + - x = timeout in seconds -1. Host tool shall copy AUTH files under ***/loader/keys/*** directory - structure. AUTH files MUST be available with keys & certificates on - a path hosted by Target or Security team +- After completion of image signing - host tool shall unmount + the ***efi.bin/dtb.bin*** from FAT partition and store the + signed ***efi.bin/dtb.bin*** on host machine on the similar path as + Host tool under the ***signed\_binaries*** directory -2. After completion of image signing – host tool shall unmount the - efi.bin from FAT partition and store the signed efi.bin on host - machine on the similar path as Host tool under the - ***signed\_binaries*** directory. +- Below is the directory structure for + signed ***efi.bin*** & ***dtb.bin*** -> ├── dtb -> -> │  ├── qcm6490-idp.dtb -> -> │  ├── qcm6490-idp.sig -> -> │  ├── qcm6490-rb3.dtb -> -> │  ├── qcm6490-rb3gen2-ia-mezz.dtb -> -> │  ├── qcm6490-rb3gen2-ia-mezz.sig -> -> │  ├── qcm6490-rb3gen2-ptz-mezz.dtb +***efi.bin*** +> ├── EFI > -> │  ├── qcm6490-rb3gen2-ptz-mezz.sig +> │  ├── BOOT > -> │  ├── qcm6490-rb3gen2-video-mezz.dtb +> │  │  └── bootaa64.efi > -> │  ├── qcm6490-rb3gen2-video-mezz.sig +> │  ├── Linux > -> │  ├── qcm6490-rb3gen2-vision-mezz.dtb +> │  │  └── uki.efi > -> │  ├── qcm6490-rb3gen2-vision-mezz.sig +> ├── loader > -> │  └── qcm6490-rb3.sig +> │  ├── keys > -> ├── EFI +> │  │  ├── authkeys > -> │  ├── BOOT +> │  │   │   └── db.auth > -> │  │  └── bootaa64.efi +> │  │   │   └── KEK.auth > -> │  └── Linux +> │  │   │   └── PK.auth > -> │  └── uki.efi +> │  └── loader.conf + +***dtb.bin*** +> ├── combined-dtb.dtb > -> └── loader +> ├── combined-dtb.sig > -> ├── keys +> ├── loader > -> │  └── authkeys +> │  ├── keys > -> │  ├── db.auth +> │  │  ├── authkeys > -> │  ├── KEK.auth +> │  │   │   └── db.auth > -> │  └── PK.auth +> │  │   │   └── KEK.auth > -> └── loader.conf +> │  │   │   └── PK.auth + +#### 3.4.2 Efi.bin signing process + +- Host tool shall use ***sbsign*** utility to + sign ***uki.efi*** & ***bootaa64.efi*** images separately. + +- ***sbsign*** requires ***certificate*** and ***key*** for the + signing process. Check the following syntax where ***dsk1.key*** is + key & ***dsk1.crt*** is certificate. Output file name is same as + input file. + +- Example: + + - ***sbsign --key < key file> --cert < cert file > < efi file > < o/p file location >*** + + - *sbsign \--key **dsk1.key** \--cert **dsk1.crt** bootaa64.efi bootaa64.efi* + + - *sbsign \--key **dsk1.key** \--cert **dsk1.crt** uki.efi uki.efi* + +#### 3.4.3 dtb.bin signing process + + +- Host tool requires the path of the ***dtb.bin*** file + +- The host tool requires the path of ***key*** and ***certificate*** + (absolute path or network path) to sign the images. + +- UEFI secure boot requires PE format files for verification. Non-PE + files, like ***dtb***, cannot be signed using ***sbsign*** as this + signing tool requires PE format files as input. + +- The Host tool uses the ***openssl*** utility to sign the ***dtb*** + file. Check the following syntax where ***<u>dsk1.key</u>*** is + key and ***<u>dsk1.crt</u>*** is certificate: + + - *<u>openssl cms -sign -inkey < .key file > -signer < .crt file > -binary -in < dtb file > --out < O/P .sig file > -outform DER</u>* + +- Example: + + - *openssl cms -sign -inkey dsk1.key -signer dsk1.crt -binary -in < foo.dtb file > --out < foo.sig file > -outform DER* + +- This command adds signature for dtb file in separate file (i.e., + foo.sig) and does not disturb original file(i.e., foo.dtb + file). Hence, the host tool must keep both the files where the + **\*.sign** file will be used during verification of UEFI secure + boot process diff --git a/config.ini b/config.ini index cc2b6d2..909982a 100644 --- a/config.ini +++ b/config.ini @@ -7,17 +7,17 @@ local_machine_private_key_path = /usr2/<user_name_for_machine>/.ssh/id_rsa loader_conf_timeout = 20 [efi_config] -efi_remote_hostname = <remotemachine_ip_or_username_where_efi.bin_available> +efi_remote_hostname = <remotemachine_ip_or_hostname_where_efi.bin_available> efi_remote_username = <username_on_remote_machine_where_efi.bin_available> efi_remote_filepath = <full_path_of_efi.bin_file_on_remotemachine> [keys_config] -keys_remote_hostname = <remotemachine_ip_or_username_where_keys_available> +keys_remote_hostname = <remotemachine_ip_or_hostname_where_keys_available> keys_remote_username = <username_on_remote_machine_where_keys_available> keys_remote_filepath = <full_path_of_keys_directory_on_remotemachine> [dtb_config] -dtb_remote_hostname = <remotemachine_ip_or_username_where_dtb_available> +dtb_remote_hostname = <remotemachine_ip_or_hostname_where_dtb_available> dtb_remote_username = <username_on_remote_machine_where_dtb_available> dtb_remote_filepath = <full_path_of_dtb_on_remotemachine> diff --git a/signing_tool.py b/signing_tool.py index 7ccfea0..b630fb5 100644 --- a/signing_tool.py +++ b/signing_tool.py @@ -1,7 +1,7 @@ # Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. # SPDX-License-Identifier: BSD-3-Clause-Clear -# K2L: Host Tool For Image Signing(efi.bin & dtb). +# K2L: Host Tool For Image Signing(efi.bin & dtb.bin). # Author: quic_chishah@quicinc.com - Feb, 2024 import os @@ -42,6 +42,7 @@ global cmd_output unsigned_bin_directory = 'unsigned_binaries/' signed_bin_directory = 'signed_binaries/' local_efi_file_path = unsigned_bin_directory + 'efi.bin' +local_dtb_file_path = unsigned_bin_directory + 'dtb.bin' keys_directory = 'keys/' tmp_keys_directory = 'temp_keys/' mount_directory = 'efimountedbin/' @@ -55,7 +56,8 @@ local_kek_auth_path = keys_directory + 'KEK.auth' local_db_auth_path = keys_directory + 'db.auth' unsigned_boot_img_path = mount_directory + 'EFI/BOOT/bootaa64.efi' unsigned_uki_img_path = mount_directory + 'EFI/Linux/uki.efi' -unsigned_dtb_file_path = mount_directory + 'dtb/' +unsigned_dtb_file_path_efi = mount_directory + 'dtb/' +unsigned_dtb_file_path_dtb = mount_directory signed_boot_img_path = unsigned_bin_directory + 'bootaa64.efi' signed_uki_img_path = unsigned_bin_directory + 'uki.efi' temp_loader_conf_path = unsigned_bin_directory + 'loader.conf' @@ -93,13 +95,13 @@ def script_prerequisite(): def script_usage_msg(): print("###################################################################") print("SCRIPT USAGE MESSAGE:") - print("1. This script will help sign bootaa64.efi, uki.efi & dtb files from efi.bin") + print("1. This script will help sign EITHER bootaa64.efi, uki.efi from efi.bin OR dtb files from dtb.bin") print("2. To sign above images - this script will require 'keys' as well") - print("3. efi.bin and 'keys' can be present at local machine or at some remote location") + print("3. efi.bin/dtb.bin and 'keys' can be present at local machine or at some remote location") print("4. To use this script first choose where this files are present - i.e., Remote Path or Local path") print("5. This script only support copy files from remote linux machine. Copy from Windows machine is not yet supported") - print("6. After signing - this script will update loader.conf and copy 'AUTH' keys in efi.bin") - print(f"7. At the end - the newly signed efi.bin will be copied to {signed_bin_directory} directory") + print("6. After signing - this script will update loader.conf in efi.bin and copy 'AUTH' keys in efi.bin/dtb.bin") + print(f"7. At the end - the newly signed efi.bin/dtb.bin will be copied to {signed_bin_directory} directory") print("###################################################################") print("\nBEGIN THE PROCESS\n") @@ -109,7 +111,7 @@ def select_path_type(): print("1. Use Local machine path") print("2. Use Remote machie path - THIS MUST BE A LINUX MACHINE PATH") print("\nHelp: For Option-1, Please copy below files in the same location as this script") - print(f"1. Create {unsigned_bin_directory} directory and copy efi.bin to that directory") + print(f"1. Create {unsigned_bin_directory} directory and copy efi.bin/dtb.bin to that directory") print(f"2. Create {keys_directory} directory and copy all required keys/cert/auth to that directory.") print("(e.g.- Required Keys/Cert/Auth files - db.key, db.crt, PK.auth, KEK.auth, db.auth)") print("###################################################################") @@ -218,9 +220,16 @@ def check_if_necessary_files_available(): if not os.path.isdir(unsigned_bin_directory): print(f"Directory {unsigned_bin_directory} does not exist - Exiting the tool!") return 1 #Failure - if not os.path.exists(local_efi_file_path): - print(f"File {local_efi_file_path} does not exist - Exiting the tool!") - return 1 #Failure + + if image_type == SignImage.SIGN_EFI: + if not os.path.exists(local_efi_file_path): + print(f"File {local_efi_file_path} does not exist - Exiting the tool!") + return 1 #Failure + elif image_type == SignImage.SIGN_DTB: + if not os.path.exists(local_dtb_file_path): + print(f"File {local_dtb_file_path} does not exist - Exiting the tool!") + return 1 #Failure + if not os.path.isdir(keys_directory): print(f"Directory {keys_directory} does not exist - Exiting the tool!") return 1 #Failure @@ -293,8 +302,8 @@ def delete_directory(directory_path): def select_image_type(): print("\n###################################################################") print("Select one of the below files for signing") - print("1. EFI -> PLEASE CHOOSE OPTION-1(EFI) ONLY. AS OF NOW EFI & DTB WILL BE SIGNED TOGETHER") - print("2. DTB -> THIS WILL BE FOR FUTURE EXPANSION - WHERE DTB CAN BE SIGNED SEPERATELY") + print("1. EFI -> TO SIGN EFI.BIN") + print("2. DTB -> TO SIGN DTB.BIN") print("###################################################################") choice = input("Enter your choice: ") if choice == "1": @@ -350,6 +359,10 @@ def get_linux_file_path(file_type): global config_keys_config_keys_remote_username global config_keys_config_keys_remote_filepath + global config_dtb_config_dtb_remote_hostname + global config_dtb_config_dtb_remote_username + global config_dtb_config_dtb_remote_filepath + """ Checks if a file exists on a remote Ubuntu machine accessible via SSH. :param remote_host: IP address or hostname of the remote Ubuntu machine @@ -407,6 +420,32 @@ def get_linux_file_path(file_type): #Ask user remote_path = input("Enter file path on remote machine: ") + elif config_status == 0 and file_type == 'dtb': + #Config is available and getting info about dtb file + if config_dtb_config_dtb_remote_hostname: + #Read from config if available + remote_host = config_dtb_config_dtb_remote_hostname + print(f"[*****Selecting remote machine ip/name as: {remote_host} from config file for further operation*****]") + else: + #Ask user + remote_host = input("Enter remote machine ip/name: ") + + if config_dtb_config_dtb_remote_username: + #Read from config if available + user_name = config_dtb_config_dtb_remote_username + print(f"[*****Selecting remote machine username as: {user_name} from config file for further operation*****]") + else: + #Ask user + user_name = input("Enter your usernme for remote machine: ") + + if config_dtb_config_dtb_remote_filepath: + #Read from config if available + remote_path = config_dtb_config_dtb_remote_filepath + print(f"[*****Selecting remote machine file path as: {remote_path} from config file for further operation*****]") + else: + #Ask user + remote_path = input("Enter file path on remote machine: ") + else: #Config is not available - get info from user remote_host = input("Enter remote machine ip/name: ") @@ -470,17 +509,20 @@ def copy_image(): #print(f"----->{os_type}") """ - #Step-1: Get hostname/ip address of machine where efi.bin is saved and - file path of the efi.bin + #Step-1: Get hostname/ip address of machine where 'bin' is saved and + file path of the 'bin' file """ if os_type == OS_LINUX: - get_linux_file_path('efi') #Get hostname and path - efi_file_path = f"//{remote_host}{remote_path}" #Construct file path + if image_type == SignImage.SIGN_EFI: + get_linux_file_path('efi') #Get hostname and path + elif image_type == SignImage.SIGN_DTB: + get_linux_file_path('dtb') + bin_file_path = f"//{remote_host}{remote_path}" else: #THIS IS For Windows system - print("Please provide path for efi.bin") - efi_file_path = get_file_path() - if efi_file_path == SignImage.SIGN_ERR: - raise Exception("Invalid file path for efi.bin - Exiting the tool!") + print("Please provide path for 'bin' file") + bin_file_path = get_file_path() + if bin_file_path == SignImage.SIGN_ERR: + raise Exception("Invalid file path for 'bin' file - Exiting the tool!") #Step-2: Check network connectivity try: @@ -489,15 +531,21 @@ def copy_image(): print(f"Error: Could not connect to {remote_host} on port 22.") exit(1) - print(f"Selected file path: {efi_file_path}") - #Step-3: Check if selected file is efi.bin or not - selected_efi_file = os.path.basename(efi_file_path) - print(f"selected_efi_file: {selected_efi_file}") + print(f"Selected file path: {bin_file_path}") + #Step-3: Check if selected file is efi.bin/dtb.bin or not + selected_bin_file = os.path.basename(bin_file_path) + print(f"selected_bin_file: {selected_bin_file}") if image_type == SignImage.SIGN_EFI: - if not selected_efi_file == "efi.bin": + if not selected_bin_file == "efi.bin": raise Exception("This is not efi.bin file - Exiting the tool!") destination_file_path = local_efi_file_path + elif image_type == SignImage.SIGN_DTB: + if not selected_bin_file == "dtb.bin": + raise Exception("This is not dtb.bin file - Exiting the tool!") + destination_file_path = local_dtb_file_path + else: + raise Exception("Wrong image type is selected - Exiting the tool!") print(f"destination_file_path: {destination_file_path}") #Step-4: Check if the destination directory exists, if so delete it @@ -506,7 +554,7 @@ def copy_image(): #TODO - SCP with windows path is not working #TODO - Try to do scp with execute_command() function - #Step-5: Copy efi.bin from remote path to local machine & error check + #Step-5: Copy efi.bin/dtb.bin from remote path to local machine & error check command = f"scp -i {private_key_path} {user_name}@{remote_host}:{remote_path} {destination_file_path}" try: @@ -534,20 +582,17 @@ def copy_image(): except Exception as e: print(f"Failed to execute command: {e}") -#TODO - Before exiting in below function - make sure to do cleanup in local folder -def sign_efi_image(): - global cmd_output +def init_signing_process(): global file_path_type global private_key_path global remote_path - global config_status global config_common_local_machine_private_key_path - global config_common_loader_conf_timeout - #Step-1: Follow this if Remote path is selected to copy efi.bin and 'keys' + print("\n########### Initializing signing process ###########\n") + #Step-1: Follow this if Remote path is selected to copy 'bin' and 'keys' if file_path_type == SelectPath.PATH_REMOTE: - print("\nWe need to copy efi.bin and 'keys' from remote machine - Please provide required details") + print("\nWe need to copy 'bin' and 'keys' from remote machine - Please provide required details") #Read from config if available #Step-1.1: Get the private key path for ssh if config_status == 0 and config_common_local_machine_private_key_path: @@ -563,7 +608,7 @@ def sign_efi_image(): #Step-1.2: Copy the required image to local machine print("\n###################################################################") - print("Provide details to copy efi.bin file from remote machine...") + print("Provide details to copy 'bin' file from remote machine...") copy_image() print("###################################################################") @@ -587,101 +632,85 @@ def sign_efi_image(): print("Some necessary files are missing - Exiting the tool!") sys.exit(status) - #Step-3: Mount efi.bin - #Create mount point & mount efi.bin - print(f"Mounting efi.bin to {mount_directory}...") + #Step-3: Mount efi/dtb.bin + #Create mount point & mount efi.bin/dtb.bin + print(f"Mounting efi.bin/dtb.bin to {mount_directory}...") create_new_directory(mount_directory) - mount_unmount_device(local_efi_file_path, mount_directory, 'mount') - - #Step-4: Sign bootaa64.efi image - # Check if the bootaa64.efi file exists - # if not os.path.isfile(unsigned_boot_img_path): - # print(f"'{unsigned_boot_img_path}' does not exist - Exiting the tool!") - # exit(1) - print("Signing bootaa64.efi...") - sign_boot_cmd = f"sudo sbsign --key {local_key_path} --cert {local_cert_path} {unsigned_boot_img_path} --output {signed_boot_img_path}" - status = execute_command(sign_boot_cmd) - if status != 0: - print("Failed to sign bootaa64.efi - Exiting the tool!") - sys.exit(status) - - #Step-4.1: copy signed bootaa64.efi to its original path in mount path - print("Copy Signed bootaa64.efi to mount path...") - copy_boot_img_cmd = f"sudo cp {signed_boot_img_path} {unsigned_boot_img_path}" - status = execute_command(copy_boot_img_cmd) - if status != 0: - print("Failed to copy bootaa64.efi - Exiting the tool!") - sys.exit(status) + if image_type == SignImage.SIGN_EFI: + mount_unmount_device(local_efi_file_path, mount_directory, 'mount') + elif image_type == SignImage.SIGN_DTB: + mount_unmount_device(local_dtb_file_path, mount_directory, 'mount') + else: + raise Exception("Wrong image type is selected - Exiting the tool!") - #Step-5: Sign uki.efi image - # Check if the uki.efi file exists - # if not os.path.isfile(unsigned_uki_img_path): - # print(f"'{unsigned_uki_img_path}' does not exist - Exiting the tool!") - # exit(1) - print("Signing uki.efi...") - sign_uki_cmd = f"sudo sbsign --key {local_key_path} --cert {local_cert_path} {unsigned_uki_img_path} --output {signed_uki_img_path}" - status = execute_command(sign_uki_cmd) - if status != 0: - print("Failed to sign uki.efi - Exiting the tool!") - sys.exit(status) +def sign_dtb_files(): + global cmd_output - #Step-5.1: copy signed uki.efi to its original path in mount path - print("Copy Signed uki.efi to mount path...") - copy_uki_img_cmd = f"sudo cp {signed_uki_img_path} {unsigned_uki_img_path}" - status = execute_command(copy_uki_img_cmd) - if status != 0: - print("Failed to copy uki.efi - Exiting the tool!") - sys.exit(status) + print("Signing DTBs :: Get DTB files information...") + if image_type == SignImage.SIGN_EFI: + unsigned_dtb_file_path = unsigned_dtb_file_path_efi + elif image_type == SignImage.SIGN_DTB: + unsigned_dtb_file_path = unsigned_dtb_file_path_dtb + else: + raise Exception("Wrong image type is selected - Exiting the tool!") - #Step-6: Sign DTB files - print("Get DTB files information...") dtb_file_names_cmd = f"sudo ls {unsigned_dtb_file_path}" status = execute_command(dtb_file_names_cmd) if status != 0: - print(f"Failed to get DTB file names from {unsigned_dtb_file_path} path - Exiting the tool!") - sys.exit(status) - - print("Sign each DTB files one by one...") - #Remove the trailing newline and split the output into a list of filenames - dtb_file_names = cmd_output.rstrip().split('\n') #Use cmd_output from execute_command() - if dtb_file_names is not None: - for dtb_file in dtb_file_names: - print(f"*****{dtb_file}*****") - temp_mounted_dtb_file = unsigned_dtb_file_path + dtb_file - temp_local_dtb_file = unsigned_bin_directory + dtb_file - copy_dtb_to_local_cmd = f"sudo cp {temp_mounted_dtb_file} {temp_local_dtb_file}" - print(f"Copy DTB command: {copy_dtb_to_local_cmd}...") - status = execute_command(copy_dtb_to_local_cmd) - if status != 0: - print(f"Failed to copy to {temp_local_dtb_file} - Exiting the tool!") - sys.exit(status) - - print(f"Changing permission to 776 for {temp_local_dtb_file}...") - change_permission_cmd = f"sudo chmod 776 {temp_local_dtb_file}" - status = execute_command(change_permission_cmd) - if status != 0: - print(f"Failed to change permission to 776 for {temp_local_dtb_file} - Exiting the tool!") - sys.exit(status) - if os.path.isfile(temp_local_dtb_file) and temp_local_dtb_file.endswith('.dtb'): - print(f"Signing DTB: {temp_local_dtb_file}...") - file_name_partition = temp_local_dtb_file.split('.') - file_name_partition[1] = "sig" - out_dtb_file = '.'.join(file_name_partition) - sign_dtb_cmd = f"sudo openssl cms -sign -inkey {local_key_path} -signer {local_cert_path} -binary -in {temp_local_dtb_file} --out {out_dtb_file} -outform DER" - print(f"Command: {sign_dtb_cmd}...") - status = execute_command(sign_dtb_cmd) + #print(f"Failed to get DTB file names from {unsigned_dtb_file_path} path - Exiting the tool!") + #sys.exit(status) + print(f"Failed to get DTB file names from {unsigned_dtb_file_path} path - Moving on without DTB") + else: + print("Sign each DTB files one by one...") + #Remove the trailing newline and split the output into a list of filenames + dtb_file_names = cmd_output.rstrip().split('\n') #Use cmd_output from execute_command() + if dtb_file_names is not None: + for dtb_file in dtb_file_names: + print(f"*****{dtb_file}*****") + temp_mounted_dtb_file = unsigned_dtb_file_path + dtb_file + temp_local_dtb_file = unsigned_bin_directory + dtb_file + copy_dtb_to_local_cmd = f"sudo cp {temp_mounted_dtb_file} {temp_local_dtb_file}" + print(f"Copy DTB command: {copy_dtb_to_local_cmd}...") + status = execute_command(copy_dtb_to_local_cmd) if status != 0: - print(f"Failed to sign {temp_local_dtb_file} - Exiting the tool!") + print(f"Failed to copy to {temp_local_dtb_file} - Exiting the tool!") sys.exit(status) - print(f"Copy {out_dtb_file} to {unsigned_dtb_file_path}...") - copy_local_to_dtb_cmd = f"sudo cp {out_dtb_file} {unsigned_dtb_file_path}" - print(f"Copy DTB command: {copy_local_to_dtb_cmd}...") - status = execute_command(copy_local_to_dtb_cmd) + + print(f"Changing permission to 776 for {temp_local_dtb_file}...") + change_permission_cmd = f"sudo chmod 776 {temp_local_dtb_file}" + status = execute_command(change_permission_cmd) if status != 0: - print(f"Failed to copy {out_dtb_file} to {unsigned_dtb_file_path} - Exiting the tool!") + print(f"Failed to change permission to 776 for {temp_local_dtb_file} - Exiting the tool!") sys.exit(status) + if os.path.isfile(temp_local_dtb_file) and temp_local_dtb_file.endswith('.dtb'): + print(f"Signing DTB: {temp_local_dtb_file}...") + file_name_partition = temp_local_dtb_file.split('.') + file_name_partition[1] = "sig" + out_dtb_file = '.'.join(file_name_partition) + sign_dtb_cmd = f"sudo openssl cms -sign -inkey {local_key_path} -signer {local_cert_path} -binary -in {temp_local_dtb_file} --out {out_dtb_file} -outform DER" + print(f"Command: {sign_dtb_cmd}...") + status = execute_command(sign_dtb_cmd) + if status != 0: + print(f"Failed to sign {temp_local_dtb_file} - Exiting the tool!") + sys.exit(status) + print(f"Copy {out_dtb_file} to {unsigned_dtb_file_path}...") + copy_local_to_dtb_cmd = f"sudo cp {out_dtb_file} {unsigned_dtb_file_path}" + print(f"Copy DTB command: {copy_local_to_dtb_cmd}...") + status = execute_command(copy_local_to_dtb_cmd) + if status != 0: + print(f"Failed to copy {out_dtb_file} to {unsigned_dtb_file_path} - Exiting the tool!") + sys.exit(status) + +def copy_auth_files_to_bin(): + if not os.path.isdir(loader_mount_directory): + print(f"Directory {loader_mount_directory} does not exist!") + print(f"Creating {loader_mount_directory}...") + create_loader_dir_cmd = f"sudo mkdir {loader_mount_directory}" + status = execute_command(create_loader_dir_cmd) + if status != 0: + print(f"Failed to create {create_loader_dir_cmd} - Exiting the tool!") + sys.exit(status) - #Step-7: Copy all auth files to efimountedbin/loader/keys/authkeys/ print(f"Creating {keys_mount_directory}...") create_key_dir_cmd = f"sudo mkdir {keys_mount_directory}" status = execute_command(create_key_dir_cmd) @@ -717,7 +746,112 @@ def sign_efi_image(): print(f"Failed to copy {local_db_auth_path} to {authkeys_mount_directory} - Exiting the tool!") sys.exit(status) - #Step-8: Update loader.conf file +def signing_process_cleanup(): + #Step-1: Un-mount efimountedbin + print(f"Unmounting {mount_directory}...") + if image_type == SignImage.SIGN_EFI: + mount_unmount_device(local_efi_file_path, mount_directory, 'umount') + elif image_type == SignImage.SIGN_DTB: + mount_unmount_device(local_dtb_file_path, mount_directory, 'umount') + else: + raise Exception("Wrong image type is selected - Exiting the tool!") + + #Step-2: Delete efimountedbin + print(f"Deleting {mount_directory}...") + status = delete_directory(mount_directory) + # dlt_mount_point_cmd = f"rm -rf {mount_directory}" + # status = execute_command(dlt_mount_point_cmd) + if status != 0: + print(f"Failed to delete {mount_directory} - Exiting the tool!") + sys.exit(status) + + #Step-3: Copy efi.bin/dtb.bin to signed_binaries directory + print(f"Create {signed_bin_directory}...") + create_new_directory(signed_bin_directory) + + if image_type == SignImage.SIGN_EFI: + local_bin_file_path = local_efi_file_path + elif image_type == SignImage.SIGN_DTB: + local_bin_file_path = local_dtb_file_path + else: + raise Exception("Wrong image type is selected - Exiting the tool!") + + print(f"Copying updated {local_bin_file_path} to {signed_bin_directory}...") + copy_signed_bin_cmd = f"cp {local_bin_file_path} {signed_bin_directory}" + status = execute_command(copy_signed_bin_cmd) + if status != 0: + print(f"Failed to copy {local_bin_file_path} to {signed_bin_directory} - Exiting the tool!") + sys.exit(status) + + #Step-4: Delete unsigned_bin_directory & keys - no need to delete this as of now + status = delete_directory(unsigned_bin_directory) + # print(f"Deleting {unsigned_bin_directory}...") + # dlt_unsigned_bin_cmd = f"rm -rf {unsigned_bin_directory}" + # status = execute_command(dlt_unsigned_bin_cmd) + #DON'T EXIT IF FAILED - AS OUR WORK IS SUCCESSFULLY DONE IN PREVIOUS STEP + if status != 0: + print(f"Failed to delete {unsigned_bin_directory}. But {signed_bin_directory} has updated efi.bin/dtb.bin, please use it.") + status = delete_directory(keys_directory) + if status != 0: + print(f"Failed to delete {keys_directory}. But {signed_bin_directory} has updated efi.bin/dtb.bin, please use it.") + +#TODO - Before exiting in below function - make sure to do cleanup in local folder +def sign_efi_image(): + global cmd_output + global config_status + global config_common_loader_conf_timeout + + #Step-1: Sign bootaa64.efi image + # Check if the bootaa64.efi file exists + # if not os.path.isfile(unsigned_boot_img_path): + # print(f"'{unsigned_boot_img_path}' does not exist - Exiting the tool!") + # exit(1) + print("Signing bootaa64.efi...") + sign_boot_cmd = f"sudo sbsign --key {local_key_path} --cert {local_cert_path} {unsigned_boot_img_path} --output {signed_boot_img_path}" + status = execute_command(sign_boot_cmd) + if status != 0: + print("Failed to sign bootaa64.efi - Exiting the tool!") + sys.exit(status) + + #Step-1.1: copy signed bootaa64.efi to its original path in mount path + print("Copy Signed bootaa64.efi to mount path...") + copy_boot_img_cmd = f"sudo cp {signed_boot_img_path} {unsigned_boot_img_path}" + status = execute_command(copy_boot_img_cmd) + if status != 0: + print("Failed to copy bootaa64.efi - Exiting the tool!") + sys.exit(status) + + #Step-2: Sign uki.efi image + # Check if the uki.efi file exists + # if not os.path.isfile(unsigned_uki_img_path): + # print(f"'{unsigned_uki_img_path}' does not exist - Exiting the tool!") + # exit(1) + print("Signing uki.efi...") + sign_uki_cmd = f"sudo sbsign --key {local_key_path} --cert {local_cert_path} {unsigned_uki_img_path} --output {signed_uki_img_path}" + status = execute_command(sign_uki_cmd) + if status != 0: + print("Failed to sign uki.efi - Exiting the tool!") + sys.exit(status) + + #Step-2.1: copy signed uki.efi to its original path in mount path + print("Copy Signed uki.efi to mount path...") + copy_uki_img_cmd = f"sudo cp {signed_uki_img_path} {unsigned_uki_img_path}" + status = execute_command(copy_uki_img_cmd) + if status != 0: + print("Failed to copy uki.efi - Exiting the tool!") + sys.exit(status) + + # IMPORTANT: + # Comment this step if efi.bin has dtb files & DO NOT have enough space to + # store signed dtb files OR remove unwanted dtb files to sign only required + # dtbs inside efi.bin + #Step-3: Sign DTB files + sign_dtb_files() + + #Step-4: Copy all auth files to efimountedbin/loader/keys/authkeys/ + copy_auth_files_to_bin() + + #Step-5: Update loader.conf file print(f"Delete old {loader_conf_path}...") dlt_loader_conf_cmd = f"sudo rm {loader_conf_path}" status = execute_command(dlt_loader_conf_cmd) @@ -759,45 +893,20 @@ def sign_efi_image(): print(f"Failed to copy {temp_loader_conf_path} to {loader_conf_path} - Exiting the tool!") sys.exit(status) - #Step-9: Un-mount efimountedbin - print(f"Unmounting {mount_directory}...") - mount_unmount_device(local_efi_file_path, mount_directory, 'umount') - - #Step-10: Delete efimountedbin - print(f"Deleting {mount_directory}...") - status = delete_directory(mount_directory) - # dlt_mount_point_cmd = f"rm -rf {mount_directory}" - # status = execute_command(dlt_mount_point_cmd) - if status != 0: - print(f"Failed to delete {mount_directory} - Exiting the tool!") - sys.exit(status) + #Step-6: Un-mount efimountedbin + signing_process_cleanup() - #Step-11: Copy efi.bin to signed_binaries directory - print(f"Create {signed_bin_directory}...") - create_new_directory(signed_bin_directory) +def sign_dtb_image(): + global cmd_output - print(f"Copying updated {local_efi_file_path} to {signed_bin_directory}...") - copy_signed_efi_cmd = f"cp {local_efi_file_path} {signed_bin_directory}" - status = execute_command(copy_signed_efi_cmd) - if status != 0: - print(f"Failed to copy {local_efi_file_path} to {signed_bin_directory} - Exiting the tool!") - sys.exit(status) + #Step-1: Sign DTB files + sign_dtb_files() - #Step-12: Delete unsigned_bin_directory & keys - no need to delete this as of now - status = delete_directory(unsigned_bin_directory) - # print(f"Deleting {unsigned_bin_directory}...") - # dlt_unsigned_bin_cmd = f"rm -rf {unsigned_bin_directory}" - # status = execute_command(dlt_unsigned_bin_cmd) - #DON'T EXIT IF FAILED - AS OUR WORK IS SUCCESSFULLY DONE IN PREVIOUS STEP - if status != 0: - print(f"Failed to delete {unsigned_bin_directory}. But {signed_bin_directory} has updated efi.bin, please use it.") - status = delete_directory(keys_directory) - if status != 0: - print(f"Failed to delete {keys_directory}. But {signed_bin_directory} has updated efi.bin, please use it.") + #Step-2: Copy all auth files to efimountedbin/loader/keys/authkeys/ + copy_auth_files_to_bin() -def sign_dtb_image(): - print("\nAS OF NOW DTB SIGNING IS NOT SUPPORTED SEPERATELY - ALL DTBs WILL BE SIGNED WITH EFI FILE") - print("EXITING THE TOOL!") + #Step-3: Signing process cleanup + signing_process_cleanup() def main(): global os_type @@ -891,10 +1000,14 @@ def main(): Step-4: Sign the image - EFI and DTB will have different process """ + init_signing_process() + if image_type == SignImage.SIGN_EFI: sign_efi_image() elif image_type == SignImage.SIGN_DTB: sign_dtb_image() + else: + raise Exception("Wrong image type is selected - Exiting the tool!") print('\n##### Signing Tool Process Completed #####\n') -- GitLab