flashP2P协议rtmfp解析
1 协议介绍Real-Time Media Flow Protocol(简称RTMFP)是Flash和Flash之间基于UDP的点对点传输协议,由Adobe公司在2008年在Flash 10.0中发布,随后在Flash10.1中加入了Groups功能。2 常见用法rtmfp在Flash 10中的典型使用场景如下图:http://img.bitscn.com/upimg/allimg/c150629/14355ST5W50-1G64.jpg
它有如下特点:l 使用Cirrus或者开源的Cumulus来提供Rendezvous服务l Cirrus或者Cumulus并不提供Peer ID的交换服务,需要提供其它的方式来交换客户端之间的Peer IDl Flash客户端之间使用NetStream来做点对点传输,Publisher需要给每一个Subscriber单独传输一份数据,这也限制集群的规模。为了解决这个问题,Adobe在Flash 10.1中提出了Groups的概念,典型的架构如下:
http://img.bitscn.com/upimg/allimg/c150629/14355ST621X0-255Y.jpg
它有如下特点:l Cirrus或者开源的Cumulus提供Rendezvous服务并提供所有连接client列表l client从Cirrus或者开源的Cumulus获取邻居节点之后,就可以组成一个完整的P2P架构,所有的audio、video和data数据都在peer之间交互。3 协议解析3.1 基本概念l session:session是两个UDP地址之间的双向管道。l flow:flow是从一个实体到另一个实体之间的逻辑路径。一个session可以包括多个flow。l packet:网络中实际传输的数据,一个packet可以包含多个message。数据传输时都经过了128 bit的AES加密l message:audio、video和data数据。3.2 Scrambled Session IDrtmfp协议中每个包的格式如下:packet := scrambled-session-id | encrypted-part其中scrambled-session-id是4字节,其后是经过AES加密的数据体。scramble-session-id的生成规则如下:scrambled-session-id = a ^ b ^ c这里^代表XOR操作,a是session-id,b和c是encrypted-part的头8个bytes。当目标收到这个包后,unscramble的操作如下:session-id = x ^ b ^ c其中x是scrambled-session-id,b和c同上。使用scramble-session-id的目的为了减少数据包流经的NAT设备和layer-4 packet inspector对数据的干扰。session-id用于标识通信双方建立的连接,并确定通信时使用的加密和解密的key,这些key是通过DH key exchange算法获得。但在session建立之前,双方使用一个公有加密key,即128 bit的字符串”Adobe System 02”。3.3 raw partencrypted-part经过解密之后就得到了raw-part,它的格式如下:raw-part := checksum | network-layer-data | padding其中checksum有16字节,network-layer-data是变长数据,padding都是0xFF,并把network-layer-data补齐为16字节的倍数,这是因为rtmfp使用的是16字节的加解密key。checksum基于network-layer-data和padding计算。3.4 network layer datanetwork-layer-data的格式如下:network-layer-data = flags | timestamp | timestamp-echo | chunks其中flags为1个字节,其格式如下:7 6 5 4 3 2 1 0TC TCR reserved reserved TS TSE model mode:11代表握手包,01代表initiator发送包,10代表responder发送包,00不是合法值l TSE:包中是否包含timestamp-echo域l TS:包中是否包含timestamp域l TCR:time critical reverse notification表明发送方正在从其它地方收到timecritical包l TC:time critical forward notification表明发送方发送的是timecritical包timestamp域有2字节,精度是4ms,他的计算方式如下:timestamp = int(time * 1000 / 4) & 0xFFFFtimestamp-echo域是server收到包的时间戳,当发送放收到这个值之后,发送方就可以计算RTT值了。chunk类型的格式如下:chunk = type | size | payloadtype字段为1个字节,其中0xFF不可用,这个是用来区分chunk数据和padding数据的标记。type的定义如下:typemeaning0x30initiator hello0x70responder hello0x38initiator initial keying0x78responder initial keying0x0fforwarded initiator hello0x71forwarded hello response0x10normal user data0x11next user data0x0csession failed on client side0x4csession died0x01reset keepalive request0x41reset keepalive response0x5enegative ack0x51some acksize是2字节payload长度。payload根据type的不同有不同的数据体。3.5 message flowsession中包括3类消息:l handshake:握手包,包括initiator hello, responder hello, initiator initial keying,responder initial keying, responder hello cookie change和responderredirectl control:控制包,包括ping, ping reply, rekeying initiate, rekeying response, close, closeacknowledge, forwarded initiator hello.l flow:流消息,包括user data, next user data, buffer probe, user data ack, user dataack, flow exception report.session的建立是通过握手(handshake)来完成的,正常的messageflow如下:如果是在NAT打洞是,cumulus server就作为一个forwarder,他会把initiatro hello包转发到其它的client:另外,cumulus server还可以让client重定向到其它server:这里所说的client是Flash Player,而server是cumulus server或者Flash media server。当然server也可以给client发送initiator hello请求,这个在cumulus中被称为man in the middle,不过这个特性还不稳定。session的建立包括4次握手:1 initiator -> target:initiator hello2 target -> initiator: responder hello3 initiator -> target:initiator initial keying4 target -> initiator: responder initial keying这个4次握手过程可以阻止Dos攻击和syn-flooding攻击。每个session都有一个session-id来唯一标识这个session,并且session中的每个packet都会包含这个session-id,但是在session建立的4个握手包中,initiator-hello, responder hello和initiator initialkeying的session-id字段都是0,在发送最后一个包responder initial keying时,session建立成功并且session-id确定,所以responderinitial keying包含合法的session-id。我们接下来详细介绍一下这4个握手包3.5.1 initiator helloinitiator hello包的格式如上所述,这里只说明payload部分的格式:initiator-hello payload = first | epd type | epd value| tag其中:l first:1 byte magic numberl epd type:1 byte,只有两个合法值:n 0x0a:client-server模式,epd value是想要连接的server的rtmfp urln 0x0f:peer-to-peer模式,epd value是想要连接的client的peer id,一般是固定的32字节l epd value:varlen + bodyl tag:16 bytes随机数
3.5.2 responder helloresponder hello包的payload格式如下:responder hello payload = tag-echo | cookie | responder-certificate其中:l tag-echo:和initiator hello中的tag一致,但和initiator hello中不同的是,这里在前面有一个varlen来表明tag的长度l cookie:responder产出的64 bytes随机数,用来防止syn-flooding攻击l responder certificate:diffie-hellman key exchange算法交换的信息,它的格式如下:certificate= \0x01\0x0A\0x41\0x0E | dh-public-num | \0x02\0x15\0x02\0x02\0x15\0x05\0x02\0x15\0x0Edh-public-num是一个64 byte(128 byte)随机数。dh-public-num的生成规则为y2 = g ^ x2 % p其中g和p是公开的两个数,其中g等于2,p是一个1024 bits的数,x2是responder随机生成的数,y2就是在网络中传输的dh-public-num。3.5.3 initiator initial keyinginitiator initial keying包的payload格式如下:payload = initiator-session-id | cookie-echo | initiator-certificate| initiator-component | ‘X’其中:l initiator-session-id:initiator选择的session-id,responder用它来发送数据给initiator(生成scrambled session id)l cookie-echo:和上一个包中的cookie一致l initiator-certificate:格式和上面的responder certificate一致和上述的一样,这里的dh-public-num的生成规则如下:y1 = g ^ x1 % p其中g和p的定义和上述一致,x1是initiator随机生成的数,y1就是传输的dh-public-num。这时initiator知道了y2和x1,就可以生成sharedsecret:shared secret = y2 ^ x1 % p这时就可以生成这个session对应的加解密key了:decode key = HMAC-SHA256(shared-secret, HMAC-SHA256(responder nonce,initiator nonce))encode key = HMAC-SHA256(shared-secret, HMAC-SHA256(initiator nonce,responder nonce))这些加解密key都只使用低位的128bitl initiator-component:在DH算法中使用的initiator nonce。3.5.4 responder initial keyingresponder initial keying的payload的格式如下:payload = responder session id | responder’s nonce | ‘X’其中:l responder session id:responder生成的session id,initiator用它来生成scrambled session id,这个值和initiator session id不一样。l responder’s nonce:这时responder知道了y1和x2,就可以生成sharedsecret:shared secret = y2 ^ x1 % pDH算法保证这个responder的sharedsecret和initiator的shared secret是一样的。这时就可以生成这个session对应的加解密key了:encode key = HMAC-SHA256(shared-secret, HMAC-SHA256(responder nonce,initiator nonce))decode key = HMAC-SHA256(shared-secret, HMAC-SHA256(initiator nonce,responder nonce))这些加解密key都只使用低位的128bit。可以看到responder的encode key和initiator的decode key是一样的,同样,responder的decode key和initiator的encode key是一样的。注意responder initial keying依然使用”Adobe System 02”作为对称key来加解密,而不是使用新生成的非对称的key来加解密,非对称的key仅在session建立之后使用。3.5.5 user data至此session就建立好了,后续传输的就是数据消息,主要包括两类:l normal user data:正常的flow中数据消息l next user data:和normal user data在一个packet中传输,不能单独使用。normal user data包的payload格式如下:payload = flags | flow-id | seq | forward-seq-offset | options |data其中:l flags:1 byte,各bit的意义如下:bitmeaning0x80options域是否存在0x400x20这个包前面还有包0x10这个包后面还有包0x080x040x02丢弃包0x01结束包l flow-id:flow标识,varlen类型l forward-seq-offset:用于滑窗的标识,varlen类型l options:一些选项l data:audio、video和data数据next user data包的payload格式如下:payload = flags | data字段定义同上
页:
[1]