obfuscator.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * Copyright (c) 2015, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package obfuscator
  20. import (
  21. "bytes"
  22. "crypto/rc4"
  23. "crypto/sha1"
  24. "encoding/binary"
  25. "errors"
  26. "io"
  27. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  28. )
  29. const (
  30. OBFUSCATE_SEED_LENGTH = 16
  31. OBFUSCATE_KEY_LENGTH = 16
  32. OBFUSCATE_HASH_ITERATIONS = 6000
  33. OBFUSCATE_MAX_PADDING = 8192
  34. OBFUSCATE_MAGIC_VALUE = 0x0BF5CA7E
  35. OBFUSCATE_CLIENT_TO_SERVER_IV = "client_to_server"
  36. OBFUSCATE_SERVER_TO_CLIENT_IV = "server_to_client"
  37. )
  38. // Obfuscator implements the seed message, key derivation, and
  39. // stream ciphers for:
  40. // https://github.com/brl/obfuscated-openssh/blob/master/README.obfuscation
  41. type Obfuscator struct {
  42. seedMessage []byte
  43. clientToServerCipher *rc4.Cipher
  44. serverToClientCipher *rc4.Cipher
  45. }
  46. type ObfuscatorConfig struct {
  47. Keyword string
  48. MinPadding *int
  49. MaxPadding *int
  50. }
  51. // NewClientObfuscator creates a new Obfuscator, staging a seed message to be
  52. // sent to the server (by the caller) and initializing stream ciphers to
  53. // obfuscate data.
  54. //
  55. //
  56. func NewClientObfuscator(
  57. config *ObfuscatorConfig) (obfuscator *Obfuscator, err error) {
  58. seed, err := common.MakeSecureRandomBytes(OBFUSCATE_SEED_LENGTH)
  59. if err != nil {
  60. return nil, common.ContextError(err)
  61. }
  62. clientToServerCipher, serverToClientCipher, err := initObfuscatorCiphers(seed, config)
  63. if err != nil {
  64. return nil, common.ContextError(err)
  65. }
  66. minPadding := 0
  67. if config.MinPadding != nil &&
  68. *config.MinPadding >= 0 &&
  69. *config.MinPadding <= OBFUSCATE_MAX_PADDING {
  70. minPadding = *config.MinPadding
  71. }
  72. maxPadding := OBFUSCATE_MAX_PADDING
  73. if config.MaxPadding != nil &&
  74. *config.MaxPadding >= 0 &&
  75. *config.MaxPadding <= OBFUSCATE_MAX_PADDING &&
  76. *config.MaxPadding >= minPadding {
  77. maxPadding = *config.MaxPadding
  78. }
  79. seedMessage, err := makeSeedMessage(minPadding, maxPadding, seed, clientToServerCipher)
  80. if err != nil {
  81. return nil, common.ContextError(err)
  82. }
  83. return &Obfuscator{
  84. seedMessage: seedMessage,
  85. clientToServerCipher: clientToServerCipher,
  86. serverToClientCipher: serverToClientCipher}, nil
  87. }
  88. // NewServerObfuscator creates a new Obfuscator, reading a seed message directly
  89. // from the clientReader and initializing stream ciphers to obfuscate data.
  90. func NewServerObfuscator(
  91. clientReader io.Reader, config *ObfuscatorConfig) (obfuscator *Obfuscator, err error) {
  92. clientToServerCipher, serverToClientCipher, err := readSeedMessage(
  93. clientReader, config)
  94. if err != nil {
  95. return nil, common.ContextError(err)
  96. }
  97. return &Obfuscator{
  98. clientToServerCipher: clientToServerCipher,
  99. serverToClientCipher: serverToClientCipher}, nil
  100. }
  101. // SendSeedMessage returns the seed message created in NewObfuscatorClient,
  102. // removing the reference so that it may be garbage collected.
  103. func (obfuscator *Obfuscator) SendSeedMessage() []byte {
  104. seedMessage := obfuscator.seedMessage
  105. obfuscator.seedMessage = nil
  106. return seedMessage
  107. }
  108. // ObfuscateClientToServer applies the client RC4 stream to the bytes in buffer.
  109. func (obfuscator *Obfuscator) ObfuscateClientToServer(buffer []byte) {
  110. obfuscator.clientToServerCipher.XORKeyStream(buffer, buffer)
  111. }
  112. // ObfuscateServerToClient applies the server RC4 stream to the bytes in buffer.
  113. func (obfuscator *Obfuscator) ObfuscateServerToClient(buffer []byte) {
  114. obfuscator.serverToClientCipher.XORKeyStream(buffer, buffer)
  115. }
  116. func initObfuscatorCiphers(
  117. seed []byte, config *ObfuscatorConfig) (*rc4.Cipher, *rc4.Cipher, error) {
  118. clientToServerKey, err := deriveKey(seed, []byte(config.Keyword), []byte(OBFUSCATE_CLIENT_TO_SERVER_IV))
  119. if err != nil {
  120. return nil, nil, common.ContextError(err)
  121. }
  122. serverToClientKey, err := deriveKey(seed, []byte(config.Keyword), []byte(OBFUSCATE_SERVER_TO_CLIENT_IV))
  123. if err != nil {
  124. return nil, nil, common.ContextError(err)
  125. }
  126. clientToServerCipher, err := rc4.NewCipher(clientToServerKey)
  127. if err != nil {
  128. return nil, nil, common.ContextError(err)
  129. }
  130. serverToClientCipher, err := rc4.NewCipher(serverToClientKey)
  131. if err != nil {
  132. return nil, nil, common.ContextError(err)
  133. }
  134. return clientToServerCipher, serverToClientCipher, nil
  135. }
  136. func deriveKey(seed, keyword, iv []byte) ([]byte, error) {
  137. h := sha1.New()
  138. h.Write(seed)
  139. h.Write(keyword)
  140. h.Write(iv)
  141. digest := h.Sum(nil)
  142. for i := 0; i < OBFUSCATE_HASH_ITERATIONS; i++ {
  143. h.Reset()
  144. h.Write(digest)
  145. digest = h.Sum(nil)
  146. }
  147. if len(digest) < OBFUSCATE_KEY_LENGTH {
  148. return nil, common.ContextError(errors.New("insufficient bytes for obfuscation key"))
  149. }
  150. return digest[0:OBFUSCATE_KEY_LENGTH], nil
  151. }
  152. func makeSeedMessage(minPadding, maxPadding int, seed []byte, clientToServerCipher *rc4.Cipher) ([]byte, error) {
  153. padding, err := common.MakeSecureRandomPadding(minPadding, maxPadding)
  154. if err != nil {
  155. return nil, common.ContextError(err)
  156. }
  157. buffer := new(bytes.Buffer)
  158. err = binary.Write(buffer, binary.BigEndian, seed)
  159. if err != nil {
  160. return nil, common.ContextError(err)
  161. }
  162. err = binary.Write(buffer, binary.BigEndian, uint32(OBFUSCATE_MAGIC_VALUE))
  163. if err != nil {
  164. return nil, common.ContextError(err)
  165. }
  166. err = binary.Write(buffer, binary.BigEndian, uint32(len(padding)))
  167. if err != nil {
  168. return nil, common.ContextError(err)
  169. }
  170. err = binary.Write(buffer, binary.BigEndian, padding)
  171. if err != nil {
  172. return nil, common.ContextError(err)
  173. }
  174. seedMessage := buffer.Bytes()
  175. clientToServerCipher.XORKeyStream(seedMessage[len(seed):], seedMessage[len(seed):])
  176. return seedMessage, nil
  177. }
  178. func readSeedMessage(
  179. clientReader io.Reader, config *ObfuscatorConfig) (*rc4.Cipher, *rc4.Cipher, error) {
  180. seed := make([]byte, OBFUSCATE_SEED_LENGTH)
  181. _, err := io.ReadFull(clientReader, seed)
  182. if err != nil {
  183. return nil, nil, common.ContextError(err)
  184. }
  185. clientToServerCipher, serverToClientCipher, err := initObfuscatorCiphers(seed, config)
  186. if err != nil {
  187. return nil, nil, common.ContextError(err)
  188. }
  189. fixedLengthFields := make([]byte, 8) // 4 bytes each for magic value and padding length
  190. _, err = io.ReadFull(clientReader, fixedLengthFields)
  191. if err != nil {
  192. return nil, nil, common.ContextError(err)
  193. }
  194. clientToServerCipher.XORKeyStream(fixedLengthFields, fixedLengthFields)
  195. buffer := bytes.NewReader(fixedLengthFields)
  196. var magicValue, paddingLength int32
  197. err = binary.Read(buffer, binary.BigEndian, &magicValue)
  198. if err != nil {
  199. return nil, nil, common.ContextError(err)
  200. }
  201. err = binary.Read(buffer, binary.BigEndian, &paddingLength)
  202. if err != nil {
  203. return nil, nil, common.ContextError(err)
  204. }
  205. if magicValue != OBFUSCATE_MAGIC_VALUE {
  206. return nil, nil, common.ContextError(errors.New("invalid magic value"))
  207. }
  208. if paddingLength < 0 || paddingLength > OBFUSCATE_MAX_PADDING {
  209. return nil, nil, common.ContextError(errors.New("invalid padding length"))
  210. }
  211. padding := make([]byte, paddingLength)
  212. _, err = io.ReadFull(clientReader, padding)
  213. if err != nil {
  214. return nil, nil, common.ContextError(err)
  215. }
  216. clientToServerCipher.XORKeyStream(padding, padding)
  217. return clientToServerCipher, serverToClientCipher, nil
  218. }