github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/rlpx.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:44</date>
    10  //</624342660156493824>
    11  
    12  
    13  package p2p
    14  
    15  import (
    16  	"bytes"
    17  	"crypto/aes"
    18  	"crypto/cipher"
    19  	"crypto/ecdsa"
    20  	"crypto/elliptic"
    21  	"crypto/hmac"
    22  	"crypto/rand"
    23  	"encoding/binary"
    24  	"errors"
    25  	"fmt"
    26  	"hash"
    27  	"io"
    28  	"io/ioutil"
    29  	mrand "math/rand"
    30  	"net"
    31  	"sync"
    32  	"time"
    33  
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	"github.com/ethereum/go-ethereum/crypto/ecies"
    36  	"github.com/ethereum/go-ethereum/crypto/secp256k1"
    37  	"github.com/ethereum/go-ethereum/crypto/sha3"
    38  	"github.com/ethereum/go-ethereum/p2p/discover"
    39  	"github.com/ethereum/go-ethereum/rlp"
    40  	"github.com/golang/snappy"
    41  )
    42  
    43  const (
    44  	maxUint24 = ^uint32(0) >> 8
    45  
    46  sskLen = 16 //指定最大共享密钥长度(pubkey)/2
    47  sigLen = 65 //椭圆形S256
    48  pubLen = 64 //512位pubkey,未压缩表示,无格式字节
    49  shaLen = 32 //哈希长度(用于nonce等)
    50  
    51  	authMsgLen  = sigLen + shaLen + pubLen + shaLen + 1
    52  	authRespLen = pubLen + shaLen + 1
    53  
    54   /*eSoverhead=65/*pubkey*/+16/*iv*/+32/*mac*/
    55  
    56   encauthmsglen=authmsglen+eciesoverhead//加密的pre-eip-8发起程序握手大小
    57   encauthresplen=authresplen+eciesoverhead//加密的pre-eip-8握手回复的大小
    58  
    59   //加密握手和协议的总超时时间
    60   //双向握手。
    61   握手超时=5*次。秒
    62  
    63   //这是发送断开连接原因的超时。
    64   //这比通常的超时时间短,因为我们不希望
    65   //等待连接是否损坏。
    66   discWriteTimeout=1*时间。秒
    67  )
    68  
    69  //如果解压缩的消息长度超过
    70  //允许的24位(即长度大于等于16MB)。
    71  var errplanmessagetoolarge=errors.new(“消息长度>=16MB”)
    72  
    73  //rlpx是实际(非测试)连接使用的传输协议。
    74  //它用锁和读/写截止时间包装帧编码器。
    75  RLPX型结构
    76   康德网络
    77  
    78   rmu,wmu同步.mutex
    79   rw*rlpxframerw
    80  }
    81  
    82  func newrlpx(fd net.conn)传输
    83   fd.setDeadline(time.now().add(handshakeTimeout))。
    84   返回&rlpx fd:fd
    85  }
    86  
    87  func(t*rlpx)readmsg()(msg,错误)
    88   锁定()
    89   延迟t.rmu.unlock()
    90   t.fd.setreadDeadline(time.now().add(framereadTimeout))。
    91   返回t.rw.readmsg()。
    92  }
    93  
    94  func(t*rlpx)writemsg(msg msg)错误
    95   T.WMU.
    96   延迟t.wmu.unlock()
    97   t.fd.setWriteDeadline(time.now().add(frameWriteTimeout))。
    98   返回t.rw.writemsg(msg)
    99  }
   100  
   101  func(t*rlpx)关闭(err错误)
   102   T.WMU.
   103   延迟t.wmu.unlock()
   104   //如果可能,告诉远端为什么要断开连接。
   105   如果T.RW!= nIL{
   106    如果R,则OK:=错误(不合理);OK&&R!=DiscNetworkError
   107     //rlpx尝试向断开连接的对等端发送discreason
   108     //如果连接为net.pipe(内存模拟)
   109     //它永远挂起,因为net.pipe不实现
   110     //写入截止时间。因为这只会试图发送
   111     //如果没有错误,则显示断开原因消息。
   112     if err:=t.fd.setWriteDeadline(time.now().add(discWriteTimeout));err==nil
   113      发送项(t.rw、discmsg、r)
   114     }
   115    }
   116   }
   117   T.F.C.()
   118  }
   119  
   120  func(t*rlpx)doprotochandshake(our*protochandshake)(their*protochandshake,err error)
   121   //写我们的握手是同时进行的,我们更喜欢
   122   //返回握手读取错误。如果远端
   123   //有正当理由提前断开我们的连接,我们应该将其返回
   124   //作为错误,以便在其他地方跟踪它。
   125   werr:=make(chan错误,1)
   126   go func()werr<-send(t.rw,handshakemsg,our)()
   127   如果他们的,err=readProtocolHandshake(t.rw,我们的);err!= nIL{
   128    <-werr//确保写入也终止
   129    返回零
   130   }
   131   如果错误:=<-werr;err!= nIL{
   132    返回nil,fmt.errorf(“写入错误:%v”,err)
   133   }
   134   //如果协议版本支持快速编码,则立即升级
   135   t.rw.snappy=their.version>=snappyrotocolversion
   136  
   137   归还他们的,零
   138  }
   139  
   140  func readprotocolhandshake(rw msgreader,our*protochandshake)(*protochandshake,error)
   141   msg,err:=rw.readmsg()。
   142   如果犯错!= nIL{
   143    返回零
   144   }
   145   如果消息大小>baseprotocolmaxmsgsize
   146    返回nil,fmt.errorf(“消息太大”)
   147   }
   148   如果msg.code==discmsg
   149    //根据协议握手有效之前断开连接
   150    //spec,如果posthashake检查失败,我们将自己发送它。
   151    //但是我们不能直接返回原因,因为它被回送了
   152    //否则返回。改为用绳子把它包起来。
   153    VaR原因[1]不一致
   154    rlp.decode(消息有效负载和原因)
   155    返回零,原因[0]
   156   }
   157   如果是MSG,代码!=握手
   158    返回nil,fmt.errorf(“预期握手,得到%x”,消息代码)
   159   }
   160   var-hs协议握手
   161   如果错误:=msg.decode(&hs);错误!= nIL{
   162    返回零
   163   }
   164   if(hs.id==discover.nodeid)
   165    返回零,DiscInvalidIdentity
   166   }
   167   返回与HS
   168  }
   169  
   170  //doenchandshake使用authenticated运行协议握手
   171  / /消息。协议握手是第一条经过身份验证的消息
   172  //并验证加密握手是否“有效”以及
   173  //远程端实际上提供了正确的公钥。
   174  func(t*rlpx)doenchandshake(prv*ecdsa.privatekey,dial*discover.node)(discover.nodeid,错误)
   175   var
   176    证券交易秘密
   177    错误率
   178   )
   179   如果刻度盘=零
   180    sec,err=receiverenchandshake(t.fd,prv)
   181   }否则{
   182    sec,err=initiatorenchandshake(t.fd,prv,dial.id)
   183   }
   184   如果犯错!= nIL{
   185    返回discover.nodeid,err
   186   }
   187   T.WMU.
   188   t.rw=newrlpxframerw(t.fd,秒)
   189   解锁()
   190   返回sec.remoteid,零
   191  }
   192  
   193  //enchandshake包含加密握手的状态。
   194  类型enchandshake结构
   195   引发剂布尔
   196   remoteid发现.nodeid
   197  
   198   remotepub*ecies.publickey//远程pubk
   199   initnoce,respnoce[]字节//nonce
   200   randomprivkey*ecies.privatekey//ecdhe随机
   201   remoterandompub*ecies.publickey//ecdhe random pubk
   202  }
   203  
   204  //机密表示连接机密
   205  //在加密握手期间协商。
   206  类型机密结构
   207   remoteid发现.nodeid
   208   aes,mac[]字节
   209   出口MAC,入口MAC哈希.hash
   210   标记[]字节
   211  }
   212  
   213  //rlpx v4握手认证(在eip-8中定义)。
   214  类型authmsgv4结构
   215   gotplan bool//读包是否有纯格式。
   216  
   217   签名[siglen]字节
   218   initiatorSubkey[publen]字节
   219   nonce[shalen]字节
   220   版本uint
   221  
   222   //忽略其他字段(向前兼容)
   223   其余[]rlp.rawvalue`rlp:“tail”`
   224  }
   225  
   226  //rlpx v4握手响应(在eip-8中定义)。
   227  键入authrespv4 struct_
   228   randompubkey[publen]字节
   229   nonce[shalen]字节
   230   版本uint
   231  
   232   //忽略其他字段(向前兼容)
   233   其余[]rlp.rawvalue`rlp:“tail”`
   234  }
   235  
   236  //在握手完成后调用secrets。
   237  //从握手值中提取连接机密。
   238  func(h*enchandshake)机密(auth,authresp[]byte)(机密,错误)
   239   ecdhesecret,err:=h.randomprivkey.generateshared(h.remoterrandompub,ssklen,ssklen)
   240   如果犯错!= nIL{
   241    返回机密,错误
   242   }
   243  
   244   //从临时密钥协议派生基本机密
   245   sharedSecret:=crypto.keccak256(ecdhesecret,crypto.keccak256(h.responce,h.initonce))。
   246   aesecret:=crypto.keccak256(ecdhesecret,sharedsecret)
   247   S: =秘密{
   248    远程ID:h.remoteID,
   249    伊丝:伊丝密,
   250    mac:crypto.keccak256(ecdhesecret,aesecret)
   251   }
   252  
   253   //为macs设置sha3实例
   254   mac1:=sha3.newkeccak256()。
   255   mac1.write(xor(s.mac,h.respnce))。
   256   mac1.写入(auth)
   257   mac2:=sha3.newkeccak256()。
   258   mac2.write(xor(s.mac,h.initonce))。
   259   MAC2.写入(authresp)
   260   如果H.发起人
   261    S.egressmac,S.ingressmac=mac1,mac2
   262   }否则{
   263    S.egressmac,S.ingressmac=mac2,mac1
   264   }
   265  
   266   返回零
   267  }
   268  
   269  //StaticSharedSecret返回静态共享机密,结果
   270  //本地和远程静态节点密钥之间的密钥协议。
   271  func(h*enchandshake)staticsharedsecret(prv*ecdsa.privatekey)([]字节,错误)
   272   返回ecies.importecdsa(prv).generateshared(h.remotepub、ssklen、ssklen)
   273  }
   274  
   275  //initiatorenchandshake在conn上协商会话令牌。
   276  //应该在连接的拨号端调用它。
   277  / /
   278  //prv是本地客户端的私钥。
   279  func initiatorenchandshake(conn io.readwriter,prv*ecdsa.privatekey,remoteid discover.nodeid)(s secrets,err error)
   280   h:=&enchandshake发起方:真,远程ID:远程ID
   281   authmsg,错误:=h.makeauthmsg(prv)
   282   如果犯错!= nIL{
   283    返回S
   284   }
   285   authpacket,错误:=sealeip8(authmsg,h)
   286   如果犯错!= nIL{
   287    返回S
   288   }
   289   如果,err=conn.write(authpacket);err!= nIL{
   290    返回S
   291   }
   292  
   293   AuthRespMsg:=新建(AuthRespv4)
   294   AuthRespPacket,错误:=readHandshakemsg(AuthRespMsg,EnauthRespLen,prv,conn)
   295   如果犯错!= nIL{
   296    返回S
   297   }
   298   如果错误:=H.HandleAuthResp(AuthRespMsg);错误!= nIL{
   299    返回S
   300   }
   301   返回h.secrets(authpacket、authrespacket)
   302  }
   303  
   304  //makeauthmsg创建启动器握手消息。
   305  func(h*enchandshake)makeauthmsg(prv*ecdsa.privatekey)(*authmsgv4,错误)
   306   rpub,错误:=h.remoteid.pubkey()
   307   如果犯错!= nIL{
   308    返回nil,fmt.errorf(“错误的remoteid:%v”,err)
   309   }
   310   h.remotepub=ecies.importecdsapublic(rpub)
   311   //生成随机发起程序nonce。
   312   h.initonce=生成([]字节,shalen)
   313   如果,错误:=rand.read(h.initnoce);错误!= nIL{
   314    返回零
   315   }
   316   //为ECDH生成随机密钥对to。
   317   h.randomprivkey,err=ecies.generatekey(rand.reader,crypto.s256(),nil)
   318   如果犯错!= nIL{
   319    返回零
   320   }
   321  
   322   //为已知消息签名:静态共享机密^nonce
   323   令牌,错误:=h.staticsharedsecret(prv)
   324   如果犯错!= nIL{
   325    返回零
   326   }
   327   有符号:=xor(token,h.initonce)
   328   签名,错误:=crypto.sign(signed,h.randomprivkey.exportecdsa())
   329   如果犯错!= nIL{
   330    返回零
   331   }
   332  
   333   消息:=new(authmsgv4)
   334   副本(消息签名[:],签名)
   335   复制(msg.initiatorpubkey[:],crypto.fromecdsapub(&prv.publickey)[1:]
   336   复制(msg.nonce[:],h.initonce)
   337   版本=4
   338   返回MSG,NIL
   339  }
   340  
   341  func(h*enchandshake)handleauthresp(msg*authresp4)(错误)
   342   h.respnce=msg.nonce[:]
   343   h.remoterandompub,err=importpublickey(msg.randompubkey[:])
   344   返回错误
   345  }
   346  
   347  //receiverenchandshake在conn上协商会话令牌。
   348  //应该在连接的侦听端调用它。
   349  / /
   350  //prv是本地客户端的私钥。
   351  func receiverenchandshake(conn io.readwriter,prv*ecdsa.privatekey)(s secrets,err error)
   352   authmsg:=新建(authmsgv4)
   353   authpacket,错误:=readhandshakemsg(authmsg,encauthmsglen,prv,conn)
   354   如果犯错!= nIL{
   355    返回S
   356   }
   357   H:=新(Enchandshake)
   358   如果错误:=h.handleauthmsg(authmsg,prv);错误!= nIL{
   359    返回S
   360   }
   361  
   362   AuthRespMsg,错误:=H.MakeAuthResp()。
   363   如果犯错!= nIL{
   364    返回S
   365   }
   366   var authresppacket[]字节
   367   如果authmsg.gotplan
   368    AuthRespPacket,err=AuthRespMsg.SealPlain(H)
   369   }否则{
   370    AuthRespPacket,err=SealeIP8(AuthRespMsg,H)
   371   }
   372   如果犯错!= nIL{
   373    返回S
   374   }
   375   如果,err=conn.write(authresppacket);err!= nIL{
   376    返回S
   377   }
   378   返回h.secrets(authpacket、authrespacket)
   379  }
   380  
   381  func(h*enchandshake)handleauthmsg(msg*authmsgv4,prv*ecdsa.privatekey)错误
   382   //导入远程标识。
   383   h.initonce=msg.nonce[:]
   384   h.remoteid=msg.initiatorSubkey
   385   rpub,错误:=h.remoteid.pubkey()
   386   如果犯错!= nIL{
   387    返回fmt.errorf(“错误的remoteid:%v”,err)
   388   }
   389   h.remotepub=ecies.importecdsapublic(rpub)
   390  
   391   //为ECDH生成随机密钥对。
   392   //如果已经设置了私钥,则使用它而不是生成一个(用于测试)。
   393   如果h.randomprivkey==nil
   394    h.randomprivkey,err=ecies.generatekey(rand.reader,crypto.s256(),nil)
   395    如果犯错!= nIL{
   396     返回错误
   397    }
   398   }
   399  
   400   //检查签名。
   401   令牌,错误:=h.staticsharedsecret(prv)
   402   如果犯错!= nIL{
   403    返回错误
   404   }
   405   signedsg:=xor(token,h.initonce)
   406   remoteRandoPub,错误:=secp256k1.recoverpubkey(signedsg,msg.signature[:])
   407   如果犯错!= nIL{
   408    返回错误
   409   }
   410   H.RemoteRandompub,u=导入公共密钥(RemoteRandompub)
   411   返回零
   412  }
   413  
   414  func(h*enchandshake)makeauthresp()(msg*authresp4,err error)
   415   //生成随机nonce。
   416   h.responce=make([]字节,shalen)
   417   如果,err=rand.read(h.respnce);err!= nIL{
   418    返回零
   419   }
   420  
   421   msg=新建(authrespv4)
   422   复制(msg.nonce[:],h.respnce)
   423   复制(msg.randompubkey[:],exportpubkey(&h.randomprivkey.publickey))
   424   版本=4
   425   返回MSG,NIL
   426  }
   427  
   428  func(msg*authmsgv4)sealplain(h*enchandshake)([]字节,错误)
   429   buf:=make([]字节,authmsglen)
   430   n:=副本(buf,msg.签名[:])
   431   n+=复制(buf[n:],crypto.keccak256(exportpubkey(&h.randomprivkey.publickey)))
   432   n+=复制(buf[n:],msg.initiatorSubkey[:])
   433   n+=复制(buf[n:],msg.nonce[:])
   434   buf[n]=0//令牌标志
   435   返回ecies.encrypt(rand.reader,h.remotepub,buf,nil,nil)
   436  }
   437  
   438  func(msg*authmsgv4)decodeplain(input[]byte)
   439   n:=复制(消息签名[:],输入)
   440   n+=shalen//跳过sha3(发起人短暂发布)
   441   n+=复制(msg.initiatorSubkey[:],输入[n:]
   442   复制(msg.nonce[:],输入[n:])
   443   版本=4
   444   msg.gotplan=真
   445  }
   446  
   447  func(msg*authrespv4)sealplain(hs*enchandshake)([]字节,错误)
   448   buf:=make([]字节,authresplen)
   449   n:=复制(buf,msg.randombkey[:])
   450   复制(buf[n:],msg.nonce[:])
   451   返回ecies.encrypt(rand.reader、hs.remotepub、buf、nil、nil)
   452  }
   453  
   454  func(msg*authrespv4)decodeplain(input[]byte)
   455   n:=复制(msg.randompubkey[:],输入)
   456   复制(msg.nonce[:],输入[n:])
   457   版本=4
   458  }
   459  
   460  var padspace=make([]字节,300)
   461  
   462  func sealeip8(msg interface,h*enchandshake)([]字节,错误)
   463   buf:=新建(bytes.buffer)
   464   如果错误:=rlp.encode(buf,msg);错误!= nIL{
   465    返回零
   466   }
   467   //用随机数据量填充。数量至少需要100个字节才能
   468   //该消息可与EIP-8之前的握手区分开来。
   469   焊盘:=padspace[:mrand.intn(len(padspace)-100)+100]
   470   BUF.写(PAD)
   471   前缀:=make([]字节,2)
   472   binary.bigendian.putuint16(前缀,uint16(buf.len()+eciesoverhead))
   473  
   474   enc,err:=ecies.encrypt(rand.reader,h.remotepub,buf.bytes(),nil,前缀)
   475   返回append(prefix,enc…),err
   476  }
   477  
   478  类型普通解码器接口
   479   decodeplain([]字节)
   480  }
   481  
   482  func readhandshakemsg(msg plaindecoder,plainsize int,prv*ecdsa.privatekey,r io.reader)([]字节,错误)
   483   buf:=make([]字节,普通大小)
   484   如果,错误:=io.readfull(r,buf);错误!= nIL{
   485    返回BUF
   486   }
   487   //尝试解码pre-eip-8“plain”格式。
   488   键:=ecies.importecdsa(prv)
   489   如果是dec,则错误:=key.decrypt(buf,nil,nil);err==nil
   490    解码原稿消息(DEC)
   491    返回BUF,NIL
   492   }
   493   //可以是EIP-8格式,试试看。
   494   前缀:=buf[:2]
   495   大小:=binary.bigendian.uint16(前缀)
   496   如果尺寸<uint16(plainsize)
   497    返回buf,fmt.errorf(“大小下溢,至少需要%d字节”,plainsize)
   498   }
   499   buf=append(buf,make([]byte,size-uint16(plainsize)+2)…)
   500   如果uu,错误:=io.readfull(r,buf[plainsize:]);错误!= nIL{
   501    返回BUF
   502   }
   503   dec,err:=key.decrypt(buf[2:],nil,前缀)
   504   如果犯错!= nIL{
   505    返回BUF
   506   }
   507   //此处不能使用rlp.decodebytes,因为它拒绝
   508   //尾随数据(向前兼容)。
   509   S:=rlp.newstream(bytes.newreader(dec),0)
   510   返回buf,s.decode(msg)
   511  }
   512  
   513  //importpublickey取消标记512位公钥。
   514  func importpublickey(pubkey[]byte)(*ecies.publickey,error)
   515   var pubkey65[]字节
   516   交换长度(pubkey)
   517   案例64:
   518    //添加“uncompressed key”标志
   519    pubkey65=append([]字节0x04,pubkey…)
   520   案例65:
   521    pubKey65=发布键
   522   违约:
   523    返回nil,fmt.errorf(“无效的公钥长度%v(预期64/65)”,len(pubkey))
   524   }
   525   //TODO:更少的无意义转换
   526   pub,err:=crypto.unmashalpubkey(pubkey65)
   527   如果犯错!= nIL{
   528    返回零
   529   }
   530   退货特定进口适用性(pub),无
   531  }
   532  
   533  func exportpubkey(pub*ecies.publickey)[]字节
   534   如果Pub=nI{
   535    恐慌(“nil pubkey”)
   536   }
   537   返回Elliptic.Marshal(pub.curve,pub.x,pub.y)[1:]
   538  }
   539  
   540  func xor(一个,另一个[]字节)(xor[]字节)
   541   xor=make([]byte,len(one))。
   542   对于i:=0;i<len(one);i++
   543    xor[i]=一个[i]^另一个[i]
   544   }
   545   返回异或
   546  }
   547  
   548  var
   549   //用于代替实际帧头数据。
   550   //TODO:当msg包含协议类型代码时替换此项。
   551   ZeroHeader=[]字节0xc2、0x80、0x80_
   552   //十六个零字节
   553   zero16=make([]字节,16)
   554  )
   555  
   556  //rlpxframerw实现了rlpx framework的简化版本。
   557  //不支持分块消息,所有头等于
   558  //ZooHead。
   559  / /
   560  //rlpxframerw对于从多个goroutine并发使用不安全。
   561  类型rlpxframerw struct_
   562   连接IO.readwriter
   563   加密流
   564   DEC密码流
   565  
   566   maccipher cipher.block
   567   Egressmac哈希.hash
   568   入口MAC哈希.hash
   569  
   570   快活布尔
   571  }
   572  
   573  func newrlpxframerw(conn io.readwriter,s secrets)*rlpxframerw_
   574   macc,err:=aes.newcipher(s.mac)
   575   如果犯错!= nIL{
   576    panic(“无效的MAC机密:”+err.error())
   577   }
   578   ENCC,错误:=aes.newcipher(s.aes)
   579   如果犯错!= nIL{
   580    panic(“无效的aes秘密:”+err.error())
   581   }
   582   //我们对aes使用全零IV,因为使用的密钥
   583   //因为加密是短暂的。
   584   iv:=make([]字节,encc.blocksize())
   585   返回&rlpxframerw_
   586    连接件:连接件,
   587    Enc:Cipher.Newctr(Encc,IV),第
   588    DEC:cipher.newctr(encc,iv)
   589    麦克西弗:麦克西弗,
   590    白鹭:白鹭,
   591    入口MAC:S.IngressMAC,
   592   }
   593  }
   594  
   595  func(rw*rlpxframerw)writemsg(msg msg)错误
   596   ptype,:=rlp.encodetobytes(消息代码)
   597  
   598   //如果启用了snappy,则立即压缩消息
   599   如果RW.SNAPPY {
   600    如果消息大小>maxuint24
   601     返回errplanmessagetoolarge
   602    }
   603    有效载荷,:=ioutil.readall(msg.payload)
   604    有效载荷=快速编码(零,有效载荷)
   605  
   606    msg.payload=bytes.newreader(有效负载)
   607    msg.size=uint32(len(有效载荷)
   608   }
   609   //写头
   610   headbuf:=make([]字节,32)
   611   fsize:=uint32(len(ptype))+消息大小
   612   如果fsize>maxuint24
   613    返回errors.new(“消息大小溢出uint24”)
   614   }
   615   putint24(fsize,headbuf)//todo:检查溢出
   616   复制(headbuf[3:],zeroheader)
   617   rw.enc.xorkeystream(headbuf[:16],headbuf[:16])//上半部分现在已加密
   618  
   619   //写入头MAC
   620   副本(headbuf[16:],updatemac(rw.egissmac,rw.maccipher,headbuf[:16]))
   621   如果uux,错误:=rw.conn.write(headbuf);错误!= nIL{
   622    返回错误
   623   }
   624  
   625   //写入加密帧,更新出口MAC哈希
   626   //写入conn的数据。
   627   tee:=cipher.streamwriter s:rw.enc,w:io.multiwriter(rw.conn,rw.egissmac)
   628   如果ux,err:=tee.write(ptype);err!= nIL{
   629    返回错误
   630   }
   631   如果uu,错误:=io.copy(tee,msg.payload);错误!= nIL{
   632    返回错误
   633   }
   634   如果填充:=fsize%16;填充>0
   635    如果u,err:=tee.write(zero16[:16 padding]);err!= nIL{
   636     返回错误
   637    }
   638   }
   639  
   640   //写入帧mac。出口MAC哈希是最新的,因为
   641   //框架内容也写入了它。
   642   fmacseed:=rw.egissmac.sum(零)
   643   mac:=更新mac(rw.egissmac、rw.maccipher、fmacseed)
   644   _uuErr:=rw.conn.write(mac)
   645   返回错误
   646  }
   647  
   648  func(rw*rlpxframerw)readmsg()(msg,err error)
   649   //读取头
   650   headbuf:=make([]字节,32)
   651   如果,错误:=io.readfull(rw.conn,headbuf);错误!= nIL{
   652    返回MSG
   653   }
   654   //验证头MAC
   655   shouldmac:=updateMac(rw.ingressmac,rw.maccipher,headbuf[:16])
   656   如果!hmac.相等(shouldmac,headbuf[16:)
   657    返回msg,errors.new(“错误的header mac”)
   658   }
   659   rw.dec.xorkeystream(headbuf[:16],headbuf[:16])//上半部分现在被解密
   660   fsize:=readInt24(headbuf)
   661   //暂时忽略协议类型
   662  
   663   //读取帧内容
   664   var rsize=fsize//帧大小四舍五入到16字节边界
   665   如果填充:=fsize%16;填充>0
   666    rsize+=16-填充
   667   }
   668   framebuf:=make([]字节,rsize)
   669   如果uux,错误:=io.readfull(rw.conn,framebuf);错误!= nIL{
   670    返回MSG
   671   }
   672  
   673   //读取并验证帧MAC。我们可以重复使用headbuf。
   674   rw.ingressmac.write(framebuf)
   675   fmacseed:=rw.ingressmac.sum(零)
   676   如果uu,错误:=io.readfull(rw.conn,headbuf[:16]);错误!= nIL{
   677    返回MSG
   678   }
   679   shouldmac=updateMac(rw.ingressmac,rw.maccipher,fmacseed)
   680   如果!hmac.equal(shouldmac,headbuf[:16]);
   681    返回msg,errors.new(“坏帧MAC”)
   682   }
   683  
   684   //解密帧内容
   685   rw.dec.xorkeystream(framebuf,framebuf)代码
   686  
   687   //解码消息代码
   688   内容:=bytes.newreader(framebuf[:fsize])
   689   如果错误:=rlp.decode(content,&msg.code);错误!= nIL{
   690    返回MSG
   691   }
   692   msg.size=uint32(content.len())
   693   msg.payload=内容
   694  
   695   //如果启用了snappy,则验证并解压缩消息
   696   如果RW.SNAPPY {
   697    有效负载,错误:=ioutil.readall(msg.payload)
   698    如果犯错!= nIL{
   699     返回MSG
   700    }
   701    大小,错误:=snappy.decodedlen(有效负载)
   702    如果犯错!= nIL{
   703     返回MSG
   704    }
   705    如果大小>int(maxuint24)
   706     返回消息,errplanmessagetoolarge
   707    }
   708    有效载荷,err=snappy.decode(nil,有效载荷)
   709    如果犯错!= nIL{
   710     返回MSG
   711    }
   712    msg.size,msg.payload=uint32(大小),bytes.newreader(有效负载)
   713   }
   714   返回MSG,NIL
   715  }
   716  
   717  //updateMac使用加密种子重新设置给定哈希。
   718  //返回种子设定后哈希和的前16个字节。
   719  func updatemac(mac hash.hash,block cipher.block,seed[]byte)[]byte_
   720   aesbuf:=make([]字节,aes.blocksize)
   721   block.encrypt(aesbuf,mac.sum(nil))。
   722   对于i:=范围aesbuf
   723    aesbuf[i]^=种子[i]
   724   }
   725   书写(aesbuf)
   726   返回mac.sum(nil)[:16]
   727  }
   728  
   729  func readint24(b[]字节)uint32
   730   返回uint32(b[2])uint32(b[1])<<8_uint32(b[0])<<16
   731  }
   732  
   733  func putint24(v uint32,b[]字节)
   734   B[0]=字节(V>>16)
   735   B[1]=字节(V>>8)
   736   B〔2〕=字节(V)
   737  }
   738