rlpx.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package p2p
  2. import (
  3. "bytes"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/hmac"
  7. "errors"
  8. "hash"
  9. "io"
  10. "github.com/ethereum/go-ethereum/rlp"
  11. )
  12. var (
  13. // this is used in place of actual frame header data.
  14. // TODO: replace this when Msg contains the protocol type code.
  15. zeroHeader = []byte{0xC2, 0x80, 0x80}
  16. // sixteen zero bytes
  17. zero16 = make([]byte, 16)
  18. )
  19. // rlpxFrameRW implements a simplified version of RLPx framing.
  20. // chunked messages are not supported and all headers are equal to
  21. // zeroHeader.
  22. //
  23. // rlpxFrameRW is not safe for concurrent use from multiple goroutines.
  24. type rlpxFrameRW struct {
  25. conn io.ReadWriter
  26. enc cipher.Stream
  27. dec cipher.Stream
  28. macCipher cipher.Block
  29. egressMAC hash.Hash
  30. ingressMAC hash.Hash
  31. }
  32. func newRlpxFrameRW(conn io.ReadWriter, s secrets) *rlpxFrameRW {
  33. macc, err := aes.NewCipher(s.MAC)
  34. if err != nil {
  35. panic("invalid MAC secret: " + err.Error())
  36. }
  37. encc, err := aes.NewCipher(s.AES)
  38. if err != nil {
  39. panic("invalid AES secret: " + err.Error())
  40. }
  41. // we use an all-zeroes IV for AES because the key used
  42. // for encryption is ephemeral.
  43. iv := make([]byte, encc.BlockSize())
  44. return &rlpxFrameRW{
  45. conn: conn,
  46. enc: cipher.NewCTR(encc, iv),
  47. dec: cipher.NewCTR(encc, iv),
  48. macCipher: macc,
  49. egressMAC: s.EgressMAC,
  50. ingressMAC: s.IngressMAC,
  51. }
  52. }
  53. func (rw *rlpxFrameRW) WriteMsg(msg Msg) error {
  54. ptype, _ := rlp.EncodeToBytes(msg.Code)
  55. // write header
  56. headbuf := make([]byte, 32)
  57. fsize := uint32(len(ptype)) + msg.Size
  58. putInt24(fsize, headbuf) // TODO: check overflow
  59. copy(headbuf[3:], zeroHeader)
  60. rw.enc.XORKeyStream(headbuf[:16], headbuf[:16]) // first half is now encrypted
  61. // write header MAC
  62. copy(headbuf[16:], updateMAC(rw.egressMAC, rw.macCipher, headbuf[:16]))
  63. if _, err := rw.conn.Write(headbuf); err != nil {
  64. return err
  65. }
  66. // write encrypted frame, updating the egress MAC hash with
  67. // the data written to conn.
  68. tee := cipher.StreamWriter{S: rw.enc, W: io.MultiWriter(rw.conn, rw.egressMAC)}
  69. if _, err := tee.Write(ptype); err != nil {
  70. return err
  71. }
  72. if _, err := io.Copy(tee, msg.Payload); err != nil {
  73. return err
  74. }
  75. if padding := fsize % 16; padding > 0 {
  76. if _, err := tee.Write(zero16[:16-padding]); err != nil {
  77. return err
  78. }
  79. }
  80. // write frame MAC. egress MAC hash is up to date because
  81. // frame content was written to it as well.
  82. fmacseed := rw.egressMAC.Sum(nil)
  83. mac := updateMAC(rw.egressMAC, rw.macCipher, fmacseed)
  84. _, err := rw.conn.Write(mac)
  85. return err
  86. }
  87. func (rw *rlpxFrameRW) ReadMsg() (msg Msg, err error) {
  88. // read the header
  89. headbuf := make([]byte, 32)
  90. if _, err := io.ReadFull(rw.conn, headbuf); err != nil {
  91. return msg, err
  92. }
  93. // verify header mac
  94. shouldMAC := updateMAC(rw.ingressMAC, rw.macCipher, headbuf[:16])
  95. if !hmac.Equal(shouldMAC, headbuf[16:]) {
  96. return msg, errors.New("bad header MAC")
  97. }
  98. rw.dec.XORKeyStream(headbuf[:16], headbuf[:16]) // first half is now decrypted
  99. fsize := readInt24(headbuf)
  100. // ignore protocol type for now
  101. // read the frame content
  102. var rsize = fsize // frame size rounded up to 16 byte boundary
  103. if padding := fsize % 16; padding > 0 {
  104. rsize += 16 - padding
  105. }
  106. framebuf := make([]byte, rsize)
  107. if _, err := io.ReadFull(rw.conn, framebuf); err != nil {
  108. return msg, err
  109. }
  110. // read and validate frame MAC. we can re-use headbuf for that.
  111. rw.ingressMAC.Write(framebuf)
  112. fmacseed := rw.ingressMAC.Sum(nil)
  113. if _, err := io.ReadFull(rw.conn, headbuf[:16]); err != nil {
  114. return msg, err
  115. }
  116. shouldMAC = updateMAC(rw.ingressMAC, rw.macCipher, fmacseed)
  117. if !hmac.Equal(shouldMAC, headbuf[:16]) {
  118. return msg, errors.New("bad frame MAC")
  119. }
  120. // decrypt frame content
  121. rw.dec.XORKeyStream(framebuf, framebuf)
  122. // decode message code
  123. content := bytes.NewReader(framebuf[:fsize])
  124. if err := rlp.Decode(content, &msg.Code); err != nil {
  125. return msg, err
  126. }
  127. msg.Size = uint32(content.Len())
  128. msg.Payload = content
  129. return msg, nil
  130. }
  131. // updateMAC reseeds the given hash with encrypted seed.
  132. // it returns the first 16 bytes of the hash sum after seeding.
  133. func updateMAC(mac hash.Hash, block cipher.Block, seed []byte) []byte {
  134. aesbuf := make([]byte, aes.BlockSize)
  135. block.Encrypt(aesbuf, mac.Sum(nil))
  136. for i := range aesbuf {
  137. aesbuf[i] ^= seed[i]
  138. }
  139. mac.Write(aesbuf)
  140. return mac.Sum(nil)[:16]
  141. }
  142. func readInt24(b []byte) uint32 {
  143. return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16
  144. }
  145. func putInt24(v uint32, b []byte) {
  146. b[0] = byte(v >> 16)
  147. b[1] = byte(v >> 8)
  148. b[2] = byte(v)
  149. }