시큐어부트(Secure Boot)

시큐어부트(Secure Boot)는 말 그대로 안전한 부팅을 의미합니다. 시큐어 부팅은 ROM으로부터 시작되는 부팅의 시작부터 파일 시스템이 얹어지는 부팅 완료까지 안전하게 부팅을 하는 절차입니다. 주로 세 단계의 인증을 거치게 됩니다. 맨 처음 부팅의 시작인 부트로더들의 인증, 그리고 부트로더에 의해 커널 이미지가 올라오게 되면 커널 이미지를 인증, 이후 커널에 의해 파일 시스템이 마운트되기 전에 파일 시스템의 인증을 거치게 됩니다. 

결론은 부트 로더 인증 -> 커널 인증 -> 파일 시스템 인증 순으로 이어지게 된다는 것이죠. 

부트 로더 인증

임베디드 시스템에서 Boot Loader는 한 가지만 존재하지 않습니다. ROM에서 시작되는 부트 로더1부터 시작해 부트로더2, 부트로더 3 등이 있으며, 여기서도 부트로더 순서대로 인증하는 CoT(Chain Of Trust)라는 기술이 존재하지만 이 포스팅에는 부트로더와 다른 Firmware 이미지들을 한 꺼번에 인증하는 FIP 인증을 다룹니다.

FIP(Firmware Image Package)라고하는 펌웨어 이미지 바이너리를 인증합니다. FIP는 단순히 부트로더들의 모임이라고 생각하시면 되고, FIP를 서명한 서명값이 FIP끝에 달리게 됩니다. 이때 인증은 각 SoC 업체의 Firmware를 사용하여 인증을 하게 되는데요. NXP사의 s32g 칩의 경우에는 HSE(Hardware Security Engine)이라는 펌웨어가 그 역할을 하게 되지요. 

 

인증을 하는 경우에는 공개키가 있어야겠죠? 이 공개키는 어딘가에 저장이 되어야하는데 nxp s32g의 경우 FAT 파일 시스템을 통해서 특정 파티션에 저장하고 있습니다.

커널 인증 

부트로더 인증이 되었다면 그 이후 커널 인증을 거쳐야합니다. 커널 인증은 커널이 변조되었는지 아닌지를 판별하게 됩니다. 커널 인증을 하는 대표적인 방법은 Verified Boot라는 건데요. U-boot에서 사용하는 커널 인증 방법으로 FIT(Flattened Image Tree)를 이용합니다. 이 FIT는 아래와 같은 형식의 .its라는 파일을 가지고 생성되어 집니다. its는 FIT에 대한 정보를 갖고 있는 소스 파일입니다. 

/dts-v1/;
/ {
    description = "Verified boot FIT Image";
    #address-cells = <2>;
    images {
        kernel-1 {
            description = "FIT kernel Image";
            data = /incbin/("Image");
            type = "kernel";
            arch = "arm64";
            os = "linux";
            compression = "none";
            load =  <0x00080000>;
            entry = <0x00080000>;
            hash@1 {
                algo = "sha256";
            };
        };
        tee-1 {
            description = "Arm Trusted Firmware";
            data = /incbin/("optee.bin");
            type = "standalone";
            arch = "arm64";
            compression = "none";
            load =  <0x08400000>;
            entry = <0x08400000>;
            hash@1 {
                algo = "sha256";
            };
        };
        fdt-1 {
            description = "FIT device tree";
            data = /incbin/("fit-linux-kernel.dtb");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
            hash@1 {
                algo = "sha256";
            };
        };
    };
    configurations {
        default = "config-1";
        config-1 {
            description = "verified boot FIT configuration";
            kernel = "kernel-1";
            loadables = "tee-1";
            fdt = "fdt-1";
            signature-1 {
                algo = "sha256,rsa2048";
                key-name-hint = "dev";
                sign-images = "fdt", "kernel", "loadables";
            };
        };
    };
};

마지막 configurations에서 signature-1을 보시면 FIT 인증을 위한 정보들이 저장이 됩니다. 이 its를 가지고 mkimage라는 u-boot의 툴을 통해서 FIT를 아래와 같은 방법으로 만들어냅니다.

./u-boot/tools/mkimage -f fit-image.its -K u-boot.dtb -k keys -r image.fit
  • -f : its의 파일 이름
  • -K : 그렇다면 공개키는 어디 있을까요? 공개키는 앞서 인증한 FIP의 커널을 부팅하는 부터로더인 U-boot의 dtb에 존재합니다. 그 dtb 파일의 이름을 -K 옵션으로 정해주면 이 dtb 파일에 공개키가 삽입이 됩니다. 
  • -k : 서명할 키와 인증서가 있는 디렉토리를 정해줍니다. keys라는 디렉토리에 위치해있으며 openssl이든 다른 키 생성 툴이든 사용하여 키와 인증서를 만들어야합니다. 위의 예시에는 키에 대한 정보(key-name-hint)가 dev입니다.
  • -r : 인증이 필수라는 뜻으로 인증을 강제합니다. 이 옵션을 정해줌으로써 인증이 실패한 커널은 부팅하지 않습니다. 
  • image.fit : 최종적으로 나오는 fit 이미지의 이름을 정해줍니다. input이 아닙니다.

 

이 FIT 이미지는 U-boot의 bootm 부팅 커맨드로만 동작이 가능하며 u-boot에서 iminfo 라는 명령을 통해서 이미지의 정보를 확인할 수도 있습니다.

자세한 사용 방법은 아래를 참고하시면 될 것 같네요.

https://blog.crysys.hu/2018/06/verified-boot-on-the-raspberry-pi/

 

Verified boot on the Raspberry Pi – CrySyS Blog

This blog post, written by István Telek, is the third post in a series of blog posts on transforming the Raspberry Pi into a security enhanced IoT platform. It describes how you can implement a verified boot process on the Raspberry pi. Introduction Secur

blog.crysys.hu

 

파일 시스템 인증

자, 이제 마지막 인증 과정입니다. 바로 실제 데이터들이 존재하는 파일 시스템을 마운트하기전 파일 시스템을 인증하는 과정이 필요하게 되지요. 파일 시스템을 인증하는 대표적인 기술로 사용되는 것이 dm-verity라는 기술입니다. dm-verity는 파일 시스템 이미지의 원본 블록 을 1차로 나온 Hash 데이터를 다시 2차 Hash를 가하고, 이후 n 번의 Hash를 가하게 되면 결국 마지막 나오는 hash값을 갖고 파일 시스템의 무결성을 검증합니다. 하지만 인증을 위해서는 이 hash값을 서명해야됩니다. 결국 우리는 파일 시스템 원본 이미지 + 해쉬 블록을 미리 생성하고, root hash까지 미리 생성하여 안전하게 보관해야합니다. 이때 hash가 계산되는 모습을 보면 트리 형태로 보여지는데 이를 merkle Tree 라고합니다. 결과적으로 나온 root hash는 노출되어서는 안돼요. 부팅이 되고 마운트될때 파일 시스템의 이미지를 root hash로 만들어서 갖고 있는 root hash와 같다면 인증에 성공합니다.

https://www.timesys.com/security/dm-verity-without-an-initramfs/

한가지 중요한 것은 파일 시스템의 쓰기가 발생하면 안된다는 점입니다. 쓰기가 일어나면 인증은 다음 부팅때 당연히 실패합니다. 그래서 파일 시스템은 read-only 파일 시스템에서만 dm-verity가 사용이 가능합니다. 이 dm-verity에 관한 내용과 파일 시스템 인증에 관한 방법은 아래의 페이지에서 세세하게 다루고 있으니 이 페이지를 참고하시기 바랍니다.

https://www.timesys.com/security/dm-verity-without-an-initramfs/

 

DM-Verity Without an Initramfs - Timesys

Learn how you can implement file system verification on your embedded system without the use of an initramfs. This can significantly save boot time and storage requirements in many situations.

www.timesys.com

 

이상으로 시큐어부팅에 관한 포스팅을 마치도록 하겠습니다.

반응형

'컴퓨터 > 보안 기술' 카테고리의 다른 글

[사이버 보안] dm-verity 개념과 실습(Manual)  (1) 2023.05.18
블로그 이미지

REAKWON

와나진짜

,

 

DM-verity 

Data Mapper Verity의 약자로 쓰이는데, 루트 파일 시스템(Root FS)의 검증에 쓰인다. 과거에 안드로이드에 쓰였지만 현재는 점차 임베디드에 쓰이기 시작한다. 기본 원리는 아래의 해시 트리(Hash-Tree)를 만드는 것인데, Data Block마다 Hash를 생성해서 가장 아래 level의 hash 노드를 만들기 시작하고 그 위 level의 hash 노드를 그 아래 level에 hash node를 이용하여 만들어 점차 트리를 완성시켜가는데, 이때 가장 맨 위의 Hash 노드를 Root Hash라고 하며 안전하게 보관되어져야한다. 이 Root Hash값을 이용해서 dm-verity를 검증해야하기 때문.   

이러한 Hash tree를 Merkel Tree라고도 한다. 

https://www.timesys.com/security/dm-verity-without-an-initramfs/

 

1. 블록 장치 생성 + 파일 시스템 설정

# truncate -s 10G data_partition.ext4
# mkfs -t ext4 data_partition.ext4
# mkdir mnt 
# mount -o loop data_partition.ext4 mnt/ 
# echo "hello" > mnt/one.txt 
# echo "integrity" > mnt/two.txt 
# umount mnt/ 
# truncate -s 100M hash_partition.verity

10G의 크기를 갖는 블록을 하나 생성하여 ext4 파일 시스템을 적용한다. 그리고 그 안에 one.txt, two.txt를 만들고 난 후 unmount를 한다.

이후 100M 크기의 hash의 값을 갖는 영역을 생성해낸다. 이곳에 dm-verity의 merkle-tree가 저장된다.

 

2. dm-verity format

# veritysetup -v --debug format data_partition.ext4 hash_partition.verity > root_hash.txt

 

root_hash.txt의 내용

# cryptsetup 2.2.2 processing "veritysetup -v --debug format data_partition.img hash_partition.img" 
# Running command format. 
# Allocating context for crypt device hash_partition.img. 
# Trying to open and read device hash_partition.img with direct-io. 
# Initialising device-mapper backend library. 
# Formatting device hash_partition.img as type VERITY. 
# Crypto backend (OpenSSL 1.1.1f  31 Mar 2020) initialized in cryptsetup library version 2.2.2. 
# Detected kernel Linux 5.13.0-52-generic x86_64. 
# Setting ciphertext data device to data_partition.img. 
# Trying to open and read device data_partition.img with direct-io. 
# Hash creation sha256, data device data_partition.img, data blocks 2621440, hash_device hash_partition.img, offset 1. 
# Using 4 hash levels. 
# Data device size required: 10737418240 bytes. 
# Hash device size required: 84557824 bytes. 
# Updating VERITY header of size 512 on device hash_partition.img, offset 0. 
VERITY header information for hash_partition.img 
UUID:               4da1ecb5-5111-4922-8747-5e867036d9de 
Hash type:          1 
Data blocks:        2621440 
Data block size:    4096 
Hash block size:    4096 
Hash algorithm:     sha256 
Salt:       	     f2790cf141405152cf61b6eb176128ad0676b41524dd32ac39760d3be2d495cf 
Root hash:          a2a8fd07889deb10b4cdf53c01637ed373212cd7d0877a8aa9ae6fd4240f0f71 
# Releasing crypt device hash_partition.img context. 
# Releasing device-mapper backend. 
# Closing read write fd for hash_partition.img. 
Command successful.

Root Hash(a2a8fd07889deb10b4cdf53c01637ed373212cd7d0877a8aa9ae6fd4240f0f71)의 내용은 trusted된 내용이어야하고, 안전하게 보관되어야한다.

 

3. dm-verity open

# veritysetup open \ 
>         data_partition.ext4 \ 
>         verity-test \ 
>         hash_partition.verity \ 
>         a2a8fd07889deb10b4cdf53c01637ed373212cd7d0877a8aa9ae6fd4240f0f71

data_partition.ext4의 mapper를 생성한다.

/dev/mapper/verity-test 가 생성된 것을 확인할 수 있다.

# mkdir mnt 
# mount /dev/mapper/verity-test mnt/ 
mount: /home/ubuntu/ext4/mnt: WARNING: source write-protected, mounted read-only.
# cat mnt/one.txt mnt/two.txt 
hello
integrity

read-only로 mount 되었다.

 

4. dm-verity verify

user-space에서 검증이 가능하다.

  • 검증 성공 시
# veritysetup verify \
> /dev/mapper/verity-test \
> hash_partition.verity \
> a2a8fd07889deb10b4cdf53c01637ed373212cd7d0877a8aa9ae6fd4240f0f71
#
  • 검증 실패 시

  hash의 값을 변경(끝 네자리 0f71 0f73)

# veritysetup verify \
> /dev/mapper/verity-test \
> hash_partition.verity \
> a2a8fd07889deb10b4cdf53c01637ed373212cd7d0877a8aa9ae6fd4240f0f73
Verification of root hash failed.

 

5. Corruption 발생 처리

dm-verity는 디스크를 binary level 수준으로 보호하기 때문에 한 비트라도 어긋나게 되면 corruption 발생 처리하며 mount되지 않는다.

disk를 read-write로 mount하는 것 만으로도 meta-data 변형이 일어나기 때문에 binary가 변경된다. 따라서 corruption을 일으키기 충분하다.

# umount mnt/ 
# veritysetup close verity-test 
# mount -o loop data_partition.ext4 mnt/ 
# umount mnt/ 
# veritysetup open \ 
>         data_partition.ext4 \ 
>         verity-test \ 
>         hash_partition.verity \ 
>         a2a8fd07889deb10b4cdf53c01637ed373212cd7d0877a8aa9ae6fd4240f0f71 
Verity device detected corruption after activation 
root# mount /dev/mapper/verity-test mnt/ 
mount: /path/to/mnt: can't read superblock on /dev/mapper/verity-testroot## mount /dev/mapper/verity

 

dmesg를 확인하면 kernel에서 dm-verity의 log를 확인 할 수 있다.

[412036.212897] device-mapper: verity: 7:16: data block 0 is corrupted 
[412036.212996] device-mapper: verity: 7:16: data block 0 is corrupted 
[412036.213009] buffer_io_error: 91 callbacks suppressed 
[412036.213011] Buffer I/O error on dev dm-0, logical block 0, async page read 
[412036.223697] device-mapper: verity: 7:16: data block 0 is corrupted

 

5. dm-verity status

현재 mapping 상태를 확인한다.

# veritysetup status verity-test
/dev/mapper/verity-test is active.
  type:        VERITY
  status:      corrupted
  hash type:   1
  data block:  4096
  hash block:  4096
  hash name:   sha256
  salt:        f2790cf141405152cf61b6eb176128ad0676b41524dd32ac39760d3be2d495cf
  data device: /dev/loop11
  data loop:   /home/ubuntu/ext4/data_partition.ext4
  size:        20971520 sectors
  mode:        readonly
  hash device: /dev/loop10
  hash loop:   /home/ubuntu/ext4/hash_partition.verity
  hash offset: 8 sectors
  root hash:   a2a8fd07889deb10b4cdf53c01637ed373212cd7d0877a8aa9ae6fd4240f0f71

 

6. dm-verity close

mapping을 제거한다.

# veritysetup close verity-test

/dev/mapper에서 사라진것을 확인할 수 있다.

 

Reference)

https://www.timesys.com/security/dm-verity-without-an-initramfs/

 

DM-Verity Without an Initramfs - Timesys

Learn how you can implement file system verification on your embedded system without the use of an initramfs. This can significantly save boot time and storage requirements in many situations.

www.timesys.com

https://www.starlab.io/blog/dm-verity-in-embedded-device-security

반응형
블로그 이미지

REAKWON

와나진짜

,