兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx

上传人:b****3 文档编号:16953771 上传时间:2022-11-27 格式:DOCX 页数:18 大小:23.04KB
下载 相关 举报
兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx_第1页
第1页 / 共18页
兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx_第2页
第2页 / 共18页
兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx_第3页
第3页 / 共18页
兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx_第4页
第4页 / 共18页
兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx

《兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx(18页珍藏版)》请在冰豆网上搜索。

兄弟连Go语言+区块链技术培训以太坊源码分析47p2prlpx节点之间的加密链路Word文档下载推荐.docx

墨克(RalphC.Merkle)提出,而这个密钥交换方法,由惠特菲尔德·

迪菲(BaileyWhitfieldDiffie)和马丁·

赫尔曼(MartinEdwardHellman)在1976年首次发表。

马丁·

赫尔曼曾主张这个密钥交换方法,应被称为迪菲-赫尔曼-墨克密钥交换(英语:

Diffie–Hellman–Merklekeyexchange)。

迪菲-赫尔曼密钥交换的同义词包括:

迪菲-赫尔曼密钥协商

迪菲-赫尔曼密钥创建

指数密钥交换

迪菲-赫尔曼协议

虽然迪菲-赫尔曼密钥交换本身是一个匿名(无认证)的密钥交换协议,它却是很多认证协议的基础,并且被用来提供传输层安全协议的短暂模式中的完备的前向安全性。

####描述

迪菲-赫尔曼通过公共信道交换一个信息,就可以创建一个可以用于在公共信道上安全通信的共享秘密(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(

secsecrets

errerror

ifdial==nil{

sec,err=receiverEncHandshake(t.fd,prv,nil)

}else{

sec,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)

returns,err

authPacket,err:

=sealEIP8(authMsg,h)

if_,err=conn.Write(authPacket);

err!

authRespMsg:

=new(authRespV4)

authRespPacket,err:

=readHandshakeMsg(authRespMsg,encAuthRespLen,prv,conn)

iferr:

=h.handleAuthResp(authRespMsg);

returnh.secrets(authPacket,authRespPacket)

makeAuthMsg。

这个方法创建了initiator的handshakemessage。

首先对端的公钥可以通过对端的ID来获取。

所以对端的公钥对于发起连接的人来说是知道的。

但是对于被连接的人来说,对端的公钥应该是不知道的。

//makeAuthMsgcreatestheinitiatorhandshakemessage.

func(h*encHandshake)makeAuthMsg(prv*ecdsa.PrivateKey,token[]byte)(*authMsgV4,error){

rpub,err:

=h.remoteID.Pubkey()

returnnil,fmt.Errorf("

badremoteID:

%v"

err)

h.remotePub=ecies.ImportECDSAPublic(rpub)

//Generaterandominitiatornonce.

//生成一个随机的初始值,是为了避免重放攻击么?

还是为了避免通过多次连接猜测密钥?

h.initNonce=make([]byte,shaLen)

if_,err:

=rand.Read(h.initNonce);

returnnil,err

//GeneraterandomkeypairtoforECDH.

//生成一个随机的私钥

h.randomPrivKey,err=ecies.GenerateKey(rand.Reader,crypto.S256(),nil)

//Signknownmessage:

static-shared-secret^nonce

//这个地方应该是直接使用了静态的共享秘密。

使用自己的私钥和对方的公钥生成的一个共享秘密。

token,err=h.staticSharedSecret(prv)

//这里我理解用共享秘密来加密这个initNonce。

signed:

=xor(token,h.initNonce)

//使用随机的私钥来加密这个信息。

signature,err:

=crypto.Sign(signed,h.randomPrivKey.ExportECDSA())

msg:

=new(authMsgV4)

copy(msg.Signature[:

],signature)

//这里把发起者的公钥告知对方。

这样对方使用自己的私钥和这个公钥可以生成静态的共享秘密。

copy(msg.InitiatorPubkey[:

],crypto.FromECDSAPub(&

prv.PublicKey)[1:

])

copy(msg.Nonce[:

],h.initNonce)

msg.Version=4

returnmsg,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)

=rlp.Encode(buf,msg);

//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

readHandshakeMsg这个方法会从两个地方调用。

一个是在initiatorEncHandshake。

一个就是在receiverEncHandshake。

这个方法比较简单。

首先用一种格式尝试解码。

如果不行就换另外一种。

应该是一种兼容性的设置。

基本上就是使用自己的私钥进行解码然后调用rlp解码成结构体。

结构体的描述就是下面的authRespV4,里面最重要的就是对端的随机公钥。

双方通过自己的私钥和对端的随机公钥可以得到一样的共享秘密。

而这个共享秘密是第三方拿不到的。

//RLPxv4handshakeresponse(definedinEIP-8).

typeauthRespV4struct{

RandomPubkey[pubLen]byte

Nonce[shaLen]byte

Versionuint

//Ignoreadditionalfields(forward-compatibility)

Rest[]rlp.RawValue`rlp:

"

tail"

`

funcreadHandshakeMsg(msgplainDecoder,plainSizeint,prv*ecdsa.PrivateKey,rio.Reader)([]byte,error){

=make([]byte,plainSize)

=io.ReadFull(r,buf);

returnbuf,err

//Attemptdecodingpre-EIP-8"

plain"

format.

key:

=ecies.ImportECDSA(prv)

ifdec,err:

=key.Decrypt(rand.Reader,buf,nil,nil);

err==nil{

msg.decodePlain(dec)

returnbuf,nil

//CouldbeEIP-8format,trythat.

=buf[:

2]

size:

=binary.BigEndian.Uint16(prefix)

ifsize<

uint16(plainSize){

returnbuf,fmt.Errorf("

sizeunderflow,needatleast%dbytes"

plainSize)

buf=append(buf,make([]byte,size-uint16(plainSize)+2)...)

=io.ReadFull(r,buf[plainSize:

]);

dec,err:

=key.Decrypt(rand.Reader,buf[2:

],nil,prefix)

//Can'

tuserlp.DecodeBytesherebecauseitrejects

//trailingdata(forward-compatibility).

s:

=rlp.NewStream(bytes.NewReader(dec),0)

returnbuf,s.Decode(msg)

handleAuthResp这个方法非常简单。

func(h*encHandshake)handleAuthResp(msg*authRespV4)(errerror){

h.respNonce=msg.Nonce[:

]

h.remoteRandomPub,err=importPublicKey(msg.RandomPubkey[:

returnerr

最后是secrets函数,这个函数是在handshake完成之后调用。

它通过自己的随机私钥和对端的公钥来生成一个共享秘密,这个共享秘密是瞬时的(只在当前这个链接中存在)。

所以当有一天私钥被破解。

之前的消息还是安全的。

//secretsiscalledafterthehandshakeiscompleted.

//Itextractstheconnectionsecretsfromthehandshakevalues.

func(h*encHandshake)secrets(auth,authResp[]byte)(secrets,error){

ecdheSecret,err:

=h.randomPrivKey.GenerateShared(h.remoteRandomPub,sskLen,sskLen)

returnsecrets{},err

//derivebasesecretsfromephemeralkeyagreement

sharedSecret:

=crypto.Keccak256(ecdheSecret,crypto.Keccak256(h.respNonce,h.initNonce))

aesSecret:

=crypto.Keccak256(ecdheSecret,sharedSecret)

//实际上这个MAC保护了ecdheSecret这个共享秘密。

respNonce和initNonce这三个值

=secrets{

RemoteID:

h.remoteID,

AES:

aesSecret,

MAC:

crypto.Keccak256(ecdheSecret,aesSecret),

//setupsha3instancesfortheMACs

mac1:

=sha3.NewKeccak256()

mac1.Write(xor(s.MAC,h.respNonce))

mac1.Write(auth)

mac2:

mac2.Write(xor(s.MAC,h.initNonce))

mac2.Write(authResp)

//收到的每个包都会检查其MAC值是否满足计算的结果。

如果不满足说明有问题。

ifh.initiator{

s.EgressMAC,s.IngressMAC=mac1,mac2

s.EgressMAC,s.IngressMAC=mac2,mac1

returns,nil

receiverEncHandshake函数和initiatorEncHandshake的内容大致相同。

但是顺序有些不一样。

//receiverEncHandshakenegoti

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 研究生入学考试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1