From 536c84d82f689503d24dd3709b164a2f0d686d2f Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Thu, 5 Oct 2023 11:44:42 +0200 Subject: [PATCH 1/4] Update the way ELF metadata sections are added. Update metadata to keep the exact same values --- Makefile.defines | 13 ++++++-- Makefile.rules_generic | 17 ---------- Makefile.standard_app | 3 -- src/app_metadata.c | 71 +++++++++++++++++++++++++++++++++++++++++ target/nanos/script.ld | 10 ++++++ target/nanos2/script.ld | 10 ++++++ target/nanox/script.ld | 10 ++++++ target/stax/script.ld | 10 ++++++ 8 files changed, 121 insertions(+), 23 deletions(-) create mode 100644 src/app_metadata.c diff --git a/Makefile.defines b/Makefile.defines index 3fa78fcf1..0b7761d3b 100644 --- a/Makefile.defines +++ b/Makefile.defines @@ -48,10 +48,17 @@ endif ifeq ($(SDK_HASH),) SDK_HASH := "None" endif -# Expose API_LEVEL, SDK_VERSION and SDK_HASH to the app. + +# APPNAME exposed to the app as a CFLAG because it might contain spaces +CFLAGS += -DAPPNAME=\"$(APPNAME)\" + +# API_LEVEL exposed to the app as an integer DEFINES += API_LEVEL=$(API_LEVEL) -DEFINES += SDK_VERSION=\"$(SDK_VERSION)\" -DEFINES += SDK_HASH=\"$(SDK_HASH)\" + +# Define list of other items to be exposed to the app as strings +# TARGET_ID is not in this list: it is already defined in bolos_target.h. +APP_METADATA_LIST := TARGET TARGET_NAME APPVERSION SDK_NAME SDK_VERSION SDK_HASH +DEFINES += $(foreach item,$(APP_METADATA_LIST), $(item)=\"$($(item))\") # extra load parameters for loadApp script ifneq ($(SCP_PRIVKEY),) diff --git a/Makefile.rules_generic b/Makefile.rules_generic index b82baf444..f074367d8 100644 --- a/Makefile.rules_generic +++ b/Makefile.rules_generic @@ -116,15 +116,6 @@ $(BIN_DIR)/app.elf: $(LINK_DEPENDENCIES) $(L)$(call link_cmdline,$(OBJECT_FILES) $(LDLIBS),$(BIN_DIR)/app.elf) $(L)$(GCCPATH)arm-none-eabi-objcopy -O ihex -S $(BIN_DIR)/app.elf $(BIN_DIR)/app.hex $(L)$(GCCPATH)arm-none-eabi-objdump -S -d $(BIN_DIR)/app.elf > $(DBG_DIR)/app.asm - $(L)$(call objcopy_add_section_cmdline,$(TARGET), ledger.target) - $(L)$(call objcopy_add_section_cmdline,$(TARGET_NAME), ledger.target_name) - $(L)$(call objcopy_add_section_cmdline,$(TARGET_ID), ledger.target_id) - $(L)$(call objcopy_add_section_cmdline,$(APPNAME), ledger.app_name) - $(L)$(call objcopy_add_section_cmdline,$(APPVERSION), ledger.app_version) - $(L)$(call objcopy_add_section_cmdline,$(API_LEVEL), ledger.api_level) - $(L)$(call objcopy_add_section_cmdline,$(SDK_NAME), ledger.sdk_name) - $(L)$(call objcopy_add_section_cmdline,$(SDK_VERSION), ledger.sdk_version) - $(L)$(call objcopy_add_section_cmdline,$(SDK_HASH), ledger.sdk_hash) # This targets are generated along $(OBJ_DIR)/app.elf but we can't make them co-target # otherwise building with `make -j` fails due to multiple threads running simultaneously @@ -172,14 +163,6 @@ cc_cmdline = $(CC) -c $(CFLAGS) -MMD -MT $(OBJ_DIR)/$(basename $(notdir $(4))).o as_cmdline = $(AS) -c $(AFLAGS) $(addprefix -D,$(2)) $(addprefix -I,$(1)) -o $(4) $(3) -# objcopy_add_section_cmdline(data,section_name) -TMPFILE := $(shell mktemp) -objcopy_add_section_cmdline = echo $(1) > $(TMPFILE) && \ - $(GCCPATH)arm-none-eabi-objcopy --add-section $(2)="$(TMPFILE)" \ - --set-section-flags $(2)=noload,readonly \ - $(BIN_DIR)/app.elf $(BIN_DIR)/app.elf && \ - rm $(TMPFILE) - ### END GCC COMPILER RULES # Run Clang Static Analyzer diff --git a/Makefile.standard_app b/Makefile.standard_app index 4199a179a..950ea792d 100644 --- a/Makefile.standard_app +++ b/Makefile.standard_app @@ -75,9 +75,6 @@ endif # STANDARD DEFINES # ##################################################################### DEFINES += $(DEFINES_LIB) -# Added directly as a CFLAG because it might contain spaces -CFLAGS += -DAPPNAME=\"$(APPNAME)\" -DEFINES += APPVERSION=\"$(APPVERSION)\" DEFINES += MAJOR_VERSION=$(APPVERSION_M) MINOR_VERSION=$(APPVERSION_N) PATCH_VERSION=$(APPVERSION_P) DEFINES += IO_HID_EP_LENGTH=64 diff --git a/src/app_metadata.c b/src/app_metadata.c new file mode 100644 index 000000000..b46a4dd99 --- /dev/null +++ b/src/app_metadata.c @@ -0,0 +1,71 @@ +/******************************************************************************* + * Ledger - Secure firmware + * (c) 2023 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#if !defined(HAVE_BOLOS) + +#include + +#include "bolos_target.h" + +#define STR_IMPL_(x) #x +#define STRINGIFY(x) STR_IMPL_(x) + +#define CREATE_METADATA_STRING_ITEM(ITEM_NAME, section_name) \ + __attribute__((section("ledger." #section_name))) \ + const char section_name[sizeof(ITEM_NAME) - 1] \ + = ITEM_NAME; + +#define CREATE_METADATA_STRING_ITEM_FROM_INT(ITEM_NAME, section_name) \ + CREATE_METADATA_STRING_ITEM(STRINGIFY(ITEM_NAME), section_name) + +#if defined(TARGET) +CREATE_METADATA_STRING_ITEM(TARGET, target) +#endif + +#if defined(TARGET_NAME) +CREATE_METADATA_STRING_ITEM(TARGET_NAME, target_name) +#endif + +#if defined(TARGET_ID) +CREATE_METADATA_STRING_ITEM_FROM_INT(TARGET_ID, target_id) +#endif + +#if defined(APPNAME) +CREATE_METADATA_STRING_ITEM(APPNAME, app_name) +#endif + +#if defined(APPVERSION) +CREATE_METADATA_STRING_ITEM(APPVERSION, app_version) +#endif + +#if defined(API_LEVEL) +CREATE_METADATA_STRING_ITEM_FROM_INT(API_LEVEL, api_level) +#endif + +#if defined(SDK_NAME) +CREATE_METADATA_STRING_ITEM(SDK_NAME, sdk_name) +#endif + +#if defined(SDK_VERSION) +CREATE_METADATA_STRING_ITEM(SDK_VERSION, sdk_version) +#endif + +#if defined(SDK_HASH) +CREATE_METADATA_STRING_ITEM(SDK_HASH, sdk_hash) +#endif + +#endif diff --git a/target/nanos/script.ld b/target/nanos/script.ld index f9446822d..aeb948b34 100644 --- a/target/nanos/script.ld +++ b/target/nanos/script.ld @@ -191,6 +191,16 @@ SECTIONS .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } + + ledger.target (INFO): { KEEP(*(ledger.target)) } + ledger.target_name (INFO): { KEEP(*(ledger.target_name)) } + ledger.target_id (INFO): { KEEP(*(ledger.target_id)) } + ledger.app_name (INFO): { KEEP(*(ledger.app_name)) } + ledger.app_version (INFO): { KEEP(*(ledger.app_version)) } + ledger.api_level (INFO): { KEEP(*(ledger.api_level)) } + ledger.sdk_name (INFO): { KEEP(*(ledger.sdk_name)) } + ledger.sdk_version (INFO): { KEEP(*(ledger.sdk_version)) } + ledger.sdk_hash (INFO): { KEEP(*(ledger.sdk_hash)) } } PROVIDE(_nvram = ABSOLUTE(_nvram_start)); diff --git a/target/nanos2/script.ld b/target/nanos2/script.ld index 8f9b2f308..e89c97749 100644 --- a/target/nanos2/script.ld +++ b/target/nanos2/script.ld @@ -168,6 +168,16 @@ SECTIONS .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } + + ledger.target (INFO): { KEEP(*(ledger.target)) } + ledger.target_name (INFO): { KEEP(*(ledger.target_name)) } + ledger.target_id (INFO): { KEEP(*(ledger.target_id)) } + ledger.app_name (INFO): { KEEP(*(ledger.app_name)) } + ledger.app_version (INFO): { KEEP(*(ledger.app_version)) } + ledger.api_level (INFO): { KEEP(*(ledger.api_level)) } + ledger.sdk_name (INFO): { KEEP(*(ledger.sdk_name)) } + ledger.sdk_version (INFO): { KEEP(*(ledger.sdk_version)) } + ledger.sdk_hash (INFO): { KEEP(*(ledger.sdk_hash)) } } PROVIDE(_nvram = ABSOLUTE(_nvram_start)); diff --git a/target/nanox/script.ld b/target/nanox/script.ld index 8c42cc4c2..fb850ad0e 100644 --- a/target/nanox/script.ld +++ b/target/nanox/script.ld @@ -179,6 +179,16 @@ SECTIONS .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } + + ledger.target (INFO): { KEEP(*(ledger.target)) } + ledger.target_name (INFO): { KEEP(*(ledger.target_name)) } + ledger.target_id (INFO): { KEEP(*(ledger.target_id)) } + ledger.app_name (INFO): { KEEP(*(ledger.app_name)) } + ledger.app_version (INFO): { KEEP(*(ledger.app_version)) } + ledger.api_level (INFO): { KEEP(*(ledger.api_level)) } + ledger.sdk_name (INFO): { KEEP(*(ledger.sdk_name)) } + ledger.sdk_version (INFO): { KEEP(*(ledger.sdk_version)) } + ledger.sdk_hash (INFO): { KEEP(*(ledger.sdk_hash)) } } PROVIDE(_nvram = ABSOLUTE(_nvram_start)); diff --git a/target/stax/script.ld b/target/stax/script.ld index 806a7861a..f23457928 100644 --- a/target/stax/script.ld +++ b/target/stax/script.ld @@ -169,6 +169,16 @@ SECTIONS .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } + + ledger.target (INFO): { KEEP(*(ledger.target)) } + ledger.target_name (INFO): { KEEP(*(ledger.target_name)) } + ledger.target_id (INFO): { KEEP(*(ledger.target_id)) } + ledger.app_name (INFO): { KEEP(*(ledger.app_name)) } + ledger.app_version (INFO): { KEEP(*(ledger.app_version)) } + ledger.api_level (INFO): { KEEP(*(ledger.api_level)) } + ledger.sdk_name (INFO): { KEEP(*(ledger.sdk_name)) } + ledger.sdk_version (INFO): { KEEP(*(ledger.sdk_version)) } + ledger.sdk_hash (INFO): { KEEP(*(ledger.sdk_hash)) } } PROVIDE(_nvram = ABSOLUTE(_nvram_start)); From 34448c2bd8627bba8d53cbb5ed39420f7e1e4bf6 Mon Sep 17 00:00:00 2001 From: Xavier Chapron Date: Wed, 22 Nov 2023 18:48:52 +0100 Subject: [PATCH 2/4] Makefile: Improve app _install_parameters generation This includes: - Generation before build time and inclusion in the binary at build time whereas it was previously done only during sideloading. This allows: * to have a proper app size when using arm-none-eabi-size bin/app.elf, taking into account the size of the install_parameters * to have complete artifacts app.elf, app.hex, app.map, app.asm which can be used for debug * to have a proper Speculos emulation on install_parameters interactions. - Parsing of APP_LOAD_PARAMS variable so that its differents fields can be used cleanly - New symbols in the linker script to expose the start and the end of install_parameters, allowing to retrieve its size. - A small change in src/pic.c to avoid application sha256 change in this commit. The change will be removed in the next commit. This commit as been tested without modification on the application sha256 (which includes install_parameters generated at build time or as previously by ledgerblue.loadapp script) on numerous applications. --- Makefile.app_params | 126 ++++++++++++++++++++++++++ Makefile.defines | 20 ----- Makefile.rules_generic | 4 + Makefile.standard_app | 14 +-- extract_param.py | 20 +++++ install_params.py | 192 ++++++++++++++++++++++++++++++++++++++++ src/app_metadata.c | 5 ++ src/pic.c | 6 +- target/nanos/script.ld | 3 + target/nanos2/script.ld | 3 + target/nanox/script.ld | 3 + target/stax/script.ld | 3 + 12 files changed, 365 insertions(+), 34 deletions(-) create mode 100644 Makefile.app_params create mode 100644 extract_param.py create mode 100644 install_params.py diff --git a/Makefile.app_params b/Makefile.app_params new file mode 100644 index 000000000..be6b3b4a6 --- /dev/null +++ b/Makefile.app_params @@ -0,0 +1,126 @@ +#******************************************************************************* +# Ledger SDK +# (c) 2023 Ledger +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#******************************************************************************* + +# Command to print ICONNAME hexadecimal bitmap on stdout according to the +# hardware target. +ifneq ($(TARGET),nanos) +#inverse B&W for non Stax +ifneq ($(TARGET_NAME),TARGET_STAX) +ICONHEX_CMD=python3 $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py --reverse --hexbitmaponly $(ICONNAME) +else +ICONHEX_CMD=python3 $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py --hexbitmaponly $(ICONNAME) +endif +else +ICONHEX_CMD=python3 $(BOLOS_SDK)/icon3.py --hexbitmaponly $(ICONNAME) +endif + + +######################################### +# Parse APP_LOAD_PARAMS # +######################################### +# This is necessary when makefile.standard_app is not completely used. +# Correctly implemented apps should not set anything in APP_LOAD_PARAMS anymore +# Potential presents info are: +# --appFlags +# --curve +# --path +# --path_slip21 +# --tlvraw +# --dep +# --nocrc +# Other info are considered an error and will be silently discarded. + +ifneq ($(APP_LOAD_PARAMS),) + EXTRACTED_APP_FLAGS := $(shell python3 $(BOLOS_SDK)/extract_param.py --appFlags $(APP_LOAD_PARAMS)) + APP_FLAGS_APP_LOAD_PARAMS += $(EXTRACTED_APP_FLAGS) + + EXTRACTED_CURVE := $(shell python3 $(BOLOS_SDK)/extract_param.py --curve $(APP_LOAD_PARAMS)) + CURVE_APP_LOAD_PARAMS += $(EXTRACTED_CURVE) + + EXTRACTED_PATH := $(shell python3 $(BOLOS_SDK)/extract_param.py --path $(APP_LOAD_PARAMS)) + PATH_APP_LOAD_PARAMS += $(EXTRACTED_PATH) + + EXTRACTED_PATH_SLIP21 := $(shell python3 $(BOLOS_SDK)/extract_param.py --path_slip21 $(APP_LOAD_PARAMS)) + PATH_SLIP21_APP_LOAD_PARAMS += $(EXTRACTED_PATH_SLIP21) + + EXTRACTED_TLVRAW := $(shell python3 $(BOLOS_SDK)/extract_param.py --tlvraw $(APP_LOAD_PARAMS)) + TLVRAW_APP_LOAD_PARAMS += $(EXTRACTED_TLVRAW) + + EXTRACTED_DEP := $(shell python3 $(BOLOS_SDK)/extract_param.py --dep $(APP_LOAD_PARAMS)) + DEP_APP_LOAD_PARAMS += $(EXTRACTED_DEP) + + ifneq ($(findstring --nocrc,$(APP_LOAD_PARAMS)),) + ENABLE_NOCRC_APP_LOAD_PARAMS = 1 + endif +endif + + +######################################### +# Generate install_params # +######################################### +# Compute params to call install_params.py +# Consider only one path_slip21 can be added, whereas LedgerBlue seems to +# support multiple, but has the path can hold a " " in it, it mess with the +# foreach, so we choose to restrict to only one path_slip21. +APP_INSTALL_PARAMS = --appName $(APPNAME) +APP_INSTALL_PARAMS += --appVersion $(APPVERSION) +APP_INSTALL_PARAMS += `ICONHEX=\`$(ICONHEX_CMD) 2>/dev/null\` ; [ ! -z "$$ICONHEX" ] && echo "--icon $$ICONHEX"` +APP_INSTALL_PARAMS += $(foreach curve, $(CURVE_APP_LOAD_PARAMS), --curve $(curve)) +APP_INSTALL_PARAMS += $(foreach path, $(PATH_APP_LOAD_PARAMS), --path $(path)) +ifneq ($(PATH_SLIP21_APP_LOAD_PARAMS),) + APP_INSTALL_PARAMS += --path_slip21 $(PATH_SLIP21_APP_LOAD_PARAMS) +endif +APP_INSTALL_PARAMS += $(foreach tlvraw, $(TLVRAW_APP_LOAD_PARAMS), --tlvraw $(tlvraw)) +APP_INSTALL_PARAMS += $(foreach dep, $(DEP_APP_LOAD_PARAMS), --dep $(dep)) + +# Compute install_params tlv binary blob then expose it via a define to +# src/app_metadata.c so that it is inserted in the binary at link time +APP_INSTALL_PARAMS_DATA := $(shell python3 $(BOLOS_SDK)/install_params.py $(APP_INSTALL_PARAMS)) +DEFINES += APP_INSTALL_PARAMS_DATA=$(APP_INSTALL_PARAMS_DATA) + +######################################### +# Generate APP_LOAD_PARAMS # +######################################### +# Rewrite APP_LOAD_PARAMS with params needed for generating the sideloading +# APDUs. +# This variable is then used in some Makefiles target as Ledgerblue.loadapp +# script parameters. +APP_LOAD_PARAMS = --targetId $(TARGET_ID) +APP_LOAD_PARAMS += --targetVersion="$(TARGET_VERSION)" +APP_LOAD_PARAMS += --apiLevel $(API_LEVEL) +APP_LOAD_PARAMS += --fileName bin/app.hex +APP_LOAD_PARAMS += --appName $(APPNAME) +ifneq ($(APP_FLAGS_APP_LOAD_PARAMS),) + APP_LOAD_PARAMS += --appFlags $(APP_FLAGS_APP_LOAD_PARAMS) +endif +APP_LOAD_PARAMS += --delete +APP_LOAD_PARAMS += --tlv +APP_LOAD_PARAMS += --dataSize $$((0x`cat debug/app.map | grep _envram_data | tr -s ' ' | cut -f2 -d' ' |cut -f2 -d'x' ` - 0x`cat debug/app.map | grep _nvram_data | tr -s ' ' | cut -f2 -d' ' | cut -f2 -d'x'`)) +APP_LOAD_PARAMS += --installparamsSize $$((0x`cat debug/app.map | grep _einstall_parameters | tr -s ' ' | cut -f2 -d' ' |cut -f2 -d'x'` - 0x`cat debug/app.map | grep _install_parameters | tr -s ' ' | cut -f2 -d' ' |cut -f2 -d'x'`)) + +ifeq ($(ENABLE_NOCRC_APP_LOAD_PARAMS), 1) + APP_LOAD_PARAMS += --nocrc +endif + +COMMON_DELETE_PARAMS = --targetId $(TARGET_ID) --appName $(APPNAME) + +# Extra load parameters for loadApp script +ifneq ($(SCP_PRIVKEY),) + PARAM_SCP += --rootPrivateKey $(SCP_PRIVKEY) + APP_LOAD_PARAMS += $(PARAM_SCP) + COMMON_DELETE_PARAMS += $(PARAM_SCP) +endif diff --git a/Makefile.defines b/Makefile.defines index 0b7761d3b..72a2c12fd 100644 --- a/Makefile.defines +++ b/Makefile.defines @@ -60,26 +60,6 @@ DEFINES += API_LEVEL=$(API_LEVEL) APP_METADATA_LIST := TARGET TARGET_NAME APPVERSION SDK_NAME SDK_VERSION SDK_HASH DEFINES += $(foreach item,$(APP_METADATA_LIST), $(item)=\"$($(item))\") -# extra load parameters for loadApp script -ifneq ($(SCP_PRIVKEY),) -PARAM_SCP+=--rootPrivateKey $(SCP_PRIVKEY) -endif - -# Command to print ICONNAME hexadecimal bitmap on stdout -# according to the hardware target. -ifneq ($(TARGET),nanos) -#inverse B&W for non Stax -ifneq ($(TARGET_NAME),TARGET_STAX) -ICONHEX_CMD=python3 $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py --reverse --hexbitmaponly $(ICONNAME) -else -ICONHEX_CMD=python3 $(BOLOS_SDK)/lib_nbgl/tools/icon2glyph.py --hexbitmaponly $(ICONNAME) -endif -else -ICONHEX_CMD=python3 $(BOLOS_SDK)/icon3.py --hexbitmaponly $(ICONNAME) -endif - -COMMON_LOAD_PARAMS=--tlv --targetId $(TARGET_ID) --targetVersion="$(TARGET_VERSION)" --apiLevel $(API_LEVEL) --delete --fileName bin/app.hex --appName $(APPNAME) --appVersion $(APPVERSION) --dataSize $$((0x`cat debug/app.map |grep _envram_data | tr -s ' ' | cut -f2 -d' '|cut -f2 -d'x'` - 0x`cat debug/app.map |grep _nvram_data | tr -s ' ' | cut -f2 -d' '|cut -f2 -d'x'`)) `ICONHEX=\`$(ICONHEX_CMD) 2>/dev/null\` ; [ ! -z "$$ICONHEX" ] && echo "--icon $$ICONHEX"` $(PARAM_SCP) -COMMON_DELETE_PARAMS=--targetId $(TARGET_ID) --appName $(APPNAME) $(PARAM_SCP) BUILD_DIR := build TARGET_BUILD_DIR := $(BUILD_DIR)/$(TARGET) diff --git a/Makefile.rules_generic b/Makefile.rules_generic index f074367d8..584fe343e 100644 --- a/Makefile.rules_generic +++ b/Makefile.rules_generic @@ -15,6 +15,10 @@ # limitations under the License. #******************************************************************************* +# Include Makefile.app_params here so that it is: +# - included on all apps Makefile, without changing them +# - included after apps Makefile changes on APP_LOAD_PARAMS +include $(BOLOS_SDK)/Makefile.app_params # consider every intermediate target as final to avoid deleting intermediate files .SECONDARY: diff --git a/Makefile.standard_app b/Makefile.standard_app index 950ea792d..461f3f08f 100644 --- a/Makefile.standard_app +++ b/Makefile.standard_app @@ -165,22 +165,14 @@ ifeq ($(HAVE_APPLICATION_FLAG_LIBRARY), 1) STANDARD_APP_FLAGS := $(shell echo $$(($(STANDARD_APP_FLAGS) + 0x800))) endif -APP_FLAGS = $(shell printf '0x%x' $$(( $(STANDARD_APP_FLAGS) + $(CUSTOM_APP_FLAGS) )) ) - -APP_LOAD_PARAMS += --appFlags $(APP_FLAGS) - -APP_LOAD_PARAMS += $(foreach curve, $(CURVE_APP_LOAD_PARAMS), --curve $(curve)) - -APP_LOAD_PARAMS += $(foreach path, $(PATH_APP_LOAD_PARAMS), --path $(path)) - -APP_LOAD_PARAMS += $(COMMON_LOAD_PARAMS) - # Pending review flag ifeq ($(ENABLE_PENDING_REVIEW_SCREEN), 1) - APP_LOAD_PARAMS += --tlvraw 9F:01 + TLVRAW_APP_LOAD_PARAMS += 9F:01 DEFINES += HAVE_PENDING_REVIEW_SCREEN endif +APP_FLAGS_APP_LOAD_PARAMS = $(shell printf '0x%x' $$(( $(STANDARD_APP_FLAGS) + $(CUSTOM_APP_FLAGS) )) ) + ##################################################################### # COMPILER SETTINGS # ##################################################################### diff --git a/extract_param.py b/extract_param.py new file mode 100644 index 000000000..0dc614a5f --- /dev/null +++ b/extract_param.py @@ -0,0 +1,20 @@ +""" +Helper to extract APP_LOAD_PARAMS parameters values. +It takes as a first parameter the parameter name to be search and output the +corresponding values from the rest of the script parameters. +""" + +from sys import argv + +if __name__ == '__main__': + + assert len(argv) > 2 + searching = argv[1] + + res = [] + args = argv[2:] + + for i, arg in enumerate(args): + if arg == searching and len(args) > i: + res.append(repr(args[i + 1])) + print(" ".join(res)) diff --git a/install_params.py b/install_params.py new file mode 100644 index 000000000..3110d9238 --- /dev/null +++ b/install_params.py @@ -0,0 +1,192 @@ +""" +/******************************************************************************* + * Ledger - Secure firmware + * (c) 2023 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ +""" + +import struct +import argparse +import binascii + + +BOLOS_TAG_APPNAME = 1 +BOLOS_TAG_APPVERSION = 2 +BOLOS_TAG_ICON = 3 +BOLOS_TAG_DERIVEPATH = 4 +BOLOS_TAG_DEPENDENCY = 6 + + +CURVE_SECP256K1 = 1 << 0 +CURVE_PRIME256R1 = 1 << 1 +CURVE_ED25519 = 1 << 2 +CURVE_SLIP21 = 1 << 3 +CURVE_BLS12381G1 = 1 << 4 + + +def auto_int(x): + return int(x, 0) + + +def string_to_bytes(x): + return bytes(x, 'ascii') + + +def encodelv(value): + length = len(value) + res = b"" + if length < 128: + res += struct.pack(">B", length) + elif length < 256: + res += struct.pack(">B", 0x81) + res += struct.pack(">B", length) + elif length < 65536: + res += struct.pack(">B", 0x82) + res += struct.pack(">H", length) + else: + raise Exception("Unimplemented LV encoding") + res += value + return res + + +def encodetlv(tag, value): + res = struct.pack(">B", tag) + res += encodelv(value) + return res + + +def get_curve_mask(curves, slip21_paths): + if curves: + curve_mask = 0x00 + for curve in curves: + if curve == 'secp256k1': + curve_mask |= CURVE_SECP256K1 + elif curve == 'secp256r1': + curve_mask |= CURVE_PRIME256R1 + elif curve == 'ed25519': + curve_mask |= CURVE_ED25519 + elif curve == 'bls12381g1': + curve_mask |= CURVE_BLS12381G1 + else: + raise Exception("Unknown curve " + curve) + + if slip21_paths: + curve_mask |= CURVE_SLIP21 + else: + curve_mask = 0xff + + return curve_mask + + +def parse_bip32_path(path): + elements = path.split('/') + result = struct.pack('>B', len(elements)) + for element in elements: + if element.endswith("\'"): + value = 0x80000000 | int(element[:-1]) + else: + value = int(element) + result += struct.pack(">I", value) + return result + + +def parse_slip21_path(path): + result = struct.pack('>B', 0x80 | (len(path) + 1)) + result = result + b'\x00' + string_to_bytes(path) + return result + + +def get_serialized_path(curves, bip32_paths, slip21_paths): + curve_mask = get_curve_mask(args.curve, args.path_slip21) + serialized_path = struct.pack('>B', curve_mask) + + serialized_bip32_paths = b"" + if bip32_paths: + for path in bip32_paths: + if path: + serialized_bip32_paths += parse_bip32_path(path) + serialized_path += serialized_bip32_paths + + serialized_slip21_path = b"" + if slip21_paths: + for path in slip21_paths: + if path: + serialized_slip21_path += parse_slip21_path(path) + if not serialized_bip32_paths: + # Unrestricted, authorize all paths for regular derivation + serialized_slip21_path += struct.pack('>B', 0) + serialized_path += serialized_slip21_path + + return serialized_path + + +def get_argparser(): + parser = argparse.ArgumentParser(description="Generate application install_params TLV bytes.") + parser.add_argument("--appName", help="The name to give the application after loading it", required=True) + parser.add_argument("--appVersion", help="The application version (as a string)") + parser.add_argument("--icon", help="The icon content to use (hex encoded)") + parser.add_argument("--curve", help="""A curve on which BIP 32 derivation is locked ("secp256k1", "secp256r1", +"ed25519" or "bls12381g1"), can be repeated""", action='append') + parser.add_argument("--path", help="""A BIP 32 path to which derivation is locked (format decimal a'/b'/c), can be +repeated""", action='append') + parser.add_argument("--path_slip21", help="""A SLIP 21 path to which derivation is locked""", action='append') + parser.add_argument("--tlvraw", help="Add a custom install param with the hextag:hexvalue encoding", action='append') + parser.add_argument("--dep", help="Add a dependency over an appname[:appversion]", action='append') + + return parser + + +if __name__ == '__main__': + + args = get_argparser().parse_args() + + # Build install parameters + install_params = b"" + + # express dependency + if args.dep: + for dep in args.dep: + app_name, app_version = dep, None + + # split if version is specified + if ":" in dep: + app_name, app_version = dep.split(":") + + dep_value = encodelv(string_to_bytes(app_name)) + if app_version: + dep_value += encodelv(string_to_bytes(app_version)) + install_params += encodetlv(BOLOS_TAG_DEPENDENCY, dep_value) + + # Add raw install parameters as requested + if args.tlvraw: + for tlvraw in args.tlvraw: + hextag, hexvalue = tlvraw.split(":") + install_params += encodetlv(int(hextag, 16), binascii.unhexlify(hexvalue)) + + # App name is mandatory + install_params += encodetlv(BOLOS_TAG_APPNAME, string_to_bytes(args.appName)) + + if args.appVersion: + install_params += encodetlv(BOLOS_TAG_APPVERSION, string_to_bytes(args.appVersion)) + + if args.icon: + install_params += encodetlv(BOLOS_TAG_ICON, bytes.fromhex(args.icon)) + + serialized_path = get_serialized_path(args.curve, args.path, args.path_slip21) + if len(serialized_path) > 0: + install_params += encodetlv(BOLOS_TAG_DERIVEPATH, serialized_path) + + output = ",".join(f"0x{i:02x}" for i in install_params) + print(output) diff --git a/src/app_metadata.c b/src/app_metadata.c index b46a4dd99..ab2abf277 100644 --- a/src/app_metadata.c +++ b/src/app_metadata.c @@ -68,4 +68,9 @@ CREATE_METADATA_STRING_ITEM(SDK_VERSION, sdk_version) CREATE_METADATA_STRING_ITEM(SDK_HASH, sdk_hash) #endif +#ifdef APP_INSTALL_PARAMS_DATA +__attribute__((section(".install_parameters"))) const uint8_t install_parameters[] + = {APP_INSTALL_PARAMS_DATA}; +#endif + #endif diff --git a/src/pic.c b/src/pic.c index aee063083..8bfb56cd2 100644 --- a/src/pic.c +++ b/src/pic.c @@ -20,14 +20,14 @@ __attribute__((naked, no_instrument_function)) void *pic_internal(void *link_add // only apply PIC conversion if link_address is in linked code (over 0xC0D00000 in our example) // this way, PIC call are armless if the address is not meant to be converted extern void _nvram; -extern void _envram; +extern void _install_parameters; #if defined(ST31) void *pic(void *link_address) { // check if in the LINKED TEXT zone - if (link_address >= &_nvram && link_address < &_envram) { + if (link_address >= &_nvram && link_address < &_install_parameters) { link_address = pic_internal(link_address); } @@ -45,7 +45,7 @@ void *pic(void *link_address) // check if in the LINKED TEXT zone __asm volatile("ldr %0, =_nvram" : "=r"(n)); - __asm volatile("ldr %0, =_envram" : "=r"(en)); + __asm volatile("ldr %0, =_install_parameters" : "=r"(en)); if (link_address >= n && link_address <= en) { link_address = pic_internal(link_address); } diff --git a/target/nanos/script.ld b/target/nanos/script.ld index aeb948b34..447f13ff6 100644 --- a/target/nanos/script.ld +++ b/target/nanos/script.ld @@ -81,6 +81,9 @@ SECTIONS _envram_data = .; _install_parameters = .; + *(.install_parameters) + KEEP(*(.install_parameters)) + _einstall_parameters = .; _nvram_end = .; } > FLASH = 0x00 diff --git a/target/nanos2/script.ld b/target/nanos2/script.ld index e89c97749..62d995fa7 100644 --- a/target/nanos2/script.ld +++ b/target/nanos2/script.ld @@ -75,6 +75,9 @@ SECTIONS _envram_data = .; _install_parameters = .; + *(.install_parameters) + KEEP(*(.install_parameters)) + _einstall_parameters = .; _nvram_end = .; } > FLASH = 0x00 diff --git a/target/nanox/script.ld b/target/nanox/script.ld index fb850ad0e..aaca967eb 100644 --- a/target/nanox/script.ld +++ b/target/nanox/script.ld @@ -77,6 +77,9 @@ SECTIONS _envram_data = .; _install_parameters = .; + *(.install_parameters) + KEEP(*(.install_parameters)) + _einstall_parameters = .; _nvram_end = .; } > FLASH diff --git a/target/stax/script.ld b/target/stax/script.ld index f23457928..ae52569cf 100644 --- a/target/stax/script.ld +++ b/target/stax/script.ld @@ -76,6 +76,9 @@ SECTIONS _envram_data = .; _install_parameters = .; + *(.install_parameters) + KEEP(*(.install_parameters)) + _einstall_parameters = .; _nvram_end = .; } > FLASH = 0x00 From 2a823e5e7c0fa4223384fc5aea36a72149aaa743 Mon Sep 17 00:00:00 2001 From: Xavier Chapron Date: Fri, 24 Nov 2023 18:26:36 +0100 Subject: [PATCH 3/4] src/pic.c: Restore PIC functionnality on all linked text zone Following previous commit, this now included the install_parameters that are added during the build process. --- src/pic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pic.c b/src/pic.c index 8bfb56cd2..aee063083 100644 --- a/src/pic.c +++ b/src/pic.c @@ -20,14 +20,14 @@ __attribute__((naked, no_instrument_function)) void *pic_internal(void *link_add // only apply PIC conversion if link_address is in linked code (over 0xC0D00000 in our example) // this way, PIC call are armless if the address is not meant to be converted extern void _nvram; -extern void _install_parameters; +extern void _envram; #if defined(ST31) void *pic(void *link_address) { // check if in the LINKED TEXT zone - if (link_address >= &_nvram && link_address < &_install_parameters) { + if (link_address >= &_nvram && link_address < &_envram) { link_address = pic_internal(link_address); } @@ -45,7 +45,7 @@ void *pic(void *link_address) // check if in the LINKED TEXT zone __asm volatile("ldr %0, =_nvram" : "=r"(n)); - __asm volatile("ldr %0, =_install_parameters" : "=r"(en)); + __asm volatile("ldr %0, =_envram" : "=r"(en)); if (link_address >= n && link_address <= en) { link_address = pic_internal(link_address); } From db83c5cbe26895cbecc028bee705c7cc1d6bff4b Mon Sep 17 00:00:00 2001 From: Xavier Chapron Date: Mon, 27 Nov 2023 16:16:59 +0100 Subject: [PATCH 4/4] src/checks.c: Use correct install_parameters length --- include/checks.h | 1 - src/checks.c | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/checks.h b/include/checks.h index f0e9851af..157e44003 100644 --- a/include/checks.h +++ b/include/checks.h @@ -23,7 +23,6 @@ #define CHECK_NOT_AUDITED_TLV_TAG 0x9F #define CHECK_NOT_AUDITED_TLV_VAL 0x01 -#define CHECK_NOT_AUDITED_MAX_LEN 0x40 void check_audited_app(void); #endif // !defined(HAVE_BOLOS) && defined(HAVE_PENDING_REVIEW_SCREEN) diff --git a/src/checks.c b/src/checks.c index ae253f2fb..1ae993961 100644 --- a/src/checks.c +++ b/src/checks.c @@ -28,6 +28,7 @@ // This label ultimately comes from the application link. extern unsigned int const _install_parameters; +extern unsigned int const _einstall_parameters; #ifdef HAVE_BAGL @@ -173,8 +174,10 @@ void check_audited_app(void) { unsigned char data = BOLOS_FALSE; unsigned char *buffer = &data; - unsigned int length = os_parse_bertlv((unsigned char *) (&_install_parameters), - CHECK_NOT_AUDITED_MAX_LEN, + unsigned int install_parameters_len + = (unsigned char *) &_einstall_parameters - (unsigned char *) &_install_parameters; + unsigned int length = os_parse_bertlv((unsigned char *) &_install_parameters, + install_parameters_len, NULL, CHECK_NOT_AUDITED_TLV_TAG, 0x00,