type
status
date
slug
summary
tags
category
icon
password
😁背景
特斯拉今年这不是割韭菜割得很猛嘛,我在一波波的降价面前没忍住,终于在年初也加入了特斯拉的韭菜大军。使用特斯拉的官方App的时候,在使用的时候遇到了一些不是很方便的点,于是开始计划自己写一款车机控制的App,初步定位在只使用蓝牙控制。于是研究了斯拉车机和钥匙App之间配对和数据交互的过程,其中最为复杂的就是手机与特斯拉车机建立可信任连接的过程,这篇文章旨在记录建连的过程,希望可以帮助有需要的朋友。
友情提示:本文的流程描述尽量避免了使用端特性代码,少量App的逻辑使用iOS端的能力进行描述,其他端的开发细节辛苦自行转换。
📝特斯拉蓝牙连接流程
1. 蓝牙配对
1.1 中央设备和外设
首先了解蓝牙配对中重要的两个概念:“中央设备(Central device)” 和 “外设(Peripheral device)”,虽然现代设备大多是情况下即可以是中央设备,也可以是外设。但是在建立连接的过程中还是需要进行区分的,有一个很好区分的原则(绝大多数情况下成立):谁在搜索其他蓝牙设备,谁就是中央设备; 谁是用于被搜索的,谁就是外设。
那么手机和特斯拉这两者之间,谁是中央设备是不是很清晰了?千万不要觉得手机比车机小,所以手机就是外设,只要车机是被搜索的一方,就算是手表跟车机配对,车机也是外设。
1.2 蓝牙设备建立连接的过程
接下来了解一下蓝牙设备之间建立连接的通用过程
1. 扫描:设备A(中央设备)通过蓝牙扫描功能搜索附近可连接设备,并将搜索到的设备展示给用户。
2. 选择:用户选择要连接的设备B(外设)。
3. 请求连接:设备A(中央设备)向设备B(外设)发送连接请求。
4. 检查PIN码:设备B在收到连接请求后,询问用户输入PIN码,并将PIN码与设备A发送的PIN码进行比对。
5. 配对:如果PIN码比对成功,则设备A与设备B建立配对关系。
配对过程主要涉及了两个部分:找到正确的外设 和 建立配对关系
现在我们把这两部分转换到连接特斯拉过程中:
1. 使用手机扫描周围蓝牙设备,车机的BLE名称并不是像我们想象中那样明明白白地标明了Tesla之类的字样
特斯拉车机蓝牙外设的名称规则
1. 首先我们得知道车辆的
VIN
,车辆的 VIN
是一辆车的唯一标识码。2. 将
VIN
进行 SHA1
哈希加密赋值到变量 hashVIN
,获取 hashVIN
的 Hax String
的前16个字符串,赋值为 haxVIN
。3. 在
haxVIN
前拼接字符 'S' ,在haxVIN
后拼接的字符为 'C' 'D' 'P' 'R' 中的任意一个。2. 通过车机的名称规则,在手机App选择对应的外设连接进行连接,不出意外就可以成功连接到外设。至于通用流程中提到的 “检查PIN码”,在特斯拉连接过程中并没有这一步。但是在配对过程中确实有看到车机屏幕上需要用户进行点击确认的弹窗,这一步并非在配对过程中,而是发生在授权加白名单过程中,后文中将会提到,当前暂时不需要关心。
1.3 数据发送和接收通道
在手机App与特斯拉的蓝牙设备建立连接之后,手机App作为中央设备需要对外设进行服务扫描。
这里涉及到两个概念:服务(Service) 和 特征(Characteristic)
服务是一组相关的特征的集合,它们提供一种功能或者特定的行为。每个服务都有一个唯一的16位UUID,用于在设备之间进行识别和区分。在通信中,服务可以被广告和查找,以便其他设备能够发现和连接到它们。
特征是特定的服务中的某个属性,它通常包含读取、写入和通知等操作。每个特征都有一个唯一的16位UUID,用于标识特征的类型和属性。在通信中,特征描述了服务的具体实现细节和交互方式,如何读取和写入数据,以及如何发送通知和响应等。
这里的信息非常简洁,我直接列出特斯拉中我们会使用到的服务和特性,并标记它们分别的作用。
Services
Service UUID | Description |
00000211-b2d1-43f0-9b88-960cebf8b91e | 所有的会话都是在这个Service之下 |
由于纯蓝牙连接车机的方式App无法获取到VIN,这个情况下可以使用 Service 进行验证连接的外设是否为特斯拉车机。
Characteristic
Characteristic UUID | Description |
00000212-b2d1-43f0-9b88-960cebf8b91e | 发消息给车辆 |
00000213-b2d1-43f0-9b88-960cebf8b91e | 接收车辆消息 |
蓝牙框架的调用方法就不在这里描述了,网上有很多教程或者问ChatGPT会更加直接哦。
Tips:由于发送和接收消息的特性区分非常明显,可以直接全局进行维持它们的生命周期以支持随时的消息的发送和接收。
2. 数据交换
配对完成之后,手机App和特斯拉车机之间就可以进行通讯了。但是这仅仅是理论上的可通讯,App发送到车机的消息还并不能被车机认可并响应。这种情况相当于你去健身房办了张卡,但是销售还没有给你的卡开门禁权限。这时候你是可以刷卡,门禁也可以识别到你刷了一张卡,但是对不起没有权限不给进。所以我们接下来要做的就是对这张卡进行授权。
2.1 加密算法
授权涉及到的就是数据加密和传输,我们先从涉及到的加密算法开始了解。
蓝牙连接中常见的加密算法有哪些?
1. AES(Advanced Encryption Standard):是一种对称加密算法,被广泛用于蓝牙连接中的数据加密。AES算法的密钥长度支持128位、192位、256位三种。
2. RSA(Rivest-Shamir-Adleman):是一种非对称加密算法,在蓝牙连接中主要用于配对过程中PIN码的加密,以确保连接安全性。RSA算法的密钥长度支持1024位、2048位、3072位等多种长度。
3. ECC(Elliptic Curve Cryptography):是一种基于椭圆曲线数学原理的加密算法,被广泛用于物联网和移动设备中的加密通信。ECC算法的密钥长度相对较短,但加密强度较高。
与特斯拉车机通讯用的是那种?
特斯拉使用的是混合加密,非对称加密使用 ECC P256 对称加密使用 AES GCM。
为什么物联网和移动设备中都常用ECC进行加密通讯呢?
1. 安全性高:与其他加密算法相比,ECC算法在相同密钥长度的情况下,具有更高的安全性。
2. 密钥长度短:ECC算法的密钥长度比其他加密算法短,只需要几十个字节就可以实现与其他算法相同的安全性。
3. 处理速度快:由于ECC算法的密钥长度短,因此对数据的计算速度也更快。
综上所述,ECC算法具有非常优越的安全性,短的密钥长度和优秀的计算速度,因而成为了物联网和移动设备中常用的加密算法。
特斯拉中处理使用 ECC 进行加密信息,还使用了AES对回话进行加密,这个逻辑有点类似 TLS 的加密过程,混合加密既保证了加密强度,也提高了加密效率。
2.2 生成秘钥
- 使用椭圆曲线ECC prime256v1算法在App本地生成公私钥对,注意在本地需要妥善保存。例如iOS可以存储在钥匙串中。
关于加密算法在移动端怎么调用,在iOS中可以使用
CryptoKit
/ CommonCrypto
或则流行的跨端加密库 OpenSSL
中都有对ECC加密算法的实现。- 将公钥序列化为
ANSI X9.62/X9.63
未压缩点格式二进制数据,如果操作正确,第一个字节应该总是0x04
。
2.3 加白名单
App中创建成功公私钥并妥善保存之后,就要将公钥传输到车机进行加白。这个加白的操作对应到特斯拉车机系统中就是 “车锁-钥匙” 列表中将会多一个钥匙。很显然想要在车机系统中新增一个钥匙,需要驾驶员(可操作车机的人员)授权。这也就是我们配对过程中唯一需要在车机上操作的步骤。
具体流程:
- 公钥二进制数据被组装到特斯拉专用数据格式(可从 proto 中获取)中发送到车机
- 车机返回相应消息提示需要授权
- 实体钥匙卡放到车机中控钥匙台上
- 点击车机系统屏幕上的弹窗授权添加新钥匙
- 车机返回加白名单成功并返回当前已绑定的其他钥匙的信息
至此新的蓝牙钥匙添加成功,但是我们会发现特斯拉车机系统中“车锁-钥匙”列表中新增加的钥匙处于不可用的状态(未高亮),这是因为我们还是需要为这把钥匙认证。
从加密的流程上来看,当前就是处于外设收到了公钥(可见配对和加密时序图 步骤5)。中央设备拥有公私钥,外设有公钥,这两者理论上已经可以加密传输数据了,但是特斯拉使用的是混合加密,会话数据传输使用的是AES GCM对称加密的,所以此时我们还需要进行对称秘钥的认证。
2.4 生成对称秘钥
对称秘钥的作用想必都已经清楚了,对称秘钥的使用需要双方都持有。关于谁来生成,在特斯拉配对过程中生成对称秘钥 SharedKey 一方是车机。
具体流程(参见 配对和加密时序图 步骤6,7,8):
- 车机收到公钥之后,使用AES GCM在本地生成对称秘钥
SharedKey
- 车机使用公钥加密
SharedKey
, 生成一个临时秘钥我们赋值为EphemeralKey
,发送给手机
- 手机收到
EphemeralKey
后使用私钥进行解密获得SharedKey
至此我们在手机端获得了对称秘钥
SharedKey
,这是后续所有会话消息的加密关键秘钥。建议也是在App中妥善保存,没必要每次都重新从车机获取。2.5 认证
在上一步我们在手机App中获取到了车机生成的
SharedKey
,要进行正常数据传输前,还有个步骤:要让车机知道你已经收到了正确的SharedKey
。经典的握手嘛:我要让你知道我知道了。我们把这一步称为认证,经过认证之后的钥匙在车机系统的钥匙列表上就会高亮。也就是之后每次手机蓝牙与车机连接时,钥匙就会自动生效。
具体流程(参见 配对和加密时序图 步骤9,10):
- 手机获取到
SharedKey
之后,生成认证消息结构体(由proto文件提供)赋值为msgData
- 手机App使用
SharedKey
和随机数Counter
(每次发消息都递增) 对msgData
进行 AES GCM加密 赋值为encryptMsg
- 手机App将
encryptMsg
发送到车机
- 车机收到加密消息后进行解密并处理执行,回送认证结果
- 手机App收到回送的结果使用
SharedKey
对接收到的数据进行 AES GCM解密,获取到认证结果
支持认证完成,手机钥匙App当前就已经可以正常与特斯拉车机进行消息传递。
2.6 加密消息
加密消息这个环节其实已经在 2.5认证 的环节中有涉及到,就是将proto结构体消息进行加密发送给车机,再将车机发送回来的数据进行解密为 proto 中结构体消息的过程。
具体流程(参见 配对和加密时序图 步骤12,15)
🤗总结归纳
使用一个时序图将整个过程总结一下
如果有朋友正在实践这块逻辑,在配对实践过程中可能还会遇到一些问题,估计比较多的疑惑会在加解密这块的逻辑。本文主要关注车机配对的流程阐述,关于加解密部分,若在iOS侧的实践遇到问题我或许可以提供一些帮助,欢迎文章底部评论或者邮件交流。
由于笔者也是边学边实践与ChatGPT结对编程一路调试出来,所以对于加解密框架尚无系统性理解,自知尚不够资格写关于加解密相关的分享,主要考虑若单独针对单点问题解决做分享的话,在做拓展阅读时会显得捉襟见肘对自己和读者都不够负责任,还需要深入学习。不过当下我还是非常乐于跟同样专注在相近领域的朋友交流。
参考资料&鸣谢
感谢 @ArchGryphon9362 逆向App后开源的文档,以及 @trifinite 开源的 proto 文件 🍗
本文部分内容由ChatGPT协助编写 😄