You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

468 lines
14 KiB

  1. package privval
  2. import (
  3. "bytes"
  4. "crypto/cipher"
  5. crand "crypto/rand"
  6. "crypto/sha256"
  7. "encoding/binary"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "math"
  12. "net"
  13. "time"
  14. gogotypes "github.com/gogo/protobuf/types"
  15. "github.com/gtank/merlin"
  16. pool "github.com/libp2p/go-buffer-pool"
  17. "golang.org/x/crypto/chacha20poly1305"
  18. "golang.org/x/crypto/curve25519"
  19. "golang.org/x/crypto/hkdf"
  20. "golang.org/x/crypto/nacl/box"
  21. "github.com/tendermint/tendermint/crypto"
  22. "github.com/tendermint/tendermint/crypto/ed25519"
  23. cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
  24. "github.com/tendermint/tendermint/libs/async"
  25. "github.com/tendermint/tendermint/libs/protoio"
  26. tmsync "github.com/tendermint/tendermint/libs/sync"
  27. tmprivval "github.com/tendermint/tendermint/proto/tendermint/privval"
  28. )
  29. // This code has been duplicated from p2p/conn prior to the P2P refactor.
  30. // It is left here temporarily until we migrate privval to gRPC.
  31. // https://github.com/tendermint/tendermint/issues/4698
  32. // 4 + 1024 == 1028 total frame size
  33. const (
  34. dataLenSize = 4
  35. dataMaxSize = 1024
  36. totalFrameSize = dataMaxSize + dataLenSize
  37. aeadSizeOverhead = 16 // overhead of poly 1305 authentication tag
  38. aeadKeySize = chacha20poly1305.KeySize
  39. aeadNonceSize = chacha20poly1305.NonceSize
  40. )
  41. var (
  42. ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer")
  43. labelEphemeralLowerPublicKey = []byte("EPHEMERAL_LOWER_PUBLIC_KEY")
  44. labelEphemeralUpperPublicKey = []byte("EPHEMERAL_UPPER_PUBLIC_KEY")
  45. labelDHSecret = []byte("DH_SECRET")
  46. labelSecretConnectionMac = []byte("SECRET_CONNECTION_MAC")
  47. secretConnKeyAndChallengeGen = []byte("TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN")
  48. )
  49. // SecretConnection implements net.Conn.
  50. // It is an implementation of the STS protocol.
  51. // See https://github.com/tendermint/tendermint/blob/0.1/docs/sts-final.pdf for
  52. // details on the protocol.
  53. //
  54. // Consumers of the SecretConnection are responsible for authenticating
  55. // the remote peer's pubkey against known information, like a nodeID.
  56. // Otherwise they are vulnerable to MITM.
  57. // (TODO(ismail): see also https://github.com/tendermint/tendermint/issues/3010)
  58. type SecretConnection struct {
  59. // immutable
  60. recvAead cipher.AEAD
  61. sendAead cipher.AEAD
  62. remPubKey crypto.PubKey
  63. conn io.ReadWriteCloser
  64. // net.Conn must be thread safe:
  65. // https://golang.org/pkg/net/#Conn.
  66. // Since we have internal mutable state,
  67. // we need mtxs. But recv and send states
  68. // are independent, so we can use two mtxs.
  69. // All .Read are covered by recvMtx,
  70. // all .Write are covered by sendMtx.
  71. recvMtx tmsync.Mutex
  72. recvBuffer []byte
  73. recvNonce *[aeadNonceSize]byte
  74. sendMtx tmsync.Mutex
  75. sendNonce *[aeadNonceSize]byte
  76. }
  77. // MakeSecretConnection performs handshake and returns a new authenticated
  78. // SecretConnection.
  79. // Returns nil if there is an error in handshake.
  80. // Caller should call conn.Close()
  81. // See docs/sts-final.pdf for more information.
  82. func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (*SecretConnection, error) {
  83. var (
  84. locPubKey = locPrivKey.PubKey()
  85. )
  86. // Generate ephemeral keys for perfect forward secrecy.
  87. locEphPub, locEphPriv := genEphKeys()
  88. // Write local ephemeral pubkey and receive one too.
  89. // NOTE: every 32-byte string is accepted as a Curve25519 public key (see
  90. // DJB's Curve25519 paper: http://cr.yp.to/ecdh/curve25519-20060209.pdf)
  91. remEphPub, err := shareEphPubKey(conn, locEphPub)
  92. if err != nil {
  93. return nil, err
  94. }
  95. // Sort by lexical order.
  96. loEphPub, hiEphPub := sort32(locEphPub, remEphPub)
  97. transcript := merlin.NewTranscript("TENDERMINT_SECRET_CONNECTION_TRANSCRIPT_HASH")
  98. transcript.AppendMessage(labelEphemeralLowerPublicKey, loEphPub[:])
  99. transcript.AppendMessage(labelEphemeralUpperPublicKey, hiEphPub[:])
  100. // Check if the local ephemeral public key was the least, lexicographically
  101. // sorted.
  102. locIsLeast := bytes.Equal(locEphPub[:], loEphPub[:])
  103. // Compute common diffie hellman secret using X25519.
  104. dhSecret, err := computeDHSecret(remEphPub, locEphPriv)
  105. if err != nil {
  106. return nil, err
  107. }
  108. transcript.AppendMessage(labelDHSecret, dhSecret[:])
  109. // Generate the secret used for receiving, sending, challenge via HKDF-SHA2
  110. // on the transcript state (which itself also uses HKDF-SHA2 to derive a key
  111. // from the dhSecret).
  112. recvSecret, sendSecret := deriveSecrets(dhSecret, locIsLeast)
  113. const challengeSize = 32
  114. var challenge [challengeSize]byte
  115. challengeSlice := transcript.ExtractBytes(labelSecretConnectionMac, challengeSize)
  116. copy(challenge[:], challengeSlice[0:challengeSize])
  117. sendAead, err := chacha20poly1305.New(sendSecret[:])
  118. if err != nil {
  119. return nil, errors.New("invalid send SecretConnection Key")
  120. }
  121. recvAead, err := chacha20poly1305.New(recvSecret[:])
  122. if err != nil {
  123. return nil, errors.New("invalid receive SecretConnection Key")
  124. }
  125. sc := &SecretConnection{
  126. conn: conn,
  127. recvBuffer: nil,
  128. recvNonce: new([aeadNonceSize]byte),
  129. sendNonce: new([aeadNonceSize]byte),
  130. recvAead: recvAead,
  131. sendAead: sendAead,
  132. }
  133. // Sign the challenge bytes for authentication.
  134. locSignature, err := signChallenge(&challenge, locPrivKey)
  135. if err != nil {
  136. return nil, err
  137. }
  138. // Share (in secret) each other's pubkey & challenge signature
  139. authSigMsg, err := shareAuthSignature(sc, locPubKey, locSignature)
  140. if err != nil {
  141. return nil, err
  142. }
  143. remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig
  144. if _, ok := remPubKey.(ed25519.PubKey); !ok {
  145. return nil, fmt.Errorf("expected ed25519 pubkey, got %T", remPubKey)
  146. }
  147. if !remPubKey.VerifySignature(challenge[:], remSignature) {
  148. return nil, errors.New("challenge verification failed")
  149. }
  150. // We've authorized.
  151. sc.remPubKey = remPubKey
  152. return sc, nil
  153. }
  154. // RemotePubKey returns authenticated remote pubkey
  155. func (sc *SecretConnection) RemotePubKey() crypto.PubKey {
  156. return sc.remPubKey
  157. }
  158. // Writes encrypted frames of `totalFrameSize + aeadSizeOverhead`.
  159. // CONTRACT: data smaller than dataMaxSize is written atomically.
  160. func (sc *SecretConnection) Write(data []byte) (n int, err error) {
  161. sc.sendMtx.Lock()
  162. defer sc.sendMtx.Unlock()
  163. for 0 < len(data) {
  164. if err := func() error {
  165. var sealedFrame = pool.Get(aeadSizeOverhead + totalFrameSize)
  166. var frame = pool.Get(totalFrameSize)
  167. defer func() {
  168. pool.Put(sealedFrame)
  169. pool.Put(frame)
  170. }()
  171. var chunk []byte
  172. if dataMaxSize < len(data) {
  173. chunk = data[:dataMaxSize]
  174. data = data[dataMaxSize:]
  175. } else {
  176. chunk = data
  177. data = nil
  178. }
  179. chunkLength := len(chunk)
  180. binary.LittleEndian.PutUint32(frame, uint32(chunkLength))
  181. copy(frame[dataLenSize:], chunk)
  182. // encrypt the frame
  183. sc.sendAead.Seal(sealedFrame[:0], sc.sendNonce[:], frame, nil)
  184. incrNonce(sc.sendNonce)
  185. // end encryption
  186. _, err = sc.conn.Write(sealedFrame)
  187. if err != nil {
  188. return err
  189. }
  190. n += len(chunk)
  191. return nil
  192. }(); err != nil {
  193. return n, err
  194. }
  195. }
  196. return n, err
  197. }
  198. // CONTRACT: data smaller than dataMaxSize is read atomically.
  199. func (sc *SecretConnection) Read(data []byte) (n int, err error) {
  200. sc.recvMtx.Lock()
  201. defer sc.recvMtx.Unlock()
  202. // read off and update the recvBuffer, if non-empty
  203. if 0 < len(sc.recvBuffer) {
  204. n = copy(data, sc.recvBuffer)
  205. sc.recvBuffer = sc.recvBuffer[n:]
  206. return
  207. }
  208. // read off the conn
  209. var sealedFrame = pool.Get(aeadSizeOverhead + totalFrameSize)
  210. defer pool.Put(sealedFrame)
  211. _, err = io.ReadFull(sc.conn, sealedFrame)
  212. if err != nil {
  213. return
  214. }
  215. // decrypt the frame.
  216. // reads and updates the sc.recvNonce
  217. var frame = pool.Get(totalFrameSize)
  218. defer pool.Put(frame)
  219. _, err = sc.recvAead.Open(frame[:0], sc.recvNonce[:], sealedFrame, nil)
  220. if err != nil {
  221. return n, fmt.Errorf("failed to decrypt SecretConnection: %w", err)
  222. }
  223. incrNonce(sc.recvNonce)
  224. // end decryption
  225. // copy checkLength worth into data,
  226. // set recvBuffer to the rest.
  227. var chunkLength = binary.LittleEndian.Uint32(frame) // read the first four bytes
  228. if chunkLength > dataMaxSize {
  229. return 0, errors.New("chunkLength is greater than dataMaxSize")
  230. }
  231. var chunk = frame[dataLenSize : dataLenSize+chunkLength]
  232. n = copy(data, chunk)
  233. if n < len(chunk) {
  234. sc.recvBuffer = make([]byte, len(chunk)-n)
  235. copy(sc.recvBuffer, chunk[n:])
  236. }
  237. return n, err
  238. }
  239. // Implements net.Conn
  240. func (sc *SecretConnection) Close() error { return sc.conn.Close() }
  241. func (sc *SecretConnection) LocalAddr() net.Addr { return sc.conn.(net.Conn).LocalAddr() }
  242. func (sc *SecretConnection) RemoteAddr() net.Addr { return sc.conn.(net.Conn).RemoteAddr() }
  243. func (sc *SecretConnection) SetDeadline(t time.Time) error { return sc.conn.(net.Conn).SetDeadline(t) }
  244. func (sc *SecretConnection) SetReadDeadline(t time.Time) error {
  245. return sc.conn.(net.Conn).SetReadDeadline(t)
  246. }
  247. func (sc *SecretConnection) SetWriteDeadline(t time.Time) error {
  248. return sc.conn.(net.Conn).SetWriteDeadline(t)
  249. }
  250. func genEphKeys() (ephPub, ephPriv *[32]byte) {
  251. var err error
  252. // TODO: Probably not a problem but ask Tony: different from the rust implementation (uses x25519-dalek),
  253. // we do not "clamp" the private key scalar:
  254. // see: https://github.com/dalek-cryptography/x25519-dalek/blob/34676d336049df2bba763cc076a75e47ae1f170f/src/x25519.rs#L56-L74
  255. ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
  256. if err != nil {
  257. panic("Could not generate ephemeral key-pair")
  258. }
  259. return
  260. }
  261. func shareEphPubKey(conn io.ReadWriter, locEphPub *[32]byte) (remEphPub *[32]byte, err error) {
  262. // Send our pubkey and receive theirs in tandem.
  263. var trs, _ = async.Parallel(
  264. func(_ int) (val interface{}, abort bool, err error) {
  265. lc := *locEphPub
  266. _, err = protoio.NewDelimitedWriter(conn).WriteMsg(&gogotypes.BytesValue{Value: lc[:]})
  267. if err != nil {
  268. return nil, true, err // abort
  269. }
  270. return nil, false, nil
  271. },
  272. func(_ int) (val interface{}, abort bool, err error) {
  273. var bytes gogotypes.BytesValue
  274. _, err = protoio.NewDelimitedReader(conn, 1024*1024).ReadMsg(&bytes)
  275. if err != nil {
  276. return nil, true, err // abort
  277. }
  278. var _remEphPub [32]byte
  279. copy(_remEphPub[:], bytes.Value)
  280. return _remEphPub, false, nil
  281. },
  282. )
  283. // If error:
  284. if trs.FirstError() != nil {
  285. err = trs.FirstError()
  286. return
  287. }
  288. // Otherwise:
  289. var _remEphPub = trs.FirstValue().([32]byte)
  290. return &_remEphPub, nil
  291. }
  292. func deriveSecrets(
  293. dhSecret *[32]byte,
  294. locIsLeast bool,
  295. ) (recvSecret, sendSecret *[aeadKeySize]byte) {
  296. hash := sha256.New
  297. hkdf := hkdf.New(hash, dhSecret[:], nil, secretConnKeyAndChallengeGen)
  298. // get enough data for 2 aead keys, and a 32 byte challenge
  299. res := new([2*aeadKeySize + 32]byte)
  300. _, err := io.ReadFull(hkdf, res[:])
  301. if err != nil {
  302. panic(err)
  303. }
  304. recvSecret = new([aeadKeySize]byte)
  305. sendSecret = new([aeadKeySize]byte)
  306. // bytes 0 through aeadKeySize - 1 are one aead key.
  307. // bytes aeadKeySize through 2*aeadKeySize -1 are another aead key.
  308. // which key corresponds to sending and receiving key depends on whether
  309. // the local key is less than the remote key.
  310. if locIsLeast {
  311. copy(recvSecret[:], res[0:aeadKeySize])
  312. copy(sendSecret[:], res[aeadKeySize:aeadKeySize*2])
  313. } else {
  314. copy(sendSecret[:], res[0:aeadKeySize])
  315. copy(recvSecret[:], res[aeadKeySize:aeadKeySize*2])
  316. }
  317. return
  318. }
  319. // computeDHSecret computes a Diffie-Hellman shared secret key
  320. // from our own local private key and the other's public key.
  321. func computeDHSecret(remPubKey, locPrivKey *[32]byte) (*[32]byte, error) {
  322. shrKey, err := curve25519.X25519(locPrivKey[:], remPubKey[:])
  323. if err != nil {
  324. return nil, err
  325. }
  326. var shrKeyArray [32]byte
  327. copy(shrKeyArray[:], shrKey)
  328. return &shrKeyArray, nil
  329. }
  330. func sort32(foo, bar *[32]byte) (lo, hi *[32]byte) {
  331. if bytes.Compare(foo[:], bar[:]) < 0 {
  332. lo = foo
  333. hi = bar
  334. } else {
  335. lo = bar
  336. hi = foo
  337. }
  338. return
  339. }
  340. func signChallenge(challenge *[32]byte, locPrivKey crypto.PrivKey) ([]byte, error) {
  341. signature, err := locPrivKey.Sign(challenge[:])
  342. if err != nil {
  343. return nil, err
  344. }
  345. return signature, nil
  346. }
  347. type authSigMessage struct {
  348. Key crypto.PubKey
  349. Sig []byte
  350. }
  351. func shareAuthSignature(sc io.ReadWriter, pubKey crypto.PubKey, signature []byte) (recvMsg authSigMessage, err error) {
  352. // Send our info and receive theirs in tandem.
  353. var trs, _ = async.Parallel(
  354. func(_ int) (val interface{}, abort bool, err error) {
  355. pbpk, err := cryptoenc.PubKeyToProto(pubKey)
  356. if err != nil {
  357. return nil, true, err
  358. }
  359. _, err = protoio.NewDelimitedWriter(sc).WriteMsg(&tmprivval.AuthSigMessage{PubKey: pbpk, Sig: signature})
  360. if err != nil {
  361. return nil, true, err // abort
  362. }
  363. return nil, false, nil
  364. },
  365. func(_ int) (val interface{}, abort bool, err error) {
  366. var pba tmprivval.AuthSigMessage
  367. _, err = protoio.NewDelimitedReader(sc, 1024*1024).ReadMsg(&pba)
  368. if err != nil {
  369. return nil, true, err // abort
  370. }
  371. pk, err := cryptoenc.PubKeyFromProto(pba.PubKey)
  372. if err != nil {
  373. return nil, true, err // abort
  374. }
  375. _recvMsg := authSigMessage{
  376. Key: pk,
  377. Sig: pba.Sig,
  378. }
  379. return _recvMsg, false, nil
  380. },
  381. )
  382. // If error:
  383. if trs.FirstError() != nil {
  384. err = trs.FirstError()
  385. return
  386. }
  387. var _recvMsg = trs.FirstValue().(authSigMessage)
  388. return _recvMsg, nil
  389. }
  390. //--------------------------------------------------------------------------------
  391. // Increment nonce little-endian by 1 with wraparound.
  392. // Due to chacha20poly1305 expecting a 12 byte nonce we do not use the first four
  393. // bytes. We only increment a 64 bit unsigned int in the remaining 8 bytes
  394. // (little-endian in nonce[4:]).
  395. func incrNonce(nonce *[aeadNonceSize]byte) {
  396. counter := binary.LittleEndian.Uint64(nonce[4:])
  397. if counter == math.MaxUint64 {
  398. // Terminates the session and makes sure the nonce would not re-used.
  399. // See https://github.com/tendermint/tendermint/issues/3531
  400. panic("can't increase nonce without overflow")
  401. }
  402. counter++
  403. binary.LittleEndian.PutUint64(nonce[4:], counter)
  404. }