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.

258 lines
5.9 KiB

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