windows开启接收端服务:raop_port为5001、airplay_port为7001、mirror_port为7100
开始投屏,通过抓包发现,通讯端口均为5001,原因未知
下面根据抓包分析镜像连接的过程,流程如下:
- GET /info
- pair
- pair-setup
- pair-verify
- fairplay
- SETUP1
- GET PARAMETER
- RECORD
- SET PARAMETER
- SETUP2
GET /info
发送端请求接收端信息,报文格式是bplist
1 | GET /info RTSP/1.0 |
解析后如下
1 | <?xml version="1.0" encoding="UTF-8"?> |
接收端返回信息
1 | RTSP/1.0 200 OK |
1 | <?xml version="1.0" encoding="UTF-8"?> |
目前在这一步还没有发现关键的字段
pair配对认证
配对认证分为两部分:设置setup与认证verify
pair所使用到的一种算法是ECDH密钥协商算法,属于DC密钥协商算法
pair-setup
发送端会发送一个32位的数据1
2
3
4
5
6
7
8
9
10POST /pair-setup RTSP/1.0
Content-Length: 32
Content-Type: application/octet-stream
CSeq: 1
DACP-ID: 1FF65A04A9252F60
Active-Remote: 2814835199
User-Agent: AirPlay/409.16
f8} ᵫCÂA®,/øØ×+zè;ÀePöB // 32位的数据接收端并不会实际处理这个数据,而是检查它的位数是否为32位。如果正确,则回应协商用公钥
ed_ours
,同时把配对状态置为STATUS_SETUP
状态1
2
3
4
5
6
7
8
9
10
11
12RTSP/1.0 200 OK
CSeq: 1
Server: AirTunes/220.68
Content-Type: application/octet-stream
Content-Length: 32
[àý-HøÃ+$6dK×z¿ëzÕÎz
// 16进制
00b0 xx xx xx 5b 95 e0 1e 01 fd 2d 48 8d 08 f8 04 c3
00c0 2b 24 1d 36 64 4b 0b d7 7a 1b 81 86 bf eb 7a d5
00d0 ce 0e 7asetup完成
为什么只检查位数而不使用数据呢?
我猜测这跟ED25519有关。发送端与接收端在pair配对流程之前,需要各自生成公钥与私钥。在这个例子里,接收端生成了公钥ed_ours
与ed_private
。生成公私钥是需要一个随机数作为种子的,而接收端是使用32位数据作为随机数。因此这个包应该是确认,发送端也是使用32位数据作为随机数生成公私钥。确保大家的公私钥格式一致,才能正确执行下面的ECDH认证流程。pair-verify
在verify前,接收端检查配对状态STATUS_SETUP
以及报文字节68,不正确则不回应
verify有点类似三次握手,发送方的报文有两种形式,根据报文的首个字节判断:- 首字节为1(第一次握手) 前4个字节保留,中间32个字节是公钥
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19POST /pair-verify RTSP/1.0
X-Apple-PD: 1
X-Apple-AbsoluteTime: 609411011
Content-Length: 68
Content-Type: application/octet-stream
CSeq: 2
DACP-ID: 1FF65A04A9252F60
Active-Remote: 2814835199
User-Agent: AirPlay/409.16
// 报文
.ÚÃGÚòHÉLú¡¾ÐÈר¨¹b/²´D3Ïf8} ᵫCÂA®,/øØ×+zè;ÀePöB
// 报文16进制展示
0120 xx xx xx xx xx xx xx 01 00 00 00 2e da c3 13 47
0130 15 da f2 48 c9 4c fa a1 be 15 d0 c8 d7 a8 80 06
0140 a8 b9 62 2f b2 b4 44 33 10 cf 0f 66 38 7d a0 e1
0150 b5 ab 87 43 c2 41 ae 2c 94 1d 2f f8 d8 d7 8a 2b
0160 7a e8 3b c0 ad 83 65 50 1f f6 42ed_their
,后32个字节是公钥ecdh_theirs
接收端回应96个字节的报文,前32个字节是公钥1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 回应包
RTSP/1.0 200 OK
CSeq: 2
Server: AirTunes/220.68
Content-Type: application/octet-stream
Content-Length: 96
// 报文
ꨥ.ËpDî²Yl÷|ÎP=¡0ÑÞj:TIÝx%´
2\\Rm¡ ¹äÏ_Ýz<ÅLغoè¿B?H[:ÀØnöÆðI!pø¯Tà<Ò
// 报文16
00b0 xx xx xx 88 ea a8 a5 2e 8c cb 18 70 87 44 ee b2
00c0 59 6c f7 7c 14 ce 50 8a 3d a1 30 91 02 d1 de 6a
00d0 3a 07 54 49 dd 17 03 9c 78 25 b4 0d 12 32 5c 5c
00e0 88 52 9b 6d a1 a0 b9 e4 91 cf 5f 9c dd 7a 3c c5
00f0 4c 1c d8 ba 8b 6f 17 e8 bf 42 3f 48 5b 1f 3a 83
0100 c0 83 d8 6e f6 c6 f0 49 21 93 0e 70 f8 af 54 e0
0110 10 3c d2edch_ours
,后面64个字节是签名(第二次握手)
握手结束,接收端将状态设置为STATUS_HANDSHAKE
- 首字节为0(第三次握手) 发送方发送68位数据,前4位保留,后64位是签名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 接收包
POST /pair-verify RTSP/1.0
X-Apple-PD: 1
X-Apple-AbsoluteTime: 609411011
Content-Length: 68
Content-Type: application/octet-stream
CSeq: 3
DACP-ID: 1FF65A04A9252F60
Active-Remote: 2814835199
User-Agent: AirPlay/409.16
// 报文
áp=s_
2TàÐiv®yÜùeÈj,(ãk±½áéë2°JÛ×7æßþcQkc» Y2eh&O?
// 报文16进制
0120 xx xx xx xx xx xx xx 00 00 00 00 0c 00 e1 70 3d
0130 73 5f 02 1b 0a 32 54 e0 d0 69 76 ae 79 dc 8a f9
0140 65 c8 1d 6a 2c 28 e3 6b 90 90 b1 bd e1 e9 eb 32
0150 b0 94 4a db d7 37 e6 df fe 9f 63 51 6b 0b 63 98
0160 bb 09 59 32 65 68 26 06 4f 3f 13
接收端先检查状态STATUS_HANDSHAKE
,没问题后验证签名。验证签名成功,则三次握手成功,配对成功接收端回空包结束,将状态置为1
2
3RTSP/1.0 200 OK
CSeq: 3
Server: AirTunes/220.68STATUS_FINISHED
- 首字节为1(第一次握手)
pair协商密钥流程
流程中用了几套加密协议:AES、ED25519、EDCH、SHA512。其中ED25519与EDCH十分容易让人混淆
假设发送端为Alice,接收端为Blob
- Alice根据32位随机数生成公钥
PublicA
与私钥PrivateA
,Blob同样根据32位随机数生成公钥PublicB
与私钥PrivateB
。(ed25519) - pair-setup确保ed25519随机数位数一致
- 进行标准ECDH流程
- Alice生成32位随机数作为私钥
ECDH_Private_A
- Alice通过Curve25519椭圆曲线与常量
basepoint
生成公钥EDCH_Public_A
- Alice将
PublicA
以及ECDH_Public_A
发送给Blob(pair-verify 1)
- Blob重复1、2步生成私钥
ECDH_Private_B
以及公钥EDCH_Public_B
- Blob收到
ECDH_Public_A
后,通过Curve25519椭圆曲线与私钥ECDH_Private_B
生成共享密钥EDCH_Key
注意这是EDCH的关键,它保证了双方都能通过对方公钥以及己方私钥生成相同的共享密钥 - Blob以此拼接己方公钥
EDCH_Public_B
与对方公钥EDCH_Public_A
作为64 bit信息msg
- Blob使用对方公钥
PublicA
与己方私钥PrivateB
进行ED25519签名信息msg
,得到ed_msg
- Blob使用SHA512签名常量字符串
Pair-Verify-AES-Key
与Pair-Verify-AES-IV
,得到aes_key
(16 bit)与aes_iv
(16 bit) - Blob使用AES算法,参数是
aes_key
、aes_iv
,加密签名ed_msg
,得到64 bit加密签名aes_ed_msg
- Blob 传输96 bit报文,前32位是
PublicB
,后64位是aes_ed_msg
。(pair-verify 2)
- Alice重复Blob 5-10步的步骤,复现出
aes_ed_msg
验签 - Alice验签成功后,拼接己方公钥
EDCH_Public_A
与对方公钥EDCH_Public_B
(恰好与第6步反过来了),得到msg2
- Alice重复7-9步,加密
msg2
得到签名aes_ed_msg2
,传送给Blob(pair-verify 3) - Blob验签,回复空包完成ECDH(pair-verify 4)
- Alice生成32位随机数作为私钥
之后,大家都使用EDCH_Key
作为密钥加解密