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.

273 lines
6.4 KiB

  1. package conn
  2. import (
  3. "bytes"
  4. "errors"
  5. "io"
  6. "testing"
  7. gogotypes "github.com/gogo/protobuf/types"
  8. "github.com/gtank/merlin"
  9. "github.com/stretchr/testify/assert"
  10. "golang.org/x/crypto/chacha20poly1305"
  11. "github.com/tendermint/tendermint/crypto"
  12. "github.com/tendermint/tendermint/crypto/ed25519"
  13. cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
  14. "github.com/tendermint/tendermint/internal/libs/protoio"
  15. tmp2p "github.com/tendermint/tendermint/proto/tendermint/p2p"
  16. )
  17. type buffer struct {
  18. next bytes.Buffer
  19. }
  20. func (b *buffer) Read(data []byte) (n int, err error) {
  21. return b.next.Read(data)
  22. }
  23. func (b *buffer) Write(data []byte) (n int, err error) {
  24. return b.next.Write(data)
  25. }
  26. func (b *buffer) Bytes() []byte {
  27. return b.next.Bytes()
  28. }
  29. func (b *buffer) Close() error {
  30. return nil
  31. }
  32. type evilConn struct {
  33. secretConn *SecretConnection
  34. buffer *buffer
  35. locEphPub *[32]byte
  36. locEphPriv *[32]byte
  37. remEphPub *[32]byte
  38. privKey crypto.PrivKey
  39. readStep int
  40. writeStep int
  41. readOffset int
  42. shareEphKey bool
  43. badEphKey bool
  44. shareAuthSignature bool
  45. badAuthSignature bool
  46. }
  47. func newEvilConn(shareEphKey, badEphKey, shareAuthSignature, badAuthSignature bool) *evilConn {
  48. privKey := ed25519.GenPrivKey()
  49. locEphPub, locEphPriv := genEphKeys()
  50. var rep [32]byte
  51. c := &evilConn{
  52. locEphPub: locEphPub,
  53. locEphPriv: locEphPriv,
  54. remEphPub: &rep,
  55. privKey: privKey,
  56. shareEphKey: shareEphKey,
  57. badEphKey: badEphKey,
  58. shareAuthSignature: shareAuthSignature,
  59. badAuthSignature: badAuthSignature,
  60. }
  61. return c
  62. }
  63. func (c *evilConn) Read(data []byte) (n int, err error) {
  64. if !c.shareEphKey {
  65. return 0, io.EOF
  66. }
  67. switch c.readStep {
  68. case 0:
  69. if !c.badEphKey {
  70. lc := *c.locEphPub
  71. bz, err := protoio.MarshalDelimited(&gogotypes.BytesValue{Value: lc[:]})
  72. if err != nil {
  73. panic(err)
  74. }
  75. copy(data, bz[c.readOffset:])
  76. n = len(data)
  77. } else {
  78. bz, err := protoio.MarshalDelimited(&gogotypes.BytesValue{Value: []byte("drop users;")})
  79. if err != nil {
  80. panic(err)
  81. }
  82. copy(data, bz)
  83. n = len(data)
  84. }
  85. c.readOffset += n
  86. if n >= 32 {
  87. c.readOffset = 0
  88. c.readStep = 1
  89. if !c.shareAuthSignature {
  90. c.readStep = 2
  91. }
  92. }
  93. return n, nil
  94. case 1:
  95. signature := c.signChallenge()
  96. if !c.badAuthSignature {
  97. pkpb, err := cryptoenc.PubKeyToProto(c.privKey.PubKey())
  98. if err != nil {
  99. panic(err)
  100. }
  101. bz, err := protoio.MarshalDelimited(&tmp2p.AuthSigMessage{PubKey: pkpb, Sig: signature})
  102. if err != nil {
  103. panic(err)
  104. }
  105. n, err = c.secretConn.Write(bz)
  106. if err != nil {
  107. panic(err)
  108. }
  109. if c.readOffset > len(c.buffer.Bytes()) {
  110. return len(data), nil
  111. }
  112. copy(data, c.buffer.Bytes()[c.readOffset:])
  113. } else {
  114. bz, err := protoio.MarshalDelimited(&gogotypes.BytesValue{Value: []byte("select * from users;")})
  115. if err != nil {
  116. panic(err)
  117. }
  118. n, err = c.secretConn.Write(bz)
  119. if err != nil {
  120. panic(err)
  121. }
  122. if c.readOffset > len(c.buffer.Bytes()) {
  123. return len(data), nil
  124. }
  125. copy(data, c.buffer.Bytes())
  126. }
  127. c.readOffset += len(data)
  128. return n, nil
  129. default:
  130. return 0, io.EOF
  131. }
  132. }
  133. func (c *evilConn) Write(data []byte) (n int, err error) {
  134. switch c.writeStep {
  135. case 0:
  136. var (
  137. bytes gogotypes.BytesValue
  138. remEphPub [32]byte
  139. )
  140. err := protoio.UnmarshalDelimited(data, &bytes)
  141. if err != nil {
  142. panic(err)
  143. }
  144. copy(remEphPub[:], bytes.Value)
  145. c.remEphPub = &remEphPub
  146. c.writeStep = 1
  147. if !c.shareAuthSignature {
  148. c.writeStep = 2
  149. }
  150. return len(data), nil
  151. case 1:
  152. // Signature is not needed, therefore skipped.
  153. return len(data), nil
  154. default:
  155. return 0, io.EOF
  156. }
  157. }
  158. func (c *evilConn) Close() error {
  159. return nil
  160. }
  161. func (c *evilConn) signChallenge() []byte {
  162. // Sort by lexical order.
  163. loEphPub, hiEphPub := sort32(c.locEphPub, c.remEphPub)
  164. transcript := merlin.NewTranscript("TENDERMINT_SECRET_CONNECTION_TRANSCRIPT_HASH")
  165. transcript.AppendMessage(labelEphemeralLowerPublicKey, loEphPub[:])
  166. transcript.AppendMessage(labelEphemeralUpperPublicKey, hiEphPub[:])
  167. // Check if the local ephemeral public key was the least, lexicographically
  168. // sorted.
  169. locIsLeast := bytes.Equal(c.locEphPub[:], loEphPub[:])
  170. // Compute common diffie hellman secret using X25519.
  171. dhSecret, err := computeDHSecret(c.remEphPub, c.locEphPriv)
  172. if err != nil {
  173. panic(err)
  174. }
  175. transcript.AppendMessage(labelDHSecret, dhSecret[:])
  176. // Generate the secret used for receiving, sending, challenge via HKDF-SHA2
  177. // on the transcript state (which itself also uses HKDF-SHA2 to derive a key
  178. // from the dhSecret).
  179. recvSecret, sendSecret := deriveSecrets(dhSecret, locIsLeast)
  180. const challengeSize = 32
  181. var challenge [challengeSize]byte
  182. challengeSlice := transcript.ExtractBytes(labelSecretConnectionMac, challengeSize)
  183. copy(challenge[:], challengeSlice[0:challengeSize])
  184. sendAead, err := chacha20poly1305.New(sendSecret[:])
  185. if err != nil {
  186. panic(errors.New("invalid send SecretConnection Key"))
  187. }
  188. recvAead, err := chacha20poly1305.New(recvSecret[:])
  189. if err != nil {
  190. panic(errors.New("invalid receive SecretConnection Key"))
  191. }
  192. b := &buffer{}
  193. c.secretConn = &SecretConnection{
  194. conn: b,
  195. recvBuffer: nil,
  196. recvNonce: new([aeadNonceSize]byte),
  197. sendNonce: new([aeadNonceSize]byte),
  198. recvAead: recvAead,
  199. sendAead: sendAead,
  200. }
  201. c.buffer = b
  202. // Sign the challenge bytes for authentication.
  203. locSignature, err := signChallenge(&challenge, c.privKey)
  204. if err != nil {
  205. panic(err)
  206. }
  207. return locSignature
  208. }
  209. // TestMakeSecretConnection creates an evil connection and tests that
  210. // MakeSecretConnection errors at different stages.
  211. func TestMakeSecretConnection(t *testing.T) {
  212. testCases := []struct {
  213. name string
  214. conn *evilConn
  215. errMsg string
  216. }{
  217. {"refuse to share ethimeral key", newEvilConn(false, false, false, false), "EOF"},
  218. {"share bad ethimeral key", newEvilConn(true, true, false, false), "wrong wireType"},
  219. {"refuse to share auth signature", newEvilConn(true, false, false, false), "EOF"},
  220. {"share bad auth signature", newEvilConn(true, false, true, true), "failed to decrypt SecretConnection"},
  221. {"all good", newEvilConn(true, false, true, false), ""},
  222. }
  223. for _, tc := range testCases {
  224. tc := tc
  225. t.Run(tc.name, func(t *testing.T) {
  226. privKey := ed25519.GenPrivKey()
  227. _, err := MakeSecretConnection(tc.conn, privKey)
  228. if tc.errMsg != "" {
  229. if assert.Error(t, err) {
  230. assert.Contains(t, err.Error(), tc.errMsg)
  231. }
  232. } else {
  233. assert.NoError(t, err)
  234. }
  235. })
  236. }
  237. }