描述
<h1 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8; text-align: center;">免责声明</h1>
<p>1. PocketTrustee(以下简称本项目)是由社区支持的开源项目,没有任何个人或集体可以为其安全性提供长期保障;</p>
<p>2. 本项目仅供学习、交流使用,严禁将PocketTrustee用于任何需要安全保障的领域;</p>
<p>3. 由于使用本项目造成的财产损失,社区开发者不承担任何责任;</p>
<p>4. 本项目硬件部分以CC BY 4.0协议发放,软件部分以Apache-2.0协议发放,社区开发者不对本项目任何部分的可靠性负责,社区开发者不对衍生项目负责;</p>
<p>5. 本项目所使用的开源库不在“本项目软件”的范围内,因此不受Apache-2.0协议约束,不随同本项目分发,关于这些库的开源协议,请查看库文件夹下的LICENSE文件;</p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">1、项目功能介绍</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8; text-align: center;"><strong><span style="font-size: 24px;">“数字钥匙超进化”</span></strong></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="color: #34495e;"><em>卡片太多,总是忘记带卡片?</em></span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="color: #34495e;"><em>小区门禁是低频卡,手机无法模拟?尝试复制卡片,却被防火墙识破?</em></span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="color: #34495e;"><em>CPU卡,滚动码卡,全加密卡,手机模拟无能为力?</em></span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="color: #34495e;"><em>聊天软件的密码,工作平台的密码,某个神秘小网站的密码......密码太多记不来,设置为相同的密码又担心泄漏一个全部完蛋?</em></span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="color: #34495e;"><em>Authenticator为我生成了强密码,却还要手动输入,断网直接同步失败?</em></span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="color: #34495e;"><em>TOTP,FIDO,双因素验证希望有一个物理密钥,但市场上的物理密钥却太贵?</em></span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">PocketTrustee将解决以上所有问题!</p>
<p>如果你有了解过一些安全产品,那么简单来说,PocketTrustee是这些产品的整合;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/8TvJfro5GyOvGLzKY8z9j8V2P9xFisVOBEMhAp2n.png" width="931" height="600"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">PocketTrustee有一个13.56Mhz的NFC天线,首先可以模拟IC卡(用于传输NDEF数据),其次充当IC卡芯片的天线,用于支持物理卡片;由于本质只是给卡片换了个天线,因此理论上PocketTrustee支持所有类型的卡片,只要你能把芯片整出来;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">PocketTrustee还有一个125khz的低频天线,用于读取和模拟低频ID卡,弥补了手机/手环NFC模拟不能模拟低频卡的缺点;目前低频ID卡仍有大量老旧小区在用,因此支持ID卡是非常必要的;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">除了卡片相关功能,我们还有数字密钥功能;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">PocketTrustee的主控STM32L443支持USB Device,TRNG和AES加密,当工作在HID模式下时,可以充当一个键盘;用户可以生成密码,保存密码,然后在需要时使用PocketTrustee自动输入密码,这弥补了Authenticator类软件不能自动输入的缺点,毕竟手动输入一串字母数字符号组合的随机密码可是很痛苦的()</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">此外,还支持双因素验证,主要体现在TOTP(时间一次性密码)和FIDO2(无密码身份验证协议,目前正在实现),除此之外,其他一切验证协议都是有可能实现的,只是软件更新的问题;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">这些安全功能都是常规功能,PocketTrustee不会止步于此,这块小小的数字钥匙有着无穷的开发潜力,请看我们目前想到的特色功能;</p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">2、项目属性</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">项目为首次公开,公开前由本人独立开发,并为首次参与“立创电子设计大赛”</p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">3、开源协议</h3>
<hr class="horizontal-splitline normal-bold-2">
<p>项目硬件部分以CC BY 4.0协议发放,软件部分以Apache-2.0协议发放;</p>
<p>你可以在Github上找到本项目的软件部分,并为其贡献代码;</p>
<p>本项目所使用的开源库不在“本项目软件”的范围内,因此不受Apache-2.0协议约束,不随同本项目分发,关于这些库的开源协议,请查看库文件夹下的LICENSE文件;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/Juf9vkV4n1e371XkOBOvowg62wGBrqT2DxhTYbD3.png" width="905" height="601"></p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4、硬件部分</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">PocketTrustee的硬件部分可以分为高频射频部分、低频射频部分、数字部分,硬件框图如下:</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/ATz1Ags3yd9pcrTpBSSyQdueysd4Ul7RPhVsmpZ5.png" width="772" height="504">4.1、高频射频部分实现</h4>
<p>IC卡工作在13.56Mhz高频段,相比于ID卡可以传输更多信息。IC卡的分类非常多,最常见的是Mifare 1卡(简称M1卡),部分扇区加密的M1卡也称半加密卡,全部扇区加密的卡即全加密卡;</p>
<p>M1卡可以通过漏洞攻击破解,一些厂家在M1卡的基础上改进,出现了无漏洞卡、滚动码卡、CPU卡等,这些卡通常无法被模拟(使用手机NFC模拟时提示卡片被加密,仅能模拟卡号);</p>
<p>PocketTrustee考虑到此问题,集成了IC卡模拟和物理卡槽;</p>
<p> </p>
<p>IC卡模拟通过PN532芯片实现,虽然PN532是一款比较老的芯片,但是这款经典的芯片支持许多IC卡工具(如MifareOneTool),配合PocketTrustee的透传模式,可以将PocketTrustee作为一个全功能读卡器使用,用于编辑其他卡片;此外PN532支持非加密IC卡模拟功能,支持NDEF协议,可以传递Wi-Fi,URI,个人名片等多种信息;</p>
<p> </p>
<p>IC卡物理卡槽是PocketTrustee全能的关键,等同于为IC卡更换工作环境;</p>
<p>稚晖君大佬曾设计过一款多功能NFC卡片Link-Card,采用物理开关切换;PocketTrustee选择使用模拟开关,并将物理卡槽设计为SD卡形状,可以很方便地插拔而无需拆开设备外壳,便于添加/去除卡片,算是对Link-Card的一种改进(在这里感谢Link-Card项目为本项目提供了部分设计灵感)</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/ZDhhpzA7g6gYFPv725uOZpL0Qfp3DtJ4SVfKnaaT.jpeg" width="545" height="409"></p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.2、低频射频部分实现</h4>
<p>ID卡工作于125Khz低频,仅能传递ID卡号信息,ID卡号以Manchester编码载波于125Khz信号上;</p>
<p>PocketTrustee选择纯模拟电路实现载波信号提取,BL1551是一个低功耗模拟开关,用于激励线圈,随后的4.7nF用于谐振,这样我们就能得到载有Manchester编码的信号;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/6vM4E3OsSQXmeCGF4T76hIQ7c1ybDz3Li3QlwSbH.png"></p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/VaGuTqAGTRuLuGUQ0FdS3COy8RbCqAO01iMpYXog.png"></p>
<p>该信号通过检波二极管,两阶RC滤波,最后LF_IN将得到检波后的信号;</p>
<p>此时,STM32L443的COMP(低功耗比较器)就起到关键作用了,通过与0v电平进行比较,我们就能捕获到Manchester编码的上升下降沿,最后进行软件处理即可,实现原理较为简单;</p>
<p>LF_RSSI和LF_MOD是ID卡模拟时用到的功能,其中RSSI即信号强度,用于检测是否有读卡器接近,避免不必要的功耗;LF_MOD是负载,用于产生载波;</p>
<p>为了防止第三方可复制卡片,部分ID卡门禁读头会尝试先写入卡片,再读取卡片,以此检测用户是否使用了复制卡,也被称为防火墙,PocketTrustee的ID卡模拟只会简单纯粹地输出ID卡号信息,并不会响应写入信号,因此不会被防火墙识破;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.3、数字部分实现</h4>
<p>这部分介绍比较简单,主控选用STM32L443,一款超低功耗MCU,最主要特点是具有硬件AES加密,用户的密码经过加密后存储于外部NAND Flash;</p>
<p>PocketTrustee使用了两颗LDO作为电源,其中TPS7A2033为主要电源,处于常开状态;而TPS7A0333专为指纹模块供电,并且可控关闭;这两款LDO均为超低IQ(静态电流) LDO,关断状态下静态电流仅有3nA;</p>
<p>是否存在指纹模块被替换以破解认证机制的可能?并不会,PocketTrustee选用的ZW0919支持口令认证和多种加密认证(SM4,AES,RSA等),更换指纹模块将无法识别并导致设备锁死;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.4、某次神秘bug的启示</h4>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8;">PocketTrustee的前几版硬件,出现了按钮一带三的情况,即按下一个按钮,三个按钮的中断全部触发;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8;">为了排查了这个问题,重复打了数版PCB,而问题迟迟不能解决,可把我急坏了(</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8;">被干扰的通道belike:</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/J5J5zlJ4NF49QoXpDyx9RyWWX5suTATF6dxgsrbW.png" width="823" height="483"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8;">在多位热心大佬的帮助下,问题最后解决,在这里和大家分享一些可能的原因,供大家设计PCB时规避;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: center;"><span style="font-size: 18px;">问题1:回路面积过大</span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;"><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/9qpU5bT9GXjNx3CNio8tmc5WRSPbLOVoIZnXf0y5.png" width="687" height="455"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;">回路面积过大可能形成天线,影响信号传递,请勿模仿(</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: center;"><span style="font-size: 18px;">问题2:地平面不完整/地回路过窄</span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;"><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/155Gewu3m5RPt4q2cvfcAvxfG5XtNgb4svWiRfWN.png" width="667" height="441"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;">当地回路过窄,电容放电导致局部地平面电平被抬高,导致中断触发,请勿模仿x2;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;">现在的PocketTrustee留一个完整的地平面,不怕你流不回去(</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;"><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/N7GD2PoV5mVTMEHSIH0Xu9IoFxNQCpHAVgpcZLNy.png" width="620" height="307"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: center;"><span style="font-size: 18px;">问题3:陈年老助焊剂</span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;">问题在这里终于得到彻底解决,我使用的助焊剂有些年头了,性质发生了一些变化,导致严重的信号耦合;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;">各位一定要使用优质的助焊剂,并且记住洗板也是很重要的(</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8; text-align: left;"><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/z90bGPCcfefKvlqyCtCOFOdoBTJqm5k8eLiFpWri.jpeg" width="143" height="143"></p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">5、软件部分</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">PocketTrustee的软件设计很有意思,也是一个庞大的工程,在这颗256KB Flash 64KB SRAM的MCU上,可能是无限的!让我们慢慢道来;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.1、软件特性概述</h4>
<p>PocketTrustee运行在FreeRTOS上,并针对性写了一套GUI(我们稍后详细介绍);</p>
<p>PocketTrustee选择的工具链是CMake+ARM-GCC+Ninja,主要程序选择C++编写,全开源工具链也不失为本项目的一大特点;</p>
<p>PocketTrustee的软件长期更新,你可以在Github获取最新的固件,也欢迎各位大佬的贡献;</p>
<p>PocketTrustee有一些安全性设计,使得我无法直接提供烧录文件,你必须根据你使用的芯片的特性修改源代码,编译出独属于你的PocketTrustee设备的固件,这部分我们稍后介绍;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.2、GUI</h4>
<p>虽然PocketTrustee是一个数字钥匙,开发重心应该是安全功能,但我却在GUI的设计上头疼了很久;</p>
<p>大部分的GUI库,即使是轻量级GUI库,都是为彩屏设计,集成了许多控件,对于单色屏(尤其是刷新率极低的墨水屏)非常不友好,并且占用大量flash;</p>
<p>因此我为PocketTrustee写了一个极简GUI,专为墨水屏设计,仅由一个cpp文件和一个头文件组成,且自由度极高(本身不带有任何控件,渲染行为均由用户定义的回调函数实现);</p>
<p>这个GUI占用了9.4K的静态内存(双缓冲,每一个缓存4736字节),不使用动态内存,目前在PocketTrustee上表现良好;</p>
<p>STM32L443的SRAM分为SRAM1(48KB)和SRAM2(16KB),PocketTrustee将SRAM2用于显存和FS缓存,而堆栈和全局变量存储在SRAM1中,充分利用L4的内存布局特性;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/AOaDtciFaFAmVXr1WD6LK35uwGVqtPvK66y7XT3P.png"></p>
<p>GUI任务运行在一个独立的Task中,并在阻塞状态下等待Notify,阻塞状态的Task不会影响Tickless LowPower模式的进入;</p>
<p>FreeRTOS的直达任务通知在这里发挥了很大作用;一方面,我们不希望GUI任务在没必须刷新屏幕时占用CPU,另一方面,刷新这种耗时操作也不应阻塞其他任务;</p>
<p>通过直达通知,GUI任务在空闲时会自动处于阻塞状态,在刷新完成前,不会响应其他通知,从另一个角度完成了“软件消抖”的操作;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.3、AES密钥,PIN保护,MPU和Firewall</h4>
<p>用户的所有密码数据(包括ID卡数据)都在经过AES加密后存储于外部NAND Flash;</p>
<p>此外,PocketTrustee有一个由用户设置的六位PIN码,用于在设备被暂时锁定(如指纹错误次数过多)时进行验证解锁;</p>
<p>PIN码仅有一次输入机会,若输入错误,AES寄存器将被清空,同时设备被锁死;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/10GkXPHcBAESeanh9jtTtlVYYr5gZqpiBXwSI0w9.jpeg" width="507" height="380"></p>
<p>除了爆破性破解(然而很难),只要我们能安全存储AES密钥和PIN码,那么其他数据就能被安全存储;</p>
<p>依靠L系列MCU超低功耗的特性,PocketTrustee的MCU设计为永不断电,并且AES密钥存储在AES-KEY寄存器中,而PIN码存储于一个受MPU和Firewall保护的特定内存区域中;</p>
<p>这种操作带来的好处是,常规方法将难以提取出密钥,保证了安全性,但同时意味着一旦MCU复位,密钥将丢失,设备无法再使用;</p>
<p>不必担心因为电量耗尽而复位的情况,由于被清空的只是密钥寄存器,数据仍然存在于Flash中,可以通过授权的上位机工具恢复数据,避免意外复位丢失密码的情况;</p>
<p>那如果设备丢失该怎么办?PocketTrustee提供了数据备份功能,如果设备丢失,通过上位机可以查询到备份的密码;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/62YZ0a0TaFCJhIZ9HWeV9H1j3rqDQPj9I9o7AgVC.png"></p>
<p>Firewall是一个很少被大家提及的特性,鲜有资料可以查询,那么我们来看看PocketTrustee中Firewall是如何保护PIN码的;</p>
<p>Firewall的工作特点是:被保护的数据段(可以是变量也可以是可执行数据)只能通过特定的入口进入,且进入后不能“非法跳出”;</p>
<p>这样就为受保护的数据和代码构建起一道墙,将墙内操作和墙外隔离开,因此叫Firewall;Firewall在使能后只能通过系统复位来关闭;</p>
<p>PocketTrustee将PIN码的存储和管理部分的代码使用Firewall保护起来,即使对于发起验证的方法来说,这部分操作都是不透明的;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/eosr41Mryx2U3mmbynX40mvcHnZK9S6d6jQUVtad.png" width="897" height="378"></p>
<p>而MPU相比Firewall就比较常见了,再次不赘述原理;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.4、Manchester解码</h4>
<p>ID卡号通过Manchester编码载波于125Khz信号上,通过上面介绍的硬件电路,我们已经可以得到载波信号;</p>
<p>要读出卡号,还需要对载波信号进行Manchester解码;</p>
<p>STM32的COMP(低功耗比较器)会在每一个上升下降沿触发中断,通过HAL_COMP_GetOutputLevel可以得到此时的LF_IN电平;</p>
<p>LPTIM提供一个4us的时基,配合COMP,很容易读出数据;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/wRNtoqKOrder7KshNEudnQJBZ704qmUbyn84BxIE.png"></p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.5、USB-Device和CBOR</h4>
<p>PocketTrustee的USB协议栈是CherryUSB,这是一个国创开源的USB协议栈,详细信息请见<a href="https://github.com/cherry-embedded/CherryUSB" target="_blank">cherry-embedded/CherryUSB: CherryUSB is a tiny and beautiful, high performace and portable USB host and device stack for embedded system with USB IP</a></p>
<p>我们主要使用到CDC和HID class,分别用于与上位机通讯和模拟键盘输入;</p>
<p>使用CDC与上位机通信有一个优势,即不需要安装驱动,兼容性更好;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/h2k3ICKCoET61OVmiPemk4B2cqWT2SFTjK9ztSJS.png" width="1397" height="498"></p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/PdjaKalFrI9gLXb7VOcFWov4MkVSUj43hvu9bd4b.png" width="1296" height="393"></p>
<p>CBOR是简明二进制对象的缩写,在WebAuth中使用广泛;</p>
<p>CBOR的编码类似JSON,但以二进制形式编码;</p>
<p>PocketTrustee的cbor编解码库是zcbor,详细情况请见<a href="https://github.com/NordicSemiconductor/zcbor" target="_blank">NordicSemiconductor/zcbor: Low footprint C/C++ CBOR library and Python tool providing code generation from CDDL descriptions.</a></p>
<p>不论是FIDO2协议还是与上位机的通信,都用到了CBOR,受于篇幅限制这里不赘述;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.6、外部Flash与LittleFS</h4>
<p>GUI所使用的资源,以及用户数据全部存储与外部Flash,并使用LittleFS进行管理;</p>
<p>LittleFS是开源的文件系统,支持掉电保护(虽然我们用不上)和擦写均衡;</p>
<p>PocketTrustee的文件加密在前端进行,这意味着加密文件的文件信息是开放的,但只能读出被加密后内容,这种设计避免了不必要的加密带来的性能损耗,同时保证数据安全;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/Boz3zBgnzeRuaU6sQmBzr5sV4w0NsCido3RuRrhr.png" width="864" height="278"></p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.7、RTC,Timestamp与TOTP</h4>
<p>不同于F1中直接使用CNTH和CNTL计秒的操作,L4提供了两个32bit寄存器,以BCD格式直接存储时间和日期</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/HEV8I8p6sEyfUWdNmvEmgQGnf1i7sDAXQuBWaLwh.png"></p>
<p>TOTP(基于时间的一次性口令)是一种双因素认证方法,TOTP通过当前时间戳来计算口令,因此不依赖于网络连接;</p>
<p>理解TOTP前,先来看看OTP(一次性口令)</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/kcAhjL1tM18fk6ZA3QGgwqgmk4Rr0p4UmaB6N0Hs.png" width="1000" height="778"></p>
<p>了解原理后,PocketTrustee的TOTP实现就非常清晰了;</p>
<p>支持TOTP的网站会交给用户一个二维码,这个二维码包含密钥信息,当然PocketTrustee设备本身不能扫描二维码,但可以通过上位机分析后将密钥数据发送给设备;</p>
<p>密钥被AES加密后保存在外部NAND-flash内,使用时解密,结合当前时间戳计算即得到口令;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.8、低功耗设计,FreeRTOS Tickless LowPower</h4>
<p>低功耗是本项目设计的最大难点,体积限制了最大电池容量,而“钥匙”的定位会让人觉得这并不是需要经常充电的设备;</p>
<p>为了保持设备低功耗,首先我们使用了FreeRTOS的Tickless低功耗模式;</p>
<p>FreeRTOS的Tickless LowPower模式保证设备在空闲时能及时进入睡眠状态;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/wtLCUlR139BCz03VX6bDcYYdqF8aFINcLHYdrQh1.png" width="794" height="291"></p>
<p>然而,最大深度睡眠时长受限于SysTick计时器,一般只有500ms;</p>
<p>好在PocketTrustee的软件设计十分紧凑,因此跳出睡眠只可能由外部操作触发;在进入停止模式后,MCU的PLL,MSI,HSI,HSE全部停止,SysTick也随之停止,此时睡眠跳出只能被外部中断触发,延长了最大睡眠时间;</p>
<p>一旦MCU空闲,即进入STOP2模式,该状态下MCU几乎完全不耗电;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/1k62Ufk777kpjnkcEgM6Lt7T3vx9AahlMGryTkV0.png" width="323" height="471"></p>
<p>通过CubeMX的PCC分析,主控在正常速度运作时的功耗为5.08mA<br><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/G6SrrNpHaiCEv0RaObQUGQYcifdKPcGsIckXbjMS.png" width="1226" height="365"></p>
<p>可以看到,主要功耗来源是用于产生125khz激励信号的TIM1,以及与PN532通讯的USART3;</p>
<p>不过,PocketTrustee的软件设计中,当不使用到某一项功能时,其相关外设时钟会被完全关闭,因此,即使是运行状态下,也不会达到最大功耗;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/U64d4g02Ec53bwNQjGMSbvYrBsLIGGSAXejlEZcy.png" width="714" height="387"></p>
<p>通过以上低功耗操作,算入其他元器件后,我们将PocketTrustee的<strong>静态总功耗降低到了200uA</strong>,如果是一颗500mAh的电池,那么理论续航就来到了2500小时,即104天;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/EepYXhRiHJCHqdFeHJioOkpKPLsrwJTrOdakydlZ.jpeg" width="471" height="628"></p>
<p>平时刷卡就完全不耗电吗?其实还真是!这里我们测量的静态总功耗包括了模拟开关的功耗,刷卡时,PocketTrustee并不必要从睡眠中切出,只有切换卡片时才有必要唤醒设备;</p>
<p>需要USB的功能就更不必说,接上usb后只会越用电越多(</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.x、PocketTrustee CLI</h4>
<p>在一个硬件平台上介绍由Python编写的上位机真是奇怪的体验呢(划;</p>
<p>不过CLI工具也是本项目重要的组成部分,我们还是来简单介绍一下;</p>
<p>CLI工具使用Python编写,主要使用cmd2和rich库构建,相关源码依旧可以在github上找到;</p>
<p><a href="https://github.com/KamiyamaNoir/PocketTrusteeCLI" target="_blank">KamiyamaNoir/PocketTrusteeCLI</a></p>
<p> </p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.x、如何编译,刷写固件</h4>
<p>为了便于评委评估项目,附件区提供了拍摄演示视频时所使用的固件的源代码;</p>
<p><strong>如果你打算复刻PocketTrustee,请前往<a href="https://github.com/KamiyamaNoir/PocketTrustee" target="_blank">Github</a>下载最新版本源码进行编译,并参照<a href="https://github.com/KamiyamaNoir/PocketTrustee" target="_blank">Github</a>上的编译指南,而不是使用这里的过时版本;</strong></p>
<p><strong><a href="https://github.com/KamiyamaNoir/PocketTrustee" target="_blank">KamiyamaNoir/PocketTrustee</a></strong></p>
<p>对于安全设备,使用最新固件非常重要,请一定注意,以下编译方法仅适用于附件区提供的源码;</p>
<ol>
<li>下载工具链<br>前往ST官网下载<a href="https://www.st.com/en/development-tools/stm32cubeclt.html" target="_blank">STM32 CubeCLT</a>并安装,如果你已经预先安装了CMake,ARM-GCC和ninja则可以跳过此步;</li>
<li>下载刷写工具(可选)<br>前往ST官网下载<a href="https://www.st.com/en/development-tools/stm32cubeprog.html" target="_blank">STM32 CubeProgrammer</a>,实际上Cube CLT集成了CLI版本的CubeProgrammer,如果你愿意,也可以选择使用CLI版本的CubeProgrammer;</li>
<li>编译<br>打开终端并转到项目目录下,逐行执行以下命令<br><code>cmake --preset "Release"<br></code><code>cd ./build/Release<br>ninja</code><br>在build/Release文件夹下的PocketTrustee.elf即为编译产物</li>
<li>烧录固件<br>使用Cube Prog将固件上传到设备上;</li>
<li>使用上位机初始化<br>使用USB连接设备到电脑,然后使用上位机工具初始化设备;</li>
</ol>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">4.x、PocketTrusteeCLI与PocketTrusteeController</h4>
<p>PocketTrusteeCLI是用于管理PocketTrustee的命令行工具,使用Python编写,你可以在<a href="https://github.com/KamiyamaNoir/PocketTrusteeCLI" target="_blank">Github</a>上找到PocketTrusteeCLI的源码;</p>
<p><a href="https://github.com/KamiyamaNoir/PocketTrusteeCLI" target="_blank">KamiyamaNoir/PocketTrusteeCLI</a></p>
<p>使用CLI工具可以初始化设备,添加/删除/编辑卡片和密码,管理TOTP,以及名片WiFi信息等</p>
<p>PocketTrusteeController是一个基于WinUI 3的图形化PocketTrustee管理软件,目前正在开发;相比于CLI工具,图形化工具更加精美,易于上手;</p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">6、BOM清单</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">元器件BOM表请见附件区,这里仅补充其他配件的参考购买方式;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">墨水屏选用了佳显公司的GDEY029T94,296x128分辨率,4灰度,在淘宝上有佳显的自营店,目前39元可以拿下;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">指纹模块是海凌科公司的ZW0919,球形无灯指纹模块,淘宝上也有自营店,目前15元可以拿下;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">电池选择锂离子聚合物电池,型号为213455,额定容量500mAh,很容易买到;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">外壳可以直接在嘉立创3D打印,树脂材质和尼龙材质均可,总计不到20元;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">NFC天线和低频天线可以自行选购,需要注意的是记得根据线圈电感修改谐振电容大小;我自己选用的是漆包线线圈,NFC线圈尺寸为30x35mm,低频线圈为32mm圆形线圈(电感345uH),在淘宝上很容易找到定制商家,这里不贴链接;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">7、大赛LOGO验证</h3>
<hr class="horizontal-splitline normal-bold-2">
<p style="line-height: 1.8;"> </p>
<p style="line-height: 1.8;"><img src="//image.lceda.cn/pullimage/k8tvTgam4QFxfm8iuf5SDkK0pQ5Odr4l7s7syjKi.jpeg" width="718" height="539"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><img src="//image.lceda.cn/pullimage/Q8Xjr2Op6KHOJMpZJmBaXm9uZ1XSJh15YCjA0xhY.png" width="776" height="381"></p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">8、演示您的项目并录制成视频上传</h3>
<hr class="horizontal-splitline normal-bold-2">
<p style="line-height: 1.8;">请见视频区</p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">9、复刻指南</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">哪个工科男不想拥有一块PocketTrustee,让我手把手教你复刻!</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">9.1、打板,打印外壳</h4>
<p>可以直接下载附件区的Gerber制版文件或者自行导出进行打样,推荐使用沉金工艺;<span style="text-decoration: line-through;">(每月都可以白嫖嘉立创的4层沉金)</span></p>
<p>主板板厚:0.8mm;IC卡槽板厚:1.6mm;</p>
<p><strong>请务必检查好板厚,打错了很可能无法安装</strong></p>
<p>下载附件区的外壳文件(两个),然后进行打印,推荐嘉立创的1172 Pro尼龙,当然LEDO 6060树脂也是可以的;</p>
<p>PS:外壳有开发版和普通版,区别在于调试接口有没有开孔;虽然出于安全考虑,应当打普通版,然而为了便于上传固件,还是建议打开发版;</p>
<p><strong>开发版的调试接口开小了,需要手动再剪一下,1172Pro比较软,使用小刀切出口子即可;</strong></p>
<p><img src="//image.lceda.cn/pullimage/h71eIt9gAf3bzGURV19iJ53OPsBByu1uoaa53iI2.jpeg" width="175" height="311"></p>
<p>打板和3D打印在嘉立创都可以白嫖,我们默认这步不要钱(逃;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">9.2、采购元器件,焊接,实体卡片转移</h4>
<p>建议PCB打样时也开钢网,有钢网后续焊接会方便很多;</p>
<p>元器件按照BOM表买就可以了,关于其他的配件(如电池和指纹模块)请见第6节BOM清单部分;</p>
<p>刷锡膏,热风枪,各位焊武帝一定不需要我多指导了(</p>
<p>这里大家可以选择性修改充电IC的电阻,默认的设定是50mA(也即0.1C),如果你希望充电电流能更大,可以适当调小阻值(计算公式给到了下方)</p>
<p>然而注意,普通锂离子聚合物电池的充电C数都比较小,请考虑由于增大充电电流带来的风险</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/vvAvP8KwlKXdPHXNY2dQiw1DKxvA54UUKSmU5eJF.png" width="767" height="287"></p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/lSyYXC8FwAhI4qkLsTAsMDkFAOJmkguGiNeiB13M.png"></p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/fSsEu5SuGW2lrXbYWVzlHHW2OtFqHk6a1pQO51Nv.png"></p>
<p>实体卡片转移到卡槽上,需要剪卡然后将芯片取出来</p>
<p>用手机闪光灯找到并标记芯片所在位置,然后剪卡,就能拿出芯片了</p>
<p><img src="//image.lceda.cn/pullimage/XT33eUfliTEevib1ewB2euLekxL9XXuXUUNUGnya.jpeg" width="526" height="394"></p>
<p><img src="//image.lceda.cn/pullimage/Ym3IQMuWFgGyaJU87Omx5LaKsHmR19hAGBuDWfnb.jpeg" width="366" height="488"></p>
<p>用少量焊锡以贴片的形式焊上芯片,<strong>一定有芯片的一面朝下</strong>,背面的铜箔是导电的,会导致短路</p>
<p><strong>焊接时不要高温</strong>,这些ic芯片非常不耐热,拿风枪吹久了就会导致损坏(我已经废掉一张武汉通了(</p>
<p><img src="//image.lceda.cn/pullimage/jS3fpM9TdpETnRmU27JBsfQUjvzHIzMRIrB44hwk.jpeg" width="333" height="444"></p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">9.3、组装</h4>
<p>先将指纹模块上自带的插座吹掉,那个太厚了;随后焊几根线上去,注意不要使用太粗的线(我这里直接使用漆包线);</p>
<p><img src="//image.lceda.cn/pullimage/4tWbDNlXssN6VzWroFHdFCYaAhYcP7xfsSbgiQ9k.jpeg" width="557" height="418"></p>
<p>卡到外壳上,周围封胶(708硅胶好使);</p>
<p>连接指纹模块和主板,再连接天线;<br><img src="//image.lceda.cn/pullimage/Wim836WGcz2zqIWOkC20eWinA2GxGRCXFFiKcVcW.jpeg" width="570" height="427">注意,我这里选择了漆包线天线导致太厚无法塞入下方空间,只能放在主板上面;<strong>这会导致信号大打折扣,尤其是低频天线</strong>(作者亲自踩坑);因此<strong>尽量把你的天线放在底下</strong>;</p>
<p><img src="//image.lceda.cn/pullimage/8LSNkPm8Z5QqJzoWURpi01ZBHmvbgbfvi2K5YHUC.jpeg" width="582" height="436"></p>
<p>扣上屏幕,连接电池,然后<strong>记得短接用来测试电流的两个脚</strong>,固定好主板,就能把屏幕合上了;</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="//image.lceda.cn/pullimage/V0HxfEZdpJq0uHL0bKkxMQF93guu9GXHgU55k5IH.png" width="680" height="253"></p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">9.4、编译,上传固件,初始化</h4>
<p>这部分可能随着软件更新而发生变化,所以请前往<a href="https://github.com/KamiyamaNoir/PocketTrustee" target="_blank">KamiyamaNoir/PocketTrustee</a>并参照那里的README进行操作;</p>
<h4 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">9.5、结束</h4>
<p>Enjoy your device.</p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">10、开源精神:共享,贡献</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">PocketTrustee的源码目前由我本人维护,长期更新,<span style="text-decoration: line-through;">请大家在Github上给我一个Star</span>,留意后续更新;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">如果你发现了源码中的Bug,或者有功能新点子,可以通过Issue反馈给我,我看到后会及时处理;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">如果不会使用Github,也可以加入我们的Issue&Ideas群,在这里反馈问题或新点子;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">若两者都不便使用,可以通过邮箱联系到我:xlimitarea@outlook.com</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">不过本人不经常看邮箱,邮件可能无法即使回复;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">为Repo贡献可直接PR,我会在评估代码后拉取PR;</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><img src="//image.lceda.cn/pullimage/GUTn2qGSORvThwHDVjsThY6EU4r9WS6eILdf259c.png" width="203" height="252"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
评论(1)