2023년 1월 28일 토요일

OrangePi R1 Plus LTS 보드로 PQC WireGuard VPN Router 만들기

랜만(거의 2년만)에 blog에 글을 다시 올리는 것 같다.😎 이번 시간에는 OrangePi R1 Plus LTS board를 이용하여 PQC(Post-Quantum Cryptography) 기반 초소형 VPN(WireGuard) router를 만드는 과정을 소개해 보고자 한다.



PQC(양자내성 암호 알고리즘)이 탑재된 초소형 VPN Router(Tiny VPN Router)를 만들어 보면 어떨까 ?



목차
1. OrangePi R1 Plus LTS board 소개
2. OpenWrt build 하기
3. i2c device tree 및 device driver 추가하기
4. PQC(Post Quantum Cryptography) 개요
5. PQC 기반 WireGuard VPN Router 만들기
6. References

Keyword: Orange Pi R1 Plus LTS, i2c, PQC(Kyber, Dilithium), WireGuard


1. OrangePi R1 Plus LTS board 소개

일전에 camera가 장착된 Orange Pi(모델명은 정확히 기억이 안남)를 하나 테스트했던 적이 있는데, 열도 많이 나고 camera 동작도 형편없었던 안좋은 기억이 난다. 2개의 LAN port를 장착한 보드를 찾던 중, NanoPi R4S와 더불어 Orange Pi를 다시 살펴 보게 되었는데, 과연 이번에는 만족할만한 결과를 얻을 수 있을지 기대해 보도록 하자. 💢


본 blog에서 소개하는 Orange Pi 관련 내용은 아래 wiki page와 user manual을  기초로 하였다.

http://www.orangepi.org/orangepiwiki/index.php/Orange_Pi_R1_Plus_LTS

https://drive.google.com/drive/folders/1VLsrHLXRNGINbXcuxSsCtvX1430Pjmmt/OrangePi_R1_Plus_LTS_RK3328_User_Manual_v2.0.pdf


[그림 1.1] OrangePi R1 Plus LTS 보드

[그림 1.2] OrangePi R1 Plus LTS 보드 Case

[그림 1.3] OrangePi R1 Plus LTS 보드 확장 pinmap


2. OpenWrt build 하기

개발 board가 준비되면,  제일 먼저 해보게 되는 일은 역시 kernel을 build한 후 이를 target board에 올려 보는 일일 것이다. 정확히는 bootloader/kernel/rootfs 등을 build해서 올리는 일이 될 것인데, linux 배포판 중, router 용 board(or wifi access point)에 최적화되어 있는 것은 단연 openwrt가 최고라고 말할 수 있다.


a) openwrt build하기

각설하고 Orange Pi L1 Plus LTS용 openwrt source code를 내려 받아 build하는 절차를 적어보면 다음과 같다.

git clone https://github.com/orangepi-xunlong/openwrt.git -b openwrt-21.02

$ cd openwrt

$ ./scripts/feeds update -a

./scripts/feeds install -a

cp configs/OrangePi_R1_Plus_LTS_defconfig .config

$ make defconfig

$ make V=s

$ cd openwrt/bin/targets/rockchip/armv8


[그림 2.1] build 결과 - openwrt 설치용 image 파일 생성 모습

참고: 위의 내용은 Ubuntu 18.04 or 20.04를 기준으로 정리한 것이다.

b) target board 부팅하기

적당한 크기의 microSD card를 준비하고, 아래 명령을 이용하여 앞서 build한 image 파일에 대한 writing을 시도하도록 한다.


sudo dd if=./openwrt-rockchip-armv8-xunlong_orangepi-r1-plus-lts-ext4-sysupgrade.img of=/dev/sdb bs=1M
664+0 records in
664+0 records out
696254464 bytes (696 MB, 664 MiB) copied, 159.026 s, 4.4 MB/s

주의: of=/dev/sdb는 독자의 실제 환경에 맞게 지정해 주어야 한다.


이후 microSD를 target board에 장착하고 부팅을 시도해본다.

[그림 2.2] Orange Pi R1 LTS board 부팅 모습(1)


$ sudo minicom -b 1500000 -D /dev/ttyUSB0

참고) 요즘 나오는 board의 serial port baudrate은 1.5Mbps(= 1500000)을 기본으로 한다. 캬~ 빠르다 빨러~ 🏂


[그림 2.3] Orange Pi R1 LTS board 부팅 모습(2)


Target board용 image를 생성하고, image를 target board에 올리는 방법과 관련해서는 이미 다른 blog posting을 통해 아주 여러차례 소개한 바 있으므로, 여기서는 이 정도 선에서 마무리하고 다음으로 넘어가기로 한다.


https://slowbootkernelhacks.blogspot.com/2017/01/sbcsmall-board-computer-embedded-linux.html


3. i2c device tree 및 device driver 추가하기

이 장에서는 OrangePi board에 i2c 주변장치를  하나 연결하고, 이를 인식시키는 과정(device tree & device driver)을 소개해 보고자 한다.
i2c 장치로는 이전 posting에서 한 차례 사용했던, 온습도 센서인 HTU21D를 다시 활용해 보기로 하겠다.

https://slowbootkernelhacks.blogspot.com/2020/06/linux-device-driver-for-embedded_17.html


[그림 3.1] HTU21D 온습도 센서

참고: 일단 가지고 있는 sensor를 활용하다 보니, HTU21D 센서를 다시 사용하게 되었는데, 조만간 다른 센서로 교체해 보도록 하자.

a) rk3328 orangepi r1 plus lts device tee 분석

먼저, 언제나 그렇듯 target board의 device tree 관계를 파악해 보는 것이 첫번째로 해야 할 일이다.


rk3328.dtsi 
^
|
rk3328-nanopi-r2s.dts
^
|
rk3328-orangepi-r1-plus.dts
^
|
rk3328-orangepi-r1-plus-lts.dts 

[그림 3.2] Orange Pi R1 Plus LTS board device tree 관계도


[그림 3.3] rk3328.dtsi

[그림 3.4] rk3328.dtsi - i2c controller 중심

[그림 3.5] OrangePi R1 Plus LTS device tree

참고: device tree를 이해하기 위해서는 많은 부분이 참고가 되어야 한다. 특히  rk3328.dtsi 파일의 include 문에 명기된 파일을 면밀히 살펴 볼 필요가 있다.


b) i2c device를 위한 device tree 내용 수정하기

다음으로 해야 할 일은 Orange Pi 확장 핀 중에서 i2c pin이 어느 것인지를 확인하는 것이다. 눈치챘겠지만, 아래 그림의 3, 4번 pin 즉 TWI0-SDA, TWI-SCK가 i2c용 pin임을 쉽게 알 수 있다. 물론, VCC_SYS(3.3V)와 GND도 함께 사용되어야 한다.


[그림 3.6] OrangePi R1 Plus LTS 확장 pin


Orange Pi도 i2c pin을 그냥 단독으로 사용하게 내버려 두지는 않는다. 즉, GPIO 등 다른 용도로 사용 가능하도록 pinmux되어 있는데, 이와 관련한 정확한 내용을 파악하게 위해서는 rk3328 datasheet를 반드시 읽어 보아야만 한다.


[그림 3.7] rk3328 pinmux table



[그림 3.8] rk3328 pinmux table 중 i2c0 관련 내용



[그림 3.9] Orange Pi 확장 핀에 htu21d 센서 연결 모습


자, 이제 device tree 및 pinmux에 관한 어느 정도의 내용 파악이 끝났으니, device tree에 i2c0 device를 하나 추가해 보도록 하자.


[그림 3.10] OrangePi R1 Plus LTS용 i2c device 추가


c) 새로운 device tree blob 만들기

Device tree 내용을 수정하였으니, 이를 build하여 target board에 반영해 보도록 하자.


$ cd openwrt/build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-5.4.179


$ export STAGING_DIR=YOUR_PATH/openwrt/staging_dir
$ export TOOLCHAIN_DIR=$STAGING_DIR/toolchain-aarch64_generic_gcc-8.4.0_musl
$ export LDCFLAGS=$TOOLCHAIN_DIR/usr/lib
$ export LD_LIBRARY_PATH=$TOOLCHAIN_DIR/usr/lib
$ export PATH=$TOOLCHAIN_DIR/bin:$PATH
$ export ARCH=arm64
$ export CROSS_COMPILE=aarch64-openwrt-linux-musl-

$ make dtbs

build가 제대로 진행되고 나면 아래 위치(arch/arm64/boot/dts/rockchip)에 dtb 파일이 만들어지게 된다.


[그림 3.11] rockchip용 device tree blob

이 중 우리에게 필요한 파일은 아래 파일이다. 이 파일을 microSD에 복사하도록 하자.
rk3328-orangepi-r1-plus-lts.dtb


참고: MicroSD card를 Ubuntu PC에 꽂고, mount된 디렉토리를 확인한 후, 위의 파일을 기존 dtb 파일명(rockchip.dtb)으로 복사한다.


Target board를 재 부팅 후, console login하여 아래 디렉토리로 이동하도록 한다.


$ cd /sys/bus/i2c/devices


이 디렉토리 아래에 보면 (기존에는 없었던) i2c-0가 생성된 것을 확인할 수 있다.



[그림 3.12] sysfs i2c device 디렉토리 모습

i2cdetect -l 명령을 실행해 보아도 비슷한 결과(2개의 i2c controller 출력)를 얻을 수 있다.



i2c device 즉, htu21d가 제대로 인식되는지를 보기 위해 i2cdetect -y 0 명령을 사용해 보니, 아래와 같이 device address 40(hex)이 보인다. 참고로 htu21d의 7-bit i2c address는 0x40이다.


[그림 3.13] i2c detect -y 0 명령 실행 모습


참고: i2cdetect는 device tree 설정만 제대로 되어 있어도 위와 같이 address를 감지해 낼 수 있다.


<여기서 잠깐>

OpenWrt에서는 i2cdetect 명령어를 아래의 방법을 통해 설치해 주어야 한다.


# opkg update

# opkg install i2c-tools

_____________________________________


d) i2c device driver 구동하기

온습도 센서인 HTU21의 device driver code는 아래 위치에 존재한다.

openwrt/build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-5.4.179/drivers/iio/humidity/htu21.c


당연히 기본 disable 상태이므로, menuconfig를 통해 htu21 module을 enable 시켜 보도록 하자.

Kernel modules -> Industrial I/O Modules -> kmod-iio-htu21

$ make menuconfig

[그림 3.14] openwrt make menuconfig 모습

설정 변경한 내용을 저장한 후, kernel을 재 build하도록 한다. 이후, 아래 3개의 kernel module을 찾아 target board로 복사 후, 구동(modprobe or insmod)시켜 보도록 한다.

참고: modprobe htu21를 실행해 보면, 왜 3개의 kernel module이 필요한지를 알 수 있게 된다.


<Ubuntu Desktop>

make V=s

$ cd build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-5.4.179/drivers/iio/common/ms_sensors

scp ./ms_sensors_i2c.ko root@192.168.2.1:~/workspace

$ cd build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-5.4.179/drivers/iio

scp ./industrialio.ko root@192.168.2.1:~/workspace

$ cd build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-5.4.179/drivers/iio/humidity

scp ./htu21.ko root@192.168.2.1:~/workspace

참고: 192.168.2.1은 target board LAN의 ip 주소이다.


<Target Board>

$ ssh root@192.168.2.1
# cd ~/workspace
# insmod ./ms_sensors_i2c.ko
# insmod ./industrialio.ko
# insmod ./htu21.ko

[그림 3.15] htu21d driver 구동 모습

물론, 위의 방법 보다는 openwrt 전체 build를 통해 image file을 다시 생성한 후, microSD에 재 설치하는 방법이 더 쉬울 수도 있다.


나머지 센서 동작 확인 과정(온도, 습도 값, battery low 상태, heater enable 상태 값 확인 등)은 아래 blog post의 내용과 동일하므로 여기서는 자세한 사항은 생략하기로 한다. Pass~  😋

https://slowbootkernelhacks.blogspot.com/2020/06/linux-device-driver-for-embedded_17.html


e) i2c device driver 직접 만들기 - kernel용 드라이버

앞선 내용은 이미 구현되어 있는 드라이버 코드를 소개하는 것에 불과하다. 실제로 독자 여러분 중에는 이와 유사한 i2c device driver를 직접 구현하고 싶을 수도 있을 것이다. 따라서 이 절에서는 i2c driver 구현과 관련한 보다 구체적인 사항을 소개하고자 한다.
먼저, 아래 그림은 linux i2c subsystem을 하나의 그림으로 표현한 것인데, 이 중 지금 우리가 구현해야 할 부분은 우측의 i2c client driver 부분이 되겠다.


[그림 3.16] Linux i2c subsystem [출처: 참고문헌 3]


이 그림에는 표현되지 않았지만, i2c client driver로 부터 i2c controller로 이어지는 read or write 함수 흐름은 다음과 같다.

I2C client driver-> I2C bus core driver -> I2C controller driver:
i2c_smbus_write_byte_data() -> i2c_smbus_xfer() -> i2c_imx_xfer()


본론으로 들어가서, i2c client driver를 구현하기 위해서는 제일 먼저 struct i2c_driver를 정의(예: htu21_driver)해 주어야 한다.

static struct i2c_driver htu21_driver = {
    .probe = htu21_probe,
    .remove = htu21_remove,
    .id_table = htu21_id,
    .driver = {
        .name = "htu21_simple",
        .of_match_table = of_match_ptr(htu21_of_match),
    },
};

앞서 정의한 struct i2c_driver를 실제로  i2c bus core에 등록 or 해지하기 위해서는 아래와 같이  i2c_add_driver() or  i2c_del_driver() 함수가 각각 사용된다.

static int __init i2c_init(void)
{
      return i2c_add_driver(&htu21_driver);
}
module_init(i2c_init);
static void __exit i2c_cleanup(void)
{
      i2c_del_driver(&htu21_driver);
}
module_exit(i2c_cleanup);

참고로, module_i2c_driver(htu21_driver) 함수는 위의 코드를 한줄로 대신해 준다.


of_device_id 구조체는 device tree와의 연결 고리인 .compatible 필드를 갖고 있다. 또한 struct i2c_driver의 of_match_table 필드는 struct of_device_id 구조체 배열의 pointer이다. 만일 of_device_id의 항목 중 하나가 device tree node의 compatible 속성과 일치하는 것이 있을 경우, 해당 probe() 함수가 자동으로 호출되게 된다.

struct of_device_id {
      char name[32];
      char type[32];
      char compatible[128];
};

static struct of_device_id htu21_of_match[] = {
    { .compatible = "meas,htu21", },
    { },
};
MODULE_DEVICE_TABLE(of, htu21_of_match);


[그림 3.17] htu21_simple i2c driver 구현(1)

probe() 함수는 device tree node로 부터 얻은 속성 값을 이용하여 device를 초기화하는 역할을 수행하는 것은 물론이고, 적절한 kernel framework(예: input, iio frameworks 등)에 device를 등록시켜 주는 역할을 하게 된다.

[그림 3.18] htu21_simple i2c driver 구현(2)

probe() 함수를 통해 device가 초기화 되었으니, 그 다음으로 해야할 일은 i2c device를 통해 data를 읽거나 쓰는 코드를 작성하는 일이 될 것이다. 앞서 기술했던 내용을 여기에 다시한번 정리해 본다.

i2c_smbus_write_byte_data() -> i2c_smbus_xfer() -> i2c_imx_xfer()
i2c_smbus_read_byte_data() -> i2c_smbus_xfer() -> i2c_imx_xfer()


read, write 함수 호출 부분은 각각의 device 마다 일정한 시나리오를 가지고 운용되는 부분인 만큼 관련 해당 datasheet를 면밀히 살펴 본 후 작업에 들어가야만 한다. 끝으로 i2c client driver에서 사용 가능한 read/write 함수를 정리하는 것으로 이번 절을 마무리하도록 하겠다.

_______________________________________________________________________

<i2c/smbus read/write 함수 정리>

int i2c_master_send(struct i2c_client *client, const char *buf, int count);

int i2c_master_recv(struct i2c_client *client, char *buf, int count);

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num);

s32 i2c_smbus_read_byte(struct i2c_client *client);

s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value);

s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);

s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value);

s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);

s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value);

s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command, u8 *values);

s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command, 

 u8 length, const u8 *values);

s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, 

 u8 length, u8 *values);

s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command, 

 u8 length, const u8 *values);

_______________________________________________________________________


f) i2c device driver 직접 만들기 - userspace 용 드라이버

i2c나 spi driver는 usrespace 용으로도 구현이 가능하다. 이 절에서는 이와 관련된 내용을 소개하고자 한다.

<Userspace용 i2c driver 구현 절차 요약>
a) /dev/i2c-0 or i2c-1 등의 device file을 open한다.
 => 이를 위해서는 i2c device가 i2c 몇번 controller에 연결되었는지를 확인하는 작업이 선행되어야 한다.
b) ioctl(fd, I2C_SLAVE, 0x40) 형태로 ioctl 함수를 이용하여 i2c slave 모드 지정 및 i2c address를 설정해 준다.
c) read(), write() 함수를 사용하여 i2c device와 통신을 한다. 앞서도 언급했다시피 이 부분은 각각의 i2c device 마다 사용하는 register 정보 및 동작 시나리오 등이 차이가 있으므로 사전에 datasheet를 면밀히 분석한 후 read, write 함수 부분을 적절히 구현해 주어야 한다.
d) 통신이 모두 끝난 경우에는 앞서 open한 부분을 반드시 닫아(close) 주도록 한다. 

________________________________________________________________________

userspace용 i2c driver 구현은 크게 어렵지 않다. 아래 site에 좋은 예제 코드가 있으니, 함께 참조하기 바란다.

https://github.com/Digilent/linux-userspace-examples

https://docs.kernel.org/i2c/dev-interface.html


___________________________________________________________________

<여기서 잠깐 - i2c 말고 SPI 용 센서를 붙여 보고 싶다면, 어떻게 해야 할까 ?>
a) 아쉽게도 Orange Pi L1 Plus LTS board에는 SPI 확장 pin이 보이질 않는다.
b) 따라서 Raspberry Pi 등을 이용해서 SPI 테스트를 해 볼 수 있겠다.
c) 예를 들어, 아래 SPI용 3축 가속도 센서(ADXL345)를 하나 붙여 보도록 하자.


[그림 3.19] SPI 



[그림 3.20] Raspberry Pi + ADXL345 spi sensor 연결 모습[출처 - 참고문헌 4]

참고: adxl345 linux driver는 아래 위치에 있으니, 함께 분석해 보면 좋겠다.
linux-5.4.179/drivers/iio/accel/adxl345_spi.c, adxl345_core.c
___________________________________________________________________


지금까지 Orange Pi L1 LTS 보드를 가지고 i2c 용 device tree 및 device driver를 만드는 과정을 간략히 살펴 보았다.

이는 약간 과장해서, h/w 보드를 하나 만들고, OS(openwrt - bootloader/kernel/rootfs)를 porting해서 올린 후, 필요한 device driver를 구현하는 일반적인 보드 개발 과정을 가정하고 정리한 것으로 이해해 주시면 좋겠다. 😂 그렇다면, 그 다음에 할일은 ...


이번 blog post에서 소개하는 OrangePi board는 2개의 ethernet port로 무장(?)하고 있는게 특징이다. 이는 OrangePi board를 router(정확히는 security gateway)로 활용할 수 있다는 뜻이기도 하다.  이제 부터는 Orange Pi L1 LTS 보드를 PQC 알고리즘을 장착한 security gateway로 만드는 방법에 관하여 소개해 보고자 한다.


4. PQC(Post Quantum Cryptography) 개요

PQC는 양자 computer가 곧 도래할 경우를 대비하여 등장한 공개키 기반의 암호 알고리즘으로 키 교환(KEM) 알고리즘과 전자 서명(DS) 알고리즘으로 구성되어 있다. 

a) PQC(Post-Quantum Cryptography) 개요

[그림 4.1] PQC 알고리즘 군 [출처 - 참고 문헌 6]


[그림 4.2] NIST PQC Round 3 KEM 후보간 비교[출처 - 참고문헌 7]

참고: 특이하게도 CRYSTALS Kyber는 x25519보다도 실행 시간이 덜 걸린다(더 빠르다).


[그림 4.3] NIST PQC Round 3 DS 후보간 비교[출처 - 참고문헌 7]

NIST는 2022년에 들어서 드디어 최종 알고리즘(NIST PQC Standard)과 4 round 후보군을 아래와 같이 선정하였다.


[그림 4.4] NIST PQC Finalist [출처: 참고 문헌 8]

참고: 필자 개인적으로는 Classic McEliece가 매우 간결하면서도, 오랫동안 안정성이 검증된 알고리즘으로 4 round에서는 꼭 선정되기를 희망해 본다(단점: public & private key가 지나치게 큰 것이 문제). 😍

 

b) 격자 기반 LWE(Learning with Errors)

PQC 알고리즘은 대부분 수학적인 난제(NP-Hard)에 기초하고 있는데, 이 중 가장 hot한 알고리즘은 Lattice(격자) 기반 문제(난제)에 기초하고 있다고 해도 과언이 아닐 것이다. NIST 표준으로 선정된 CRYSTALS KyberDilithium 역시 Lattice 기반 LWE 문제를 기반으로 구현되어 있다. Lattice 기반의 Closest Vector Problem을 입체적으로 표현한 그림이 있어 여기에 옮겨 본다.

As + e = t

[그림 4.5] Lattice 기반 CVP(Closest Vector Problem) [출처: 참고문헌 17]

위의 그림을 한마디로 정리하면,  
"A(행렬)라는 무한 차원 격자와 t vector 값을 알고 있는 상태에서 감춰진 에러 값 e vector가 주어질 경우, t에서 가장 가까운 s(vector)를 알아내는 것은 무지 무지 어렵다(양자 computer로도 불가능하다)"
라고 말할 수 있다. 이때 행령 At는 Public key로, Vector s는 Private key(or secret key)로 활용될 수 있다. 어떤까 대략 무슨 얘기를 하려는 것인지 감이 오는가 ? 😓

c) KEM(Key Encapsulation Mechanism)

KEM은 PQC 기반의 키교환 방식을 뜻한다. KEM 절차를 간략히 정리해 보면 다음과 같다.

(1) Alice(Initiator)는 Key 쌍 (pk, sk)를 하나 만든 후, Bob(Responder)에게 pk(public key)를 전달한다.
(2) pk를 받은 Bob은 Enc(encapsulation) 함수를 이용하여 대칭키 K와 K를 암호화한 값 c를 만든 후, Alice에게 c 값을 전달한다.
(3) c 값을 수신한 Alice는 자신이 이미 가지고 있는 sk(secret key = private key)와 Dec(decapsulation) 함수를 이용해 대칭 키 K를 획득한다.
(4) 이후, Alice와 Bob은 서로 공유하게 된 K 값을 이용해 암호 통신(예: AES, ChaCha20 등)을 수행하게 된다.


[그림 4.6] PQC KEM 개요(1)


KEM의 동작 원리를 좀 더 구체적으로 표현해 보면 아래 3개의 그림과 같다. 어떤가 ... 이해가 되는가 ?  😋

[그림 4.7] PQC KEM 개요(2)


[그림 4.8] PQC KEM 개요(3)

[그림 4.9] PQC KEM 개요(4)


d) Digital Signature

PQC 기반의 서명/검증 알고리즘은 아래 그림으로 표현 가능한데, 서명/검증 절차를 간략히 요약해 보면 다음과 같다.

(1) Alice(Initiator)는 서명용 Key 쌍 즉 (pk, sk)를 하나 생성한 후, message M과 sk(secret key)를 이용하여 서명 값 sig를 생성한다.
(2) Alice는 (1)에서 생성한 sig 값과 더불어 pk(public key) 및 message M을 Bob(Responder)에게 전달한다.
(3) Alice로 부터 pk, sig, M을 수신한 Bob(Reponder)는 Verify 함수를 통해 서명 값을 검증해 낸다. 당연한 거지만 서명을 검증하는 verify 함수 내부는 PQC 알고리즘에 따라 서로 차이가 난다.
(4) Verify 결과 검증에 실패로 판단된 경우는 Alice를 신뢰할 수 없게 되며, 검증에 성공할 경우는 이후 통신을 이어나가게 된다.


[그림 4.10] PQC Digital Signature 개요(1)

서명 알고리즘은 KEM 보다는 난이도가 있다. 아래 몇장의 그림은 digital 서명의 탄생(?) 원리와 동작 방식을 정리한 것으로, PQC 알고리즘의 경우도 내부 동작 방식에 차이가 있을 뿐 기본 원리는 동일하다고 말할 수 있다.


[그림 4.11] Digital Signature 개요(1) - 영지식 증명


[그림 4.12] Digital Signature 개요(2) - Fiat-Shamir Heuristic 기반의 서명 알고리즘


[그림 4.13] Digital Signature 개요(3) - DSA 서명/검증 알고리즘


[그림 4.14] PQC CRYSTALS Dilithium(1)


[그림 4.15] PQC CRYSTALS Dilithium(2)

참고: CRYSTALS Dilithium 같은 알고리즘 코드를 그냥 무턱대고 들여다 봐서는 당췌 이해가 가질 않는다. 사전에 동작 원리를 이해하기 위한 노력이 선행되어야 한다.

e) PQC 관련 open source 모음

PQC 관련 open source는 인터넷에서 쉽게 찾아볼 수 있는데, 그 중에서도 나름 의미가 있는 몇가지를 하나로 정리해 보았다.

[5] 기타


5. PQC 기반 WireGuard VPN Router 만들기

WireGuard와 관련해서는 필자의 다른 posting을 통해 이미 몇차례 소개한 바 있다. 


이번 장에서는 PQC 알고리즘을 WireGuard와 접목하는 방법(일명 PQ-WireGuard)에 관하여 소개하고, 이를 이용하여 Orange Pi를 VPN router로 변모시키는 과정을 소개하고자 한다.


[그림 5.1] PQC 기반의 Tiny VPN Router

a) Kernel code로 구현한 pq-wireguard 소개
이 절에서는 kernel 기반으로 구현한 pq-wireguard를 Orange Pi L1 Plus LTS에서 동작시키는 방법을 소개하고자 한다. 여기에서 소개하는 내용은 아래 논문의 내용을 기초로 하였다.

Post-quantum WireGuard

https://eprint.iacr.org/2020/379.pdf



PQ-Wireguard를 이해하기 위해서는 먼저 Wireguard handshaking(a.k.a Noise protocol)을 제대로 이해할 필요가 있다. Wireguard handshaking은 아래 그림에 표현되어 있는 것과 같이, 4번의 ECDH 수행 과정이라고 말할 수 있다. 4번의 ECDH를 위해서는 Long-term curve25519 키 쌍과, 2분 간격으로 반복 생성되는 Ephemeral curve25519 키 쌍이 각각 필요하다. (이미 알고 있는 것 처럼) Long-term 키 쌍 중 public key 부분은 사전에 미리 교환되어 있어야만 한다. 
참고: Wireguard는 인증서 없이 간접적인 방법(Implicit method)으로 상호 인증을 수행하는 protocol이다.


[그림 5.2] Wireguard handshaking 과정

한편, PQ-WireGuard는 ECDH를 KEM으로 변경한 protocol로 Long-term ECDH의 경우는 Classic McEliece 기반의 KEM으로, Ephemeral ECDH의 경우는 Saber(정확히는 Tweaked Saber)로 교체하였다고 보면 된다. 각각의 내용을 요약해 보면 다음과 같다.

• Long-term IND-CCA-secure KEM : Classic McEliece
    - 코드 기반 암호 알고리즘
    - NIST PQC 후보 중 Ciphertext의 길이가 가장 작음(WireGuard Handshaking에 사용되므로 중요)
    - Public key size는 매우 크나, 사전에 전달된다면 크게 문제 될 것 없음.
    - Key 생성 시간이 오래 걸리지만, 역시 처음에 한번 발생하므로 역시 크게 문제가 안됨.
• Ephemeral IND-CPA-secure KEMTweaked Saber
    - 격자 기반(LWR) 암호 알고리즘
    - WireGuard는 2분 간격으로 키 생성(Ephemeral key)을 해야 하므로 빠른 알고리즘을 요구함.
    - Packet fragmentation 이 일어나지 않도록 public key size <= 928 bytes, ciphertext <= 984 bytes이어야 함(IPv6 packet 기준)
    - 1-RTT 유지 위해 중요
    - Level 3 후보군에서 선택
    - 특허 문제가 없어야 함
    - Tweaked (smaller, more lightweight) Saber
참고: 1-RTT는 보장할 수 없지만, Tweaked Saber를 CRYSTALS Kyber로 바꿔 보는 것도 좋을 듯 하다.

_________________________________________________________________________________


아래 그림에서 보라색으로 표시된 부분이 Wireguard protocol에 PQC 알고리즘을 적용한 부분이 되겠다.

[그림 5.3] PQ-Wireguard Handshaking 과정

지금부터는 아래와 같은 네트워크 구성에서 Orange Pi와 AWS EC2 각각에 PQ-WireGuard를 올리는 과정을 소개하도록 하겠다.

PC(notebook) => Orange Pi L1 Plus LTS(wg0:10.0.0.5) => Internet <= AWS EC2(wg0:10.0.0.2)


b) Orange Pi 위에 PQ-Wireguard 올리기
아래 내용은 Ubuntu 18.04 or 20.04 환경을 기준으로 정리한 것이다.

<비 x86 machine에의 porting 전략>
[1] mceliece code는 x86_64용 avx2 환경에 최적화되어 있으니, PQClean code로 교체하자.
  - Avx2 code 는 모두 제거하자 (mc_buf 관련 코드는 제거 )
  - User space 용 C header 는 kernel porting 시 대부분 제거되어야 함 .
  - Kbuild 파일 중, 사용하지 않는 파일은 모두 제거하자 .
[2] mceliece code는 wg tool code에도 포함되어 있음.
  - PQClean code로 교체하자 . Kernel code와는 달리 거의 대부분의 PQClean code 사용 가능.
[3] ephemeral code(saber)는 대부분 그대로 사용하되, fips202.[ch]는 PQClean(common 폴더)의 것을 사용하자.
[4] kernel build 시, -Wframe-larger-than=2048 관련 warning 수정 [매우 중요]
  - Stack 에 buffer 크기가 2048 이상을 사용하는 함수는 모두 kmalloc으로 수정하자 . 이걸 수정하지 않으면 system이 뻗어 버리게 된다 .
[5] Noise handshaking code (noise.c) 중 mceliece code 호출 부분은 새로 porting한 PQClean 함수로 적절히 교체하자.

[wireguard.ko build 하기]
$ cd pqwireguard-tiny/WireGuard/src

$ vi Makefile
..
KERNELDIR =YOUR_PATH/openwrt/build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-5.4.179
..

$ make clean; make
$ ls -l wireguard.ko
-rw-r--r-- 1 pi pi 333336 May  8 09:35 wireguard.ko <= for ARM aarch64

$ ls -l tools/wg
-rwxr-xr-x 1 pi pi 152112 May  8 09:35 tools/wg <= for x86_64
참고: wg tool에 대한 Makefile을 수정하지 않고 build했으므로, 일단은 x86_64용 binary가 만들어진다.

[libmnl build 하기]
예전 wg tool은 libmnl을 사용한다. 따라서 libmnl을 download 받아 cross-compile을 해 두어야 한다.

$ export ARCH=arm64
$ export STAGING_DIR=YOUR_PATH/openwrt/staging_dir
$ export PATH=YOUR_PATH/openwrt/staging_dir/toolchain-aarch64_generic_gcc-8.4.0_musl/bin:$PATH

$ git clone git://git.netfilter.org/libmnl
$ cd libmnl/
$ mkdir output
$ ./autogen.sh
$ ./configure --host=aarch64-openwrt-linux-musl  --enable-shared --prefix=$(pwd)/output
$ make
$ make install
$ cd output/lib
$ file libmnl.so.0.2.0
libmnl.so.0.2.0: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, with debug_info, not stripped

[wg tool build 다시 하기]
$ cd pqwireguard-tiny/WireGuard/src/tools
$ vi Makefile
..
CFLAGS += -IYOUR_PATH/pqwireguard-tiny/libmnl/output/include
LDLIBS += -LYOUR_PATH/pqwireguard-tiny/libmnl/output/lib -lmnl
..
CC := aarch64-openwrt-linux-musl-gcc
LD := aarch64-openwrt-linux-musl-ld
..
~

$ cd ..

$ make clean; make

$ file tools/wg 

tools/wg: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped

자, 이번에는 aarch64용으로 제대로 build가 되었다.

[target board에 wireguard binary 복사하기]
앞서 build 한 wireguard.ko, wg 및 libmnl.so 등을 target board로 복사(scp 이용)하도록 한다.

root@OpenWrt:~/workspace/pq-wireguard# ls -al
drwxr-xr-x    3 root     root          4096 Jan 25 01:47 .
drwxr-xr-x    4 root     root          4096 Jan 25 00:59 ..
-rwxr-xr-x    1 1000     1000           977 Jan 25 01:37 libmnl.la
lrwxrwxrwx    1 root     root            15 Jan 25 01:44 libmnl.so -> libmnl.so.0.2.0
lrwxrwxrwx    1 root     root            15 Jan 25 01:44 libmnl.so.0 -> libmnl.so.0.2.0
-rwxr-xr-x    1 1000     1000         79680 Jan 25 01:37 libmnl.so.0.2.0
-rwxr-xr-x    1 root     root        222512 Jan 25 01:44 wg
-rw-r--r--    1 root     root       5604768 Jan 25 00:59 wireguard.ko

<Target board에서 pq-wireguard 돌려 보기>
# cd ~/workspace/pq-wireguard
# export LD_LIBRARY_PATH=/root/workspace/pq-wireguard:$LD_LIBRARY_PATH
참고: wg tool을 돌리기 위해 libmnl.so가 필요하므로, ld library path 설정을 해 준다.
 
Orange pi용 openwrt 21.02에는 이미 wireguard가 포함되어 있다. 따라서 먼저 wireguard를 내려 주도록 한다.

# rmmod wireguard
# rmmod libchacha20poly1305
# rmmod libblake2s
참고: 무엇을 내려 주어야 하는지는 wireguard를 내려 보면 알 수 있다. dmesg 명령을 통해 kernel error를 확인해 보기 바란다.

# insmod ./wireguard.ko
root@OpenWrt:/# [  231.072485] wireguard: WireGuard 0.0.20191219-158-gfc6f000-dirty loaded. See www.wireguard.co.
[  231.073383] wireguard: Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.

# mkdir wg_config; cd wg_config
# ../wg mckey privkey pubkey
root@OpenWrt:~/workspace/pq-wireguard/wg_config# ls -la
drwxr-xr-x    2 root     root          4096 Jan 25 01:48 .
drwxr-xr-x    3 root     root          4096 Jan 25 01:47 ..
-rw-r--r--    1 root     root         13568 Jan 25 01:48 privkey
-rw-r--r--    1 root     root        524160 Jan 25 01:48 pubkey
참고: McEliece privte/public key 쌍(level 3)을 하나 만든다. 이때 만들어진 pubkey는 수동으로 aws ec2에 전달해 주어야 한다.

# vi wg0.conf
wg0.conf 파일을 열어 아래와 같이 interface 및 peer 설정을 해 주도록 한다.

[Interface]  //내 interface(orangepi) 설정을 해 준다.
McEliecePrivateKey = /root/workspace/pq-wireguard/wg_config/privkey
McEliecePublicKey = /root/workspace/pq-wireguard/wg_config/pubkey
ListenPort = 59760

[Peer]  //상대방(aws ec2) 설정을 해준다.
McEliecePublicKey = /root/workspace/pq-wireguard/wg_config/awsec2/pubkey
AllowedIPs = 10.0.0.2/32
Endpoint = 13.124.203.0:51820
~
참고: 사전에 미리 peer의 pubkey를 전달 받아, 위의 디렉토리에 맞게 복사해 두어야 한다.

# ip link add dev wg0 type wireguard
# ip addr add 10.0.0.5/24 dev wg0
# ip link set wg0 up
# ../wg setconf wg0 /root/workspace/pq-wireguard/wg_config/wg0.conf

# ifconfig wg0
# ../wg show

[그림 5.4] Orange Pi: pq-wireguard wg0 interface 명령 및 wg show 명령 실행 모습
참고: Wireguard와는 달리 PQ-Wireguard의 경우는 fingerprint라는 항목을 사용하여, 길이가 아주 길어 출력하기 힘든 Classic McEliece의 public key를 대신하고 있다.

root@OpenWrt:~/workspace/pq-wireguard/wg_config# ping 10.0.0.2
aws ec2의 vpn ip(10.0.0.2)로 ping을 시도한다.

PING 10.0.0.2 (10.0.0.2): 56 data bytes
64 bytes from 10.0.0.2: seq=0 ttl=64 time=4.888 ms
64 bytes from 10.0.0.2: seq=1 ttl=64 time=5.151 ms
64 bytes from 10.0.0.2: seq=2 ttl=64 time=5.240 ms
64 bytes from 10.0.0.2: seq=3 ttl=64 time=4.446 ms
64 bytes from 10.0.0.2: seq=4 ttl=64 time=4.524 ms
^C
--- 10.0.0.2 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 4.446/4.849/5.240 ms
참고: 위와 같이 ping이 성공하려면 aws ec2에도 적절한 설정이 이미 진행되어 있어야만 한다.

c) AWS EC2 위에 PQ-Wireguard 올리기
아래 내용은 Ubuntu 18.04 환경을 기준으로 정리한 것이다.

[kernel upgrade]
$ sudo apt --fix-broken install
$ sudo apt-get dist-upgrade -y
$ sudo reboot
$ uname -r
5.4.0-1094-aws
참고: 위 과정은 필요시 최초 한번만 해 주면 된다.

$ cd ~/pqwireguard/WireGuard/src
$ vi Makefile
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.

KERNELRELEASE ?= $(shell uname -r)
#KERNELDIR ?= /lib/modules/$(KERNELRELEASE)/build
KERNELDIR = /usr/src/linux-headers-5.4.0-1094-aws   # 이 부분을 OS version과 일치 시켜야 함.
PREFIX ?= /usr
DESTDIR ?=
SRCDIR ?= $(PREFIX)/src
DKMSDIR ?= $(SRCDIR)/wireguard
DEPMOD ?= depmod
~

$ make clean; make
$ ls -l wireguard.ko 
-rw-rw-r-- 1 ubuntu ubuntu 618304 Jan 25 04:03 wireguard.ko

$ sudo cp tools/wg /usr/bin/wg.pqc

$ sudo insmod ./wireguard.ko
insmod: ERROR: could not insert module ./wireguard.ko: Invalid module format

[  222.799577] wireguard: exports duplicate symbol blake2s_final (owned by kernel)
[ 4796.090494] wireguard: exports duplicate symbol blake2s_update (owned by kernel)

참고: 위의 에러 message를 없애기 위해 crypto/zinc/blake2s/blake2s.c file의 blake2s_final(),  blake2s_update() codes 부분을 강제로 막아 두었다.

$ sudo insmod ./wireguard.ko
$ lsmod | grep wireguard
wireguard             503808  0
ip6_udp_tunnel         16384  1 wireguard
udp_tunnel             16384  1 wireguard
참고: udp_tunnel, ip6_udp_tunnel이 구동되지 않았을 경우에는 "Unknown symbol udp_sock_create4"과 같은 에러가 발생하게 되니, wireguard를 구동하기 전에 각각에 대해 modprobe udp_tunnel; modprobe ip6_udp_tunnel해 주어야 한다.

wg.pqc mckey privkey pubkey
참고: 여기서 만든 McEliece public, private key를 /wg_config/awsec2에 복사한다.

$ vi wg0.conf
[Interface]
McEliecePrivateKey = /wg_config/awsec2/privkey   // 이 부분을 앞 단계에서 맞춰 주어야 함.
McEliecePublicKey = /wg_config/awsec2/pubkey
ListenPort = 51820

[Peer]
McEliecePublicKey = /wg_config/orangepi/pubkey  // 사전에 orange pi pubkey를 복사하여 이 곳에 위치시켜야 함.
AllowedIPs = 10.0.0.5/32
Endpoint = 0.0.0.0:0

$ sudo ip link add dev wg0 type wireguard
$ sudo ip addr add 10.0.0.2/24 dev wg0
$ sudo ip link set wg0 up
$ sudo /usr/bin/wg.pqc setconf wg0 ./wg0.conf

$ ifconfig wg0
sudo wg.pqc show

[그림 5.5] AWS EC2: pq-wireguard wg0 interface 명령 및 wg show 명령 실행 모습

$ ping 10.0.0.5
Orange pi vpn ip(10.0.0.5)로 ping을 시도한다.

PING 10.0.0.5 (10.0.0.5) 56(84) bytes of data.
64 bytes from 10.0.0.5: icmp_seq=1 ttl=64 time=4.53 ms
64 bytes from 10.0.0.5: icmp_seq=2 ttl=64 time=4.60 ms
64 bytes from 10.0.0.5: icmp_seq=3 ttl=64 time=4.61 ms
64 bytes from 10.0.0.5: icmp_seq=4 ttl=64 time=4.66 ms
64 bytes from 10.0.0.5: icmp_seq=5 ttl=64 time=4.42 ms
^C
--- 10.0.0.5 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 4.428/4.570/4.669/0.102 ms

OK, 여기까지 상호간 ping이 되었다면 정상적으로 build 및 환경 설정이 이루어진 것이다.

______________________________________________________

<여기서 잠깐>

PQ-Wireguard를 Go 언어로 구현(PQC 알고리즘: CRYSTALS Kyber512 탑재)한 project도 있다.

https://github.com/kudelskisecurity/pq-wireguard

https://research.kudelskisecurity.com/2021/07/08/adding-quantum-resistance-to-wireguard/


<Ubuntu 18.04 desktop>

go version

go version go1.15 linux/amd64

참고: go version이 최신 버젼이면 gvisor에서 에러가 발생하니 주의를 요한다.


git clone https://github.com/kudelskisecurity/pq-wireguard

pq-wireguard

go build

$ ls -la
total 6488
drwxrwxr-x 12 chyi chyi    4096  1월 22 22:02 .
drwxrwxr-x  4 chyi chyi    4096 12월 26 16:04 ..
drwxrwxr-x  8 chyi chyi    4096 12월 26 16:04 .git
-rw-rw-r--  1 chyi chyi     269 12월 26 16:04 .gitignore
-rw-rw-r--  1 chyi chyi    1023 12월 26 16:04 COPYING
-rw-rw-r--  1 chyi chyi     866 12월 26 16:04 Makefile
-rw-rw-r--  1 chyi chyi    3897 12월 26 16:04 README.md
drwxrwxr-x  2 chyi chyi    4096 12월 26 16:04 conn
drwxrwxr-x  2 chyi chyi    4096 12월 26 16:04 device
-rw-rw-r--  1 chyi chyi     359 12월 26 16:04 go.mod
-rw-rw-r--  1 chyi chyi   41400 12월 26 16:04 go.sum
drwxrwxr-x  3 chyi chyi    4096 12월 26 16:04 ipc
-rw-rw-r--  1 chyi chyi    5267 12월 26 16:04 main.go
-rw-rw-r--  1 chyi chyi    1927 12월 26 16:04 main_windows.go
-rw-rw-r--  1 chyi chyi    6640 12월 26 16:04 peerA.conf
-rw-rw-r--  1 chyi chyi    6639 12월 26 16:04 peerB.conf
drwxrwxr-x  2 chyi chyi    4096 12월 26 16:04 ratelimiter
drwxrwxr-x  2 chyi chyi    4096 12월 26 16:04 replay
drwxrwxr-x  2 chyi chyi    4096 12월 26 16:04 rwcancel
drwxrwxr-x  2 chyi chyi    4096 12월 26 16:04 tai64n
drwxrwxr-x  2 chyi chyi    4096 12월 26 16:04 tests
drwxrwxr-x  5 chyi chyi    4096 12월 26 16:04 tun
-rwxrwxr-x  1 chyi chyi 6498006  1월 22 22:02 wireguard


이렇게 build한 pq wireguard를 구동시키는 방법은 위의 site에 자세히 소개되어 있으니, 확인해 보기 바란다.
______________________________________________________


d) Orange Pi를 VPN Router로 만들기
자, 모든 것이 준비되었으니, 이제 부터는 Orange Pi를 VPN router로 만들어 보도록 하자. Orange Pi L1 Plus LTS는 태생부터 router로 동작하도록 설계되어 있다. 따라서 여기서 해 주어야 할 부분은 새로 추가된 vpn interface 관련 내용을 firewall에서 열어 주는 것 뿐이다.

# netstat -nr

[그림 5.6] 라우팅 테이블 확인

<firewall 설정하기>
SSH or console login 후, 아래 명령을 일일이 실행 준다.

# Add the firewall zone
root@mango:~# uci add firewall zone
root@mango:~# uci set firewall.@zone[-1].name='wg'
root@mango:~# uci set firewall.@zone[-1].input='ACCEPT'
root@mango:~# uci set firewall.@zone[-1].forward='ACCEPT'
root@mango:~# uci set firewall.@zone[-1].output='ACCEPT'
root@mango:~# uci set firewall.@zone[-1].masq='1'17
root@mango:~# uci set firewall.@zone[-1].mtu_fix='1'
# Add the WireGuard interface to it
root@mango:~# uci set firewall.@zone[-1].network='wg0'
# Forward WAN and LAN traffic to/from it
root@mango:~# uci add firewall forwarding
root@mango:~# uci set firewall.@forwarding[-1].src='wg'
root@mango:~# uci set firewall.@forwarding[-1].dest='wan'
root@mango:~# uci add firewall forwarding
root@mango:~# uci set firewall.@forwarding[-1].src='wg'
root@mango:~# uci set firewall.@forwarding[-1].dest='lan'
root@mango:~# uci add firewall forwarding
root@mango:~# uci set firewall.@forwarding[-1].src='lan'
root@mango:~# uci set firewall.@forwarding[-1].dest='wg'
root@mango:~# uci add firewall forwarding
root@mango:~# uci set firewall.@forwarding[-1].src='wan'
root@mango:~# uci set firewall.@forwarding[-1].dest='wg'
root@mango:~# uci commit firewall
root@mango:~# /etc/init.d/firewall restart

<LuCI에서 wg interface 만들기>
다음으로 webui에 login 후, Network => Interface 메뉴에서 wg0 interface를 하나 추가해 준다.

[그림 5.7] webui에서 wg interface 생성 모습

<firewall 설정 변경>
마지막으로 webui Network => Firewall 메뉴에서 Forward rule을 Accept로 변경해 준다. 이후 Save & Apply 버튼을 눌러 주도록 한다.


[그림 5.8] webui에서 firewall 설정 변경

자, 이제 모든 설정이 끝났다. 이 상태에서 내부망의 PC로 부터 AWS EC로 ping을 시도해 본다. 정상적으로 통신이 되는가 ?

[그림 5.9] 내부망의 PC에서 VPN peer로 ping 하는 모습


지금까지 Orange Pi L1 Plus LTS board를 가지고 크게 아래 2가지를 확인해 보았다.

  1. i2c device driver 추가

  2. PQ-Wireguard 기반 VPN router 설정

부족한 부분은 추후 다른 blog post를 통해 보강 예정이다. 끝까지 읽어 주셔서 감사드린다. 😎


6. References

[1] OrangePi_R1_Plus_LTS_RK3328_User_Manual_v2.0.pdf
[2] https://rockchip.fr/RK3328%20datasheet%20V1.2.pdf
[3] Linux Driver Development for Embedded Processors 2nd edition, Alberto Liberal de los Rios
[4] https://www.friendlyelec.com/index.php?route=product/product&product_id=284
[5] https://elinux.org/images/1/1e/I2C-SPI-ELC-2020.pdf
[6] https://s3.amazonaws.com/files.douglas.stebila.ca/files/research/presentations/20230118-QuantumDays.pdf
[7] https://s3.amazonaws.com/files.douglas.stebila.ca/files/research/presentations/20220822-SAC-part1.pdf
[8] https://csrc.nist.gov/News/2022/pqc-candidates-to-be-standardized-and-round-4
[9] https://www.youtube.com/watch?v=FUb75AUXMvw&ab_channel=Chaos-WestTV
[10] https://eprint.iacr.org/2020/379.pdf
[11] https://cryptojedi.org/peter/data/zitis-20211025.pdf
[12] https://csrc.nist.gov/CSRC/media/Presentations/pq-wireguard-we-did-it-again/images-media/session-5-raynal-pq-wireguard.pdf
[13] CYSTALS-Kyber: a CCA-secure module-lattice-based KEM, Joppe Bos 
[14] CRYSTALS-Dilithium-April2018.pdf
[15] https://cryptojedi.org/papers/dilithium-20170627.pdf
[16] Active Implementation of End-to-End Post-Quantum Encryption,Anton Tutoveanu, University of Wollongong
[17] https://www.youtube.com/watch?v=FUb75AUXMvw&ab_channel=Chaos-WestTV
[18] https://www.youtube.com/watch?v=zsEj28SFyCs&ab_channel=MojtabaBishehNiasar
[19] Lattice-based NIST Candidates - Abstractions and Ninja Tricks, Thomas Prest PQShield European Cyber Week
[20] and Google~



Slowboot