obfuscator.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. paddingLength int
  44. clientToServerCipher *rc4.Cipher
  45. serverToClientCipher *rc4.Cipher
  46. }
  47. type ObfuscatorConfig struct {
  48. Keyword string
  49. MinPadding *int
  50. MaxPadding *int
  51. }
  52. // NewClientObfuscator creates a new Obfuscator, staging a seed message to be
  53. // sent to the server (by the caller) and initializing stream ciphers to
  54. // obfuscate data.
  55. //
  56. //
  57. func NewClientObfuscator(
  58. config *ObfuscatorConfig) (obfuscator *Obfuscator, err error) {
  59. seed, err := common.MakeSecureRandomBytes(OBFUSCATE_SEED_LENGTH)
  60. if err != nil {
  61. return nil, common.ContextError(err)
  62. }
  63. clientToServerCipher, serverToClientCipher, err := initObfuscatorCiphers(seed, config)
  64. if err != nil {
  65. return nil, common.ContextError(err)
  66. }
  67. minPadding := 0
  68. if config.MinPadding != nil &&
  69. *config.MinPadding >= 0 &&
  70. *config.MinPadding <= OBFUSCATE_MAX_PADDING {
  71. minPadding = *config.MinPadding
  72. }
  73. maxPadding := OBFUSCATE_MAX_PADDING
  74. if config.MaxPadding != nil &&
  75. *config.MaxPadding >= 0 &&
  76. *config.MaxPadding <= OBFUSCATE_MAX_PADDING &&
  77. *config.MaxPadding >= minPadding {
  78. maxPadding = *config.MaxPadding
  79. }
  80. seedMessage, paddingLength, err := makeSeedMessage(minPadding, maxPadding, seed, clientToServerCipher)
  81. if err != nil {
  82. return nil, common.ContextError(err)
  83. }
  84. return &Obfuscator{
  85. seedMessage: seedMessage,
  86. paddingLength: paddingLength,
  87. clientToServerCipher: clientToServerCipher,
  88. serverToClientCipher: serverToClientCipher}, nil
  89. }
  90. // NewServerObfuscator creates a new Obfuscator, reading a seed message directly
  91. // from the clientReader and initializing stream ciphers to obfuscate data.
  92. func NewServerObfuscator(
  93. clientReader io.Reader, config *ObfuscatorConfig) (obfuscator *Obfuscator, err error) {
  94. clientToServerCipher, serverToClientCipher, err := readSeedMessage(
  95. clientReader, config)
  96. if err != nil {
  97. return nil, common.ContextError(err)
  98. }
  99. return &Obfuscator{
  100. paddingLength: -1,
  101. clientToServerCipher: clientToServerCipher,
  102. serverToClientCipher: serverToClientCipher}, nil
  103. }
  104. // GetPaddingLength returns the client seed message padding length. Only valid
  105. // for NewClientObfuscator.
  106. func (obfuscator *Obfuscator) GetPaddingLength() int {
  107. return obfuscator.paddingLength
  108. }
  109. // SendSeedMessage returns the seed message created in NewObfuscatorClient,
  110. // removing the reference so that it may be garbage collected.
  111. func (obfuscator *Obfuscator) SendSeedMessage() []byte {
  112. seedMessage := obfuscator.seedMessage
  113. obfuscator.seedMessage = nil
  114. return seedMessage
  115. }
  116. // ObfuscateClientToServer applies the client RC4 stream to the bytes in buffer.
  117. func (obfuscator *Obfuscator) ObfuscateClientToServer(buffer []byte) {
  118. obfuscator.clientToServerCipher.XORKeyStream(buffer, buffer)
  119. }
  120. // ObfuscateServerToClient applies the server RC4 stream to the bytes in buffer.
  121. func (obfuscator *Obfuscator) ObfuscateServerToClient(buffer []byte) {
  122. obfuscator.serverToClientCipher.XORKeyStream(buffer, buffer)
  123. }
  124. func initObfuscatorCiphers(
  125. seed []byte, config *ObfuscatorConfig) (*rc4.Cipher, *rc4.Cipher, error) {
  126. clientToServerKey, err := deriveKey(seed, []byte(config.Keyword), []byte(OBFUSCATE_CLIENT_TO_SERVER_IV))
  127. if err != nil {
  128. return nil, nil, common.ContextError(err)
  129. }
  130. serverToClientKey, err := deriveKey(seed, []byte(config.Keyword), []byte(OBFUSCATE_SERVER_TO_CLIENT_IV))
  131. if err != nil {
  132. return nil, nil, common.ContextError(err)
  133. }
  134. clientToServerCipher, err := rc4.NewCipher(clientToServerKey)
  135. if err != nil {
  136. return nil, nil, common.ContextError(err)
  137. }
  138. serverToClientCipher, err := rc4.NewCipher(serverToClientKey)
  139. if err != nil {
  140. return nil, nil, common.ContextError(err)
  141. }
  142. return clientToServerCipher, serverToClientCipher, nil
  143. }
  144. func deriveKey(seed, keyword, iv []byte) ([]byte, error) {
  145. h := sha1.New()
  146. h.Write(seed)
  147. h.Write(keyword)
  148. h.Write(iv)
  149. digest := h.Sum(nil)
  150. for i := 0; i < OBFUSCATE_HASH_ITERATIONS; i++ {
  151. h.Reset()
  152. h.Write(digest)
  153. digest = h.Sum(nil)
  154. }
  155. if len(digest) < OBFUSCATE_KEY_LENGTH {
  156. return nil, common.ContextError(errors.New("insufficient bytes for obfuscation key"))
  157. }
  158. return digest[0:OBFUSCATE_KEY_LENGTH], nil
  159. }
  160. func makeSeedMessage(minPadding, maxPadding int, seed []byte, clientToServerCipher *rc4.Cipher) ([]byte, int, error) {
  161. padding, err := common.MakeSecureRandomPadding(minPadding, maxPadding)
  162. if err != nil {
  163. return nil, 0, common.ContextError(err)
  164. }
  165. buffer := new(bytes.Buffer)
  166. err = binary.Write(buffer, binary.BigEndian, seed)
  167. if err != nil {
  168. return nil, 0, common.ContextError(err)
  169. }
  170. err = binary.Write(buffer, binary.BigEndian, uint32(OBFUSCATE_MAGIC_VALUE))
  171. if err != nil {
  172. return nil, 0, common.ContextError(err)
  173. }
  174. err = binary.Write(buffer, binary.BigEndian, uint32(len(padding)))
  175. if err != nil {
  176. return nil, 0, common.ContextError(err)
  177. }
  178. err = binary.Write(buffer, binary.BigEndian, padding)
  179. if err != nil {
  180. return nil, 0, common.ContextError(err)
  181. }
  182. seedMessage := buffer.Bytes()
  183. clientToServerCipher.XORKeyStream(seedMessage[len(seed):], seedMessage[len(seed):])
  184. return seedMessage, len(padding), nil
  185. }
  186. func readSeedMessage(
  187. clientReader io.Reader, config *ObfuscatorConfig) (*rc4.Cipher, *rc4.Cipher, error) {
  188. seed := make([]byte, OBFUSCATE_SEED_LENGTH)
  189. _, err := io.ReadFull(clientReader, seed)
  190. if err != nil {
  191. return nil, nil, common.ContextError(err)
  192. }
  193. clientToServerCipher, serverToClientCipher, err := initObfuscatorCiphers(seed, config)
  194. if err != nil {
  195. return nil, nil, common.ContextError(err)
  196. }
  197. fixedLengthFields := make([]byte, 8) // 4 bytes each for magic value and padding length
  198. _, err = io.ReadFull(clientReader, fixedLengthFields)
  199. if err != nil {
  200. return nil, nil, common.ContextError(err)
  201. }
  202. clientToServerCipher.XORKeyStream(fixedLengthFields, fixedLengthFields)
  203. buffer := bytes.NewReader(fixedLengthFields)
  204. var magicValue, paddingLength int32
  205. err = binary.Read(buffer, binary.BigEndian, &magicValue)
  206. if err != nil {
  207. return nil, nil, common.ContextError(err)
  208. }
  209. err = binary.Read(buffer, binary.BigEndian, &paddingLength)
  210. if err != nil {
  211. return nil, nil, common.ContextError(err)
  212. }
  213. if magicValue != OBFUSCATE_MAGIC_VALUE {
  214. return nil, nil, common.ContextError(errors.New("invalid magic value"))
  215. }
  216. if paddingLength < 0 || paddingLength > OBFUSCATE_MAX_PADDING {
  217. return nil, nil, common.ContextError(errors.New("invalid padding length"))
  218. }
  219. padding := make([]byte, paddingLength)
  220. _, err = io.ReadFull(clientReader, padding)
  221. if err != nil {
  222. return nil, nil, common.ContextError(err)
  223. }
  224. clientToServerCipher.XORKeyStream(padding, padding)
  225. return clientToServerCipher, serverToClientCipher, nil
  226. }