Skip to main content
Version: Next

5.5:Code Encryption Library

tip

Integrating the Code Encryption Library into your project and using it correctly requires a high level of development experience. In addition, please read this document thoroughly and follow the procedures, which will be spelled out as clearly as possible in order to minimize the likelihood of problems arising.

note

The current library is a full-featured library, and most of the encryption algorithms, checksum algorithms, etc. are built-in algorithms. If you have higher requirements for your project, or need to tailor the library to reduce the memory requirement (at least 1K or less), to reduce the flash usage (at least 16K or so), or based on higher customization requirements, you can contact us to authorize the acquisition of the source code for the commercial cryptographic library.

5.5.1:Intro

Power Writer has built-in standard Matrix signature algorithm, and can be used with ICWKEY to do the chip's digital certificate distribution, in some basic applications, it can achieve the requirements of preventing the binary code from copying, but with the increase in the level of software protection requirements, the user has a deeper level of software protection needs, for this reason, ICWorkshop will be used in its own products to encrypt part of the library, For this reason, we have opened the function of the encryption library used in our products in the form of a library file in order to meet the needs of higher-order code encryption and protection, and the current encryption library contains the following modules:

MCU General Software Protection Library (advance_software_protect_library) available

  • Object monitoring (monitoring objects for unauthorized access)
  • Firmware verification (support partition segment verification, users can freely set the verification area)
  • Function encryption (partial firmware encryption, function level encryption to avoid core code leakage)
  • Anti-debugging service framework
  • Firmware signature (prevents firmware from being copied to other chips)
  • Firmware compression (compresses some binary code)
  • Virtual Machine (ARM instruction set converted to ARM virtual machine instructions)
  • Other security services
note

Firmware compression, virtual machine and other technologies currently belong to the confidentiality of the technology, not for the time being for the public, the current technology has been open to meet the vast majority of application scenarios, if the encryption has a customized demand, you can get in touch with us, email: cs@icworkshop.com .

5.5.2:Preliminary

5.5.2.1:Requirement

  • Supported operating systems: windows 、Linux、MacOS。
  • Supported compilers (development environments):
    • ARMCC(Clang):The typical IDE is MDK, and C99 mode must be enabled.
    • GCC:Typical IDEs are the Eclipse CDT development environment, such as the STM32 Cube IDE, or a combination of VSCode + GCC + GDB based environments.
    • IAR:IAR Embedded Workbench IDE。
  • Minimum Flash size with full functionality enabled: > 4.8K Byte (heap size).
  • Minimum Flash size when full functionality is enabled : > 46K Byte.
  • Supported CPU system: ARM Cortex series CPU.
tip
  • The download site will provide the latest library files, dependency source code, example source code, etc. based on the demand feedback.
  • There are no plans to port instruction sets other than the ARM core (e.g. RISC-V), or compilers other than those on the support list, so please let us know if you have any requests.

5.5.2.2:Download

Click on the following link to go to the Power Writer Download Centre. PowerWriter,As shown in the figure below, click [Download Now] to download the encryption library and related files to this disc, as shown below:

image-20250111104351786

5.5.2.3:File structure

tip

The file structure may change as the library is updated and will not be described additionally.

├── Docs.url        /* Online document */
├── ico.png
├── project /* Encryption library and related demos */
│   ├── lib /* Encryption library */
│   │   ├── advance_software_protect_base.h /* Basic header file definition */
│   │   ├── advance_software_protect_lib.h /* Export function declaration */
│   │   ├── advance_software_protect_porting.c /* User porting source */
│   │   ├── advance_software_protect_samples.c /* Example tutorial */
│   │   ├── advance_software_protect_user.h /* User migration and custom parameters */
│   │   ├── gcc /* GCC Library files */
│   │   ├── iar /* IAR Library files */
│   │   └── mdk /* ARMCC Library files */
│   │   ├── advance_software_protect_m0.lib
│   │   ├── advance_software_protect_m0plus.lib
│   │   ├── advance_software_protect_m0plus_mpu.lib
│   │   ├── advance_software_protect_m1.lib
│   │   ├── advance_software_protect_m23.lib
│   │   ├── advance_software_protect_m23_tz.lib
│   │   ├── advance_software_protect_m3.lib
│   │   ├── advance_software_protect_m33.lib
│   │   ├── advance_software_protect_m33_dsp_fp.lib
│   │   ├── advance_software_protect_m33_dsp_fp_tz.lib
│   │   ├── advance_software_protect_m33_tz.lib
│   │   ├── advance_software_protect_m35plus.lib
│   │   ├── advance_software_protect_m35plus_dsp_fp.lib
│   │   ├── advance_software_protect_m35plus_dsp_fp_tz.lib
│   │   ├── advance_software_protect_m35plus_tz.lib
│   │   ├── advance_software_protect_m4.lib
│   │   ├── advance_software_protect_m4_fp.lib
│   │   ├── advance_software_protect_m55.lib
│   │   ├── advance_software_protect_m7.lib
│   │   ├── advance_software_protect_m7_dp.lib
│   │   ├── advance_software_protect_m7_sp.lib
│   │   ├── advance_software_protect_m85.lib
│   │   ├── advance_software_protect_sc000.lib
│   │   ├── advance_software_protect_sc300.lib
│   │   ├── advance_software_protect_v81mml_dsp_dp_mve_fp.lib
│   │   ├── advance_software_protect_v8m_bl.lib
│   │   ├── advance_software_protect_v8m_mml.lib
│   │   ├── advance_software_protect_v8m_mml_dp.lib
│   │   ├── advance_software_protect_v8m_mml_dsp.lib
│   │   ├── advance_software_protect_v8m_mml_dsp_dp.lib
│   │   ├── advance_software_protect_v8m_mml_dsp_sp.lib
│   │   ├── advance_software_protect_v8m_mml_sp.lib
│   │   └── devices_list
│   └── mdk_demo /* MDK demo */
│   └── stm32f103rfc6
│   ├── Core
│   ├── Drivers
│   ├── EWARM
│   ├── MDK-ARM
│   └── stm32f103rfc6.ioc
├── sample /* PowerWriter series products, sample project */
│   ├── STM32F103rf_ecdsa.pkg /* Encryption library using ECDSA signature algorithm example project */
│   ├── STM32F103rf_matrix.pkg /* Encryption library using matrix signature algorithm sample project */
│   ├── ecdsa /* ICWKEY project, as well as exporting public key information */
│   │   ├── SafeLic_53ECCC98.uprj /* ICWKEY example project */
│   │   ├── password.txt /* ICWKEY example project password information */
│   │   ├── cortex_chipid_binding.c /* ICWKEY demo project, export source code information */
│   │   └── cortex_chipid_binding.h /* ICWKEY demo project, export header file information */
│   ├── matrix /* Use Matrix to export the sample project */
│   │   ├── DebugConfig
│   │   │   └── STM32F10xSample_STM32F103RG_1.0.0.dbgconf
│   │   ├── Listings
│   │   ├── Objects
│   │   ├── UidSample.uvguix.CSHSOFT
│   │   ├── UidSample.uvoptx
│   │   ├── UidSample.uvprojx
│   │   ├── cortex_chipid_binding.c
│   │   ├── cortex_chipid_binding.h
│   │   ├── main.c
│   │   └── startup_stm32f10x_hd.s
│   └── password.txt /* PowerWriter example project password description */
└── tool
├── linux
│   └── advance_software_protect /* Encryption library Linux command line */
├── macos
│   └── advance_software_protect /* Encryption library Macos command line */
└── windows
└── advance_software_protect.exe /* Encryption library Windows command line */
caution

In the actual project, please do not modify any definitions in advance_software_protect_base.h and advance_software_protect_lib.h, which are the base of the encryption library.

5.5.2.4:Test environment

  • The target test board uses the STM32F103RFT6 core board as the test board, if other system boards are used, refer to the environment build and configure.
  • The target board uses the HSI clock source with a maximum system clock of 64Mhz.
  • Use the PC13 LED as a system indicator.
  • Use USART 1 (PA9, PA10) as the log printing interface.
  • Enable SWD debugging (PA13, PA14).
  • BOOT0 pull-down resistor to GND to boot from Main Flash.
  • The debugging tool, serial port tool, and programming tool are all Power Writer, and the ECDSA signature algorithm uses ICWKEY.
note

The current library can be used on all ARM Cortex-M chips, but it is recommended to use it on projects with Flash >128K Bytes, SRAM >48K, and enough memory left.

5.5.3:Project Configuration

5.5.3.1:MDK Project Configuration

5.5.3.1.1:Prepare

In accordance with the requirements of the Demo, prepare the LED indicator, currently using PC13 as the indicator for verification, turn on the SWD debugging interface, and configure USART1, configure the system clock to 64Mhz, and generate the Demo code after the preparation is complete, as shown in the figure below.

image-20250110112625465

image-20250110112810711

image-20250110112837043

image-20250110112932329

5.5.3.1.2:Resizing the stack

The crypto library will use dynamic memory allocation to allocate internal resources. When performing signature operations, due to the deeper call layers, proper stack size adjustment may also be required. The stack configuration file for the current project is startup_stm32f103xg.s, as shown below:

Stack_Size      EQU     0x400           ;The current stack space size, if not enough, can be increased appropriately.

AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp

; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size EQU 0x2000 ;The heap size of the current project is set to 0x2000 here

AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit

PRESERVE8
THUMB
caution

Cryptographic libraries using ECDSA signatures require a minimum of 5K of dynamic memory, in addition to the cryptographic libraries themselves, at least 6K reserved, you need to use the actual project heap configuration on the basis of the heap allocation of the additional 6K or more, if you use the operating system, you need to configure the library's memory management interface callbacks, see the subsequent chapters for details.

5.5.3.1.3:Adjustment flash

The crypto library will use part of the SRAM as a code execution buffer, so the project will need to make additional adjustments to the original, by turning down the actual Flash usage and adjusting the SRAM as follows:

tip

The purpose of adjusting the resources is to use part of the SRAM (currently 4K) as the encryption library buffer, and at the same time, the corresponding FLASH capacity needs to be reduced by the corresponding capacity for storing encrypted data, and the overall code space remains unchanged after the adjustment, and the memory is reduced.

#Adjust the Flash / SRAM configuration information before modification                              
IROM1:0x08000000 0xC0000 SRAM:0x20000000 0x18000

#Adjusted Flash / SRAM configuration information
IROM1:0x08000000 0xC0000 SRAM:0x20000000 0x17000
IROM2:0x20017000 0X1000

Pre-adjustment:

image-20250110134114284

Adjusted:

image-20250110134036812

Next, let's go back to Project Settings, switch to Linker Settings, uncheck the 'Use Memory Layout from Target Dialog' option, use a custom Scatter File, and click on the Edit button to enter the editing mode, as shown below:

image-20250110134742280

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x000BF000 { ; load region size_region
ER_IROM1 0x08000000 0x000BF000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00017000 { ; RW data
.ANY (+RW +ZI)
}
}

LR_IROM2 0x20017000 0x00001000 {
ER_IROM2 0x20017000 0x00001000 { ; load address = execution address
.ANY (+RO)
.ANY (.advance_software_protect_encryption_section) ; Append the current definition after LR_ROM2.
}
}

5.5.3.1.4:Add lib and source

In the lib directory of the package, find the corresponding base source code and related library files, and add them to the project, as shown in the following figure:

image-20250110153831028

After the addition is complete, the project structure is shown below:

image-20250110153912982

caution

Please add the corresponding library file according to the kernel version of the chip. The current example project is STM32F103RFT6 and the kernel is ARM Cortex M3 kernel.

5.5.3.1.5:Add command line

MDK compiled firmware for the original firmware, such as not processed by the command line, will lead to the relevant features are not open, and in the actual product, may not run, because, we need to add the command line tool, to the project configuration, as follows:

  • RUN #1, we output the axf file to a bin file, using the command:

    fromelf --bin -o "$L@L.bin" "#L"
  • RUN#2, we take the bin file output from the first step and generate the final firmware for production, using the command:

    ../../../../tool/windows/advance_software_protect.exe 
    -s 0x08000000 #-s indicates that the starting address of the Flash firmware is 0x08000000
    -k 0xfb90ffcb9b62c2e6 #-k indicates the encryption password, which is 8 bytes in length and must correspond to the source code. The password for each project must be changed to avoid using the same password as other projects.
    -i ./stm32f103rfc6/stm32f103rfc6.bin/ER_IROM1 #-i Import the main Flash firmware
    -r ./stm32f103rfc6/stm32f103rfc6.bin/ER_IROM2 #-r Import SRAM firmware

image-20250110154444006

caution

The -k, or encryption password, for each item must be randomly generated in 8 bytes, avoiding common passwords for better security.

For more on command line usage, see the Command Line section.

5.5.3.1.6:Other settings

To avoid compilation problems, it is recommended to enable C99 mode as shown below:

image-20250110155403040

The current debugger uses the debugger that comes with PowerWriter, for usage, please refer to: 3.1.7:Debugger Tutorials | PowerWriter Docs, in the In the actual project development, because you do not need to download and verify the MDK, you need to turn off the debugger's programming and verification functions, as shown in the following figure:

image-20250110155834520

image-20250110155623028

caution

After adding the encryption library, the project can still be debugged, but the download is done with PowerWriter for download and verification, and MDK is not done for download and verification, in order to verify the final result.

When adding encrypted libraries, there may exist after the code is run, close the debugger and turn on read protection, will lead to debugging function disconnected, because, we also need to do two project configurations, one for debugging, one for release, the difference between the two is that the release version of the extra RELEASE macro definition, as shown in the figure below:

image-20250110160746781

image-20250110160810458

5.5.3.2:GCC Project Configuration

5.5.3.2.1:Prepare

In accordance with the requirements of the Demo, prepare the LED indicator, currently using PC13 as the indicator for verification, turn on the SWD debugging interface, and configure USART1, configure the system clock to 64Mhz, and generate the Demo code after the preparation is complete, as shown in the following figure.

image-20250110112625465

image-20250110112810711

image-20250115133833741

image-20250115133904923

5.5.3.1.2:Resizing the stack

The cryptographic library will use dynamic memory allocation to allocate internal resources. When performing signature operations, due to the deeper call layers, appropriate stack size adjustment may also be required. The stack configuration file for the current project is STM32F103RFTX_FLASH.ld, as shown below:

_Min_Heap_Size = 0x4000 ; /* required amount of heap */
_Min_Stack_Size = 0x1000 ; /* required amount of stack */
caution

Cryptographic libraries using ECDSA signatures require a minimum of 5K of dynamic memory, in addition to the cryptographic libraries themselves, at least 6K reserved, you need to use the actual project heap configuration on the basis of the heap allocation of the additional 6K or more, if you use the operating system, you need to configure the library's memory management interface callbacks, see the subsequent chapters for details.

5.5.3.1.3:Adjustment storage

The crypto library will use part of the SRAM as a code execution buffer, so the project will need to make additional adjustments to the original by turning down the actual Flash usage and adjusting the SRAM as follows:

tip

The purpose of adjusting the resources is to use part of the SRAM (currently 4K) as the encryption library buffer, while the corresponding FLASH capacity needs to be reduced by the corresponding capacity for storing encrypted data, and the overall code space remains unchanged after the adjustment, with a reduction in memory.

#Flash/SRAM configuration information before adjustment                                     
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 768K
}


#Adjusted Flash/SRAM configuration information
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 92K /* SRAM from 96K to 92K, 4K for cryptographic code execution */
ENCRYPT_CODE (xr) : ORIGIN = 0x20017000 LENGTH = 4K /* 4K for cryptographic code execution */
VECTOR (rx) : ORIGIN = 0x08000000 LENGTH = 0x700 /* interrupt vector table */
SN (rx) : ORIGIN = 0x08000700 LENGTH = 4 /* For storing serial numbers */
LIC (rx) : ORIGIN = 0x08000704 LENGTH = 0xFC /* For storing PowerWriter signatures */
FLASH (rx) : ORIGIN = 0x8000800, LENGTH = 762K /* 768K (0xC0000) */
}


/* Sections */
SECTIONS
{

/* ENCRYPT CODE */
.advance_software_protect_encryption_section :
{
. = ALIGN(4);
KEEP(*(.advance_software_protect_encryption_section))
} > ENCRYPT_CODE


/* SN */
.advance_software_protect_sn :
{
. = ALIGN(4);
KEEP(*(.advance_software_protect_sn))
} > SN

/*
ECDSA Signature
*/
.asp_signature_license :
{
. = ALIGN(4);
KEEP(*(.asp_signature_license))
} > LIC



/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >VECTOR
/*****/
//....more
}

5.5.3.1.4:Add lib & source

In the lib directory of the package, find the corresponding base source code and related library files, and add them to the project, as shown in the following figure:

image-20250115134854011

GIF 2025-1-15 13-49-38

GIF 2025-1-15 13-51-20

After the addition is complete, the project structure is shown below:

image-20250115135211142

caution

Please add the corresponding library file according to the kernel version of the chip. The current example project is STM32F103RFT6 and the kernel is ARM Cortex M3 kernel.

5.5.3.1.5:Add CLI

Compiled firmware for the original firmware, such as not through the command line for processing, will lead to the relevant functions are not opened, and in the actual product, may not run, because, we need to add the command line tool, to the project configuration, as follows:

image-20250115135430886

  • In the post-build steps, we take the hex file output from the first step and generate the final production firmware using the command:

    ../advance_software_protect.exe -i ./stm32f103rfc6.hex -r 0x20017000
    -s 0x08000000 #-s Indicates that the starting address of the flash firmware is 0x08000000
    -k 0xfb90ffcb9b62c2e6 #-k Indicates the encrypted password, the length is 8 bytes, must correspond to the source code, must be changed for each project, to avoid using the same password as other people's projects
    -i ./stm32f103rfc6.hex #-i Import Master Flash Firmware
    -r 0x20017000 #-r You can specify that ENCRYPT_CODE be extracted from the hex file as the encryption code interval.
caution

The -k, or encryption password, for each project must be randomly generated in 8 bytes, avoiding common passwords for better security.

For more on command line usage, see the Command Line section.

5.5.3.1.6:Other

In order for the compiled output firmware to be processed by the command line, we need to output the hex file format, set as follows

image-20250115135707841

The current debugger uses PowerWriter 's own debugger, for more information on how to use it,see 3.1.7:Debugger Tutorial | PowerWriter Docs,In the actual project development, because the IDE does not need to download and verify, so you need to turn off the debugger's programming and verification functions, as shown in the following figure:

image-20250115135922084

caution

After adding the encryption library, you can still debug the project, but the download is done with PowerWriter and the IDE does not do the download and verification in order to verify the final result.

When adding encrypted libraries, there may exist after the code is run, close the debugger and turn on read protection, which will lead to the debugging function disconnected, because, we also need to do two project configurations, one for debugging and one for release, the difference between the two is that the release version of the extra RELEASE macro definition, as shown in the figure below:

image-20250115140050505

In addition, since we define new nodes at resource allocation time, compilation may generate warnings, and we remove specific warnings in GCC Linker as shown below:

image-20250115140224757

After completing all the settings, we compile the project and will see the following output as shown below:

image-20250115140312306

5.5.3.3:IAR Project Configuration

5.5.3.3.1:Prepare

In accordance with the requirements of the Demo, prepare the LED indicator, currently using PC13 as the indicator for verification, turn on the SWD debugging interface, and configure USART1, configure the system clock to 64Mhz, and generate the Demo code after the preparation is complete, as shown in the following figure.

image-20250110112625465

image-20250110112810711

image-20250116111707295

image-20250116111736247

5.5.3.3.2:Adjustment storage

The encryption library will use dynamic memory allocation to allocate internal resources. When performing signature operations, due to the deeper call layers, you may also need to adjust the stack size appropriately, while adjusting the Flash and SRAM are reduced by 4K to store the encryption code, and by the way, the stack size will be adjusted to prevent the stack from overflowing when the encryption library is running, as shown below:

GIF 2025-1-16 11-21-09

caution

Cryptographic libraries using ECDSA signatures require a minimum of 5K of dynamic memory, in addition to the cryptographic libraries themselves, at least 6K reserved, you need to use the actual project heap configuration on the basis of the heap allocation of the additional 6K or more, if you use the operating system, you need to configure the library's memory management interface callbacks, see the subsequent chapters for details.

5.5.3.3.3:Adjustment storage

The crypto library will use part of the SRAM as a code execution buffer, so the project will need to make additional adjustments to the original by turning down the actual Flash usage and adjusting the SRAM as follows:

image-20250116112647643

tip

The purpose of adjusting the resources is to use part of the SRAM (currently 4K) as the encryption library buffer, while the corresponding FLASH capacity needs to be reduced by the corresponding capacity for storing encrypted data, and the overall code space remains unchanged after the adjustment, with a reduction in memory.

/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x08000000;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__ = 0x080BF000;
define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__ = 0x20017000;

/* Add ENCRYPT_CODE zone definition. */
define symbol __ICFEDIT_region_RAMCODE_start__ = 0x20017000;
define symbol __ICFEDIT_region_RAMCODE_end__ = 0x20018000;

/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x1000;
define symbol __ICFEDIT_size_heap__ = 0x4000;
/**** End of ICF editor section. ###ICF###*/


define memory mem with size = 4G;
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__];

/* Add ENCRYPT_CODE section */
define region RAMCODE_region = mem:[from __ICFEDIT_region_RAMCODE_start__ to __ICFEDIT_region_RAMCODE_end__];

define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };

initialize by copy { readwrite };
do not initialize { section .noinit };

place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };

place in ROM_region { readonly };
place in RAM_region { readwrite,block CSTACK, block HEAP };
/* Set the encryption code node to advance_software_protect_encryption_section !!!!!*/
place in ENCRYPT_CODE_region { readonly section .advance_software_protect_encryption_section};

5.5.3.3.4:Add lib & source

In the lib directory of the package, find the corresponding base source code and related library files, and add them to the project, as shown in the following figure:

image-20250116112903692

image-20250116112935699

image-20250116113002281

caution

Please add the corresponding library file according to the kernel version of the chip. The current example project is STM32F103RFT6 and the kernel is ARM Cortex M3 kernel.

5.5.3.3.5:Add cli

The compiled firmware is the original firmware, such as not processed by the command line, it will lead to the relevant functions are not opened, and in the actual product, may not be able to run, because, we need to add the command line tool, to the project configuration, as shown below:

image-20250116113103073

  • In the post-build steps, we take the hex file output from the first step and generate the final production firmware using the command:

    "./../../../../tool/windows/advance_software_protect.exe" -k 0xfb90ffcb9b62c2e6 -i ./stm32f103rfc6-Release/Exe/stm32f103rfc6.hex -r 0x20017004 && echo > "$BUILD_FILES_DIR$/.postbuild"
    -s #-s Indicates that the starting address of the flash firmware is 0x08000000
    -k #-k Indicates the encrypted password, the length is 8 bytes, must correspond to the source code, must be changed for each project, to avoid using the same password as other people's projects
    -i #-i Import Master Flash Firmware
    -r #-r You can specify that ENCRYPT_CODE be extracted from the hex file as the encryption code interval.
caution

The -k, or encryption password, for each project must be randomly generated in 8 bytes, avoiding common passwords for better security.

For more on command line usage, see the Command Line section.

caution

-r parameter in the IAR compilation, although we set the encryption code node to a specified address, such as 0x20001700, but the actual address output by the compiler may not be 0x20001700, such as 0x20001704, you can load the firmware through the PowerWriter to check the segmentation address, how much, and at the same time, the loading address and command line parameters in the code should also be modified simultaneously, according to the final output file of the compiler shall prevail. address in the code, as well as command line parameters, should also be synchronized to modify, according to the compiler's final output file shall prevail.

5.5.3.3.6:Other

In order for the compiled output firmware to be processed by the command line, we need to output the hex file format, set as follows

image-20250116113618874

The current debugger uses PowerWriter 's own debugger, for more information on how to use it,see 3.1.7:Debugger Tutorial | PowerWriter Docs,In the actual project development, because the IDE does not need to download and verify, When debugging, use Debug without download mode as shown below:

image-20250116113723060

caution

After adding the encryption library, you can still debug the project, but the download is done with PowerWriter and the IDE does not do the download and verification in order to verify the final result.

When adding encrypted libraries, there may exist after the code is run, close the debugger and turn on read protection, which will lead to the debugging function disconnected, because, we also need to do two project configurations, one for debugging and one for release, the difference between the two is that the release version of the extra RELEASE macro definition, as shown in the figure below:

GIF 2025-1-16 11-40-23

After completing all the settings, we compile the project and will see the following output as shown below:

GIF 2025-1-16 11-42-38

5.5.4:Configuration and usage tutorials

With the content of section 5.5.3, we have now completed the configuration of the mainstream IDE development environment. In this section, we will introduce in detail the functions of the encryption library and how to use it.

5.5.4.1:System Configuration

Before the library can be executed, we need to initialize the library and add the sample code as shown below.


/*
* IMPORTANT:
* Do not encrypt the parent function () that contains the
* advance_software_protect_encryption_code_loader!
*/
void advance_software_protect_init()
{
/* initial */
advance_software_protect_set_mem_calloc_free_callback(calloc_mem_func, free_mem_func);
advance_software_protect_set_logger_callback(advance_software_protect_log_output);
/* show informations */
advance_software_protect_rights();
#ifdef ADVANCE_SOFTWARE_PROTECT_CODE_ENCRYPT_ENABLE
/* encrypt code loader */
advance_software_protect_encryption_code_loader(ADV_ENCRYPT_CODE_SEGMENT_ADDR, ADV_DATA_ENCRYPT_KEY, advance_software_protect_encrypt_code_sample);
#endif
}

int main(void)
{
/* Initialize the encryption library */
advance_software_protect_init();

while (1){
}
/* USER CODE END 3 */
}

5.5.4.1.1:Memory Configuration

Some of the function modules inside the cryptographic library need to apply for memory dynamically. The default memory interfaces inside the library are calloc and free, but there may be memory management problems when using RTOS projects, and it is also not conducive to debugging and analysis, and data observation, so we provide a memory management allocation interface, and the prototype of the function is as follows:

bool advance_software_protect_set_mem_calloc_free_callback(calloc_func_cb calloc_func, free_func_cb free_func);

The configuration reference is shown below:

/* memory alloc & free */
void *calloc_mem_func(size_t count, size_t size)
{
return calloc(count, size);
}
void free_mem_func(void *blk_obj)
{
free(blk_obj);
}


void advance_software_protect_init()
{
/* initial */
advance_software_protect_set_mem_calloc_free_callback(calloc_mem_func, free_mem_func);
}
tip

This interface is not required if the RTOS is not used, when the operating system is used, please note that it is necessary to perform a memset of 0 on the requested memory, see OS Task Heap Allocation for details, Note that if the memory interface is registered, memory allocation and memory release must occur in pairs.

5.5.4.1.2:Logging Interface

In order to facilitate the developer to analyse and observe the data, the encryption library provides a standard logging function module, when the encryption library is running, a part of the logging information will also be output internally, so as to facilitate the debugging of the project, the function prototype is:

bool advance_software_protect_set_logger_callback(const advance_software_logger_callback cb);

The configuration reference is shown below:

/* message callback */
#ifdef STM32F103xG
extern UART_HandleTypeDef huart1;
#endif
static void advance_software_protect_log_output(const char *msg)
{
#ifdef STM32F103xG
HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), strlen(msg));
#endif
}

void advance_software_protect_init()
{
/* initial */
advance_software_protect_set_logger_callback(advance_software_protect_log_output);
//advance_software_protect_set_logger_callback(0); //If the parameter is 0, the resources will be automatically released and the function will be closed internally.
}

The standard way of calling the output log is similar to the printf function, the difference is that for the current logging module, one parameter is the log type, see the definition of E_ADV_SOFWARE_PROTECT_LOG_TYPE, as shown below:

/* Log type*/
typedef enum E_ADV_SOFWARE_PROTECT_LOG_TYPE
{
ADV_BLANK,
ADV_INFO,
ADV_DEBUG,
ADV_WARN,
ADV_EXCEPTION,
ADV_ERROR,
ADV_MAX
} E_ADV_SOFWARE_PROTECT_LOG_TYPE;

advance_software_protect_logger(type, format, ...)

//Please refer to the examples in advance_software_protect_samples.c for usage instructions

5.5.4.1.3:Encrypt function loader

Before making any function call of the crypto library, or a user-defined crypto function call, the crypto function must be initialised, otherwise an exception will be thrown, and the function prototype is declared as follows:

void advance_software_protect_encryption_code_loader(uint32_t load_address, uint64_t key, CodeLoaderCallback callback);

load_address: indicates the load address, which must be exactly the same as the address of the .advance_software_protect_encryption_section node, otherwise an exception will be thrown, see Adjusting Project Resource Allocation for details.

key: password for data encryption, must be the same as the command line pass parameter, otherwise it will lead to loading failure, see add to command line for details.

callback: load callback function, used to detect loading information.

A typical initialisation of a cryptographic function is shown below:

/* message callback */
/* segment address */
#ifdef ADVANCE_SOFTWARE_PROTECT_CODE_ENCRYPT_ENABLE
#define ADV_ENCRYPT_CODE_SEGMENT_ADDR (0x20017000) /* refer to advance_software_protect_encryption_section */
#endif


/* key for data encrypt */
#if (defined ADVANCE_SOFTWARE_PROTECT_FLASH_VERIFY_ENABLE) || (defined ADVANCE_SOFTWARE_PROTECT_CODE_ENCRYPT_ENABLE)
#define ADV_DATA_ENCRYPT_KEY (0xfb90ffcb9b62c2e6) /* 64 bit*/
#endif


/* for ram code loader */
void advance_software_protect_encrypt_code_sample(E_ENCRYPT_CODE_EVENT event)
{
switch (event)
{

/* Development mode running */
case EncryptCodeUnprocessed:
advance_software_protect_logger(ADV_EXCEPTION, "Please process the firmware using the command line, then program it using PowerWriter before trying again....\r\n");
while (1)
{
} /* suspended */

/* Loading RAM code succeeded */
case EncryptCodeOk:
advance_software_protect_logger(ADV_DEBUG, "(Success) Loaded successfully ...\r\n");
break;

/* Failed to load RAM code */
case EncryptCodeError:
advance_software_protect_logger(ADV_ERROR, "(Failure !!!!) Loading failed ...\r\n");
/* reset system */
advance_software_protect_system_reset();

break;
}
}


void advance_software_protect_init()
{
/* initial */
advance_software_protect_encryption_code_loader(ADV_ENCRYPT_CODE_SEGMENT_ADDR, ADV_DATA_ENCRYPT_KEY, advance_software_protect_encrypt_code_sample);
}
caution

Encryption code initialization is the most important function, it must be initialized before all encryption functions are called, otherwise it will lead to an exception, in addition, the load address and the password for data encryption need to be the same as the password for the command line, and after adding the encryption library, you need to use PowerWriter to carry out programming and then debugging, see Debugging and Verification for more details.

5.5.4.2:Object Monitoring

The object monitor can monitor whether any object has been modified abnormally, for example, through dynamic memory modification by the debugger, or through code patches that modify default initialization values, or internal Library code, to prevent memory modification by the Application program, intentionally or unintentionally modifying memory space belonging to other programs, with the aim of bypassing some of the Library's Object Watcher provides monitoring of single objects (e.g. structure variables, generic built-in types) and other data. If the data is modified by an external application, a periodic check will generate an error, and then the error will be handled according to the corresponding error.

5.5.4.2.1:Single Object Monitor

Define format:

// Test structure, the types inside can be of any type, and no alignment is required
typedef struct S_Object
{
uint32_t m_sample1;
uint32_t m_sample2;
uint32_t m_sample3;
uint8_t * m_ptr8;
}S_Object;

/* Declare a watcher object type, the exported type will be S_Object_Watcher */
S_ADV_SOFTWARE_PROTECT_OBJECTWATCH_Watcher(S_Object)

/* Define the managed object */
S_Object_Watcher m_Object;

API:

// Check if the object is normal
bool advance_software_protect_objectwatch_check(object,type); // where object is a pointer to an object, and type is the type of the managed object
bool advance_software_protect_objectwatch_update(object,type); // where object is a pointer to an object, and type is the managed object type

Demo code:

/*
* Single object monitoring demonstration
*/
void advance_software_protect_single_object_watcher()
{
#ifdef ADVANCE_SOFTWARE_PROTECT_OBJECTWATCH_ENABLE
advance_software_protect_logger(ADV_DEBUG," ---------------Single Object Watcher Simple Start ----------\r\n");

/* The first call to the check function will perform initialization */
bool m_object_check_result = advance_software_protect_objectwatch_check(&m_Object,S_Object_Watcher);

/* It will definitely return successfully */
if(m_object_check_result){
advance_software_protect_logger(ADV_DEBUG," m_Object check passed at first time\r\n");
}else{
advance_software_protect_logger(ADV_ERROR," m_Object check failed at first time, Never execute at first time!\r\n");
}


/*
* Simulate Object being modified: such as by a debugger, by another program, memory patch, or for other reasons, but externally it is not clear that this Object has a validation function,
* so the validation information of the Object has not been updated, which will lead to periodic Check triggering exceptions.
*/
m_Object.m_Object.m_sample1 = 1;
m_Object.m_Object.m_sample2 = 2;
m_Object.m_Object.m_sample3 = 3;
m_Object.m_Object.m_ptr8 = (uint8_t *)0x12345678;

m_object_check_result = advance_software_protect_objectwatch_check(&m_Object,S_Object_Watcher);

/* The result will trigger an exception */
if(m_object_check_result){
advance_software_protect_logger(ADV_DEBUG," m_Object has been modified but there is no update validation, which is not performed here\r\n");
}else{
advance_software_protect_logger(ADV_ERROR," m_Object will fail if it is modified without updating the checksum value!\r\n");
}


/* Update the validation value of the object and then check it, it will pass. In normal project development, after entrusting the object, update the validation information of the object after updating the members in the object, which will not trigger errors in periodic checks */
advance_software_protect_objectwatch_update(&m_Object,S_Object_Watcher); /* update checksum */

m_object_check_result = advance_software_protect_objectwatch_check(&m_Object,S_Object_Watcher);

/* will be passed through */
if(m_object_check_result){
advance_software_protect_logger(ADV_DEBUG," m_Object had modified & updated, will be passed !\r\n");
}else{
advance_software_protect_logger(ADV_ERROR," m_Object had modified & updated, never failed !\r\n");
}

advance_software_protect_logger(ADV_DEBUG," ---------------Single Object Watcher Simple End----------\r\n");
#endif
}
note
  • Object Watcher only checks the object entity data itself, it will not include the object pointed to by the pointer in the structure in the check, if you need to monitor the pointer of the pointer, please use the dynamic object monitoring mode.

  • This sample project uses the standard crc32 calibration algorithm, the actual project can be replaced with a set of its own calibration method. The interface of checksum method is in advance_software_protect_port.c (advance_software_protect_crc32).

5.5.4.2.2:Dynamic Object Monitor

A single object monitor can provide a simple monitoring target, which is easy to use for systems of low complexity. For complex systems, in order to reduce the difficulty of using the in-memory monitoring functionality and to minimize modifications to the existing code, the object monitoring hosting functionality is provided, which includes the following common functions: adding an object to the hosting pool, deleting an object from the hosting pool, updating an object in the hosting pool, and so on, update an object in the pool, update all objects in the pool, and so on.

API List:

/* Object Watcher Exception type */
typedef enum E_ObjectWatchException
{
ObjectParamError = 0, /* Parameter error */
ObjectMemoryError, /* Memory error */
ObjectAlreadyExits, /* The current object is already in the monitoring list */
ObjectModified, /* The object was accessed abnormally */
} E_ObjectWatchException;

/* callback for object watcher */
typedef void (*ObjectWatcherCallback)(E_ObjectWatchException exception,void * object);

// Set up monitoring callback
void advance_software_protect_objectwatch_list_set_callback(ObjectWatcherCallback eventCallback);
// Check if an object is under management, the parameter is a pointer to the object
bool advance_software_protect_objectwatch_list_find(void * object_ptr);
// Entrust an object with parameters: object pointer, size of the object, and whether to delete the object entity when it is removed from entrustment (applicable only to dynamically allocated objects)
bool advance_software_protect_objectwatch_list_add(void * object_ptr, size_t object_size,bool del_object_when_remove);
//Get the number of entrusted objects
size_t advance_software_protect_objectwatch_list_size(void);
//Deletes and moves an object in a managed queue with a pointer to the object.
bool advance_software_protect_objectwatch_list_remove(void * object_ptr);
//Empty all managed objects
void advance_software_protect_objectwatch_list_reset(void);
//Checks whether an object has been modified, with a pointer to the object.
bool advance_software_protect_objectwatch_list_check(void * object_ptr);
//Checks all objects in the managed object, the return value is: the number of checks passed, (note: if the event callback interface is registered, details of failures will be printed via the event interface)
size_t advance_software_protect_objectwatch_list_check_all(void);
//Updates the checksum value of an object with a pointer to the object.
bool advance_software_protect_objectwatch_list_update(void * object_ptr);
//Update checksum values for all objects
void advance_software_protect_objectwatch_list_update_all(void);

Object Watcher List demo code:

/* Define objects for dynamic hosting */
S_Object m_object;
S_A m_aType;
S_B m_bType;
S_Class m_class;

/* Event callback interface for dynamic hosting */
void advance_software_protect_dynamic_muti_object_watcher_callback(E_ObjectWatchException excep, void* param)
{
switch(excep)
{
/* The parameter error is a general error indicating a problem with the parameter */
case ObjectParamError:
advance_software_protect_logger(ADV_WARN, " Error param ...\r\n");
break;
/* Indicates that an object has been modified, please handle the exception in time to prevent the software from being cracked. param points to the first address of the object */
case ObjectModified:
advance_software_protect_logger(ADV_ERROR, " [0x%08X] Object was modified!\r\n", (uint32_t)param);
break;
/* The object already exists in the managed list, prompted when repeated additions are made */
case ObjectAlreadyExits:
advance_software_protect_logger(ADV_WARN, " [0x%08X] Object already exist!\r\n", (uint32_t)param);
break;
/* Insufficient memory, indicating that Heap memory allocation is too small */
case ObjectMemoryError:
advance_software_protect_logger(ADV_ERROR, " Memory space is used up!\r\n");
break;
}
}

void advance_software_protect_dynamic_muti_object_watcher()
{


advance_software_protect_logger(ADV_DEBUG, " ---------- Dynamic Muti-Object Watcher Simple Start ----------\r\n");

/* Set event callback interface: optional, set callback interface can get more detailed information */

advance_software_protect_logger(ADV_DEBUG, " Set ObjectWatcher Event callback...\r\n");
advance_software_protect_objectwatch_list_set_callback(advance_software_protect_dynamic_muti_object_watcher_callback);


/* Dynamically add objects to the managed list */
advance_software_protect_objectwatch_list_add(&m_object, sizeof(m_object), false);
advance_software_protect_objectwatch_list_add(&m_aType, sizeof(m_aType), false);
advance_software_protect_objectwatch_list_add(&m_bType, sizeof(m_bType), false);
advance_software_protect_objectwatch_list_add(&m_class, sizeof(m_class), false);
// Repeated additions will throw an exception (ObjectAlreadyExits)
advance_software_protect_objectwatch_list_add(&m_class, sizeof(m_class), false);
// Parameter error exception (ObjectParamError)
advance_software_protect_objectwatch_list_add(0, sizeof(m_class), false);

// Dynamically add multiple objects and automatically host memory releases
for(size_t i = 0; i < 200; i++)
{
S_Class* m_new_obj = (S_Class*)malloc(sizeof(S_Class));
if(m_new_obj)
{
//When removing the hosting, also remove the object entity, the last parameter is set to true
advance_software_protect_objectwatch_list_add(m_new_obj, sizeof(S_Class), true);
}
else
{
// Insufficient memory
//...
break;
}
}

/* Find out if an object is in escrow */
bool m_find = advance_software_protect_objectwatch_list_find(&m_aType);
if(m_find)
{
advance_software_protect_logger(ADV_INFO, " m_aType exist..\r\n");
}
else
{
advance_software_protect_logger(ADV_INFO, " m_aType not exist..\r\n");
}



/* Counting the number of hosted objects */
advance_software_protect_logger(ADV_DEBUG, " Total %d objects in watching..\r\n", advance_software_protect_objectwatch_list_size());

/* Verification of individual objects */
bool m_check = advance_software_protect_objectwatch_list_check(&m_class);
if(m_check)
{
advance_software_protect_logger(ADV_DEBUG, " m_class check passed ..\r\n");
}
else
{
//failed...
//...
}

/* Verify all objects */
m_class._data_.m_a.m_data.m_sample1 ^= 0xAB; //Simulates externally modified memory

size_t m_passed_count = advance_software_protect_objectwatch_list_check_all(); // Failed objects will print details via the event callback interface
size_t m_total_count = advance_software_protect_objectwatch_list_size();
// Determine if the sum of the passed and total is equal. to determine whether it failed or succeeded.
if(m_passed_count == m_total_count)
{
advance_software_protect_logger(ADV_DEBUG, " All objects check passed ..\r\n");
}
else
{
advance_software_protect_logger(ADV_DEBUG, " Passed: %d, Failed : %d ..\r\n", m_passed_count, m_total_count);
}

/* Update individual object checksums, normal code */
if(advance_software_protect_objectwatch_list_update(&m_class))
{
advance_software_protect_logger(ADV_DEBUG, " m_class update ok ..\r\n");
}
else
{
advance_software_protect_logger(ADV_DEBUG, " m_class update failed ..\r\n");
}
advance_software_protect_logger(); //After checking them all again, they will be passed


/* Delete individual objects */
bool m_del = advance_software_protect_objectwatch_list_remove(&m_bType);
if(m_del)
{
advance_software_protect_logger(ADV_INFO, " m_bType removed, left %d object in watching..\r\n", advance_software_protect_objectwatch_list_size());
}
else
{
advance_software_protect_logger(ADV_INFO, " m_bType not exist!..\r\n");
}
/* Update all objects */
advance_software_protect_objectwatch_list_update_all();
advance_software_protect_logger(ADV_INFO, " All objects updated...\r\n");

/* Delete all objects */
advance_software_protect_objectwatch_list_reset();
advance_software_protect_logger(ADV_INFO, " All objects removed, left %d object in watching..\r\n", advance_software_protect_objectwatch_list_size());

advance_software_protect_logger(ADV_DEBUG, " ---------------Dynamic Muti-Object Watcher Simple End----------\r\n");

}

5.5.4.2.3:Notice

The purpose of Object Watcher is to monitor whether the memory has been modified abnormally.

  • Monitor an object using a single object wrapper macro, or dynamically add monitored objects using managed methods.

  • setter method: The project code updates the internal properties of the object while updating the object's checksum value, using the update method.

  • getter method: when the project code accesses the internal properties of the object, call the check method to check whether the data has been modified abnormally, so as to determine whether there is any abnormal access, such as external use of the debugger to set a breakpoint directly, forcibly stop the run, modify the key memory data, the application layer has modified the data of the protocol layer, the Object Watcher can be used for this purpose.

  • About how to handle pointers in objects: If you want to monitor the objects pointed to by pointers inside the object entity, please tell the objects pointed to by pointers to be hosted in the monitor list again, and use the same procedure for multiple branches, as follows

struct A
{
int a;
};

struct B
{
int b;
struct A * sa;

}

struct A m_a;
struct B m_b =
{
.b = 10,
.sa = &m_a;
};

// When the m_b object is entrusted, how to entrust m_b.sa?

//Example:
advance_software_protect_objectwatch_list_add(&m_b, sizeof(m_b), false);
advance_software_protect_objectwatch_list_add(m_b.sa, sizeof(m_b.sa), false); // Pointer delegation can monitor object m_a again, or it can directly manage m_a

5.5.4.3:Firmware Signature

In order to prevent the chip firmware from being directly copied, binding or authenticating the firmware to a specific chip is a must. PowerWriter provides the Matrix firmware signature function, see 3.4 Matrix Example | PowerWriter Documentation Centre, as well as the ECDSA digital signature function, see 3.3 ECDSA Example | PowerWriter Documentation Centre for more details on how to use it. In the current encryption library, both signature methods are included, which is equivalent to the standard mode of PowerWriter, In the current encryption library, these two signature methods are already included, which is equivalent to the standard mode of PowerWriter. On the basis of the original encryption library, the signature verification code and UID reading code will be encrypted at the function level, and together with the firmware verification function and the object monitoring function, the firmware can be prevented from being decompiled, dynamically analyzed, and dynamically debugged to a fairly high degree. If you are not familiar with the use of these two signatures, it is recommended that you read the tutorial first, and then do the integration of cryptographic libraries.

Before we start, let's configure the public definitions of Matrix and ECDSA. In the lib/advance_software_protect_user.h user header file, we can find the following definitions

/* Random numbers for obfuscation must be adjusted in actual projects to obscure key information */
#define ASP_RAND_FACTOR (0x45623513)

/* Chip UID address */
#define UID_CHIP_ADDR (0x1FFFF7E8) /* Refer to the export header file cortex_chipid_binding.h 中 (UID_CHIP_ADDR_MIX ^ UID_CHIP_ADDR) */
#define UID_CHIP_SIZE (12) /* ref cortex_chipid_binding.h UID_CHIP_SIZE */
#define UID_KEYADDR_PLACEHOLDER_EN (1) /* Whether to enable signature information predefinition, if enabled, can be pre-allocated space in the corresponding address, it is recommended to enable the */

#define UID_KEYADDR_INNER_VAL (0x08002000) /* Signature storage address */
#ifdef ADVANCE_SOFTWARE_PROTECT_COMPILER_MDK
#define UID_KEYADDR_INNER ".ARM.__at_0x08002000" /* The address of the signature store is defined in MDK in the form of */
#elif defined ADVANCE_SOFTWARE_PROTECT_COMPILER_GCC
#define UID_KEYADDR_INNER ".asp_signature_license" /* Signature storage address in GCC segment, please do not modify this definition, please refer to the GCC configuration. */
#endif
caution

The above function definitions need to be adjusted according to the project configuration of PowerWriter or ICWKEY, please pay attention to the content of the notes, Matrix and ECDSA tutorials in the source code for a detailed introduction.

Implement the CID reading API and declare its functions as cryptographic functions:

lib/advance_software_protect_porting.c
/* for signature */
#ifdef ADVANCE_SOFTWARE_PROTECT_SIGNATURE_ENABLE
/*
* @brief Gets the chip ID of the current chip.
* @pram id_data : A buffer that stores ids
* size : id length
* @retval void
*/
ASP_ENCRYPT_F_DECL(void advance_software_protect_get_cid(uint32_t *id_addr, uint8_t size,uint32_t *id_data))
void advance_software_protect_get_cid(uint32_t *id_addr, uint8_t size, uint32_t *id_data)
{
// If the ID is discontinuous, you need to handle it yourself...
// offset 0x00,0x04,0x14 ( Such as STM32L0, STM32L1, STM32L4 etc)
#if ((defined STM32L0) || (defined STM32L1) || (defined STM32L4))
id_data[0] = *(id_addr + (0x00 / 4));
id_data[1] = *(id_addr + (0x04 / 4));
id_data[2] = *(id_addr + (0x14 / 4));
#else
// The ID is continuous...
for (int i = 0; i < (size / 4); i++){
*id_data++ = *id_addr++;
}
#endif
}
#endif

Add signature verification code:

#ifdef ADVANCE_SOFTWARE_PROTECT_SIGNATURE_ENABLE

void advance_software_protect_signature()
{
advance_software_protect_logger(ADV_DEBUG, " ---------------Product Signature start----------\r\n");
if (advance_software_protect_verify_authorization(&m_signature_config))
{
advance_software_protect_logger(ADV_DEBUG, " Chip signature verify passed...\r\n");
}
else
{
advance_software_protect_logger(ADV_ERROR, " Chip signature verify failed!!!!!\r\n");
}
advance_software_protect_logger(ADV_DEBUG, " ---------------Product Signature end----------\r\n");


}
#endif

ASP_ENCRYPT_F_DECL(void advance_software_protect_sample())
void advance_software_protect_sample()
{
/* chip signature */
#ifdef ADVANCE_SOFTWARE_PROTECT_SIGNATURE_ENABLE
advance_software_protect_signature();
#endif

}

caution

Key function for signature verification, recommended to be declared as a cryptographic function.

5.5.4.3.1:Using Matrix Signatures

Enable Matrix Signature Configuration:

lib/advance_software_protect_user.h
/* Signature */
#define ADVANCE_SOFTWARE_PROTECT_SIGNATURE_MATRIX /* using matrix as signature */
//#define ADVANCE_SOFTWARE_PROTECT_SIGNATURE_ECDSA /* using ecdsa as signature */

Adjustment of Matrix signature configuration:

image-20250111095505538

lib/advance_software_protect_user.h
#define UID_KEY_LENGTH (12)          /* refer to UID_KEY_LENGTH in cortex_chipid_binding.h  */
#define UID_DATAENDIAN littleEndian /* refer to UID_DATAENDIAN in cortex_chipid_binding.h */
#define UID_USERID_KEY1 (0x53A886F5) /* refer to UserID 1 in cortex_chipid_binding.h */
#define UID_USERID_KEY2 (0x5D71DDB3) /* refer to UserID 2 in cortex_chipid_binding.h */
#define UID_USERID_KEY3 (0x32677FE8) /* refer to UserID 3 in cortex_chipid_binding.h */

image-20250111095524300

lib/advance_software_protect_porting.c
ASP_ENCRYPT_F_DECL(static void ChipUIDAlgo(char pUserID[], char pChipID[], char pKey[]))

//The following code may warn in KEIL(MDK), ignore it
static void ChipUIDAlgo(char pUserID[], char pChipID[], char pKey[])
{
pKey[0] = pUserID[8] | pChipID[5] - pUserID[10] + pChipID[2] ;
pKey[1] = pChipID[1] & pChipID[0] ^ pUserID[4] * pChipID[11] ;
pKey[2] = pChipID[3] ^ pChipID[10] | pChipID[6] - pUserID[11] ;
pKey[3] = pUserID[9] & pChipID[4] + pUserID[6] * pUserID[0] ;
pKey[4] = pChipID[7] + pUserID[1] * pChipID[9] - pUserID[7] ;
pKey[5] = pUserID[2] & pUserID[3] ^ pUserID[5] | pChipID[8] ;
pKey[6] = pChipID[6] * pChipID[4] & pUserID[6] + pChipID[11] ;
pKey[7] = pChipID[1] ^ pUserID[0] - pUserID[5] | pUserID[7] ;
pKey[8] = pChipID[8] - pUserID[10] | pChipID[2] * pUserID[2] ;
pKey[9] = pChipID[10] & pUserID[11] + pChipID[5] ^ pUserID[4] ;
pKey[10] = pUserID[9] & pUserID[3] ^ pChipID[7] - pChipID[0] ;
pKey[11] = pChipID[3] + pUserID[8] | pUserID[1] * pChipID[9] ;
}
caution

Note that the signature computes the code, declared as a cryptographic function.

5.5.4.3.2:Using ECDSA Signatures

Configure the ECDSA public key:

image-20250111100422421

lib/advance_software_protect_user.h
    /* using ecdsa to signature */
// Public key
const static uint8_t PUBLIC_KEY[49] = {
0x04, 0x58, 0x82, 0x74, 0x51, 0x40, 0x7E, 0xEF, 0x99, 0x94, 0x7A, 0x66, 0xA6, 0x80, 0x07, 0x1B,
0xE4, 0xED, 0x5C, 0xD4, 0x68, 0xDB, 0xBC, 0xDB, 0x12, 0x7B, 0x5B, 0xE0, 0xA7, 0x85, 0xFA, 0xD1,
0x33, 0x61, 0x91, 0xB8, 0x37, 0x6B, 0x24, 0xC3, 0x82, 0xB5, 0x48, 0x10, 0x94, 0x32, 0x39, 0xB5,
0x46};

5.5.4.3.2:Signature Project

In order to reduce developers' doubts, we have prepared demo projects for ICWKEY and PowerWriter, and the configurations of Matrix and ECDSA are located in the /sample/ folder in the following path, with the following file structure:

.
├── STM32F103rf_ecdsa.pkg /* STM32F103 ecdsa powerwriter project */
├── STM32F103rf_matrix.pkg /* STM32F103 ecdsa powerwriter matirx project */
├── ecdsa
│   ├── SafeLic_53ECCC98.uprj /* ICWKEY ECDSA project */
│   ├── cortex_chipid_binding.c /* ICWKEY ECDSA protect */
│   └── cortex_chipid_binding.h /* ICWKEY ECDSA protect */
├── matrix /* PowerWriter Matrix source */
│   ├── DebugConfig
│   ├── Listings
│   ├── Objects
│   ├── UidSample.uvguix.CSHSOFT
│   ├── UidSample.uvoptx
│   ├── UidSample.uvprojx
│   ├── cortex_chipid_binding.c
│   ├── cortex_chipid_binding.h
│   ├── main.c
│   └── startup_stm32f10x_hd.s
└── password.txt

5.5.4.4:Firmware Verify

Firmware verification function is the most core function of the encryption library, through the firmware verification module, you can detect whether the current firmware has been tampered with, and respond to the modification, so as to avoid the core function is cracked, to protect the user firmware, firmware verification algorithm, will only check the range of the firmware compiled by the current project, will not be checked outside the range of the firmware data, and for the current firmware within the scope of the dynamic data, you can flexibly set up!Skip address and size, configure the skip check address and refer to the following configuration:

Define dynamic data:

advance_software_protect_samples.c
/* Define SN dynamic data in Flash, currently defined as 0x08001000. */
#ifdef ADVANCE_SOFTWARE_PROTECT_SN_ENABLE
#ifdef ADVANCE_SOFTWARE_PROTECT_COMPILER_GCC
#define ADVANCE_SOFTWARE_PROTECT_SN_ADDR ".advance_software_protect_sn" /*defined in ld link Script */
#elif defined(ADVANCE_SOFTWARE_PROTECT_COMPILER_IAR)
#define ADVANCE_SOFTWARE_PROTECT_SN_ADDR 0x08001000
#else
#define ADVANCE_SOFTWARE_PROTECT_SN_ADDR ".ARM.__at_0x08001000"
#endif
#ifdef ADVANCE_SOFTWARE_PROTECT_COMPILER_IAR
#pragma diag_suppress = Pa082
const uint32_t m_serialnumber @ADVANCE_SOFTWARE_PROTECT_SN_ADDR = {0}; /* Fixed address for easy maintenance */
#else
const uint32_t m_serialnumber __attribute__((section(ADVANCE_SOFTWARE_PROTECT_SN_ADDR))) = {0}; /* Fixed address for easy maintenance */
#endif
#endif

Skipping of checksum area configuration method

lib/advance_software_protect_porting.c
S_ADV_SOFTWARE_PROTECT_FLASH_VERIFY_START
/* Please add the dynamic storage area you want to skip, refer to the sequence
* number definition below and add the definition after it.
*/
/***************************** START *********************************/
/* Skip SN */
#ifdef ADVANCE_SOFTWARE_PROTECT_SN_ENABLE
{
.m_skip_addr = (void *)&m_serialnumber,
.m_skip_size = sizeof(m_serialnumber)
},
#endif
/* If you need to skip other checksum fields, please refer to the skip method of SN and define it as follows */
/* ... */
/****************************** END **********************************/
S_ADV_SOFTWARE_PROTECT_FLASH_VERIFY_END S_ADV_SOFTWARE_PROTECT_FLASH_VERIFY_APP

Call method:

/* for flash verify */
#ifdef ADVANCE_SOFTWARE_PROTECT_FLASH_VERIFY_ENABLE
void advance_software_protect_flash_verify_sample()
{
advance_software_protect_logger(ADV_DEBUG, " ---------------Flash Verify start----------\r\n");
E_ADV_SOFTWARE_PROTECT_FLASH_VERIFY_RESULT result = advance_software_protect_flash_verify(ADV_DATA_ENCRYPT_KEY);

switch (result)
{
case ConfigError: /* configure error ! */
advance_software_protect_logger(ADV_DEBUG, " configuration error!\r\n");
break;

case UnprocessedAfterComplied: /* Run in Debug mode */
advance_software_protect_logger(ADV_DEBUG, " (Currently in debug mode) Firmware not processed, features not in effect...\r\n");
break;

case FlashVerifyFailed: /* Verify Failed */
advance_software_protect_logger(ADV_DEBUG, " Validation failure!!!\r\n");
break;

case FlashVerifyPassed: /* Verify Passed */
advance_software_protect_logger(ADV_DEBUG, " Validation succeed !!!\r\n");
break;
}

advance_software_protect_logger(ADV_DEBUG, " ---------------Flash Verify end----------\r\n");
}
#endif

ASP_ENCRYPT_F_DECL(void advance_software_protect_sample())
void advance_software_protect_sample()
{
/* flash verify */
#ifdef ADVANCE_SOFTWARE_PROTECT_FLASH_VERIFY_ENABLE
advance_software_protect_flash_verify_sample();
#endif
}

5.5.4.5:Function encryption

Function encryption is one of the core functions of the encryption library, through the function encryption, will be part of the function in the chip, encrypted storage, and then through the encrypted function loader for loading, and then call, to ensure that the core code will not be decompiled to protect the security of the firmware, the declaration of encrypted function method is as follows:

// A function can be declared as an encrypted function by adding the following statement in front of the function definition.
ASP_ENCRYPT_F_DECL(function prototype)
tip
  • The core code of the cryptographic library has been declared as cryptographic functions, and all functions in advance_software_protect_porting.c are currently cryptographic functions.
  • This declaration allows the user to define the function as a cryptographic function.
note

Advance_software_protect_signature, advance_software_protect_flash_verify, advance_software_protect_anti_debugger, etc., the parent function of the core function, refer to the encryption function. function.

caution

Cryptographic functions must be loaded via the Loader before they can be called, otherwise a command bus hardware exception will result, see cryptographic function loader for details.

In addition, the storage space for encryption functions is stored according to the .advance_software_protect_encryption_section segment of the project resource allocation, and the resource allocation is adjusted appropriately if too many encryption functions cause compilation errors.

5.5.4.6:Anti-debugger service

In order to assist the project development of the project to assist in encryption, encryption library also provides a standard process to help developers, to complete the basic anti-debugging function of the firmware, as opposed to the traditional development model, anti-debugging services, in the development of the project to assist in a more convenient.

Configuration methodology:

lib/advance_software_protect_porting.c
/* for anti debugger*/
#ifdef ADVANCE_SOFTWARE_PROTECT_ANTI_DEBUGGER_ENABLE
/*
* @brief Disabling the debugger
* @pram none
* @retval none
*/
ASP_ENCRYPT_F_DECL(void advance_software_protect_debugger_disable())
void advance_software_protect_debugger_disable()
{
#ifdef RELEASE
/* dsiable debugger */
__HAL_AFIO_REMAP_SWJ_DISABLE();
#endif
}
/*
* @brief Check whether read protection is enabled
* @pram none
* @retval true is enable rdp
*/
ASP_ENCRYPT_F_DECL(bool advance_software_protect_rdp_check())
bool advance_software_protect_rdp_check()
{
#ifdef RELEASE
/*check RDP */
FLASH_OBProgramInitTypeDef m_cur_ob;
HAL_FLASHEx_OBGetConfig(&m_cur_ob);
return (m_cur_ob.RDPLevel != OB_RDP_LEVEL_0);
#else
return true; /* default return true */
#endif
}
/*
* @brief Disabling Read Protection
* @pram none
* @retval none
*/
ASP_ENCRYPT_F_DECL(void advance_software_protect_rdp_enable())
void advance_software_protect_rdp_enable()
{
#ifdef RELEASE
bool rdp_result = {0};
/* get current ob */
FLASH_OBProgramInitTypeDef m_cur_ob;
HAL_FLASHEx_OBGetConfig(&m_cur_ob);

/* unlock ob */
if (HAL_FLASH_Unlock() == HAL_OK && HAL_FLASH_OB_Unlock() == HAL_OK)
{

/* Set RDP to Level 1 or Level 2*/
m_cur_ob.RDPLevel = OB_RDP_LEVEL_1;

/* update ob */
rdp_result = (HAL_FLASHEx_OBProgram(&m_cur_ob) == HAL_OK);

/* lock flash */
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();

/*
To make the option byte take effect, note that by resetting the target chip,
the target chip reloads the option byte to make it take effect
*/
if (rdp_result)
{
HAL_FLASH_OB_Launch();
}
}
#endif
}
#endif
caution

In order to strengthen the security of the code, the core functions must be encrypted again using the ASP_ENCRYPT_F_DECL declaration, in addition, the current interface must be implemented according to the actual chip.

tip

In the development and debugging phase, use the macro without RELEASE definition for debugging, and in the final release phase, switch to Release mode for release compilation, see Other Settings for details.

Call method:

lib/advance_software_protect_samples.c

bool advance_software_protect_anti_debugger_sample(E_AntiDebuggerEvent event)
{
switch (event)
{
/* Development mode running */
case Development:
advance_software_protect_logger(ADV_DEBUG, " (Running in development mode) Anti Debugger ...\r\n");
return true;

/* The RDP mode is disabled */
case RDPDisabled:
advance_software_protect_logger(ADV_DEBUG, " (Read protection is not enabled) Anti Debugger ...\r\n");
return true; /* Return true if you want to enable RDP */

/* The RDP mode is enabled */
case RDPEnabled:
advance_software_protect_logger(ADV_DEBUG, " (Read protection enabled) Anti Debugger ...\r\n");
return true; /* Return true if you want to reset */

/* The system is resetting */
case SystemReset:
advance_software_protect_logger(ADV_DEBUG, " (Request system reset) Anti Debugger ...\r\n");
return true; /* Return true if you want to reset */

/* Disable Debugger */
case DebuggerDisable:
advance_software_protect_logger(ADV_DEBUG, " (Disabling the debugger) Anti Debugger ...\r\n");
return true; /* Return true if you want to disable the debugger */
}
return true;
}

ASP_ENCRYPT_F_DECL(void advance_software_protect_sample())
void advance_software_protect_sample()
{
#ifdef ADVANCE_SOFTWARE_PROTECT_ANTI_DEBUGGER_ENABLE
/* anti debugger */
advance_software_protect_anti_debugger(advance_software_protect_anti_debugger_sample);
#endif
}
tip

Security can be further enhanced by calling the parent function of advance_software_protect_anti_debugger and again using ASP_ENCRYPT_F_DECL for the cryptographic function declaration.

5.5.4.7:Data encryption

The encryption library has built-in private data encryption algorithms, in the actual project, please make sure that the source code configuration and the command line parameter -k are the same, the configuration location in the source code is shown as follows:

lib/advance_software_protect_user.h
/* key for data encrypt */
#if (defined ADVANCE_SOFTWARE_PROTECT_FLASH_VERIFY_ENABLE) || (defined ADVANCE_SOFTWARE_PROTECT_CODE_ENCRYPT_ENABLE)
#define ADV_DATA_ENCRYPT_KEY (0xfb90ffcb9b62c2e6) /* 64 bit*/
#endif
caution

Each project should have a different password and be consistent with the command line argument -k.

5.5.4.8:Status monitor

If the encryption library related core function, was reverse cracked, removed, by obtaining the encryption library running state interface, read the system state, according to the state, the execution of the corresponding action, to further increase the security of the firmware, the return state reference is as follows:

advance_software_protect_status
/* system executed status  */
typedef struct advance_software_protect_status
{
/* Signature executed */
bool m_signature_executed;

/* Flash verify executed */
bool m_flash_verify_executed;

/* Anti debugger */
bool m_anti_debugger_executed;

} advance_software_protect_status;

In the current demo, the running status of the library is checked in the main function, and according to the status, the execution status of the library is printed as follows

int main(void)
{
while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(500);

/* Getting feedback on the status of an encrypted library */
advance_software_protect_status status;
if(!advance_software_protect_system_status_get(&status)){
advance_software_protect_logger(ADV_ERROR," Core code detected, dynamically modified !!! \r\n");
}
if(!status.m_signature_executed){
advance_software_protect_logger(ADV_ERROR,"Signature services have been illegally removed, or tampered with !!! \r\n");
}
if(!status.m_flash_verify_executed){
advance_software_protect_logger(ADV_ERROR,"Firmware validation function has been illegally modified !!! \r\n");
}
if(!status.m_signature_executed){
advance_software_protect_logger(ADV_ERROR,"Anti Debugging Service Framework was illegally removed !!! \r\n");
}

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
tip

The system state monitoring service, belonging to the lowest kernel of the cryptographic library, is itself a cryptographic function, which cannot be removed by static decomplication, and because the data itself is signed multiple times, it is theoretically not possible to modify the system state directly by means of dynamic memory patching.

caution

The checking of the system state can be done by burying points in multiple places in the firmware, and then responding accordingly when an abnormality is detected, which is the last line of refence of the whole cryptographic library, and is used to detect that the cryptographic library itself has been illegally removed or modified.

5.5.4.9:Other

In addition to the core functionality previously provided, a number of auxiliary functions are additionally provided to facilitate project development, as shown below:

    /* Read the version of the encryption library */
extern const char *advance_software_protect_version(void);
/* dump data for analysis */
extern void advance_software_protect_dump_buf(const char *title, unsigned char *buf, size_t len);
/* random function (math.) */
extern int advance_software_protect_random(unsigned char *buf, size_t len);
/* Set the last error code, you can use this interface to clear the encryption library error code information */
extern void advance_software_protect_set_last_error(E_ADV_SOFTWARE_PROTECT_ERROR_CODE err);
/* When encountering a failure, you can read the error code information through this interface to facilitate error analysis */
extern E_ADV_SOFTWARE_PROTECT_ERROR_CODE advance_software_protect_get_last_error(void);
/* The hash function that comes with the crypto library */
extern uint32_t crc32_inner(const void *data, int nBytes);
extern uint32_t crc32_continue_inner(uint32_t prev_crc, const void *data, int nBytes);

5.5.4.2:Debugging and Release

5.5.4.2.1:Debugging method

When all the configurations are done, we compile the project and will see the compilation output from the IDE as shown below:

Rebuild started: Project: stm32f103rfc6
*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCLANG5\Bin'
Rebuild target 'stm32f103rfc6_release'
assembling startup_stm32f103xg.s...
compiling stm32f1xx_hal_gpio_ex.c...
compiling stm32f1xx_hal.c...
compiling stm32f1xx_hal_gpio.c...
compiling main.c...
compiling stm32f1xx_hal_crc.c...
compiling stm32f1xx_hal_rcc_ex.c...
compiling stm32f1xx_it.c...
compiling stm32f1xx_hal_cortex.c...
compiling stm32f1xx_hal_msp.c...
compiling stm32f1xx_hal_dma.c...
compiling stm32f1xx_hal_rcc.c...
compiling system_stm32f1xx.c...
compiling stm32f1xx_hal_tim_ex.c...
compiling stm32f1xx_hal_tim.c...
compiling stm32f1xx_hal_pwr.c...
compiling advance_software_protect_porting.c...
compiling advance_software_protect_samples.c...
compiling stm32f1xx_hal_exti.c...
compiling stm32f1xx_hal_flash_ex.c...
compiling stm32f1xx_hal_uart.c...
compiling stm32f1xx_hal_flash.c...
linking...
Program Size: Code=42962 RO-data=5574 RW-data=180 ZI-data=9956
FromELF: creating hex file...
After Build - User command #1: fromelf --bin -o "E:\MainProjects\2021\powerwriter_advance_software_protect\powerwriter_advance_software_protect\library\project\mdk_demo\stm32f103rfc6\MDK-ARM\stm32f103rfc6\stm32f103rfc6.bin" "E:\MainProjects\2021\powerwriter_advance_software_protect\powerwriter_advance_software_protect\library\project\mdk_demo\stm32f103rfc6\MDK-ARM\stm32f103rfc6\stm32f103rfc6.axf"
After Build - User command #2: ../../../../tool/windows/advance_software_protect.exe -s 0x08000000 -k 0xfb90ffcb9b62c2e6 -i ./stm32f103rfc6/stm32f103rfc6.bin/ER_IROM1 -r ./stm32f103rfc6/stm32f103rfc6.bin/ER_IROM2
-----------------------------------------------------------------------------------------------------------------------
Open source advance software protect library
This project provides a set of universal MCU software encryption algorithm.The goal is simple, easy to use,easy
to integrate, and does not require any additional hardware,The following are the test cases of the project, please
select the appropriate use method, some functions need to be used together with this tool, if you have any questions,
please contact us,customer service email:cs@icworkshop.com.
team : ICWorkshop HPT Dep Team
author : cshsoft(csh@icworkshop.com)
website: www.icworkshop.com
tools : www.powerwriter.cn(.com)
-----------------------------------------------------------------------------------------------------------------------
[advance software protect][debug]: Environment:E:\MainProjects\2021\powerwriter_advance_software_protect\powerwriter_advance_software_protect\library\project\mdk_demo\stm32f103rfc6\MDK-ARM
[advance software protect][debug]: Prepare encrypted firmware...
[advance software protect][info ]: Start address: 0x08000000
[advance software protect][info ]: End address: 0x0800b498
[advance software protect][info ]: Ram code size: 0x000009b4
[advance software protect][debug]: search flash verify ...
[advance software protect][debug]: flash verify enabled ...
[advance software protect][info ]: flash verify skip verify total [3]...
[advance software protect][info ]: flash verify skip item : Address:0x0800A078 Size:0x24
[advance software protect][info ]: flash verify skip item : Address:0x08002000 Size:0x8d
[advance software protect][info ]: flash verify skip item : Address:0x08001000 Size:0x4
[advance software protect][debug]: anti debugger enabled ...
[advance software protect][debug]: search dynamic function encryption (ram code) ...
[advance software protect][debug]: dynamic function encryption enabled ...
[advance software protect][info ]: Ram code address: 0x00000000(dynamic in user code)
[advance software protect][debug]: search flash done ...
[advance software protect][debug]: ram code updating ...
[advance software protect][debug]: ram code update ok ...
[advance software protect][debug]: Process anti debugger check ok ...
[advance software protect][debug]: Process flash verify start ...
[advance software protect][info ]: hash block item : Start:0x08000000 End:0x8001000 Size:0x1000 hash:0xa5a3e42e
[advance software protect][info ]: hash block item : Start:0x08001004 End:0x8002000 Size:0xffc hash:0x56320614
[advance software protect][info ]: hash block item : Start:0x0800208d End:0x800a078 Size:0x7feb hash:0x68c5f96b
[advance software protect][info ]: hash block item : Start:0x0800a09c End:0x800be4c Size:0x1db0 hash:0x72d02bb6
[advance software protect][debug]: Process flash verify ok ...
[advance software protect][debug]: Save to file : ./stm32f103rfc6/stm32f103rfc6.bin/ER_IROM1_ER_IROM2_encrypted.bin ...
[advance software protect][debug]: All done ...
".\stm32f103rfc6\stm32f103rfc6.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed: 00:00:06
caution

Please note that the IDE compilation whether to report errors, typical errors, such as insufficient space, can be adjusted appropriately resource allocation, or prompt file already exists, please delete the compilation output, recompile the project. And pay attention to the final output file path.

Programming Firmware with PowerWriter

image-20250111102505833

After the burning is completed, refer to the other settings Configuration method, use MDK for debugging, and turn on the serial port assistant, we will see the following information from the serial port output, as shown in the following figure:

image-20250111102824593

---------------------------------------------------------------------------------
Advance software protect lib

This project provides a set of universal MCU software encryption algorithm.
The goal is simple, easy to use, easy to integrate, and does not require any
additional hardwareThe following are the test cases of the project, please
select the appropriate use method, some functions need to be used together
with repack tool, if you have any questions, please contact us


PowerWriter team
Version:V1.0.0.0 Jan 9 2025 14:37:54
Author: csh@icworkshop.com(cshsoft)
Site: www.powerwriter.com
Docs: https://docs.powerwriter.com/docs/next/powerwriter_for_arm/application_note/advance_software_protect
---------------------------------------------------------------------------------
[D]: (Successful) Dynamic function encryption ...
[I]:---------------Product Serial Number Info Start----------
[I]: Product Serial number is : 0x123456AE , In Flash Address 0x08001000.
[I]:---------------Product Serial Number Info End----------
[D]: ---------------Single Object Watcher Simple Start ----------
[D]: m_Object check passed at first time
[E]: m_Object will fail if it is modified without updating the checksum value!
[D]: m_Object had modified & updated, will be passed !
[D]: ---------------Single Object Watcher Simple End----------
[D]: ---------- Dynamic Muti-Object Watcher Simple Start ----------
[D]: Set ObjectWatcher Event callback...
[W]: [0x2000014C] Object already exist!
[W]: Error param ...
[I]: Alloc 100 dynamic object done ..
[I]: m_aType exist..
[D]: Total 126 objects in watching..
[D]: m_class check passed ..
[E]: [0x2000014C] Object was modified!
[D]: Passed: 125, Total : 126 ..
[D]: m_class update ok ..
[I]: m_bType removed, left 125 object in watching..
[I]: All objects updated...
[I]: All objects removed, left 0 object in watching..
[D]: ---------------Dynamic Muti-Object Watcher Simple End----------
[D]: ---------------Product Signature start----------
[D]: Chip signature verify passed...
[D]: ---------------Product Signature end----------
[D]: ---------------Flash Verify start----------
[D]: Flash Verify passed...
[D]: ---------------Flash Verify end----------
[D]: (Read protection enabled) Anti Debugger ...
[D]: (Disabling the debugger) Anti Debugger ...

5.5.4.2.2:Release

When all validation of the project is complete, switch the project to release mode (with the RELEASE macro definition, which is used to turn on some of the anti-debugging features), as shown below:

image-20250111101913522

Recompile the project, refer to the debugging method, and re-add the RELEASE firmware to the PowerWriter project for final release verification for production.

5.5.5:Command line

The library supports mainstream compiler environments as well as different system development platforms. In the tool directory of the library, you can get the binaries of the corresponding platforms, as shown in the following figure:

GIF 2025-1-10 15-05-25

When the run command is executed on the console, you can see the following help message as shown below:

-----------------------------------------------------------------------------------------------------------------------
Open source advance software protect library

This project provides a set of universal MCU software encryption algorithm.The goal is simple, easy to use,easy
to integrate, and does not require any additional hardware,The following are the test cases of the project, please
select the appropriate use method, some functions need to be used together with this tool, if you have any questions,
please contact us,customer service email:cs@icworkshop.com.


team : ICWorkshop HPT Dep Team
author : cshsoft(csh@icworkshop.com)
website: www.icworkshop.com
tools : www.powerwriter.cn(.com)
-----------------------------------------------------------------------------------------------------------------------

usage:
advance_software_protect {options} -i origin_firmware_path [-o encode_firmware_path]

options:
[-s 0xxxxxxxxx] Specifies the start address of the firmware. If the Flash verification function
is enabled for the firmware, the parameters in the firmware will be updated. If
the Flash verification function is not enabled or the parameter is manually spe-
cified, the default value is 0x08000000.
[-l [0,1,2]] Specify the log output level(The default is Level 2):
Level 0: only error information is displayed
Level 1: only debug information is displayed
Level 2: displays all information, including processing infomation

-i origin_firmware_path Specifies the compiled origin firmware path,Currently, only bin files are
supported, *.afx or intel hex format firmware may be supported in the future.

[-r ramcode_path] Specify the ram code path to encrypt,The default supported executables are
binary files, ER_IROM2 is probably the default name for MDK compilation

[-o encrypted_firmware_path] Specifies the Release firmware path,The bin file is currently output directly,
If the output path is not specified, origin_firmware_path (*_encrypted.bin) is
saved by default.

-k 0xxxxxxxxxxxxxxxxx Specifies the password for encrypting data,64bit.

-g [true|false] Generate encryption matrix.true allows repetition, false does not
example:
advance_software_protect -s 0x08000000 -i ./stm32f103rfc6.bin
advance_software_protect -l 0 -i ./stm32f103rfc6.bin
advance_software_protect -i ./stm32f103rfc6.bin -o ./stm32f103rfc6_encrypted.bin

notes:
Curly braces{} indicate a group of options, and curly braces[] indicate optional options.If you do not enter this parameter, default parameters will be used instead, and all other parameters are mandatory.

5.5.5.2:Windows Demo

image-20250110151844181

5.5.5.3:Linux Demo

image-20211127141803823

5.5.5.4:MacOS Demo

image-20211129110740754

5.5.5.5:Integration with IDE

Please refer to Project Configuration section for integration with the IDE.

5.5.6:Using operating system

When using an RTOS, such as Free RTOS, adjust the heap size of the operating system appropriately, the following is the configuration reference for Free RTOS in Cube MX

image-20250110140325162

5.5.6.1:Task stack allocation

When creating a crypto library verification task, please adjust the stack size appropriately to avoid the crypto library execution depth being too deep, resulting in a stack overflow, as shown below:

/* USER CODE BEGIN PV */
osThreadId_t advance_software_protect_TaskHandle;
const osThreadAttr_t ASPLTask_attributes = {
.name = "advance_software_protect_task",
/* Need to set the stack of the encryption library larger, too small, may cause stack overflow! */
.stack_size = 1024 * 4,
.priority = (osPriority_t) osPriorityNormal1,
};

/* USER CODE BEGIN 0 */
void advance_software_protect_Task(void *argument)
{
/* USER CODE BEGIN 5 */
/* init */
advance_software_protect_init();
/* Infinite loop */
for(;;)
{
advance_software_protect_logger(ADV_INFO,"-------------OS Task in-------------\r\n");
/* sample */
advance_software_protect_sample();

/* system status */
advance_software_protect_status status;
if(!advance_software_protect_system_status_get(&status)){
advance_software_protect_logger(ADV_ERROR,"Abnormal access and modification of firmware detected !!! \r\n");
}
if(!status.m_signature_executed){
advance_software_protect_logger(ADV_ERROR,"Illegal removal of signature services !!! \r\n");
}
if(!status.m_flash_verify_executed){
advance_software_protect_logger(ADV_ERROR,"Illegal removal of flash verify services !!! \r\n");
}
if(!status.m_signature_executed){
advance_software_protect_logger(ADV_ERROR,"Illegal removal of anti debugger services !!! \r\n");
}

advance_software_protect_logger(ADV_INFO,"-------------OS Task out-------------\r\n");
osDelay(100);
}
/* USER CODE END 5 */
}

5.5.6.2:Task heap allocation

When used in an operating system, we can use the operating system's heap to manage memory by registering the heap allocation interface, as referenced below:

/* Register heap memory management interface prototype */
bool advance_software_protect_set_mem_calloc_free_callback(calloc_func_cb, free_func_cb);

/* memory alloc & free */
void *calloc_mem_func(size_t count, size_t size)
{
void * obj = pvPortMalloc(count * size); /* Using the operating system's heap memory allocation */
if(obj){
memset(obj,0,count * size); /* When using the pvPortMalloc API, be sure to clear the 0 */
}
return obj;
}
void free_mem_func(void *blk_obj)
{
vPortFree(blk_obj); /* Release Object Interface */
}

/*
* IMPORTANT:
* Do not encrypt the parent function () that contains the
* advance_software_protect_encryption_code_loader!
*/
void advance_software_protect_init()
{
/* initial */
advance_software_protect_set_mem_calloc_free_callback(calloc_mem_func, free_mem_func);
}
note

Taking Free RTOS as an example, you can also use the osMemoryPoolNew, osMemoryPoolAlloc, and osMemoryPoolFree heap memory management interfaces for crypto library heap memory management.

caution

When using pvPortMalloc, be sure to clear the allocated memory by 0.

5.5.6.3:Mix usage

When mixing with the OS and running the crypto libraries outside the OS, following the previous idea of using the included heap, then adjust the default stack size of the project, see startup_stm32f103xg.s for the default stack size adjustment, or you can pre-allocate them in the configuration interface when generating the project, as shown below:

image-20250110142504993

5.5.7:Feed back

If you encounter problems or have suggestions or comments when using the use of this encryption library, please give us feedback!

Feedback Email: cs@icworkshop.com

Online form feedback: Power Writer feed back

Alan chen (cshsoft ) @ICWorkshop all rights reserved