obfuscator.go 7.4 KB

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