handshake_uniformdh.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. * Copyright (c) 2015, Yawning Angel <yawning at schwanenlied dot me>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * * Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. *
  11. * * Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  19. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  21. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  22. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  23. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  25. * POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. package scramblesuit
  28. import (
  29. "bytes"
  30. "crypto/hmac"
  31. "crypto/sha256"
  32. "errors"
  33. "hash"
  34. "strconv"
  35. "time"
  36. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/lyrebird/common/csrand"
  37. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/lyrebird/common/uniformdh"
  38. )
  39. const (
  40. minHandshakeLength = uniformdh.Size + macLength*2
  41. maxHandshakeLength = 1532
  42. dhMinPadLength = 0
  43. dhMaxPadLength = 1308
  44. macLength = 128 / 8 // HMAC-SHA256-128()
  45. kdfSecretLength = keyLength * 2
  46. )
  47. var (
  48. errMarkNotFoundYet = errors.New("mark not found yet")
  49. // ErrInvalidHandshake is the error returned when the handshake fails.
  50. ErrInvalidHandshake = errors.New("invalid handshake")
  51. )
  52. type ssDHClientHandshake struct {
  53. mac hash.Hash
  54. keypair *uniformdh.PrivateKey
  55. epochHour []byte
  56. padLen int
  57. serverPublicKey *uniformdh.PublicKey
  58. serverMark []byte
  59. }
  60. func (hs *ssDHClientHandshake) generateHandshake() ([]byte, error) {
  61. var buf bytes.Buffer
  62. hs.mac.Reset()
  63. // The client handshake is X | P_C | M_C | MAC(X | P_C | M_C | E)
  64. x, err := hs.keypair.PublicKey.Bytes()
  65. if err != nil {
  66. return nil, err
  67. }
  68. _, _ = hs.mac.Write(x)
  69. mC := hs.mac.Sum(nil)[:macLength]
  70. pC, err := makePad(hs.padLen)
  71. if err != nil {
  72. return nil, err
  73. }
  74. // Write X, P_C, M_C.
  75. buf.Write(x)
  76. buf.Write(pC)
  77. buf.Write(mC)
  78. // Calculate and write the MAC.
  79. hs.epochHour = []byte(strconv.FormatInt(getEpochHour(), 10))
  80. _, _ = hs.mac.Write(pC)
  81. _, _ = hs.mac.Write(mC)
  82. _, _ = hs.mac.Write(hs.epochHour)
  83. buf.Write(hs.mac.Sum(nil)[:macLength])
  84. return buf.Bytes(), nil
  85. }
  86. func (hs *ssDHClientHandshake) parseServerHandshake(resp []byte) (int, []byte, error) {
  87. if len(resp) < minHandshakeLength {
  88. return 0, nil, errMarkNotFoundYet
  89. }
  90. // The server response is Y | P_S | M_S | MAC(Y | P_S | M_S | E).
  91. if hs.serverPublicKey == nil {
  92. y := resp[:uniformdh.Size]
  93. // Pull out the public key, and derive the server mark.
  94. hs.serverPublicKey = &uniformdh.PublicKey{}
  95. if err := hs.serverPublicKey.SetBytes(y); err != nil {
  96. return 0, nil, err
  97. }
  98. hs.mac.Reset()
  99. _, _ = hs.mac.Write(y)
  100. hs.serverMark = hs.mac.Sum(nil)[:macLength]
  101. }
  102. // Find the mark+MAC, if it exits.
  103. endPos := len(resp)
  104. if endPos > maxHandshakeLength-macLength {
  105. endPos = maxHandshakeLength - macLength
  106. }
  107. pos := bytes.Index(resp[uniformdh.Size:endPos], hs.serverMark)
  108. if pos == -1 {
  109. if len(resp) >= maxHandshakeLength {
  110. // Couldn't find the mark in a maximum length response.
  111. return 0, nil, ErrInvalidHandshake
  112. }
  113. return 0, nil, errMarkNotFoundYet
  114. } else if len(resp) < pos+2*macLength {
  115. // Didn't receive the full M_S.
  116. return 0, nil, errMarkNotFoundYet
  117. }
  118. pos += uniformdh.Size
  119. // Validate the MAC.
  120. _, _ = hs.mac.Write(resp[uniformdh.Size : pos+macLength])
  121. _, _ = hs.mac.Write(hs.epochHour)
  122. macCmp := hs.mac.Sum(nil)[:macLength]
  123. macRx := resp[pos+macLength : pos+2*macLength]
  124. if !hmac.Equal(macCmp, macRx) {
  125. return 0, nil, ErrInvalidHandshake
  126. }
  127. // Derive the shared secret.
  128. ss, err := uniformdh.Handshake(hs.keypair, hs.serverPublicKey)
  129. if err != nil {
  130. return 0, nil, err
  131. }
  132. seed := sha256.Sum256(ss)
  133. return pos + 2*macLength, seed[:], nil
  134. }
  135. func newDHClientHandshake(kB *ssSharedSecret, sessionKey *uniformdh.PrivateKey) *ssDHClientHandshake {
  136. hs := &ssDHClientHandshake{keypair: sessionKey}
  137. hs.mac = hmac.New(sha256.New, kB[:])
  138. hs.padLen = csrand.IntRange(dhMinPadLength, dhMaxPadLength)
  139. return hs
  140. }
  141. func getEpochHour() int64 {
  142. return time.Now().Unix() / 3600
  143. }
  144. func makePad(padLen int) ([]byte, error) {
  145. pad := make([]byte, padLen)
  146. if err := csrand.Bytes(pad); err != nil {
  147. return nil, err
  148. }
  149. return pad, nil
  150. }