obfuscator.go 7.1 KB

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