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.

469 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. // nolint
  241. func (sc *SecretConnection) Close() error { return sc.conn.Close() }
  242. func (sc *SecretConnection) LocalAddr() net.Addr { return sc.conn.(net.Conn).LocalAddr() }
  243. func (sc *SecretConnection) RemoteAddr() net.Addr { return sc.conn.(net.Conn).RemoteAddr() }
  244. func (sc *SecretConnection) SetDeadline(t time.Time) error { return sc.conn.(net.Conn).SetDeadline(t) }
  245. func (sc *SecretConnection) SetReadDeadline(t time.Time) error {
  246. return sc.conn.(net.Conn).SetReadDeadline(t)
  247. }
  248. func (sc *SecretConnection) SetWriteDeadline(t time.Time) error {
  249. return sc.conn.(net.Conn).SetWriteDeadline(t)
  250. }
  251. func genEphKeys() (ephPub, ephPriv *[32]byte) {
  252. var err error
  253. // TODO: Probably not a problem but ask Tony: different from the rust implementation (uses x25519-dalek),
  254. // we do not "clamp" the private key scalar:
  255. // see: https://github.com/dalek-cryptography/x25519-dalek/blob/34676d336049df2bba763cc076a75e47ae1f170f/src/x25519.rs#L56-L74
  256. ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
  257. if err != nil {
  258. panic("Could not generate ephemeral key-pair")
  259. }
  260. return
  261. }
  262. func shareEphPubKey(conn io.ReadWriter, locEphPub *[32]byte) (remEphPub *[32]byte, err error) {
  263. // Send our pubkey and receive theirs in tandem.
  264. var trs, _ = async.Parallel(
  265. func(_ int) (val interface{}, abort bool, err error) {
  266. lc := *locEphPub
  267. _, err = protoio.NewDelimitedWriter(conn).WriteMsg(&gogotypes.BytesValue{Value: lc[:]})
  268. if err != nil {
  269. return nil, true, err // abort
  270. }
  271. return nil, false, nil
  272. },
  273. func(_ int) (val interface{}, abort bool, err error) {
  274. var bytes gogotypes.BytesValue
  275. err = protoio.NewDelimitedReader(conn, 1024*1024).ReadMsg(&bytes)
  276. if err != nil {
  277. return nil, true, err // abort
  278. }
  279. var _remEphPub [32]byte
  280. copy(_remEphPub[:], bytes.Value)
  281. return _remEphPub, false, nil
  282. },
  283. )
  284. // If error:
  285. if trs.FirstError() != nil {
  286. err = trs.FirstError()
  287. return
  288. }
  289. // Otherwise:
  290. var _remEphPub = trs.FirstValue().([32]byte)
  291. return &_remEphPub, nil
  292. }
  293. func deriveSecrets(
  294. dhSecret *[32]byte,
  295. locIsLeast bool,
  296. ) (recvSecret, sendSecret *[aeadKeySize]byte) {
  297. hash := sha256.New
  298. hkdf := hkdf.New(hash, dhSecret[:], nil, secretConnKeyAndChallengeGen)
  299. // get enough data for 2 aead keys, and a 32 byte challenge
  300. res := new([2*aeadKeySize + 32]byte)
  301. _, err := io.ReadFull(hkdf, res[:])
  302. if err != nil {
  303. panic(err)
  304. }
  305. recvSecret = new([aeadKeySize]byte)
  306. sendSecret = new([aeadKeySize]byte)
  307. // bytes 0 through aeadKeySize - 1 are one aead key.
  308. // bytes aeadKeySize through 2*aeadKeySize -1 are another aead key.
  309. // which key corresponds to sending and receiving key depends on whether
  310. // the local key is less than the remote key.
  311. if locIsLeast {
  312. copy(recvSecret[:], res[0:aeadKeySize])
  313. copy(sendSecret[:], res[aeadKeySize:aeadKeySize*2])
  314. } else {
  315. copy(sendSecret[:], res[0:aeadKeySize])
  316. copy(recvSecret[:], res[aeadKeySize:aeadKeySize*2])
  317. }
  318. return
  319. }
  320. // computeDHSecret computes a Diffie-Hellman shared secret key
  321. // from our own local private key and the other's public key.
  322. func computeDHSecret(remPubKey, locPrivKey *[32]byte) (*[32]byte, error) {
  323. shrKey, err := curve25519.X25519(locPrivKey[:], remPubKey[:])
  324. if err != nil {
  325. return nil, err
  326. }
  327. var shrKeyArray [32]byte
  328. copy(shrKeyArray[:], shrKey)
  329. return &shrKeyArray, nil
  330. }
  331. func sort32(foo, bar *[32]byte) (lo, hi *[32]byte) {
  332. if bytes.Compare(foo[:], bar[:]) < 0 {
  333. lo = foo
  334. hi = bar
  335. } else {
  336. lo = bar
  337. hi = foo
  338. }
  339. return
  340. }
  341. func signChallenge(challenge *[32]byte, locPrivKey crypto.PrivKey) ([]byte, error) {
  342. signature, err := locPrivKey.Sign(challenge[:])
  343. if err != nil {
  344. return nil, err
  345. }
  346. return signature, nil
  347. }
  348. type authSigMessage struct {
  349. Key crypto.PubKey
  350. Sig []byte
  351. }
  352. func shareAuthSignature(sc io.ReadWriter, pubKey crypto.PubKey, signature []byte) (recvMsg authSigMessage, err error) {
  353. // Send our info and receive theirs in tandem.
  354. var trs, _ = async.Parallel(
  355. func(_ int) (val interface{}, abort bool, err error) {
  356. pbpk, err := cryptoenc.PubKeyToProto(pubKey)
  357. if err != nil {
  358. return nil, true, err
  359. }
  360. _, err = protoio.NewDelimitedWriter(sc).WriteMsg(&tmprivval.AuthSigMessage{PubKey: pbpk, Sig: signature})
  361. if err != nil {
  362. return nil, true, err // abort
  363. }
  364. return nil, false, nil
  365. },
  366. func(_ int) (val interface{}, abort bool, err error) {
  367. var pba tmprivval.AuthSigMessage
  368. err = protoio.NewDelimitedReader(sc, 1024*1024).ReadMsg(&pba)
  369. if err != nil {
  370. return nil, true, err // abort
  371. }
  372. pk, err := cryptoenc.PubKeyFromProto(pba.PubKey)
  373. if err != nil {
  374. return nil, true, err // abort
  375. }
  376. _recvMsg := authSigMessage{
  377. Key: pk,
  378. Sig: pba.Sig,
  379. }
  380. return _recvMsg, false, nil
  381. },
  382. )
  383. // If error:
  384. if trs.FirstError() != nil {
  385. err = trs.FirstError()
  386. return
  387. }
  388. var _recvMsg = trs.FirstValue().(authSigMessage)
  389. return _recvMsg, nil
  390. }
  391. //--------------------------------------------------------------------------------
  392. // Increment nonce little-endian by 1 with wraparound.
  393. // Due to chacha20poly1305 expecting a 12 byte nonce we do not use the first four
  394. // bytes. We only increment a 64 bit unsigned int in the remaining 8 bytes
  395. // (little-endian in nonce[4:]).
  396. func incrNonce(nonce *[aeadNonceSize]byte) {
  397. counter := binary.LittleEndian.Uint64(nonce[4:])
  398. if counter == math.MaxUint64 {
  399. // Terminates the session and makes sure the nonce would not re-used.
  400. // See https://github.com/tendermint/tendermint/issues/3531
  401. panic("can't increase nonce without overflow")
  402. }
  403. counter++
  404. binary.LittleEndian.PutUint64(nonce[4:], counter)
  405. }