nRF52840捣鼓U2F FIDO 安全密钥
因为最近项目需要蓝牙Dongle来抓包,所以就买了个nrf52840开发板,顺便发现还可以捣鼓一下U2F、FIDO密钥,所以就有了本文,基于Github上的nRF52-U2F项目,这个项目支持的设备是基于nrf52840的mdk开发板(nRF52840 DK)和Makerdiary nRF52840 Micro Dev Kit USB Dongle(注意:不是Nordic官方的nRF52840 Dongle),某宝也有售,就是价格有些稍贵。如果你使用的是Nordic官方的nRF52840 Dongle或者相同电路的平替产品,最好是使用google/OpenSK: OpenSK is an open-source implementation for security keys written in Rust that supports both FIDO U2F and FIDO2 standards. (github.com)项目,在文末的小节里有描述。
nRF52840 - Bluetooth 5.2 SoC - nordicsemi.com
引用一下官方的Soc介绍:
主要功能特点:多协议蓝牙5.2 SoC,支持低功耗蓝牙、蓝牙Mesh、NFC、Thread和Zigbee
nRF52840 SoC是nRF52系列中最先进的一员。它可以满足需要协议并发性、丰富外围设备和复杂应用所面临的挑战。它为闪存和RAM提供了充足的内存,而这些正是高要求应用的必备条件。
nRF52840是具有完全协议并发能力的多协议SoC。它支持低功耗蓝牙、蓝牙Mesh、Thread、Zigbee、802.15.4、ANT和2.4 GHz专有协议栈。
nRF52840基于带有浮点单元的32位ARM® Cortex™-M4 CPU,主频为64 MHz。内置NFC-A标签用于简化的配对和支付解决方案。本芯片内置ARM TrustZone® CryptoCell密码算法单元,可独立于CPU高效执行密码算法任务。它有许多数字外设和接口,如高速SPI和QSPI接口,用于连接到外部闪存和显示器,PDM和I2S连接数字麦克风和音频;还有一个全速USB设备,用于数据传输和作为电源为电池充电。
通过精密的片上自适应电源管理系统,实现了极低的功耗。
只能说我确实觉得很好用,就是最近价格太高了。
编译固件
我使用的是Ubuntu来编译固件,如果是其他系统可以看Building the Firmware - nRF52 FIDO U2F Security Key (makerdiary.com)。
1.安装所需依赖
# On Ubuntu
$ sudo apt-get install build-essential checkinstall openssl git
2.拉取makerdiary/nrf52-u2f项目的源代码
git clone --recursive https://github.com/makerdiary/nrf52-u2f
3.下载nRF5 SDK
在官网可以下载,nRF5 SDK downloads - nordicsemi.com。我们所需的版本是:15.2.0,对应的压缩包文件是:nRF5_SDK_15.2.0_9412b96。
解压到nrf52-u2f/nrf_sdks/
目录中。
目录结构为:
./nrf52-u2f/
├── LICENSE.md
├── README.md
├── boards
├── certs
├── docs
├── external
├── firmware
├── include
├── material
├── mkdocs.yml
├── nrf_sdks
│ ├── README.md
│ └── nRF5_SDK_15.2.0_9412b96
├── open_bootloader
├── source
└── tools
4.下载gcc-arm-none-eabi
从arm官网可以下载到GNU Arm Embedded Toolchain编译链工具,Downloads | GNU Arm Embedded Toolchain Downloads – Arm Developer。
此次需要的版本是:
Linux 64-bit
File: gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2 (95.90 MB)
我下载后放到了/home/cyqsd/Project/toolchain
目录。
使用tar xjf gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2
解压。
最好还配置一下全局环境变量,可以使用vi ~/.bashrc
配置环境。
export PATH="/home/cyqsd/Project/toolchain/gcc-arm-none-eabi-6-2017-q2-update/bin:$PATH"
source ~/.bashrc
生效 一下,前面都弄正确的话,此步骤应该可以得到下面的输出:
cyqsd@ubuntu:~/Project/toolchain$ arm-none-eabi-gcc -v
Using built-in specs.
COLLECT_GCC=arm-none-eabi-gcc
COLLECT_LTO_WRAPPER=/home/cyqsd/Project/toolchain/gcc-arm-none-eabi-6-2017-q2-update/bin/../lib/gcc/arm-none-eabi/6.3.1/lto-wrapper
Target: arm-none-eabi
Configured with: /tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/src/gcc/configure --target=arm-none-eabi --prefix=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/install-native --libexecdir=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/install-native/lib --infodir=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/install-native/share/doc/gcc-arm-none-eabi/info --mandir=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/install-native/share/doc/gcc-arm-none-eabi/man --htmldir=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/install-native/share/doc/gcc-arm-none-eabi/html --pdfdir=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/install-native/share/doc/gcc-arm-none-eabi/pdf --enable-languages=c,c++ --enable-plugins --disable-decimal-float --disable-libffi --disable-libgomp --disable-libmudflap --disable-libquadmath --disable-libssp --disable-libstdcxx-pch --disable-nls --disable-shared --disable-threads --disable-tls --with-gnu-as --with-gnu-ld --with-newlib --with-headers=yes --with-python-dir=share/gcc-arm-none-eabi --with-sysroot=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/install-native/arm-none-eabi --build=x86_64-linux-gnu --host=x86_64-linux-gnu --with-gmp=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/build-native/host-libs/usr --with-mpfr=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/build-native/host-libs/usr --with-mpc=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/build-native/host-libs/usr --with-isl=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/build-native/host-libs/usr --with-libelf=/tmp/jenkins-GCC-6-buildandreg-223_20170621_1498033910/build-native/host-libs/usr --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --with-pkgversion='GNU Tools for ARM Embedded Processors 6-2017-q2-update' --with-multilib-list=rmprofile
Thread model: single
gcc version 6.3.1 20170620 (release) [ARM/embedded-6-branch revision 249437] (GNU Tools for ARM Embedded Processors 6-2017-q2-update)
4.配置编译链工具
然后进入/home/cyqsd/Project/nrf52-u2f/nrf_sdks/nRF5_SDK_15.2.0_9412b96/components/toolchain/gcc
配置一下Makefile.posix
。
教程里面推荐的是:
GNU_INSTALL_ROOT := $(HOME)/gcc-arm-none-eabi/gcc-arm-none-eabi-6-2017-q2-update/bin/
GNU_VERSION := 6.3.1
GNU_PREFIX := arm-none-eabi
我写的是:
GNU_INSTALL_ROOT := /home/cyqsd/Project/toolchain/gcc-arm-none-eabi-6-2017-q2-update/bin/
GNU_VERSION := 6.3.1
GNU_PREFIX := arm-none-eabi
注意这个,我开发环境里面还有其他编译链工具,我也选用的覆盖:
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
5.配置并生成自签名证书
进入/home/cyqsd/Project/nrf52-u2f/certs
。把myserver.cnf.example
更名为myserver.cnf
,里面的内容酌情修改一下。
回到~/Project/nrf52-u2f/tools
,使用./generate-certs.sh
来生成一份证书,类似下面这样(内容有手动改动):
cyqsd@ubuntu:~/Project/nrf52-u2f/tools$ ./generate-certs.sh
read EC key
Private-Key: (256 bit)
priv:
pub:
ASN1 OID: prime256v1
NIST CURVE: P-256
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = CH, O = cyqsd, CN = www.cyqsd.cn
Validity
Not Before: May 25 18:08:45 2022 GMT
Not After : May 22 18:08:45 2032 GMT
Subject: C = CH, O = cyqsd, CN = www.cyqsd.cn
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
ASN1 OID: prime256v1
NIST CURVE: P-256
Signature Algorithm: ecdsa-with-SHA256
Converting EC key to text...
read EC key
writing EC key
Successfully Completed!
The private key and certificate are stored in certs/keys.c
Bye bye~
6.编译U2F固件
因为是使用的硬件是nrf52840-mdk-usb-dongle
,所以进入这个目录。
/home/cyqsd/Project/nrf52-u2f/boards/nrf52840-mdk-usb-dongle/armgcc
如果你的SDK版本并不是前面写的这个,还需要在当前目录的Makefile
中改为正确的版本。
PROJECT_NAME := nrf52_u2f_nrf52840_mdk_usb_dongle
TARGETS := nrf52840_xxaa
OUTPUT_DIRECTORY := _build
SDK_ROOT := ../../../nrf_sdks/nRF5_SDK_15.2.0_9412b96
PROJ_DIR := ..
$(OUTPUT_DIRECTORY)/nrf52840_xxaa.out: \
LINKER_SCRIPT := nrf52_u2f_gcc_nrf52.ld
都觉得正常了就可以使用make clean
来清理一下旧文件和make
来编译了。
如果中途出现了:
Linking target: _build/nrf52840_xxaa.out
arm-none-eabi-gcc: error: ../../../nrf_sdks/nRF5_SDK_15.2.0_9412b96/external/micro-ecc/nrf52hf_armgcc/armgcc/micro_ecc_lib_nrf52.a: No such file or directory
../../../nrf_sdks/nRF5_SDK_15.2.0_9412b96/components/toolchain/gcc/Makefile.common:292: recipe for target '_build/nrf52840_xxaa.out' failed
make: *** [_build/nrf52840_xxaa.out] Error 1
则参考compile error · Issue #14 · makerdiary/nrf52-u2f (github.com)。
进入/home/cyqsd/Project/nrf52-u2f/nrf_sdks/nRF5_SDK_15.2.0_9412b96/external/micro-ecc
目录,执行一下build_all.sh
。
完成后,回到前面的目录,重新编译,就能成功了。
armgcc/_build/
目录中的nrf52840_xxaa.hex
就是固件。
7.编译Bootloader
进入/home/cyqsd/Project/nrf52-u2f/open_bootloader/nrf52840-mdk-usb-dongle/armgcc
目录。同样使用make
命令来编译。
armgcc/_build/
目录中的nrf52840_xxaa_mbr.hex
就是bootloader文件。
8.合并固件(可选)
从官网下载到nrf-command-line-tools
,这个版本用最新的也行,我使用的是nrf-command-line-tools-10.15.4_Linux-amd64.tar.gz
。解压到/home/cyqsd/Project/nrf-command-line-tools
中:
使用tar -xvf nrf-command-line-tools-10.15.4_Linux-amd64.tar.gz
,
配置/home/cyqsd/Project/nrf-command-line-tools/nrf-command-line-tools/bin
目录到环境变量中,同前面环境配置的操作,目录中有用于固件合并的mergehex
工具。
export PATH="/home/cyqsd/Project/nrf-command-line-tools/nrf-command-line-tools/bin:$PATH"
回到/home/cyqsd/Project/nrf52-u2f/open_bootloader/nrf52840-mdk-usb-dongle/armgcc
目录,使用make mergehex
命令,即可合并固件完毕。
合并命令实际上做的工作是下面的内容:
# Merge the app and mbr
mergehex:
@echo Merging nrf52840_xxaa.hex and mbr_nrf52_2.3.0_mbr.hex
mergehex -m $(SDK_ROOT)/components/softdevice/mbr/nrf52840/hex/mbr_nrf52_2.3.0_mbr.hex $(OUTPUT_DIRECTORY)/nrf52840_xxaa.hex -o $(OUTPUT_DIRECTORY)/nrf52840_xxaa_mbr.hex
到这里整个编译步骤就完成了,就用合并好的文件烧录即可。
烧写固件
makerdiary上已经有很详细的烧写步骤了,我就不赘述了,nRF Connet for Desktop上是全图形化操作。Upgrade U2F Firmware with nRF Connet for Desktop
截图中的固件是我用的开源项目中的固件补充截图的,实际使用就使用自己新编译的就行了。
还可以使用Python下的nrfutil
工具来完成烧录。或者Segger J-Link、OpenOCD、pyOCD等一大堆工具均可。
官方Dongle支持(未测试实践)
前面提到了nRF52840 Dongle的官方版本,并不能直接刷nRF52-U2F项目,因为引脚定义不一样,不过理论上完全可以通过修改引脚来实现官方Dongle,我也看见GitHub上有人在提问,但是并没有人回复。
在boards\nrf52840-mdk-usb-dongle\config
目录下有custom_board.h
配置文件,理论上按照现有设备重新修改引脚就可以使用,但是因为时间关系我就没去试了,还是直接推荐OpenSK项目比较好。
下面的代码只截取了一部分,仅供参考。
#define BUTTONS_NUMBER 1
#define BUTTON_1 NRF_GPIO_PIN_MAP(0,18)
#define BUTTON_PULL NRF_GPIO_PIN_PULLUP
#define BUTTONS_ACTIVE_STATE 0
#define BUTTONS_LIST { BUTTON_1}
#define BSP_BUTTON_0 BUTTON_1
OpenSK
还可以使用这个项目来实现U2F、FIDO密钥。
OpenSK是谷歌的开源项目,同样是开源的FIDO U2F项目,使用的硬件也是nRF52840,支持下面的硬件:
可以使用项目提供的setup.sh
来自动化编译,这比较简单:
git clone https://github.com/google/OpenSK.git
cd OpenSK
./setup.sh
烧写时可以选用下列方式:
OpenOCD: ./deploy.py --board=nrf52840_dongle --opensk --programmer=openocd
pyOCD: ./deploy.py --board=nrf52840_dongle --opensk --programmer=pyocd
Custom(导出Hex文件): ./deploy.py --board=nrf52840_dongle --opensk --programmer=none
平替品 E104-BT5040U
我也买了E104-BT5040U,引脚是兼容官方Dongle的,属于国产版本,只是亿佰特官方的这个型号不仔细看容易分不太清楚。。。。还有要注意的就这个塑料外壳虽然挺好看,挺坚固的,但是没有给按钮打孔,这就导致U2F要按下按钮确认的时候操作不到,所以要么就用的时候不装壳子,或者在壳子上开一个孔。因为电路上的按钮、LED灯与官方Dongle是一致的,不用额外担心。
参数对比:
因为并不需要什么额外的外设,所以就使用的时候就核对一下引脚,看是否与项目支持的是否相同就行。
引脚序号 | 引脚名称 | 引脚方向 | 引脚用途 |
---|---|---|---|
36 | RST | RESET | 复位按键 |
37 | SW | P1.06 | 功能按键 |
38 | LED1 | RGB三色LED | R(红色):P0.08;G(绿色):P1.09;B(蓝色):P0.12 |
39 | LED | LED | P0.06 |
nRF Connect for Desktop
可以从官网上下载到:nRF Connect for Desktop
自带非常好用的蓝牙调试工具和RSSI分析工具。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。