10/21/2022

ESP32 Secure Boot 와 OTP

1. ESP32 Secure Boot 기본개념

  • ARM/ESP32 Secure Boot 관련비교 내용 
ARM의 Secure Boot 와 ESP32의 Secure Boot의 기본개념을 설명 
반드시 이해를 해야하며, 이를 이해해야 아래의 내용도 이해가능 

  • ESP32의 Startup Boot Flow
  1. 1st stage bootloader : ESP32 내부에 존재하는 Bootloader로 ROM에 이미 저장
  2. 2nd stage bootloader: ESP32의 SPI Flash 저장되어진 실제적인 bootloader image 이며 Partition table 접근가능
  3. application image: app image로 factory or ota 영역에 저장되는 application image

상위와 같이 ESP32는 Booting하며, User가 신경써야 할 부분은 2nd StageApplication image이다. 


1.1 ESP32 Secure Boot Version  

Espressif에서는 Secure Boot V1, V2을 제공하고 있지만, V2 더 권장하고 있으며, 다음과 같은 차이가 존재한다. 

  1. SecureBoot v1 : ESP32 Silicon Version1/2에서 지원가능
  2. SecureBoot v2 : ESP32 Silicon Version3에서 지원가능

  • ESP32 Secure Boot v1 for ESP32 Revision1/2
  1. AES-256-ECB 방식으로 bootloader Encoding/Decoding  
  2. ECSDA Signature 기반으로 appImage Signing/Verification  Image검증  
  3. OTP(eFUSE) 의 Block 2에  AES 256 bit 저장이용 
  4. OTP(eFUSE) 의 ABS_DONE_0 설정 

  • ESP32 Secure Boot v2 for ESP32 Revision3
  1. RSA3072 Key 기반으로 Signature 기반 bootloader Signing/Verification  Image검증  
  2. RSA3072 Key 기반으로 Signature 기반 appImage Signing/Verification  Image검증     
  3. OTP(eFUSE) 의 Block 2에 SHA-256 bit digest 이용하여 RSA3072 Public Key 검증 
  4. OTP(eFUSE) 의 ABS_DONE_1 설정 


1.2  Secureboot 의 Signing 과 Verfication 이해 

Secure Boot의 기본개념인 Signing 과 Verfication을 알아보도록 하자 
흔히, 비대칭키를 이용하여 Sign을 하여, Signature 생성하고, 이를 검증(Verfication)하는 것이다.

Android System의 Sign 과 Vefication 이해 

암호화 기본개념 

  • Secure Boot Signing
  1. 비대칭키 기반 사용 (RSA,ECDSA)
  2. Build를 한 후 image를 생성할때 Private Key로 Signature를 생성

  • Secure Boot Verification 
  1. 비대칭키 기반 사용 (RSA,ECDSA)
  2. 이미 생성된 Signature를 Public Key를 이용하여 이를 검증 

2. ESP32 Secure Boot 설정 과 진행 


우선 Signature를 만들기 위해서, 즉 Signing을 위한 Key가 필요하다. 


2.1 RSA Signing Key 생성방법 


Window용 ESP-IDF가 버그가 있어 아래와 같이 export.bat 수정하도록 한다.  
아래는 Powershell 용이 아니라 Command 용이므로 참고하도록하자. 

  • ESP-IDF 버그 수정 (v.4.2.3)
수정후 espsecure.py 사용가능
cd  %userprofile%\esp\esp-idf\export.bat  //수정 export.bat (Window에서 espsecure.py 미지원)

....
DOSKEY idf.py=python.exe "%IDF_PATH%\tools\idf.py" $*
DOSKEY esptool.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\esptool.py" $*
DOSKEY espefuse.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\espefuse.py" $*
DOSKEY espsecure.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\espsecure.py" $*
DOSKEY otatool.py=python.exe "%IDF_PATH%\components\app_update\otatool.py" $*
DOSKEY parttool.py=python.exe "%IDF_PATH%\components\partition_table\parttool.py" $*

echo Checking if Python packages are up to date...
python.exe "%IDF_PATH%\tools\check_python_dependencies.py"
if %errorlevel% neq 0 goto :end

echo.
echo Done! You can now compile ESP-IDF projects.
echo Go to the project directory and run:
echo.
echo   idf.py build
echo.
....


RSA Signing Key를 꼭 espsecure.py로 생성할 필요는 없으며, openssl로 쉽게 생성가능.

  • RSA3072 Key 생성방법-A 
openssl을 이용하여  Singing Key 생성 방법 
$ openssl genrsa -out my_secure_boot_signing_key.pem 3072
$ cp ./my_secure_boot_signing_key.pem /mnt/d/Works/   

  • RSA3072 Key 생성방법-B
espsecure.py 이용하여 쉽게 생성가능하며, 각 Version 별로 설정가능  
$ espsecure.py generate_signing_key -v2 test1.pem   
$ espsecure.py generate_signing_key -v1 test1.pem   
$ espsecure.py sign_data --version 2 --keyfile my_secure_boot_signing_key.pem  --output ./build/app_sign.bin ./build/app.bin 

  • ESP SecureBoot Version1
ESP32의 경우, Revision3 부터 사용권장
PEM 방식으로 ECDSA Signing/Verification 동작 

ESP32 SecureBootV1 Manual 

  • ESP SecureBoot Version2 
이를 좀 더 권장하며, ESP32 ECO3 이상에서 사용가능하며, RSA-PSS 방식이라고 말한다.
PEM 의 RSA3072 기반으로 Signing/Verfication 방식
OTP(eFUSE, One Time Programming)  Block2 의 Secure Boot에 RSA Public Key의 Hash를 넣는 구조

ESP32의 Secure Boot 기본이해 

  • ESP32 와 외부 Security Chip 사용 
ESP32에서 ECDSA의 Sign/Verify를 사용하기 위해서 외부 Chip를 사용 
이전에 설명한 CMVP/KCMVP 기능이라고 생각하면 될 것 같으며, 확장기능으로 이를 이용하여 TLS도 가능하다 


2.2 OTP(eFuse) 구조 와 역할 

ESP32 의 경우, 1024bit 의 OTP(One Time Programmable, eFUSE)를 가지고 있고, 각 Block은 256bit로 총 4개로 나누어 분리하여 사용하도록 한다. 
주의 해야할 것은 OTP(One Time Programmable) 한 번만 Write가 가능하므로, 신중하게 하도록하자. 
실패하면, 쓸모가 없어진다. 

  • OTP(eFUSE) 구조 

  • OTP(One Time Programable,eFuse) Manual 
ESP-IDF Version에 따라 조금씩 다르므로 각 버전에 맞게 참조하도록 하자 

  • ESP32 burn-efuse 설명 


  • ESP32 OTP 사용방법 
설정하는 방법은 현재 Command 중심으로 하는게 제일 편하며, ESP-IDF 개발환경에서 VS Code용 EFUSE Exploerer를 별도로 제공을 해주고 있다. 
  1. UI 중심: VS Code EFUSE Exploerer
  2. Command 중심 아래 참조 


2.3 OTP(eFuse) Command 기반 사용법 

실제로 ESP32에서 제공하는 eFuse, OTP 설정값들을 확인해보고, 제어를 해보도록하자. 

  • Window에서 ESP-IDF 활성화   
ESP-IDF가 현재 venv기반의 Python으로 되어있으므로 이를 활성화 부터 해야한다.

Command로 할 경우, 
%userprofile%\.espressif\python_env\idf4.2_py3.8_env\Scripts\activate.bat
%userprofile%\esp\esp-idf\export.bat

PowerShell 할 경우,
$Env:userprofile\.espressif\python_env\idf4.2_py3.8_env\Scripts\activate.ps1
$Env:userprofile\esp\esp-idf\export.ps1

Window 용 PowerShell 관련내용 
주의, Powershell의 경우 Version 따라 동작이 많이 다르며, 점차 Linux 처럼 변경 중 


  • idf.py show_efuse_table (각 OTP 내부구성파악)
OTP인 eFuse에 각 Table 구성과 Field name을 쉽게 파악가능하다. 

(idf4.2_py3.8_env) D:\Works\Project\>idf.py show_efuse_table  //ESP Project 내에서 실행하며, 각 Field name 파악
Executing action: show_efuse_table
Running ninja in directory d:\works\project\mod610_sw\build
Executing "ninja show_efuse_table"...
[1/1] cmd.exe /C "cd /D D:\Works\Project\mod610_sw\build\esp-idf\efus...ts/efuse/esp32/esp_efuse_table.csv -t esp32 --max_blk_len 192 --info" 
Parsing efuse CSV input file C:/Users/jhlee/esp/esp-idf/components/efuse/esp32/esp_efuse_table.csv ...
Verifying efuse table...
Max number of bits in BLK 192
Sorted efuse table:
#       field_name                      efuse_block     bit_start       bit_count  //Field name 과 Block 위치 와 Bit Start 파악
1       WR_DIS_FLASH_CRYPT_CNT          EFUSE_BLK0         2               1
2       WR_DIS_BLK1                     EFUSE_BLK0         7               1
3       WR_DIS_BLK2                     EFUSE_BLK0         8               1
4       WR_DIS_BLK3                     EFUSE_BLK0         9               1
5       RD_DIS_BLK1                     EFUSE_BLK0         16              1
6       RD_DIS_BLK2                     EFUSE_BLK0         17              1
7       RD_DIS_BLK3                     EFUSE_BLK0         18              1
8       FLASH_CRYPT_CNT                 EFUSE_BLK0         20              7
9       UART_DOWNLOAD_DIS               EFUSE_BLK0         27              1
10      MAC_FACTORY                     EFUSE_BLK0         32              8
11      MAC_FACTORY                     EFUSE_BLK0         40              8
12      MAC_FACTORY                     EFUSE_BLK0         48              8
13      MAC_FACTORY                     EFUSE_BLK0         56              8
14      MAC_FACTORY                     EFUSE_BLK0         64              8
15      MAC_FACTORY                     EFUSE_BLK0         72              8
16      MAC_FACTORY_CRC                 EFUSE_BLK0         80              8
17      CHIP_VER_DIS_APP_CPU            EFUSE_BLK0         96              1
18      CHIP_VER_DIS_BT                 EFUSE_BLK0         97              1
19      CHIP_VER_PKG                    EFUSE_BLK0        105              3
20      CHIP_CPU_FREQ_LOW               EFUSE_BLK0        108              1
21      CHIP_CPU_FREQ_RATED             EFUSE_BLK0        109              1
22      CHIP_VER_REV1                   EFUSE_BLK0        111              1
23      ADC_VREF_AND_SDIO_DREF          EFUSE_BLK0        136              6
24      XPD_SDIO_REG                    EFUSE_BLK0        142              1
25      SDIO_TIEH                       EFUSE_BLK0        143              1
26      SDIO_FORCE                      EFUSE_BLK0        144              1
27      CHIP_VER_REV2                   EFUSE_BLK0        180              1
28      ENCRYPT_CONFIG                  EFUSE_BLK0        188              4
29      CONSOLE_DEBUG_DISABLE           EFUSE_BLK0        194              1
30      ABS_DONE_0                      EFUSE_BLK0        196              1
31      DISABLE_JTAG                    EFUSE_BLK0        198              1
32      DISABLE_DL_ENCRYPT              EFUSE_BLK0        199              1
33      DISABLE_DL_DECRYPT              EFUSE_BLK0        200              1
34      DISABLE_DL_CACHE                EFUSE_BLK0        201              1
35      ENCRYPT_FLASH_KEY               EFUSE_BLK1         0              192
36      SECURE_BOOT_KEY                 EFUSE_BLK2         0              192
37      MAC_CUSTOM_CRC                  EFUSE_BLK3         0               8
38      MAC_CUSTOM                      EFUSE_BLK3         8               48
39      ADC1_TP_LOW                     EFUSE_BLK3         96              7
40      ADC1_TP_HIGH                    EFUSE_BLK3        103              9
41      ADC2_TP_LOW                     EFUSE_BLK3        112              7
42      ADC2_TP_HIGH                    EFUSE_BLK3        119              9
43      SECURE_VERSION                  EFUSE_BLK3        128              32
44      MAC_CUSTOM_VER                  EFUSE_BLK3        184              8

Used bits in efuse table:
EFUSE_BLK0
[2 2] [7 9] [16 18] [20 27] [32 87] [96 97] [105 109] [111 111] [136 144] [180 180] [188 191] [194 194] [196 196] [198 201]

EFUSE_BLK1
[0 191]

EFUSE_BLK2
[0 191]

EFUSE_BLK3
[0 55] [96 159] [184 191]

Note: Not printed ranges are free for using. (bits in EFUSE_BLK0 are reserved for Espressif)



  • espefuse.py 사용법 (OTP 제어방법 확인)
아래와 같이 기본사용법을 확인을 반드시 하도록하며, 각 명령어에 따라 사용하도록하자. 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py  //esefuse 사용법확인 
espefuse.py v3.1-dev
usage: espefuse.py [-h] [--chip {auto,esp32,esp32s2,esp32s3beta2,esp32s3beta3,esp32c3}] [--baud BAUD] [--port PORT]
                   [--before {default_reset,no_reset,esp32r1,no_reset_no_sync}] [--debug] [--virt] [--path-efuse-file PATH_EFUSE_FILE]        
                   [--do-not-confirm]
                   {burn_efuse,read_protect_efuse,write_protect_efuse,burn_block_data,burn_bit,adc_info,dump,summary,burn_key,burn_key_digest,set_flash_voltage,burn_custom_mac,get_custom_mac}
                   ...

positional arguments:
  {burn_efuse,read_protect_efuse,write_protect_efuse,burn_block_data,burn_bit,adc_info,dump,summary,burn_key,burn_key_digest,set_flash_voltage,burn_custom_mac,get_custom_mac}
                        Run espefuse.py {command} -h for additional help
    burn_efuse          Burn the efuse with the specified name   //Feild name 기반 Write 기능,  burn_bit 와 동일 
    read_protect_efuse  Disable readback for the efuse with the specified name
    write_protect_efuse
                        Disable writing to the efuse with the specified name
    burn_block_data     Burn non-key data to EFUSE blocks. (Don't use this command to burn key data for Flash Encryption or ESP32 Secure      
                        Boot V1, as the byte order of keys is swapped (use burn_key)).
    burn_bit            Burn bit in the efuse block.   //burn_efuse 거의 동일하며, Block 과 Bit를 입력하여 Write  
    adc_info            Display information about ADC calibration data stored in efuse.
    dump                Dump raw hex values of all efuses
    summary             Print human-readable summary of efuse values
    burn_key            Burn a 256-bit key to EFUSE: BLOCK1, flash_encryption, BLOCK2, secure_boot_v1, secure_boot_v2, BLOCK3
    burn_key_digest     Parse a RSA public key and burn the digest to eFuse for use with Secure Boot V2
    set_flash_voltage   Permanently set the internal flash voltage regulator to either 1.8V, 3.3V or OFF. This means GPIO12 can be high or    
                        low at reset without changing the flash voltage.
    burn_custom_mac     Burn a 48-bit Custom MAC Address to EFUSE BLOCK3.
    get_custom_mac      Prints the Custom MAC Address.

optional arguments:
  -h, --help            show this help message and exit
  --chip {auto,esp32,esp32s2,esp32s3beta2,esp32s3beta3,esp32c3}, -c {auto,esp32,esp32s2,esp32s3beta2,esp32s3beta3,esp32c3}
                        Target chip type
  --baud BAUD, -b BAUD  Serial port baud rate used when flashing/reading
  --port PORT, -p PORT  Serial port device
  --before {default_reset,no_reset,esp32r1,no_reset_no_sync}
                        What to do before connecting to the chip
  --debug, -d           Show debugging information (loglevel=DEBUG)
  --virt                For host tests, the tool will work in the virtual mode (without connecting to a chip).
  --path-efuse-file PATH_EFUSE_FILE
                        For host tests, saves efuse memory to file.
  --do-not-confirm      Do not pause for confirmation before permanently writing efuses. Use with caution.



  • espefuse.py 사용방법 (OTP 설정값 확인용)
실제 Serial에 연결하여 각 설정 값을 확인하도록 하자 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 summary     //OTP 설정된것 확인용 
Connecting....
Detecting chip type... ESP32
espefuse.py v3.1-dev
EFUSE_NAME (Block)                       Description  = [Meaningful Value] [Readable/Writeable] (Hex Value)
----------------------------------------------------------------------------------------
Calibration fuses:
BLK3_PART_RESERVE (BLOCK0):              BLOCK3 partially served for ADC calibration data   = False R/W (0b0)
ADC_VREF (BLOCK0):                       Voltage reference calibration                      = 1114 R/W (0b00010)

Config fuses:
XPD_SDIO_FORCE (BLOCK0):                 Ignore MTDI pin (GPIO12) for VDD_SDIO on reset     = False R/W (0b0)
XPD_SDIO_REG (BLOCK0):                   If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset    = False R/W (0b0)
XPD_SDIO_TIEH (BLOCK0):                  If XPD_SDIO_FORCE & XPD_SDIO_REG                   = 1.8V R/W (0b0)
CLK8M_FREQ (BLOCK0):                     8MHz clock freq override                           = 55 R/W (0x37)
SPI_PAD_CONFIG_CLK (BLOCK0):             Override SD_CLK pad (GPIO6/SPICLK)                 = 0 R/W (0b00000)
SPI_PAD_CONFIG_Q (BLOCK0):               Override SD_DATA_0 pad (GPIO7/SPIQ)                = 0 R/W (0b00000)
SPI_PAD_CONFIG_D (BLOCK0):               Override SD_DATA_1 pad (GPIO8/SPID)                = 0 R/W (0b00000)
SPI_PAD_CONFIG_HD (BLOCK0):              Override SD_DATA_2 pad (GPIO9/SPIHD)               = 0 R/W (0b00000)
SPI_PAD_CONFIG_CS0 (BLOCK0):             Override SD_CMD pad (GPIO11/SPICS0)                = 0 R/W (0b00000)
DISABLE_SDIO_HOST (BLOCK0):              Disable SDIO host                                  = False R/W (0b0)

Efuse fuses:
WR_DIS (BLOCK0):                         Efuse write disable mask                           = 0 R/W (0x0000)
RD_DIS (BLOCK0):                         Efuse read disable mask                            = 0 R/W (0x0)
CODING_SCHEME (BLOCK0):                  Efuse variable block length scheme
   = NONE (BLK1-3 len=256 bits) R/W (0b00)
KEY_STATUS (BLOCK0):                     Usage of efuse block 3 (reserved)                  = False R/W (0b0)

Identity fuses:
MAC (BLOCK0):                            Factory MAC Address
   = c4:dd:57:71:08:08 (CRC 0x5f OK) R/W
MAC_CRC (BLOCK0):                        CRC8 for factory MAC address                       = 95 R/W (0x5f)
CHIP_VER_REV1 (BLOCK0):                  Silicon Revision 1                                 = True R/W (0b1)
CHIP_VER_REV2 (BLOCK0):                  Silicon Revision 2                                 = True R/W (0b1)
CHIP_VERSION (BLOCK0):                   Reserved for future chip versions                  = 2 R/W (0b10)
CHIP_PACKAGE (BLOCK0):                   Chip package identifier                            = 1 R/W (0b001)
MAC_VERSION (BLOCK3):                    Version of the MAC field                           = 0 R/W (0x00)

Security fuses:
FLASH_CRYPT_CNT (BLOCK0):                Flash encryption mode counter                      = 0 R/W (0b0000000)
UART_DOWNLOAD_DIS (BLOCK0):              Disable UART download mode (ESP32 rev3 only)       = False R/W (0b0)
FLASH_CRYPT_CONFIG (BLOCK0):             Flash encryption config (key tweak bits)           = 0 R/W (0x0)
CONSOLE_DEBUG_DISABLE (BLOCK0):          Disable ROM BASIC interpreter fallback             = True R/W (0b1)
ABS_DONE_0 (BLOCK0):                     Secure boot V1 is enabled for bootloader image     = False R/W (0b0)
ABS_DONE_1 (BLOCK0):                     Secure boot V2 is enabled for bootloader image     = False R/W (0b0)
JTAG_DISABLE (BLOCK0):                   Disable JTAG                                       = False R/W (0b0)
DISABLE_DL_ENCRYPT (BLOCK0):             Disable flash encryption in UART bootloader        = False R/W (0b0)
DISABLE_DL_DECRYPT (BLOCK0):             Disable flash decryption in UART bootloader        = False R/W (0b0)
DISABLE_DL_CACHE (BLOCK0):               Disable flash cache in UART bootloader             = False R/W (0b0)
BLOCK1 (BLOCK1):                         Flash encryption key
   = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
BLOCK2 (BLOCK2):                         Secure boot key
   = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
BLOCK3 (BLOCK3):                         Variable Block 3
   = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W

Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).
상위보다는 거의 사용하지 않겠지만, 다른 기계와 설정값 비교할때는 dump가 최고 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 dump //OTP 설정된것 확인용 
Connecting....
Detecting chip type... ESP32
BLOCK0          (                ) [0 ] read_regs: 00000000 57710808 005fc4dd 0000a200 00000237 00100000 00000004
BLOCK1          (flash_encryption) [1 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK2          (secure_boot_v1 s) [2 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK3          (                ) [3 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
espefuse.py v3.1-dev
만약, MAC을 사서 사용한다면 그때 설정하고 확인 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 get_custom_mac  //기본으로 사용하므로 없음
Connecting.....
Detecting chip type... ESP32
espefuse.py v3.1-dev
Custom MAC Address is not set in the device.

  • Secure Boot 설정완료 후 OTP 설정 할 것 
SecureBoot를 완벽히 진행하려면, 나중에는 JTAG 과 UART를 막아야 하며, 이후 부터 OTA로만 진행 
  1. ESP32 의 JTAG 막기 
  2. ESP32 UART BOOT 막기 
//EfuseDefineFields
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_efuse   JTAG_DISABLE 1        --before default_reset --do-not-confirm   
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_efuse   UART_DOWNLOAD_DIS 1   --before default_reset --do-not-confirm   //주의: 이후 Serial로 eFUSE도 동작안됨
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_efuse   CONSOLE_DEBUG_DISABLE 1

(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_bit BLOCK0 198   //DISABLE_JTAG  , 상위와 동일 
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_bit BLOCK0 27    //UART_DOWNLOAD_DIS
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 burn_bit BLOCK0 194   //CONSOLE_DEBUG_DISABLE

--before default_reset --do-not-confirm  반드시 사용 


(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 adc_info

(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 read_protect_efuse  BLOCK0
(idf4.2_py3.8_env) D:\Works\Project> espefuse.py -p COM15 write_protect_efuse



eFUSE API를 사용할 경우 



10/13/2022

Secure Boot (ESP32/ARM) 비교 및 기본이해

1. 기본 Certificate 활용 및 암호화 이해 

일반 기본 암호화 개념부터 알고, 그 다음에 Cetificate 를 왜 사용하는지 부터 알아보도록하자.
정확히 Authentication(인증)이라고 하는게 맞을 것 같다. 

주로 많이 사용되는 것은 TLS/SSL일 것이며, 이를 이용한 HTTPS 비롯하여 다양한 네트워크 프로토콜에서 사용되어지고 있다. 
그리고, 이를 넘어 Security Boot 를 비롯하여, Android app 인증등 많은 곳에서 사용되어지는 암호화 기술이다. 


  • 각 기본용어확인 
Authentication 과 Public Key 기반의 Certificate 및 Format X.509

보통 Certificate는 X.509 Format로 저장되어지므로 상위 기본내용은 알아두도록 하자. 

  • 암호화 기본개념
암호화의 기본개념과 Cetificate 기반의 TLS 기본이해 

  • Android의 인증시스템 구조 
Android에서 App을 인증하는 방법으로 Cerficate 기반의 Singining 과 Verfication  

  • SELinux(Security-Enhanced Linux)
SELinux로  Linux에서 인증된 App 과 그렇지 못한 App을 구분하여 실행하는 구조이다. (상위 Android 와 비슷하다.)  
OP-TEE기능으로 구현이 되어있을 지도 모르며, Selinux 기능은 Kernel Argument로 on/off를 할 수 있는 것으로 기억하며 Kernel config도 있다. 

  • ECDSA  Key 및 Certificate 생성 및 분석 
요즘 대세인 ECSDA Cerficiate 구조 구성 및 세부분석 과 사용방법 

  • WIFI-WPA 인증(Authentication)
WIFI의 EAP methods에서도 사용되어지며, 이부분은 Layer 2부터 같이 동작되어지며, WIFI기본 동작으로 알고 있어야 한다. 


1.1 PKCS11 와 HW Module  

내가 PKCS11를 처음 접한 것은 KCMVP를 다루면서 관련 인증서들을 분석하며 알게되면서 부터일 것 같다.
 
일단 PKCS11는 HSMs/TPM/KCMVP/CMVP/SmartCard/uSIM 등에서 주로 HW 와 함께 사용되는 기술이라고 생각되면 될 꺼 같다. 

쉬운 예를들면, 특정 HW모듈에 PKCS11 Interface기반으로 인증서를 넣어 못 건들게 하고 이 기반으로 TLS 방식으로 암호화를 하는 것이다. 
대표적으로 쉽게 이해가능한게 TPM일 것 같다. 요즘 대부분 컴퓨터에 설치되어있으니까. 

이외에도 CMVP라고 HW모듈기반으로 TLS/DTLS 통신하는 것이 있으며, 다양한 것이 있으므로 너무 암호화를 한정적으로만  생각할 수가 없을 것 같다. 
이 내용을 Secure Boot에 다루는 이유는 이 기반으로 Booting에도 사용되어지기 때문이며, 이외 다양하게 사용되어진다. 

  • PKCS(Public-Key Cryptography Standard) 11 관련내용
PKCS11의 경우 Token or Key를 저장하는 구조이며, 이에 관련 된 정보들은 아래 
  

2. ARM HW 암호화 구성   

ARM의 경우, TrunstZone과 다양한 HW 암호화를  제공하고 있어, ARM을 기반으로 보안기능을 제공하고 있으며, 
이 기능기반으로 Secure Boot로 뿐만 아니라 다양한 암호화 기능을 사용가능하다. 
암호화된 App 실행를 비롯하여, TLS/SSL 를 비롯하여 다양하게 사용되어진다.  

암호화에 대해서 너무 한정적으로 생각하지 말도록하자. 


2.1 ARM의 TrustZone 과 TEE 기능이해 

ARM의 경우는 TEE(Trust Execution Environment)REE(Rich Execution Environment)로 ARM 내부에서 분리되어 실행되어진다. 
이는 기본으로 MMU를 기반으로 Protection을 진행하여 제어하는 방법이 필수 일 것 같다. 

  1. TEE(Trust Execution Environment) : ARM의 TrustZone을 이용하여 실행되는 환경구조 
  2. REE(Rich Execution Environment): ARM의 TrustZone을 사용안하는 환경구조 


  • ARM의 TrustZone 과 MMU 사용  
다중 Core를 사용하다보니, MMU기반으로 분리를 하는게 쉽지는 않는 것으로 보인다. 


  • ARM에서 PKCS11 기반으로 OP-TEE 사용
NXP의 i.MX8의 Secure Boot 

  • OP-TEE를 이용하여 Secure Application 실행 
TA(Truested Application)

PKCS#11 과 OP-TEE의 i.MX8 관련내용(PCKS#11 TA)

PKCS#11 과 OP-TEE 의 IoT 관련내용 

OP-TEE 사용법과 이해  


  • ARM TrustZone
ARM에서 제공하는 HW적은 암호화 기능이며, 이를 확장하여 사용하는 것 TEE/REE    

  • Uboot의 TEE(Trusted Execution Environment) 
TEE(Trusted Execution Environment) 이며, 즉 Secure Boot를 비롯하여 Secure App, 다양한 암호화를 ARM HW부터 제공한다. 
Linux Command인 TEE 명령어와 혼동하지 말도록하자 

  • Uboot의 TEE(Trusted Execution Environment) 와 OP-TEE(Open Portable TEE) 예제 
Uboot에서 TEE를 설정하여 Secure Boot를 진행가능하며, OP-TEE도 설정가능 

  • Yocto에서는 OP-TEE(Open Portable TEE) 제공
이를 이용하여 Secure Boot를 비롯하여 인증된 App을 실행가능 한걸로 기억하며, 유사기술이 SELinux인걸로 기억한다. 

기존에 알고있는것을 다 정리 할 수 없을 것 같아 상위 정도로만, ARM 관련사항 정리를 마무리하고, 
TI Sitara or DM Series 확인을 한번 더 확인 

3. ESP32 HW 암호화

ESP32의 경우는 HW 암호화 기능과 OTP(One-Time Programmable), 즉 eFUSE를 제공하며, 다양한 암호화 기능을 제공한다.  
여기서 OTP(One-Time Programmable)이며, OTP(One-Time Password)이 가 아니므로 혼동하지 말자. 

결론적으로, ESP32도  eFUSE(One Time Programing) 이곳에서는 Secure Boot  PKCS11 기반의 Secure Boot과 거의 동일한 걸로 보이며, 
ARM처럼 HW 암호화기능 있어, 거의 유사하게 동작가능하며, 이외 ESP32 HW 암호화 기능도 제공해주고 있다. 

ESP32를 가격이 싸다고 만만히 볼만한 MPU는 아닌것 같으며, 정말로 다양한 기능을 제공하고 있다

3.1 ESP32의 Secure Boot 

ESP32의 Secure Boot의 OTP(One Time Programing,eFUSE)기반으로 진행되는 Siging/Verfication 기술이며, 이를 ESP32에서 제공을 해준다. 
ESP32의 경우도 역시 내부적으로 HW적으로 암호화 기능을 제공해지만, ARM의 TrustZone 동작방식과는 다르며,
내부적으로 간단하게 구성하여 제공해주고 있다. 




  • OTP(One Time Programing,eFUSE)
ESP32 내부에 제공해주는 1024bit 저장장소 제공해주고 있으며, 각 256bit 씩 4개의 Block으로 관리되어 지고 있다. 
OTP(eFUSE)의 경우 0 -> 1로만 변경가능하며, 한번 1로 변경되면 변경 불가능하다. 
https://blog.espressif.com/understanding-esp32s-security-features-14483e465724


  • ESP32 의 Secure Boot 의 전체 Flow  
Reset 후 ROM에서 1st stage 인 ROM에서 Boot를 하고 OTP(eFUSE)의 설정에 따라 동작이 되어진다. 
  1. OTP(eFUSE)에 저장된 Key 값 기반으로 Flash의 BootLoader를 Load 하기 전에 Verify하고 Booting  
  2. Flash의 Bootloader는 Bootloader 처럼 App image를 Verify를 진행하여 Booting 

https://blog.espressif.com/understanding-esp32s-security-features-14483e465724

상위구조를 쉽게 설명하면, OTP(One Time Programiming, eFUSE)에는 저장소의 한계 (256bit) 때문에 Hash(Token) 이용하여 한 번 더 검증하는 방식이다. 
보통 다른 곳에서는 이 Hash를 Token이라고도 많이 부르기도 한다. 


  • SecureBoot 의 BootLoader 구성 과 OTP(eFuse) 
  1. Bootloader Image:  Flash 에서 실행가능한 일반적인 image 이며, 일반부팅은 이것만 필요 
  2. RSA Signature:  Certificate의 Signature 처럼 상위 Image의 RSA3072기반으로 Private Key 로 생성 
  3. RSA 3072 Public Key: 상위 RSA Signature를 확인하기 위해서 Public Key가 필요 
  4. OTP(eFUSE): 저장된 Key의 Hash(SHA-256)로 RSA 3072 Public Key가 Verify 검증 


  • Bootloader 를 실행하기 전에 아래의 동작을 수행 
  1. OTP(eFuse)의 Hash를 이용하여 RSA 3072 Public Key가 맞는지 확인(Public Key Verfication)
  2. RSA 3072 Public Key를 이용하여 RSA Signature를 확인가능  
  3. Bootloader 실행 (RSA Signature는 이미 검증된 것임)

https://blog.espressif.com/understanding-esp32s-security-features-14483e465724


  • Flash 의 App Image의 기본구성 
상위와 동일한 구조 구조 이므로 생략 

  • Flash Encryption 
Secure Boot 기능을 넘어  Flash 저장된 정보를 ESP32 HW적으로 AES Key기반으로 Decrytion을 하여 동작가능하게 하는 기능이다. 
하지만, 이 기능을 사용하면, ESP32 성능과 Flash 기반의 XiP가 어떻게 될지가 좀 의문이다. 
그림상으로는 완벽하지만, 항상 실행해보면 문제가 생기는 경험을 많이 했기때문에, 나중에 한번 해보고 말을 해야 할 것 같다. 
(특히, SRAM 문제, Cache도 SRAM에 포함되며, 설정에 따라 SRAM 사용구조가 변경)

https://blog.espressif.com/understanding-esp32s-security-features-14483e465724




ESP32-S2 Secure Boot 

10/06/2022

Cmake 와 Kconfig

1. Kconfig 와 Autotools

Cmake 를 접하게 된 것은 오래된것 같은데, Cmake와 Ninja를 본격적으로 접하게 된 것이 ESP Series를 접하면서 부터 였던걸로 기억한다. 
이전에 Cmake 언제 사용했는지는 나도 좀 많이 헷갈린다.  
이전에 사용했다면, 별 대수롭지 않게 생각하고, 기존 것과 비슷하게 고쳐서 사용했던 것 같다. 

일단, 오래전 각 Solution들을 만들면서, Open Source Application들을 구성할 일이 많이 있었는데, 
주로 전체 설정구성 과 연결을 시스템을 Autotools 구성했다. 
사실 autoconf의 script를 보면, Cmake도 생각나기도 하는 것 같다. 

개발자는, 
autotools 기반 (Autoconf/automake/makefile)으로 Script 설정하여 만들고, 
유저는 configure를 통해 처음설정(CONFIG)하며, 빌드환경인, Makefile이 자동생성되어 로 build를 하면되는 구조이다. 
보통 autotools은 Open Application 많이 사용되어졌으며, 현재 사용되어지는지는 나도 정확히 잘 모르겠다. 

Kconfig 역시 Linux Kernel에서 사용되어지만, Kconfig 문법만 매번 보고, 이를 추가하여  Linux Device Driver 구현하였다. 

기본원리만 알아서 완벽히 세부적인 설명(둘 사이 차이)을 설명을 못하겠지만, 
Autotools의 경우는 Automake 기반으로 Makefile의 설정 변경하여, Makefile 만들어서 Build 환경을 만들걸로 기억한다.
현재 Kconfig 와 Cmake 조합도, 역시 이 구성가능하다. 


이 것이 중요한 이유는 설정(Config)에 따라 전체 Build 구성 환경변경되기 때문이다. 
  1. 언어(C언어) 입장에서 config.h 설정으로, 쉽게 각 기능 ON/OFF 가능 (Preprocessing)
  2. Build 환경(Make or Cmake)입장에서는 각 언어의 Build 여부 및 Build 환경선택가능

사실 만드는 사람입장에서는 script로 다양하게, 각 설정해야하기 때문에 복잡하다.
그 복잡성을 보고 싶다면, 아래링크를 간단히 참조를 하면 되겠다.   
 
  • Autoconf 의 구성 및 Script 
간단하게 설명하면, configure.ac 가 있고, m4 script 와 이용하여 각 헤더 구성과 환경설정 하고, 
최종적으로 configure.ac 와 Makefile.am으로 Makefile을 만들어준다고 보면된다. 
(나도 너무 오래되어서 틀리수 도 있다.)

  • Linux Kconfig 
Kconfig 의 경우, 아래와 같이 문법이 존재하여, 이 부분이 Makefile 과 연결되고 autoconf와 비슷하게 전체 header를 자동으로 생성해준다. 

나중에 다시 직접 구현할 기회가 있다면, 그 때 세부적으로 정리하도록 하겠다. 

Kconfig 기본문법 

Python Kconfig 
요즘 Python으로 안되는 것이 없는 것 같으며, 확장도 쉽게되어 진다니, 신기할 뿐이며, ESP-IDF는 이것을 사용한다고 한다


1.1 Cmake 란?

Cmake는 script로 build 환경을 쉽게 구축하여 가능하고, 버전 업이되면, ninja가 추가되어 build 속도가 빨라졌다고 한다.  
하지만 지금까지 Manaul 구성보고, ESP-IDF를 이용해보고 있지만,  별도의 Config는 KConfig를 사용하고 있다.  
별도의 Linux Kernel의 Kconfig or Autoconf 같은 것의 도움이 필요한걸로 보인다. 
ninja 역할은 빌드 속도를 높이기 위해서 사용되어지는다고 하는데, Cmake에 이제는 기본으로 들어가서 동작되어진다고 한다. 

ESP, ESP-IDF 구성보면, Autoconf/Automake/Makefile 이 아니라. Kconfig/Cmake 구성되는 구조이며, Linux Kernel 구조와 거의 유사하다.
다르다면, Linux Kernel 나의 기억으로는 Cmake를 사용안했던 걸로 기억한다 (이 부분 추후 확인)  

Zephyr 구성역시 보면 Kconfig/Cmake 구성으로 ESP와 거의 유사하다. 

둘 다 OS기반으로 한 설정을 해야 하니, Linux Kernel 구조와 비슷하게 했으리라 본다. 


결론적으로 Cmake는 기본 Make 를 보다 사용하기 편한 Make로 만들어 줄 수 있도록 다양한 Command Script를 제공한다. 
이는 각 Script를 이용하여 쉽게 확장 및 독자적인 함수 구성이 가능하다.  
그래서, Zephyr  와 ESP 의 경우 세부적으로 분석하면, 독자적인 Cmake Function들이 많다.


Cmake Main Home 

Cmake 의 문서 

Cmake Manual
 
Cmake 의 Ninja


1.2 Make 와 Cmake 차이 

Makefile 과 CMakelist.txt 비교 

Cmake 와 Raspberry Pico
아래의 글을 읽어보면, Cmake도 내부에 Make를 사용한다고 한다. 

상위글 과 Manual을 보면, Cmake역시 기본적으로는  Cmakelist.txt를 이용하고 있지만, 결과적으로 Makefile을 이용하고 있으며, 대신에 부족한 부분기능들을 
Cmake에서 제공하는 각 Script로 쉽게 설정 및 Function으로 확장가능하며, 이를 사용하는 것 같다.  

cmake CMakelist.txt 


1.3 CMakelist.txt 구조 및 문법 

일단 CMakelist.txt 문법은 마치 Shell Script 처럼 구성이 되어있으며, 각 Version에 따라 다양한 기능들을 제공해준다. 

Cmake manual (최신, 본인 버전에 맞게 선택해서 보자)

CMake Tutorial 

Cmakelist.txt에서 주로 구성해서 사용하는 것들이 Cmake Script/Project Command가 주로 될 것이라고 생각되어진다. 

  • Cmake Commands
가장 중요한 Manaul로 반드시 숙지해야하며, 반드시 버전과 같이 확인해야 한다. 
Cmake-commands 전체구성  
  1. Scripting Commands
  2. Project Commands
  3. CTest Commands
  4. Deprecated Commands : 사라질 것이므로 가급적 사용말자 

Cmake Script Commands 
Cmake Script Commands 의 Function Command 와 macro 확인 
Zephyr or ESP에서 각 독자적인 Function을 만들어 구성 (아래 참조)

Cmake Project Commands

Cmake Ctest Commands

주로 사용되어지는 Command는 일반적인 Script 와 Project Build 설정 및 관리를 위해서 사용되어지는 Command들이다. 
Ctest는 아직 잘모르기에 링크만 연결한다. (단위테스트용으로 사용하는 것으로 추측 혹은 build 관련 테스트 일 것 같다.) 


  • CMakelist.txt 에서의 Script Commands 예제 
기본 if/elseif/elese/endif
if( condition)
elseif (condition)
else (condition)
endif(condition)

다음과 같이 사용하면 되겠으며, Shell Script 처럼 사용가능하며, 쉽게 확장도 가능하다  

if(ENABLE_A AND ENABLE_B )      # AND 연산 
    
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")           # Shell Script 처럼 변수 값을 설정 
    set(CMAKE_C_FLAGS_ASAN "${CMAKE_C_FLAGS_ASAN} -Wno-error")
    set(CMAKE_C_FLAGS_ASANDBG "${CMAKE_C_FLAGS_ASANDBG} -Wno-error")
    
elseif (ENABLE_A OR ENABLE_B)   # OR 연산 
    
    list(APPEND crt_paths ${DEFAULT_CRT_DIR}/cacrt_all.pem)
    list(APPEND args --filter ${DEFAULT_CRT_DIR}/cmn_crt_authorities.csv)

elseif (NOT ENABLE_B)   # NOT 연산 
    
    if(NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))    # MATCHES를 보면, regex를 사용하여 wild card 형식
      set_target_properties(sodium
                            PROPERTIES IMPORTED_LOCATION_RELWITHDEBINFO
                                       "${sodium_DLL_RELEASE}"
                                       IMPORTED_LOCATION_MINSIZEREL
                                       "${sodium_DLL_RELEASE}"
                                       IMPORTED_LOCATION_RELEASE
                                       "${sodium_DLL_RELEASE}")
    endif()

else ()     # 재미있는 것은 else에 condition을 넣을 수 있지만, 잘 사용하지 않는 것 같다. 추후 테스트 
endif()

상위 구성을 봐도 거의 Shell Script 구조이며, 내가 보기에는 다양한 예제만 있다면, 거의 쉽게 따라 할수가 있을 거라고 본다. 
다양한 예제는 Zephyr 와 ESP를 참조하도록 하자 

Cmake 관련 자료링크 


2. Kconfig 와 Cmake 구성 

ESP-IDF 와 Zephyr 의 구성은 위에서 설명했듯이 Kconfig 와 Cmake 기반 구성이며, 이를 간단히 비교해보자. 
Kconfig의 경우, Linux Kernel을 많이 보신분들이라면 쉽게 아는 구성이다.


  • Kconfig (Linux Kernel Config)
make menuconfig로 설정하면 이 설정된 값이 config에 저장되고, 저장된 config가 Build 환경환경설정하고, 
별도의 C의 header File을 생성하여, 각 Linux Kernel 소스의 preprocessor 반영한다. 

Kconfig에서 이를 설정을 정의하면, User는 이를 쉽게 선택이 가능하다. 
위에서 설명했듯이, 
Build 환경인,Cmake를 통하여 빌드 여부와 Build 환경설정 변경가능하다.
또한 Kconfig를 통해 최종 config.h 최종설정된 Header file을 얻어 C 언어에서 이를 기반으로 Preprocessing이 가능하다. 


2.1 Zephyr 의 Kconfig/Cmake구성 

Zephyr의 경우는 현재 소스가 없고, 빌드를 못하기 때문에 아래와 같이 소스로만 간단히 보도록하자. 
너무 분석할게 많아 추후 Zephyr를 사용할 기회가 있다면 그때 세부적으로 분석하고 이해하도록 하겠다. 
거의 Linux Kernel을 알고 ESP도 안다면 쉽게 이해하리라 본다. 

  • Zephyr Kconfig 구성 
항상 source로 추가되어지는 구조이므로, 전체소스를 분석하기 보다는 한번 실행해보면, 구조가 어떻게 구성되는 쉽게 파악되어진다. 
실행은 make menuconfig 

Kconfig 
....
config CODE_DATA_RELOCATION
	bool "Relocate code/data sections"
	depends on ARM
	help
	  When selected this will relocate .text, data and .bss sections from
	  the specified files and places it in the required memory region. The
	  files should be specified in the CMakeList.txt file with
	  a cmake API zephyr_code_relocate().
.......
CMakefileLists.txt
if (CONFIG_CODE_DATA_RELOCATION)
  set(CODE_RELOCATION_DEP code_relocation_source_lib)
endif() # CONFIG_CODE_DATA_RELOCATION

  • Zephyr의 Cmake 독자함수 구성 
이곳이 중요한 이유는 Zephyr의 독자적인 Cmake Function을 비롯하여 관련부분을 제공하기 때문에 중요하다.

  • Zephyr의 Cmake 독자함수 구성의 세부분석 
Kconfig 관련 구성 및 각 동작분석 

Zephyr에서 제공하는 Cmake Command로 Function 으로 Zephyr에서 독자적으로 구현했으므로, 이 관련 Manaul은 반드시 참조  

Zephyr도 이제 QEMU을 지원가능한걸로 보임 (QEMU 에뮬레이터?) 
ESP도 최근부터 지원가능하며, QEMU을 통해 PC에서 테스트 가능하다 

Linker 에 관련된 Cmake로 각 Macro를 확인  

각 Python 파일구성으로 ESP처럼 Tools로 역할할 것으로 생각되어지며, 이를 이용하여 개발환경 Tool을 구성한 것 같다. 
Python 개발 Tool 환경에 관심있다면, 이부분이 중요할 것 같다. 


2.2 ESP-IDF 의 Kconfig/Cmake구성 

개인적은 ESP32 와 ESP-IDF는 너무 잘만들어 놓았기에, 실제 이를 이용하여 사용하는 개발자 입장에서는 감사할 뿐이다. 

  • ESP-IDF Kconfig 구성 
항상 source로 추가되어지는 구조이므로, 전체소스를 분석하기 보다는 한번 실행해보면, 구조가 어떻게 구성되는 쉽게 파악되어진다. 
실행은 make menuconfig 하여 Kconfig 설정확인을 한다. 
이를 저장하면 sdkconfig 가 생성되며, 이는 build 에 config/sdkconfig.h 생성되어 모든 소스에서 이를 참조 적용가능하다    

ESP-IDF Kconfig Mainmenu 
ESP-IDF에서 menuconfig 를 실행하면 실행되어지는 Main Kconfig (mainmenu 검색)
  1. source "$COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE"  //각 Project 별의 Kconfig.projbuld 추가 확장 
  2. source "$COMPONENT_KCONFIGS_SOURCE_FILE" //각 Component 의 Kconfig 추가확장


ESP-IDF config  sdkconfig 의 확장사항 
sdkconfig.default 설정 및 다양한 설정 

  • ESP-IDF의 Cmake 독자함수구성 및 구현 
이곳이 중요한 이유는 ESP의 독자적인 Cmake Function을 비롯하여 관련부분을 제공하기 때문에 중요하다.
Espressif에서 독자 구축한 함수이므로 관련사용법은 아래의 ESP Manual을 참조하고 구현된 것은 이곳을 보도록하자.


  • ESP-IDF의 Cmake 독자함수구성 세부분석 
Kconfig 와 Cmake 관련구성으로  sdkconfig.h 생성확인  (이외 Json 파일확인)

상위 Command로 Function 각 독자적인 구성확인  

ESP의 component function 

ESP32의 Build 기본빌드환경 

ESP-IDF 기반으로 Project 구성시 


  • ESP-IDF의 Cmake 독자함수 사용법 및 빌드 Manual 
각 Espressif에서 독자 구축한 Cmake 환경구성 사용하는 방법으로 반드시 ESP-IDF 개발자는 알아야 하는 곳이다. 
Manual 구성이 잘되어 있어 기본 Kconfig 문법부터 ESP-IDF Cmake 사용법 부터 구성까지 잘 되어있다.  
이 Manual을 보고 상위 Function들을 이용목적과 사용방법을 쉽게 익혀 사용가능하다. (반드시 참조)

9/15/2022

ESP32-S3 TinyML (Edge AI )

1. Arduino IDE 와 TinyML 

우연히 LinkedIn에서 보게되었는데, MCU기반으로 Machine Learning을 해서 동작이되는 것을 보고, 좀 쇼크를 받았다. 
Arduino 기반으로 Transfer Learning하여 이를 지원하는 것으로 보이며 음성인식을 하여 로봇 손을 제어한다. 
아이러니한 것은 소스를 보면, BLE로 동작되는 걸로 보인다. 

  • HW 사양 
  1. Board: Arduino Nano 33 BLE Sense (Nordic 사)
  2. 기타: Servo 와 BreadBoard(빵판) 과 Jumper 

  • 개발 Tools 
  1. Edge Impulse Studio 
  2. Arduino IDE 

일단 Transfer Learning(Training) 알려고 하면, 어쩔 수 없이 상위 Edge impulse Studio를 좀 알아야 할 것 같다. 
 
  • 음성인식 방법(모델?)
  1. MFCC(Mel-Frequency Cepstral Coefficient)
  2. MFE 

MFE 와 MFCC 비교 


  • Inference
NVIDIA Jetson 처럼 INT8 까지는 아니여도, INT16까지 양자화해서 Inference하는 것으로 보인다. 

  • 결론 
MFCC or MFE 사용한다고 하며, 아직 MFE는 검색이 안되어서 잘 모르겠다.
MFCC는 상위 블로그로 읽어보면, 간단히 동작방식의 핵심 다음과 같다고 생각되어진다. 

Mel-Filter Bank 라는 것을 일종의 Pattern을 저장해서 Machine Learning하는 것으로 보인다. 
다만 가성주파수 영역에 (300Hz ~ 3300Hz) 집중적 배포하여 사람 목소리인 음성인식을 하는 것으로 보인다. 

다시 DCT도 나오고 그렇는데, 오랜만에 다시 DSP를 보니 골치아프다. 
DCT 와 DFT/FFT 의 차이는 간단히 말해, 허수 사용여부이다. 
추후 시간이되면 좀 더 자세히 알아보도록 하자 .


1.1 Edge Impulse Studio 

Edge Impulse가 무엇인지 궁금해서 찾아 보게되었는데, 아래 사이트에서 쉽게 답을 얻었다. 

나의 경우는 거의 Arduino를 거의 사용해본 경험이 없는데, 거의 Arduino 기반으로 Library가 제공되어지는 것으로 보인다. 

  • EdgeImpulse Studio 
이 사이트 기반으로 Project를 진행하는 것으로 보이며, 나도 오늘 가입을 했다. 


1.2 Tensorflow light for MCU

Tensorflow를 보니, 지원되어지는 MCU Board는 적은 것으로 보이며, 다행히 Espressif 사의 ESP도 지원가능한걸로 보인다. 
다만, 대체적으로 Arduino 기반으로 Project가 진행이 되는 것으로 보인다. 
일단 Arduino 와 ESP32 or 다른 MCU EVM이 필요하다 

TensorFlow light for Microcontroller

ESP32 와 Tensorflow Lite for Microcontroller
ESP-IDF 와 ESP-NN을 사용한 것으로 보이며, CNN구조의 간단한 예제 


2. TinyML 

좀 생소하기는 한데, 아직 제한적으로 사용되어지는 것 같으며, MCU기반으로 사용되어지는 걸로 보인다. 
검색을 해보면, 책까지 나왔을 정도로 많이 보편화가 된 것으로 보이며, 현재 관심을 두고 보고 있다.
대부분 Inference를 할 경우, 각 NN Library와 연결하여 사용하는 것으로 생각되어진다. 


2.1 TinyML 관련책 과 세미나  


TinyML 
O'REILLY 에서 처음 영문으로만 나왔는데, 한글로 나옴 

TinyML Home 

Google TinyML 

Meetup TinyML
Meetup을 통해서, 자주 Youtube에 관련내용들이 공유되어지고 있음 


2.2 ARM 의 TinyML

ARM을 주로 보면 Cortex-M 기반으로 CMISIS-NN으로 연결하여, 주로 Inference용으로 사용하는 것 같다. 





2.3 ESP32의 TinyML 

ESP경우, ESP-NN이 존재하며, 연결하여 확장해서 ESP-DL 
주로 Inference용으로 사용


ESP-NN(Neural Network) 와 ESP-DL(Deep Learning Model)


9/14/2022

VS Code Extension

1. VS Code Extension 

이전에 VS Code 와 관련해서 사용했던 것들을 아래와 같이 링크 

  • VS Code 기본사용 및 Extension 추천 
이전에 설치하여 주로 사용하던 걸로 Remote Development 관련설명 

  • VS Code Extension 저장장소 (본인계정에 저장)
C:\Users\jhlee\.vscode\extensions 저장되어짐 
** 만약 설치시 문제 생기면 상위부분에 가서 각 extensions 직접 삭제

각각의 VS Code extensions program들이 존재 및 설정값도 직접확인 가능 


1.1 VS Code Extension Tools

VS Code Extension 이 2020 과 2022년 비교를 하면 많이 달라졌으며, 현재 내가 주로 사용하고있는 Extension List들을 표시한다. 
그리고, VS Code Extension Program이 계속 Update 되고 사용법도 매번 조금씩 변경되므로, 각 세부사항은 Manaul을 반드시 참조 

  • VS Code Extension- Remote Package 필수사용  
MS사에서 제공하는 Remote Package로 요즘 한꺼번에 다 설치를 해줌(이전과 다름)

  • VS Code Extension- Markdown 관련사항 
Markdown 쉽게 작성하기 위해서 사용 

  • VS Code Extension- Hex File 볼 경우 

  • VS Code Extension- Git 관련사항 

  • VS Code Extension- Cmake 관련사항 
흐음 필요사항을 아직 모름

  • VS Code Extension- C/C++ 관련사항 
C/C++ 관련것으로 소스 분석용 설치 


1.2 VS Code Extension Tools for Comments

주석(Comments)에 관련부분들은 따로 분류하여 링크를 한다. 

Better Comments
가장 많이 사용되어지는 Tool로 현재 나는 Disable 
간단한 사용법은 아래이며, 실제 사이트에서 가서 Update가 되므로 상위에서 확인  

Doxygen Documentation Generator
Doxygen 과 Comment를 쉽게 생성하는방법 
소스위치에서 ( /** 만 입력한 후 엔터 ) 입력하며, Function 위/ File 맨위 각 다르게 생성 

Auto Comment Blocks
주석을 자동으로 Blocking 관리 ( /** or /*!  입력한 후 엔터) 입력 
상위를 이용하면 불필요

TODO Highlight
주석(Comment) 에 TODO/FIXME 쉽게 파악 

이외 Google Translate를 이용하여 주석 자동으로 번역해주는 기능을 비롯하여 다양하다.

1.3 VS Code Extension ESP-IDF

  • VS Code Extension (ESP-IDF 설치)
ESP-IDF 설치 
이전과 변경되어있으며, 쉽게 IDF를 설치를 진행한다.

최근꺼는 MS 의 Email 기반으로도 VS Code Extesion을 Sync를 맞출수 있는 것으로 보여진다.

Espressif IDF VS Code Extension (Node.js 소스)

VS Code Extension 설치위치 
  C:\Users\jhlee\.vscode\extensions


2. VS Code Extension 사용법


이전에 내가 작성한 VS Code Extension Tools 사용법들을 아래에 링크 


2.1 VS Code Remote Package 

VS Code에서 Remote에 있는 Linux Server or WSL를 연결하여 사용하는 법 

VS Code 에서 Remote X11 연결방법 
현재 필요 WSL을 사용하면 필요가 없어보이며, 외부에서 사용할 경우에만 필요 

VS Code 에서 WSL 설치 후 Remote 진행 


2.2 VS Code Git 사용 

VS Code는 기본적으로 Git Package가 없으므로 반드시 먼저 Git을 먼저 설치 한후 사용진행 
세부내용은 아래링크 확인 
 
VS Code에서 Git 사용법 

VS Code VersionControl 


2.3 VS Code JTAG 설정 및 Debug

이 부분은 매번 조금씩 변경되어왔으므로, 만약 더 최신버전을 사용한다면, 반드시 Manaul 참조


ESP32의 OpenOCD 관련설정 (거의 유사하지만 쉽게 설정가능)

  • launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [

        {
            "name": "ESP32 Debugger",
            "type": "espidf", 
            "request": "launch",
            "gdbinitFile":"${workspaceFolder}/.vscode/gdbinit",    
                    
        }
    ]
}  

상위와 같이 ${workspaceFolder} 변수 이용 

  • settings.json 
{
// ESP-IDF 기반으로 개인적으로 생성하여,설정한 설정값들 (불편한것은, 직접 Setting.json에서 변경해야함)     
    "idf.RS485Port": "COM8",     
    "idf.RS485Baudrate": "921600",  
    "idf.RS485ResponseTimeout": "0.100", //0.050 -> 0.100
    "idf.RS485MaxUnit": "5",       
    "idf.RS485TimerInterval": "1", 
    "idf.RS485TesTimeHour": "10",      
    
//ESP-IDF 설치 후 환경설정 값들 (VS Code의 아래 버튼으로 변경가능)     
    "idf.adapterTargetName": "esp32",  
    "idf.flashType": "UART",
    "idf.portWin": "COM3",                  
    "idf.svdFilePath": "${workspaceFolder}/esp32.svd",  //OpenOCD를 위해서 Register Map을 보기 위한 도구 https://github.com/espressif/svd
    "idf.openOcdConfigs": [                             //OpenOCD의 설정 이부분이 이해가 안된다면, OpenOCD를 참조 
        "interface/ftdi/esp32_devkitj_v1.cfg",
        "target/esp32.cfg"
    ]
} 

상위 설정된 값은 tasks.json의 command에서 사용가능하며, template으로 숨겨진 설정값도 존재한다. 

VS Code에 Espressif IDF 포함된 Template 으로 좀 더 다양하게 확인가능 
Command가 Window/Linux 지원해야하므로, 두개로 분리 운영 

상위 소스는 C:\Users\jhlee\.vscode\extensions\espressif.esp-idf-extension-xxxx\\templates\.vscode 존재하며, 나의 경우 직접 수정해서 사용함 

  • gdbinit
target remote :3333
set remote hardware-watchpoint-limit 2
symbol-file ./build/xxxxx.elf
mon reset halt
flushregs
thb app_main
c

2.4 VS Code Task 설정 및 사용법  

각 Project 마다 본인의 Task를 만들어서 TEST Program들을 쉽게 설치하고, Chainging도 가능하므로, 각 자동화도 가능하다. 

우선 내부적으로 사용되어지는 환경변수 및 VS Coe Extesion Tools 내부에서만 사용하는 환경변수들을 알아 두면 좋다. 
또한 dependson을 이용하여 다른 Task 와 Chaining도 가능하다. 

  • tasks.json (Window용)
아래와 같이 자동화 및 Window에서도 Powershell을 이용하여 다양하게 확장가능하다.
만약 Linux/Window 같이 사용하고 자하면 command 위에 별도의 window용을 별도로 설정
{
// See task.json Manual in VSCode
// for the documentation about the tasks.json format
// 아래의 예제들은 내가 간단하게 사용하는 예제 중 일부를 가져온 것이며, 나의 경우, TEST Task가 너무 많아 이를 Chain해서 사용한다. 

    "version": "2.0.0",
    "tasks": [
        {
            "label": "IP Config",
            "type": "shell",
            "command": "ipconfig"
        }, 
// 기본 실행되는 위치가 workspaceFoder가 아닐 경우, 
//다음과 같이 Command를 연속해서 실행가능하며,아래와 같이 ; 세미콜론을 사용하자 (PowerShell Script는 관련 Manaul 참조하도록)   
        {
            "label": "TEST Release",
            "type": "shell",
            "command": "cd ${workspaceFolder}/release; ./makeFirmware.ps1"
        },          
        {
            "label": "ESP-IDF Erase ALL",
            "type": "shell",
            "command": "${userHome}/.espressif/python_env/idf4.2_py3.8_env/Scripts/python.exe ${userHome}/esp/esp-idf/components/esptool_py/esptool/esptool.py -p ${config:idf.portWin}  -b 460800 --before default_reset --after hard_reset --chip ${config:idf.adapterTargetName} erase_flash"
        },       
//아래와 같이 dependson을 사용하여 각 Command의 Label로 적어 Macro로 형식으로, Chain하여 사용하자
//아래처럼 실행하하면, TEST Release -> ESP-IDF Erase ALL -> ESP-IDF WRITE ALL        
        {
            "label": "ESP-IDF WRITE ALL",
            "type": "shell",
            "command": "${config:idf.pythonBinPathWin} ${userHome}/esp/esp-idf/components/esptool_py/esptool/esptool.py -p ${config:idf.portWin} -b 460800 --before default_reset --after hard_reset --chip ${config:idf.adapterTargetName} write_flash --flash_mode dio --flash_freq 80m --flash_size detect 0x070000 ${workspaceFolder}/release/firmware/mxx610-app.bin 0x1000 ${workspaceFolder}/release/firmware/bootloader.bin 0x10000 ${workspaceFolder}/release/firmware/partition-table.bin 0x15000 ${workspaceFolder}/release/firmware/ota_data_initial.bin 0x17000 ${workspaceFolder}/release/firmware/phy_init_data.bin 0x39000 ${workspaceFolder}/release/firmware/storage_data.bin",       
            "dependsOrder": "sequence",
            "dependsOn": ["TEST Release", "ESP-IDF Erase ALL", "ESP-IDF WRITE ALL"]
        
        },
//아래의 경우, 상위 내가 정의한 변수를 이용하여 각각 Python TEST Program을 Jenkins 처럼 사용하고 있다. 
        {
            "label": "TEST MODBUS-CHK-EventGR",
            "type": "shell",
            "command": "& D:/Tools/python/python-3.7.5-embed-amd64/python.exe ${workspaceFolder}/main/module/modbus/modbus_test_check_rspevent_general.py -p ${config:idf.RS485Port} -b ${config:idf.RS485Baudrate}  "
        },
        {
            "label": "TEST MODBUS-CHK-EventBT",
            "type": "shell",
            "command": "& D:/Tools/python/python-3.7.5-embed-amd64/python.exe ${workspaceFolder}/main/module/modbus/modbus_test_check_rspevent_burst.py -p ${config:idf.RS485Port} -b ${config:idf.RS485Baudrate}  -r ${config:idf.RS485ResponseTimeout}  -u ${config:idf.RS485MaxUnit}  -i ${config:idf.RS485TimerInterval} -t ${config:idf.RS485TesTimeHour}  "
        },
        {
            "label": "TEST MODBUS-CHK-SystemInfo",
            "type": "shell",
            "command": "& D:/Tools/python/python-3.7.5-embed-amd64/python.exe ${workspaceFolder}/main/module/modbus/modbus_test_check_systeminfo.py -p ${config:idf.RS485Port} -b ${config:idf.RS485Baudrate}  -r ${config:idf.RS485ResponseTimeout}  -u ${config:idf.RS485MaxUnit}  -i ${config:idf.RS485TimerInterval} -t ${config:idf.RS485TesTimeHour}  "
        },
        
//나의 경우는 다양하게 최대한 편한게 사용하도록 하고 있으며, Jenkins가 없이 쉽게 Task로 모든 TEST 가 가능하다 
        {
            "label": "TEST MQTT ONLY Subscribe",
            "type": "shell",
            "command": "& D:/Tools/python/python-3.7.5-embed-amd64/python.exe ${workspaceFolder}/main/module/mqtt/mqtt_only_subscribe.py"
        },
        {
            "label": "TEST MQTT General Publish",
            "type": "shell",
            "command": "& D:/Tools/python/python-3.7.5-embed-amd64/python.exe ${workspaceFolder}/main/module/mqtt/mqtt_publish_general.py"
        },
        {
            "label": "TEST MQTT RSPSetup Publish",
            "type": "shell",
            "command": "& D:/Tools/python/python-3.7.5-embed-amd64/python.exe ${workspaceFolder}/main/module/mqtt/mqtt_publish_rspsetup.py"
        },    
        
//다만 현재 Window인데, Embedded Python으로 설치해서 사용해야 다른 이에게 동일한 환경을 전해줄 수 있으므로, 추천         
// Linux에서도 VS Code가 사용가능한데, 좀 더 화려하게 사용가능하다. (grep.xargs,sed/awk 비롯하여 tee 등)  Python venv 기반으로 추천  
// EPS-IDF는 Window or Linux 이건 Python은 venv되어있지만, Window에서는 좀 venv를 사용하기가 좀 귀찮다. (export.ps1) 
        {
            "label": "TEST Python List",
            "type": "shell",
            "command": "& D:/Tools/python/python-3.7.5-embed-amd64/python.exe -m pip list  "
        },      
        
// 아래와 같이 Powershell에서 Linux의 Pipe를 비롯하여 간단한 명령어 비롯하여, tee 역시 지원가능하다, 
// Powershell이 매번 Upgrade 될때마다, 매번 Linux Command를 좀 더 다양하게 지원해주는 것 같다.              
        {
            "label": "TEST Shell",
            "type": "shell",
            "command": "echo Hellow | tee ${workspaceFolder}/test.log  "
        }           
     ]
}

모든예제를 기록할 수 없지만, 아래의 predefined 변수(variable)을 비롯하여, Powershell 변수(variable) 이외 외부 설정된 변수 (variable)가능 
더불어 조금 더 고급스럽게 사용하고자 한다면, Node.js는 필수인것 같다. 
이 부분은 추후 다시 사용하도록 하자 

참고로 VS Code도 계속 업그레이드 중이며, Task 내부 Command도 업그레이드 되면서 새로 추가된 것이니, 추후에도 계속 VS Code Manual는 봐야한다.
앞으로 계속 추가되어 변경되어질테니 ㅋㅋㅋ (나중에 Jenkins 없이 자동화로 해도 되겠다)

  • 상위 Tasks.json 과 launch.json 에 사용되어지는 내부환경변수(predefined)
  1. ${workspaceFolder}  : Project PATH
  2. ${userHome} : User Home PATH
VS Code에서 predefined-variables 내부변수로 반드시 확인 
각 값들은 기본으로 알아두도록하자 

  • Tasks.Json 세부 구성방법 
세부사항은 VS Code도 항상 업데이트 되므로 아래 사이트에서 사용방법과 구조를 확인
매번 VS Code가 업데이트 될때 마다 조금씩 변경됨 (Node.js도 지원됨) 
  1. version
  2. configurations
  3. tasks
  4. inputs

  • PowerShell 변수 사용법 
PowerShell에서 사용하는 변수를 동일하게 사용가능 

  • Tasks.json에서 외부 설정된 변수값 (settings.json) 사용법 
VS Extension에 이미 설정되어진것의 값 or settings.json 에서도 변수 값 가져와 tasks.json에서 사용가능하다.

예를들면 VS Code의 extension인 ESP-IDF를 설치하면, 아래에서 각 값들을 찾을수 있다. 
or VS Code의 각 Extension manual을 보면 각 값의 용도 설명을 알려준다. 

ESP-IDF 의 vscode-esp-idf-extension Github
설사 상위 Manual이 없다고 해도 .vscode/extensions/xxxxx 관련부분을 본인이 직접 분석하면된다. 

외부의 환경변수 값을 사용할 경우, 
아래와 같이 ${config:xxxxx} 로 형태로 사용하면된다.  
  1. ${config:idf.portWin} : 현재 settings.json 설정된 Port
  2. ${config:idf.adapterTargetName}: 현재 settings.json 설정된 Target
  3. ${config:idf.pythonBinPathWin} : ESP-IDF Python PATH

ESP-IDF ${config:xxxxx} 의 다양한 예제 
VS Extension에는 이미 숨겨진 환경값도 많으니, 관련사항을  찾아 보도록 하자
더불어 Task도 depends on을 이용하여 Chaining도 가능  


  • Tasks.json에서 Command들 간의 종속연결(depends) 
Tasks.json에서 Command를 많이 만들 경우, 한꺼번에 연속으로 실행하여, 자동화를 하고 싶을 경우가 많을 것이다.  
이 때 사용하는게 dependsOrder dependson 이며 이를 이용하여 쉽게 각 Command들을 종속연결하여 실행가능하다  
상위와 같이 간단히 자동화를 할수 있다. 

  • VSCode JSON 기본사용법 
JSON의 기본문법으로 어떻게 사용하는지 확인 



3. VS Code Tips Update 

주로 사용하는 것이 멀티커서 에디팅이며 다른 Tips들도 알아두도록하자. 

VS Code의 사용 Tips 관련사항 

VS Code Multi Cursor
좌측으로 이동정렬: Shift+Tab
우측으로 이동정렬: Tab



VS Code Markdown View

VS code Search / Modify (Regular Expression)

VS Code에서 diff 기능