您好,欢迎访问三七文档
当前位置:首页 > 金融/证券 > 投融资/租赁 > 兄弟连Go语言+区块链技术培训以太坊源码分析(47)p2p-rlpx节点之间的加密链路
1/15兄弟连Go语言+区块链技术培训以太坊源码分析(47)p2p-rlpx节点之间的加密链路RLPxEncryption(RLPx加密)之前介绍的discover节点发现协议,因为承载的数据不是很重要,基本是明文传输的。每一个节点会开启两个同样的端口,一个是UDP端口,用来节点发现,一个是TCP端口,用来承载业务数据。UDP的端口和TCP的端口的端口号是同样的。这样只要通过UDP发现了端口,就等于可以用TCP来连接到对应的端口。RLPx协议就定义了TCP链接的加密过程。RLPx使用了(PerfectForwardSecrecy),简单来说。链接的两方生成生成随机的私钥,通过随机的私钥得到公钥。然后双方交换各自的公钥,这样双方都可以通过自己随机的私钥和对方的公钥来生成一个同样的共享密钥(shared-secret)。后续的通讯使用这个共享密钥作为对称加密算法的密钥。这样来说。如果有一天一方的私钥被泄露,也只会影响泄露之后的消息的安全性,对于之前的通讯是安全的(因为通讯的密钥是随机生成的,用完后就消失了)。##前向安全性(引用自维基百科)前向安全或前向保密(英语:ForwardSecrecy,缩写:FS),有时也被称为完美前向安全[1](英语:PerfectForwardSecrecy,缩写:PFS),是密码学中通讯协议的安全属性,指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏。[2]前向安全能够保护过去进行的通讯不受密码或密钥在未来暴露的威胁。[3]如果系统具有前向安全性,就可以保证万一密码或密钥在某个时刻不慎泄露,过去已经进行的通讯依然是安全,不会受到任何影响,即使系统遭到主动攻击也是如此。###迪菲-赫尔曼密钥交换迪菲-赫尔曼密钥交换(英语:Diffie–Hellmankeyexchange,缩写为D-H)是一种安全协议。它可以让双方在完全没有对方任何预先信息的条件下通过不安全信道创建起一个密钥。这个密钥可以在后续的通讯中作为对称密钥来加密通讯内容。公钥交换的概念最早由瑞夫·墨克(RalphC.Merkle)提出,而这个密钥交换方法,由惠特菲尔德·迪菲(BaileyWhitfieldDiffie)和马丁·赫尔曼(MartinEdwardHellman)在1976年首次发表。马丁·赫尔曼曾主张这个密钥交换方法,应被称为迪菲-赫尔曼-墨克密钥交换(英语:Diffie–Hellman–Merklekeyexchange)。2/15-迪菲-赫尔曼密钥交换的同义词包括:-迪菲-赫尔曼密钥协商-迪菲-赫尔曼密钥创建-指数密钥交换-迪菲-赫尔曼协议虽然迪菲-赫尔曼密钥交换本身是一个匿名(无认证)的密钥交换协议,它却是很多认证协议的基础,并且被用来提供传输层安全协议的短暂模式中的完备的前向安全性。####描述迪菲-赫尔曼通过公共信道交换一个信息,就可以创建一个可以用于在公共信道上安全通信的共享秘密(sharedsecret)。##p2p/rlpx.go源码解读这个文件实现了RLPx的链路协议。链接联系的大致流程如下:1.doEncHandshake()通过这个方法来完成交换密钥,创建加密信道的流程。如果失败,那么链接关闭。2.doProtoHandshake()这个方法来进行协议特性之间的协商,比如双方的协议版本,是否支持Snappy加密方式等操作。链接经过这两次处理之后,就算建立起来了。因为TCP是流式的协议。所有RLPx协议定义了分帧的方式。所有的数据都可以理解为一个接一个的rlpxFrame。rlpx的读写都是通过rlpxFrameRW对象来进行处理。###doEncHandshake链接的发起者被称为initiator。链接的被动接受者被成为receiver。这两种模式下处理的流程是不同的。完成握手后。生成了一个sec.可以理解为拿到了对称加密的密钥。然后创建了一个newRLPXFrameRW帧读写器。完成加密信道的创建过程。func(t*rlpx)doEncHandshake(prv*ecdsa.PrivateKey,dial*discover.Node)(discover.NodeID,error){var(secsecretserrerror)ifdial==nil{sec,err=receiverEncHandshake(t.fd,prv,nil)}else{3/15sec,err=initiatorEncHandshake(t.fd,prv,dial.ID,nil)}iferr!=nil{returndiscover.NodeID{},err}t.wmu.Lock()t.rw=newRLPXFrameRW(t.fd,sec)t.wmu.Unlock()returnsec.RemoteID,nil}initiatorEncHandshake首先看看链接的发起者的操作。首先通过makeAuthMsg创建了authMsg。然后通过网络发送给对端。然后通过readHandshakeMsg读取对端的回应。最后调用secrets创建了共享秘密。//initiatorEncHandshakenegotiatesasessiontokenonconn.//itshouldbecalledonthedialingsideoftheconnection.////prvisthelocalclient'sprivatekey.funcinitiatorEncHandshake(connio.ReadWriter,prv*ecdsa.PrivateKey,remoteIDdiscover.NodeID,token[]byte)(ssecrets,errerror){h:=&encHandshake{initiator:true,remoteID:remoteID}authMsg,err:=h.makeAuthMsg(prv,token)iferr!=nil{returns,err}authPacket,err:=sealEIP8(authMsg,h)iferr!=nil{returns,err}if_,err=conn.Write(authPacket);err!=nil{returns,err}authRespMsg:=new(authRespV4)authRespPacket,err:=readHandshakeMsg(authRespMsg,encAuthRespLen,prv,conn)iferr!=nil{returns,err}iferr:=h.handleAuthResp(authRespMsg);err!=nil{returns,err}4/15returnh.secrets(authPacket,authRespPacket)}makeAuthMsg。这个方法创建了initiator的handshakemessage。首先对端的公钥可以通过对端的ID来获取。所以对端的公钥对于发起连接的人来说是知道的。但是对于被连接的人来说,对端的公钥应该是不知道的。//makeAuthMsgcreatestheinitiatorhandshakemessage.func(h*encHandshake)makeAuthMsg(prv*ecdsa.PrivateKey,token[]byte)(*authMsgV4,error){rpub,err:=h.remoteID.Pubkey()iferr!=nil{returnnil,fmt.Errorf(badremoteID:%v,err)}h.remotePub=ecies.ImportECDSAPublic(rpub)//Generaterandominitiatornonce.//生成一个随机的初始值,是为了避免重放攻击么?还是为了避免通过多次连接猜测密钥?h.initNonce=make([]byte,shaLen)if_,err:=rand.Read(h.initNonce);err!=nil{returnnil,err}//GeneraterandomkeypairtoforECDH.//生成一个随机的私钥h.randomPrivKey,err=ecies.GenerateKey(rand.Reader,crypto.S256(),nil)iferr!=nil{returnnil,err}//Signknownmessage:static-shared-secret^nonce//这个地方应该是直接使用了静态的共享秘密。使用自己的私钥和对方的公钥生成的一个共享秘密。token,err=h.staticSharedSecret(prv)iferr!=nil{returnnil,err}//这里我理解用共享秘密来加密这个initNonce。signed:=xor(token,h.initNonce)//使用随机的私钥来加密这个信息。signature,err:=crypto.Sign(signed,h.randomPrivKey.ExportECDSA())iferr!=nil{5/15returnnil,err}msg:=new(authMsgV4)copy(msg.Signature[:],signature)//这里把发起者的公钥告知对方。这样对方使用自己的私钥和这个公钥可以生成静态的共享秘密。copy(msg.InitiatorPubkey[:],crypto.FromECDSAPub(&prv.PublicKey)[1:])copy(msg.Nonce[:],h.initNonce)msg.Version=4returnmsg,nil}//staticSharedSecretreturnsthestaticsharedsecret,theresult//ofkeyagreementbetweenthelocalandremotestaticnodekey.func(h*encHandshake)staticSharedSecret(prv*ecdsa.PrivateKey)([]byte,error){returnecies.ImportECDSA(prv).GenerateShared(h.remotePub,sskLen,sskLen)}sealEIP8方法,这个方法是一个组包方法,对msg进行rlp的编码。填充一些数据。然后使用对方的公钥把数据进行加密。这意味着只有对方的私钥才能解密这段信息。funcsealEIP8(msginterface{},h*encHandshake)([]byte,error){buf:=new(bytes.Buffer)iferr:=rlp.Encode(buf,msg);err!=nil{returnnil,err}//padwithrandomamountofdata.theamountneedstobeatleast100bytestomake//themessagedistinguishablefrompre-EIP-8handshakes.pad:=padSpace[:mrand.Intn(len(padSpace)-100)+100]buf.Write(pad)prefix:=make([]byte,2)binary.BigEndian.PutUint16(prefix,uint16(buf.Len()+eciesOverhead))enc,err:=ecies.Encrypt(rand.Reader,h.remotePub,buf.Bytes(),nil,prefix)returnappend(prefix,enc...),err}6/15readHandshakeMsg这个方法会从两个地方调用。一个是在initiatorEncHandshake。一个就是在receiverEncHandshake。这个方法比较简单。首先用一种格式尝试解码。如果不行就换另外一种。应该是一种兼容性的设置。基本上就是使用自己的私钥
本文标题:兄弟连Go语言+区块链技术培训以太坊源码分析(47)p2p-rlpx节点之间的加密链路
链接地址:https://www.777doc.com/doc-4107416 .html