handshake.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. package p2p
  2. import (
  3. "crypto/ecdsa"
  4. "crypto/rand"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "net"
  9. "github.com/ethereum/go-ethereum/crypto"
  10. "github.com/ethereum/go-ethereum/crypto/ecies"
  11. "github.com/ethereum/go-ethereum/crypto/secp256k1"
  12. "github.com/ethereum/go-ethereum/p2p/discover"
  13. "github.com/ethereum/go-ethereum/rlp"
  14. )
  15. const (
  16. sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2
  17. sigLen = 65 // elliptic S256
  18. pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte
  19. shaLen = 32 // hash length (for nonce etc)
  20. authMsgLen = sigLen + shaLen + pubLen + shaLen + 1
  21. authRespLen = pubLen + shaLen + 1
  22. eciesBytes = 65 + 16 + 32
  23. iHSLen = authMsgLen + eciesBytes // size of the final ECIES payload sent as initiator's handshake
  24. rHSLen = authRespLen + eciesBytes // size of the final ECIES payload sent as receiver's handshake
  25. )
  26. type conn struct {
  27. *frameRW
  28. *protoHandshake
  29. }
  30. func newConn(fd net.Conn, hs *protoHandshake) *conn {
  31. return &conn{newFrameRW(fd, msgWriteTimeout), hs}
  32. }
  33. // encHandshake represents information about the remote end
  34. // of a connection that is negotiated during the encryption handshake.
  35. type encHandshake struct {
  36. ID discover.NodeID
  37. IngressMAC []byte
  38. EgressMAC []byte
  39. Token []byte
  40. }
  41. // protoHandshake is the RLP structure of the protocol handshake.
  42. type protoHandshake struct {
  43. Version uint64
  44. Name string
  45. Caps []Cap
  46. ListenPort uint64
  47. ID discover.NodeID
  48. }
  49. // setupConn starts a protocol session on the given connection.
  50. // It runs the encryption handshake and the protocol handshake.
  51. // If dial is non-nil, the connection the local node is the initiator.
  52. func setupConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *discover.Node) (*conn, error) {
  53. if dial == nil {
  54. return setupInboundConn(fd, prv, our)
  55. } else {
  56. return setupOutboundConn(fd, prv, our, dial)
  57. }
  58. }
  59. func setupInboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake) (*conn, error) {
  60. // var remotePubkey []byte
  61. // sessionToken, remotePubkey, err = inboundEncHandshake(fd, prv, nil)
  62. // copy(remoteID[:], remotePubkey)
  63. rw := newFrameRW(fd, msgWriteTimeout)
  64. rhs, err := readProtocolHandshake(rw, our)
  65. if err != nil {
  66. return nil, err
  67. }
  68. if err := writeProtocolHandshake(rw, our); err != nil {
  69. return nil, fmt.Errorf("protocol write error: %v", err)
  70. }
  71. return &conn{rw, rhs}, nil
  72. }
  73. func setupOutboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *discover.Node) (*conn, error) {
  74. // remoteID = dial.ID
  75. // sessionToken, err = outboundEncHandshake(fd, prv, remoteID[:], nil)
  76. rw := newFrameRW(fd, msgWriteTimeout)
  77. if err := writeProtocolHandshake(rw, our); err != nil {
  78. return nil, fmt.Errorf("protocol write error: %v", err)
  79. }
  80. rhs, err := readProtocolHandshake(rw, our)
  81. if err != nil {
  82. return nil, fmt.Errorf("protocol handshake read error: %v", err)
  83. }
  84. if rhs.ID != dial.ID {
  85. return nil, errors.New("dialed node id mismatch")
  86. }
  87. return &conn{rw, rhs}, nil
  88. }
  89. // outboundEncHandshake negotiates a session token on conn.
  90. // it should be called on the dialing side of the connection.
  91. //
  92. // privateKey is the local client's private key
  93. // remotePublicKey is the remote peer's node ID
  94. // sessionToken is the token from a previous session with this node.
  95. func outboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, remotePublicKey []byte, sessionToken []byte) (
  96. newSessionToken []byte,
  97. err error,
  98. ) {
  99. auth, initNonce, randomPrivKey, err := authMsg(prvKey, remotePublicKey, sessionToken)
  100. if err != nil {
  101. return nil, err
  102. }
  103. if _, err = conn.Write(auth); err != nil {
  104. return nil, err
  105. }
  106. response := make([]byte, rHSLen)
  107. if _, err = io.ReadFull(conn, response); err != nil {
  108. return nil, err
  109. }
  110. recNonce, remoteRandomPubKey, _, err := completeHandshake(response, prvKey)
  111. if err != nil {
  112. return nil, err
  113. }
  114. return newSession(initNonce, recNonce, randomPrivKey, remoteRandomPubKey)
  115. }
  116. // authMsg creates the initiator handshake.
  117. func authMsg(prvKey *ecdsa.PrivateKey, remotePubKeyS, sessionToken []byte) (
  118. auth, initNonce []byte,
  119. randomPrvKey *ecdsa.PrivateKey,
  120. err error,
  121. ) {
  122. // session init, common to both parties
  123. remotePubKey, err := importPublicKey(remotePubKeyS)
  124. if err != nil {
  125. return
  126. }
  127. var tokenFlag byte // = 0x00
  128. if sessionToken == nil {
  129. // no session token found means we need to generate shared secret.
  130. // ecies shared secret is used as initial session token for new peers
  131. // generate shared key from prv and remote pubkey
  132. if sessionToken, err = ecies.ImportECDSA(prvKey).GenerateShared(ecies.ImportECDSAPublic(remotePubKey), sskLen, sskLen); err != nil {
  133. return
  134. }
  135. // tokenFlag = 0x00 // redundant
  136. } else {
  137. // for known peers, we use stored token from the previous session
  138. tokenFlag = 0x01
  139. }
  140. //E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0)
  141. // E(remote-pubk, S(ecdhe-random, token^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x1)
  142. // allocate msgLen long message,
  143. var msg []byte = make([]byte, authMsgLen)
  144. initNonce = msg[authMsgLen-shaLen-1 : authMsgLen-1]
  145. if _, err = rand.Read(initNonce); err != nil {
  146. return
  147. }
  148. // create known message
  149. // ecdh-shared-secret^nonce for new peers
  150. // token^nonce for old peers
  151. var sharedSecret = xor(sessionToken, initNonce)
  152. // generate random keypair to use for signing
  153. if randomPrvKey, err = crypto.GenerateKey(); err != nil {
  154. return
  155. }
  156. // sign shared secret (message known to both parties): shared-secret
  157. var signature []byte
  158. // signature = sign(ecdhe-random, shared-secret)
  159. // uses secp256k1.Sign
  160. if signature, err = crypto.Sign(sharedSecret, randomPrvKey); err != nil {
  161. return
  162. }
  163. // message
  164. // signed-shared-secret || H(ecdhe-random-pubk) || pubk || nonce || 0x0
  165. copy(msg, signature) // copy signed-shared-secret
  166. // H(ecdhe-random-pubk)
  167. var randomPubKey64 []byte
  168. if randomPubKey64, err = exportPublicKey(&randomPrvKey.PublicKey); err != nil {
  169. return
  170. }
  171. var pubKey64 []byte
  172. if pubKey64, err = exportPublicKey(&prvKey.PublicKey); err != nil {
  173. return
  174. }
  175. copy(msg[sigLen:sigLen+shaLen], crypto.Sha3(randomPubKey64))
  176. // pubkey copied to the correct segment.
  177. copy(msg[sigLen+shaLen:sigLen+shaLen+pubLen], pubKey64)
  178. // nonce is already in the slice
  179. // stick tokenFlag byte to the end
  180. msg[authMsgLen-1] = tokenFlag
  181. // encrypt using remote-pubk
  182. // auth = eciesEncrypt(remote-pubk, msg)
  183. if auth, err = crypto.Encrypt(remotePubKey, msg); err != nil {
  184. return
  185. }
  186. return
  187. }
  188. // completeHandshake is called when the initiator receives an
  189. // authentication response (aka receiver handshake). It completes the
  190. // handshake by reading off parameters the remote peer provides needed
  191. // to set up the secure session.
  192. func completeHandshake(auth []byte, prvKey *ecdsa.PrivateKey) (
  193. respNonce []byte,
  194. remoteRandomPubKey *ecdsa.PublicKey,
  195. tokenFlag bool,
  196. err error,
  197. ) {
  198. var msg []byte
  199. // they prove that msg is meant for me,
  200. // I prove I possess private key if i can read it
  201. if msg, err = crypto.Decrypt(prvKey, auth); err != nil {
  202. return
  203. }
  204. respNonce = msg[pubLen : pubLen+shaLen]
  205. var remoteRandomPubKeyS = msg[:pubLen]
  206. if remoteRandomPubKey, err = importPublicKey(remoteRandomPubKeyS); err != nil {
  207. return
  208. }
  209. if msg[authRespLen-1] == 0x01 {
  210. tokenFlag = true
  211. }
  212. return
  213. }
  214. // inboundEncHandshake negotiates a session token on conn.
  215. // it should be called on the listening side of the connection.
  216. //
  217. // privateKey is the local client's private key
  218. // sessionToken is the token from a previous session with this node.
  219. func inboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, sessionToken []byte) (
  220. token, remotePubKey []byte,
  221. err error,
  222. ) {
  223. // we are listening connection. we are responders in the
  224. // handshake. Extract info from the authentication. The initiator
  225. // starts by sending us a handshake that we need to respond to. so
  226. // we read auth message first, then respond.
  227. auth := make([]byte, iHSLen)
  228. if _, err := io.ReadFull(conn, auth); err != nil {
  229. return nil, nil, err
  230. }
  231. response, recNonce, initNonce, remotePubKey, randomPrivKey, remoteRandomPubKey, err := authResp(auth, sessionToken, prvKey)
  232. if err != nil {
  233. return nil, nil, err
  234. }
  235. if _, err = conn.Write(response); err != nil {
  236. return nil, nil, err
  237. }
  238. token, err = newSession(initNonce, recNonce, randomPrivKey, remoteRandomPubKey)
  239. return token, remotePubKey, err
  240. }
  241. // authResp is called by peer if it accepted (but not
  242. // initiated) the connection from the remote. It is passed the initiator
  243. // handshake received and the session token belonging to the
  244. // remote initiator.
  245. //
  246. // The first return value is the authentication response (aka receiver
  247. // handshake) that is to be sent to the remote initiator.
  248. func authResp(auth, sessionToken []byte, prvKey *ecdsa.PrivateKey) (
  249. authResp, respNonce, initNonce, remotePubKeyS []byte,
  250. randomPrivKey *ecdsa.PrivateKey,
  251. remoteRandomPubKey *ecdsa.PublicKey,
  252. err error,
  253. ) {
  254. // they prove that msg is meant for me,
  255. // I prove I possess private key if i can read it
  256. msg, err := crypto.Decrypt(prvKey, auth)
  257. if err != nil {
  258. return
  259. }
  260. remotePubKeyS = msg[sigLen+shaLen : sigLen+shaLen+pubLen]
  261. remotePubKey, _ := importPublicKey(remotePubKeyS)
  262. var tokenFlag byte
  263. if sessionToken == nil {
  264. // no session token found means we need to generate shared secret.
  265. // ecies shared secret is used as initial session token for new peers
  266. // generate shared key from prv and remote pubkey
  267. if sessionToken, err = ecies.ImportECDSA(prvKey).GenerateShared(ecies.ImportECDSAPublic(remotePubKey), sskLen, sskLen); err != nil {
  268. return
  269. }
  270. // tokenFlag = 0x00 // redundant
  271. } else {
  272. // for known peers, we use stored token from the previous session
  273. tokenFlag = 0x01
  274. }
  275. // the initiator nonce is read off the end of the message
  276. initNonce = msg[authMsgLen-shaLen-1 : authMsgLen-1]
  277. // I prove that i own prv key (to derive shared secret, and read
  278. // nonce off encrypted msg) and that I own shared secret they
  279. // prove they own the private key belonging to ecdhe-random-pubk
  280. // we can now reconstruct the signed message and recover the peers
  281. // pubkey
  282. var signedMsg = xor(sessionToken, initNonce)
  283. var remoteRandomPubKeyS []byte
  284. if remoteRandomPubKeyS, err = secp256k1.RecoverPubkey(signedMsg, msg[:sigLen]); err != nil {
  285. return
  286. }
  287. // convert to ECDSA standard
  288. if remoteRandomPubKey, err = importPublicKey(remoteRandomPubKeyS); err != nil {
  289. return
  290. }
  291. // now we find ourselves a long task too, fill it random
  292. var resp = make([]byte, authRespLen)
  293. // generate shaLen long nonce
  294. respNonce = resp[pubLen : pubLen+shaLen]
  295. if _, err = rand.Read(respNonce); err != nil {
  296. return
  297. }
  298. // generate random keypair for session
  299. if randomPrivKey, err = crypto.GenerateKey(); err != nil {
  300. return
  301. }
  302. // responder auth message
  303. // E(remote-pubk, ecdhe-random-pubk || nonce || 0x0)
  304. var randomPubKeyS []byte
  305. if randomPubKeyS, err = exportPublicKey(&randomPrivKey.PublicKey); err != nil {
  306. return
  307. }
  308. copy(resp[:pubLen], randomPubKeyS)
  309. // nonce is already in the slice
  310. resp[authRespLen-1] = tokenFlag
  311. // encrypt using remote-pubk
  312. // auth = eciesEncrypt(remote-pubk, msg)
  313. // why not encrypt with ecdhe-random-remote
  314. if authResp, err = crypto.Encrypt(remotePubKey, resp); err != nil {
  315. return
  316. }
  317. return
  318. }
  319. // newSession is called after the handshake is completed. The
  320. // arguments are values negotiated in the handshake. The return value
  321. // is a new session Token to be remembered for the next time we
  322. // connect with this peer.
  323. func newSession(initNonce, respNonce []byte, privKey *ecdsa.PrivateKey, remoteRandomPubKey *ecdsa.PublicKey) ([]byte, error) {
  324. // 3) Now we can trust ecdhe-random-pubk to derive new keys
  325. //ecdhe-shared-secret = ecdh.agree(ecdhe-random, remote-ecdhe-random-pubk)
  326. pubKey := ecies.ImportECDSAPublic(remoteRandomPubKey)
  327. dhSharedSecret, err := ecies.ImportECDSA(privKey).GenerateShared(pubKey, sskLen, sskLen)
  328. if err != nil {
  329. return nil, err
  330. }
  331. sharedSecret := crypto.Sha3(dhSharedSecret, crypto.Sha3(respNonce, initNonce))
  332. sessionToken := crypto.Sha3(sharedSecret)
  333. return sessionToken, nil
  334. }
  335. // importPublicKey unmarshals 512 bit public keys.
  336. func importPublicKey(pubKey []byte) (pubKeyEC *ecdsa.PublicKey, err error) {
  337. var pubKey65 []byte
  338. switch len(pubKey) {
  339. case 64:
  340. // add 'uncompressed key' flag
  341. pubKey65 = append([]byte{0x04}, pubKey...)
  342. case 65:
  343. pubKey65 = pubKey
  344. default:
  345. return nil, fmt.Errorf("invalid public key length %v (expect 64/65)", len(pubKey))
  346. }
  347. return crypto.ToECDSAPub(pubKey65), nil
  348. }
  349. func exportPublicKey(pubKeyEC *ecdsa.PublicKey) (pubKey []byte, err error) {
  350. if pubKeyEC == nil {
  351. return nil, fmt.Errorf("no ECDSA public key given")
  352. }
  353. return crypto.FromECDSAPub(pubKeyEC)[1:], nil
  354. }
  355. func xor(one, other []byte) (xor []byte) {
  356. xor = make([]byte, len(one))
  357. for i := 0; i < len(one); i++ {
  358. xor[i] = one[i] ^ other[i]
  359. }
  360. return xor
  361. }
  362. func writeProtocolHandshake(w MsgWriter, our *protoHandshake) error {
  363. return EncodeMsg(w, handshakeMsg, our.Version, our.Name, our.Caps, our.ListenPort, our.ID[:])
  364. }
  365. func readProtocolHandshake(r MsgReader, our *protoHandshake) (*protoHandshake, error) {
  366. // read and handle remote handshake
  367. msg, err := r.ReadMsg()
  368. if err != nil {
  369. return nil, err
  370. }
  371. if msg.Code == discMsg {
  372. // disconnect before protocol handshake is valid according to the
  373. // spec and we send it ourself if Server.addPeer fails.
  374. var reason DiscReason
  375. rlp.Decode(msg.Payload, &reason)
  376. return nil, discRequestedError(reason)
  377. }
  378. if msg.Code != handshakeMsg {
  379. return nil, fmt.Errorf("expected handshake, got %x", msg.Code)
  380. }
  381. if msg.Size > baseProtocolMaxMsgSize {
  382. return nil, fmt.Errorf("message too big (%d > %d)", msg.Size, baseProtocolMaxMsgSize)
  383. }
  384. var hs protoHandshake
  385. if err := msg.Decode(&hs); err != nil {
  386. return nil, err
  387. }
  388. // validate handshake info
  389. if hs.Version != our.Version {
  390. return nil, newPeerError(errP2PVersionMismatch, "required version %d, received %d\n", baseProtocolVersion, hs.Version)
  391. }
  392. if (hs.ID == discover.NodeID{}) {
  393. return nil, newPeerError(errPubkeyInvalid, "missing")
  394. }
  395. return &hs, nil
  396. }