# 云丁鹿客门锁BLE通信的分析（上）

## **简介**

此次要分析的产品是Loock Touch智能门锁，由云丁科技公司出品。云丁科技是一家专注于研发和生产智能家居安全产品的公司，旗下有两大品牌，分别是针对家用市场的“鹿客”和针对公寓市场的“云丁”。Loock Touch是鹿客品牌的主打产品之一，其架构与云丁品牌的D2、D3智能门锁很相似，最新的鹿客旗舰产品是Loock Touch 2 Pro，其硬件架构与此前所有产品均不同，变化较大。

接下来的几篇的分析和讨论中，我们只对分析方法和研究过程做讨论，而不涉及任何有关漏洞的细节，这与此前的关于海康萤石智能网关的文章类似。

## **开锁流程分析**

在本专题第6篇中，我们分析了果加智能门锁，在该分析中我们以BLE开锁为分析切入点。云丁鹿客的Loock Touch门锁同样可以通过手机BLE直接开锁，那么我们就从app的开锁流程开始分析。

应用市场中下载到的云丁鹿客智能门锁的配套app加了梆梆企业版的壳，脱壳之后可以看到该app打印出来的日志（脱壳不在本系列文章的讨论范畴中，分析iOS版也是可以的）。我们操作手机开启门锁后，app的日志中有这样几条记录吸引到了我们。

![图2-1 app开锁时打印的日志](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQ1Hj3NHrRfv8GU8lN%2F-MdQPctLJsIa2WiwxmRW%2Fimage.png?alt=media\&token=354137db-1f9c-4e68-88b9-d2c4c74aa896)

根据上图中3个红框中的记录内容，我们可以推测开锁过程是基于Challenge/Response模式进行认证的，具体过程如下：&#x20;

（1）app与门锁建立BLE连接后，当使用app开启门锁时，app会先下发一条通知指令，告知门锁准备进入开锁流程；

（2）随后门锁会向手机发送一组数字，称之为Challenge；&#x20;

（3）app对Challenge进行处理生成Response并发送给门锁，门锁验证Response正确时，就会开锁。 我们对比了多次开锁时的app日志，发现每次开锁时第（1）步发送的通知指令仅有一个字节内容不同，如下图所示：

![图2-2 通知指令对比](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQ1Hj3NHrRfv8GU8lN%2F-MdQPkmsce-9TT4uHIoU%2Fimage.png?alt=media\&token=8250d289-4eeb-4022-b582-f1bd86cb91b3)

上图中，我们对比了3条通知指令，发现仅有第7个字节不同，在app封装数据包的代码段中，可以确定这个字节的含义是seqID，即数据包的序列号。因此我们可以推测这一条通知指令的作用类似于TCP协议中的SYN数据包，仅起到握手的作用，而门锁对手机的认证过程，主要是第（2）和第（3）步，那么接下来我们就分析一下app是如何处理Challenge并生成Response的。

## **Challenge的处理**

### 3.1 处理流程分析

通过在代码中搜索日志内容，我们可以确定，app收到Challenge数据后，会由下图中的方法进行处理。

![图3-1 Challenge数据处理](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQ1Hj3NHrRfv8GU8lN%2F-MdQPyGKbWl98IDJAxiV%2Fimage.png?alt=media\&token=8628b621-a4b1-4993-ada7-2adf8ce16e9b)

上图中， handleCommand方法用于处理BLE返回数据，门锁返回的Challenge就作为参数传给了方法sendUnlockCommandNewProtocol，从参数和方法名称来看，这就是处理Challenge数据、生成Response并发送回门锁的方法。&#x20;

进一步查看代码，我们发现在sendUnlockCommandNewProtocol方法中，最终调用了下图中的encryptBleKeyNewProtocol方法对Challenge数据进行处理，该方法返回后的数据，添加包头后会被直接发送给门锁。

![图3-2 对Challenge数据进行加密](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQ1Hj3NHrRfv8GU8lN%2F-MdQQ1wVdsAnNivNSrza%2Fimage.png?alt=media\&token=be0e148b-2427-4fc3-b16f-222b323193c1)

上图中，参数arg13即为Challenge数据，可以看到该方法中使用了AES EBC加密，密钥是参数arg9，加密完成后使用密文又异或了一组固定数据。&#x20;

经过以上分析我们可以推测，开锁流程中，门锁对手机的认证依赖于图3-2中的AES EBC算法，即双方使用相同的密钥对Challenge进行加密和解密处理，仅当手机拥有正确的AES密钥才能通过认证开启门锁。那么开锁流程的安全性，就取决于门锁与手机使用的密钥是否安全了。&#x20;

### 3.2 加密密钥的获取&#x20;

图3-2中，加密密钥是参数arg9，通过对arg9的回溯可以确定，对Challenge进行加密的密钥通过resetBleToken方法获取，如下图所示。

![图3-3 加密密钥的获取](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQ1Hj3NHrRfv8GU8lN%2F-MdQQ8lJZxAaK0Qt85tl%2Fimage.png?alt=media\&token=3390dd5c-0772-4c74-be68-e2efd7824460)

resetBleToken方法中发出了一个http请求，收到返回的BleToken并进行处理后，会对一些变量进行赋值。图中的mBleKey是BleKeyInfo类型的变量，其token成员变量，就是3.1章节中Challenge数据的加密密钥。下文中，我们将以BleKey来代称这个加密密钥。&#x20;

由以上分析可以知道，BleKey是app从服务器上请求到的，这里就有两种可能：（1）每次app与门锁建立通信时，都会请求一个Key并下发给门锁；（2）当app与门锁建立通信时，如果没有可用的BleKey，才会向服务器发送请求。&#x20;

为了验证以上推测，我们使用手机多次重复开启门锁，这样使手机和门锁反复建立通信，这时我们在app日志和抓取到的数据包中，都没有发现这个请求以及相关内容，可以判断app应该是本地保存了BleKey，而不需要从服务器获得了。&#x20;

我们可以通过清空app缓存的方式，删除本地存储的BleKey，删除后app开锁时的日志发生了一些变化，如下图所示：

![图3-4 删除app的蓝牙钥匙后app开启门锁时的日志](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQ1Hj3NHrRfv8GU8lN%2F-MdQQGE-az3MCu_YvHRw%2Fimage.png?alt=media\&token=5237e701-5771-4a9d-a8d2-c89bf685bc2f)

对比图3-4和图2-1中的日志内容，可以看到删除BleKey后，在开启门锁时先执行了resetBleToken，这和我们的分析吻合。这里我们注意到，resetBleToken之后，还有另外一条日志sendBleKeyCommandNewProtocol，这里是向门锁发送BleKey的方法，下面我们先继续手机获取BleKey这一过程的分析，手机向门锁下发BleKey的过程将在3.4节中分析。&#x20;

与此同时，我们在fiddler中也抓到的对应的http请求，如下图所示。

![图3-5 reset\_token请求](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQ1Hj3NHrRfv8GU8lN%2F-MdQQLqOVA1mmo2OnYRw%2Fimage.png?alt=media\&token=51d9b0d0-7c5a-4daa-a3ef-c222f87629da)

app收到返回的BleToken后，会使用AES CBC算法，对上图中红框内的secret和token进行解密，相关代码如下图所示，

![图3-6 reset\_token返回值的处理](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQWtv2lTHc374u4isy%2F-MdQWw6vQk0JCDL6qPxU%2Fimage.png?alt=media\&token=0d67d5ce-4489-497a-9235-6fb68e24f8e6)

此时可以确定，app从服务器获取到的BleKey同样被AES CBC加密算法保存。通过图3-6可以看到，其密钥由方法getCryptSecret返回，与方法名称对应，这个密钥我们就称之为CryptSecret，下一步我们就来分析CryptSecret的来源。&#x20;

### 3.3 CryptSecret的获取&#x20;

BleKey解密时的密钥通过getCryptSecret方法获得。那么我们可以从这个方法入手，相关代码的搜索结果如下图所示。

![图3-7 获取CryptSecret的代码流程](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQWtv2lTHc374u4isy%2F-MdQX1rTiLjCl4NvgC3D%2Fimage.png?alt=media\&token=a29664bd-b802-4126-92f0-722f45c45c07)

上图中，getCryptSecret方法返回的是this.mCryptSecret，而这个变量是通过setCryptSecret赋值的，setCryptSecret方法则是在getSecret方法中被调用。在getSecret方法中，可以看到CryptSecret是从云丁鹿客的服务器获取到的，其url为”api/v1/crypt\_secret”。同样的，我们在fiddler里也可以抓到crypt\_secret数据包，如下图：

![图3-8 crypt\_secret请求及其响应](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQWtv2lTHc374u4isy%2F-MdQX6mbyi_ec41KdmoS%2Fimage.png?alt=media\&token=aef56838-d45d-495c-8447-c44b83ff6041)

由于这里的信息有些敏感，所以我们做了打码处理。&#x20;

### 3.3 向门锁下发BleKey&#x20;

3.2节中说到sendBleKeyCommandNewProtocol函数负责向门锁下发BleKey，该函数如下图所示。

![图3-9 手机向门锁下发BleKey的函数](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQWtv2lTHc374u4isy%2F-MdQXCICjYezU_9s3qVT%2Fimage.png?alt=media\&token=9add3779-5b20-43fe-948c-2bb579d18c4e)

结合上图与图3-4的日志和图3-5中reset\_token请求的返回数据包，可以发现，手机向门锁下发BleKey的流程非常简单，就是对reset\_token返回数据包中的totalData进行base64解码，解码后的二进制数据直接通过BLE通信发送给了门锁，门锁对totalData的处理我们将在后续文章中分析。

## **小结**

经过第二、三章的分析，我们可以将Loock Touch门锁的开锁流程总结如下图所示。

![图4-1 开锁流程图](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQWtv2lTHc374u4isy%2F-MdQXI9eAGdUF5L5SAvg%2Fimage.png?alt=media\&token=84ef1c1d-2170-413b-a27b-628297018d60)

上图中，开锁流程可以分为两个部分：&#x20;

（1）虚线部分表示，当app没有存储可用的BleKey时，会向服务器请求CryptKey和BleToken两组数据，以CryptSecret为密钥，对BleToken中的数据进行AES CBC解密后，就可以得到BleKey。&#x20;

（2）实线部分则是当app中有可用的BleKey时，会以BleKey为密钥对Challenge进行AES EBC加密，密文异或一组固定数据后，作为Response的主要内容并发送给门锁，当Response校验成功后才会开启门锁。 本篇文章侧重于云丁鹿客智能门锁的app端的分析。至此我们对app的开锁流程基本已经理清了：开锁时，门锁与app之间采用了Challenge/Response的方式进行身份校验，app对Challenge数据进行处理时使用了AES EBC加密算法，密钥我们称之为BleKey；BleKey的获取流程是：门锁配套的app向服务器发送reset\_token请求，并对返回内容进行AES CBC解密，这里解密密钥称之为CryptSecret；CryptSecret是app通过向服务器发送crypt\_secret请求获取到的。&#x20;

此外，在第二章中提到，智能门锁和门锁配套的app中应该存储着相同的BleKey，这样才能完成认证过程。而智能门锁中的BleKey也是由手机通过BLE通信下发的。通过本专题的前几篇文章，我们知道某些情况下BLE通信会很容易被监听，那么如何保护BleKey的下发过程是安全的呢？我们将在后续文章中分析这一过程。
