values.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * Copyright (c) 2019, 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. /*
  20. Package values provides a mechanism for specifying and selecting dynamic
  21. values employed by the Psiphon client and server.
  22. */
  23. package values
  24. import (
  25. "bytes"
  26. "encoding/gob"
  27. "errors"
  28. "strings"
  29. "sync/atomic"
  30. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  31. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/nacl/secretbox"
  32. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  33. )
  34. // ValueSpec specifies a value selection space.
  35. type ValueSpec struct {
  36. Parts []PartSpec
  37. Padding []byte
  38. }
  39. type PartSpec struct {
  40. Items []string
  41. MinCount, MaxCount int
  42. }
  43. // NewPickOneSpec creates a simple spec to select one item from a list as a
  44. // value.
  45. func NewPickOneSpec(items []string) *ValueSpec {
  46. return &ValueSpec{Parts: []PartSpec{{Items: items, MinCount: 1, MaxCount: 1}}}
  47. }
  48. // GetValue selects a value according to the spec. An optional seed may
  49. // be specified to support replay.
  50. func (spec *ValueSpec) GetValue(seed *prng.Seed) string {
  51. rangeFunc := prng.Range
  52. intnFunc := prng.Intn
  53. if seed != nil {
  54. PRNG := prng.NewPRNGWithSeed(seed)
  55. rangeFunc = PRNG.Range
  56. intnFunc = PRNG.Intn
  57. }
  58. var value strings.Builder
  59. for _, part := range spec.Parts {
  60. count := rangeFunc(part.MinCount, part.MaxCount)
  61. for i := 0; i < count; i++ {
  62. value.WriteString(part.Items[intnFunc(len(part.Items))])
  63. }
  64. }
  65. return value.String()
  66. }
  67. // Obfuscate creates an obfuscated blob from a spec.
  68. func (spec *ValueSpec) Obfuscate(
  69. obfuscationKey []byte,
  70. minPadding, maxPadding int) ([]byte, error) {
  71. if len(obfuscationKey) != 32 {
  72. return nil, common.ContextError(errors.New("invalid key length"))
  73. }
  74. var key [32]byte
  75. copy(key[:], []byte(obfuscationKey))
  76. spec.Padding = prng.Padding(minPadding, maxPadding)
  77. var obfuscatedValueSpec bytes.Buffer
  78. err := gob.NewEncoder(&obfuscatedValueSpec).Encode(spec)
  79. if err != nil {
  80. return nil, common.ContextError(err)
  81. }
  82. return secretbox.Seal(
  83. nil, []byte(obfuscatedValueSpec.Bytes()), &[24]byte{}, &key), nil
  84. }
  85. // DeobfuscateValueSpec reconstitutes an obfuscated spec.
  86. func DeobfuscateValueSpec(obfuscatedValueSpec, obfuscationKey []byte) *ValueSpec {
  87. if len(obfuscationKey) != 32 {
  88. return nil
  89. }
  90. var key [32]byte
  91. copy(key[:], obfuscationKey)
  92. deobfuscatedValueSpec, ok := secretbox.Open(nil, obfuscatedValueSpec, &[24]byte{}, &key)
  93. if !ok {
  94. return nil
  95. }
  96. spec := new(ValueSpec)
  97. err := gob.NewDecoder(bytes.NewBuffer(deobfuscatedValueSpec)).Decode(spec)
  98. if err != nil {
  99. return nil
  100. }
  101. spec.Padding = nil
  102. return spec
  103. }
  104. var (
  105. revision atomic.Value
  106. sshClientVersionsSpec atomic.Value
  107. sshServerVersionsSpec atomic.Value
  108. userAgentsSpec atomic.Value
  109. hostNamesSpec atomic.Value
  110. )
  111. // SetRevision set the revision value, which may be used to track which value
  112. // specs are active. The revision is not managed by this package and must be
  113. // set by the package user.
  114. func SetRevision(rev string) {
  115. revision.Store(rev)
  116. }
  117. // GetRevision gets the previously set revision.
  118. func GetRevision() string {
  119. rev, ok := revision.Load().(string)
  120. if !ok {
  121. return "none"
  122. }
  123. return rev
  124. }
  125. // SetSSHClientVersionsSpec sets the corresponding value spec.
  126. func SetSSHClientVersionsSpec(spec *ValueSpec) {
  127. if spec == nil {
  128. return
  129. }
  130. sshClientVersionsSpec.Store(spec)
  131. }
  132. // GetSSHClientVersion selects a value based on the previously set spec, or
  133. // returns a default when no spec is set.
  134. func GetSSHClientVersion() string {
  135. spec, ok := sshClientVersionsSpec.Load().(*ValueSpec)
  136. if !ok {
  137. return ""
  138. }
  139. return spec.GetValue(nil)
  140. }
  141. // SetSSHServerVersionsSpec sets the corresponding value spec.
  142. func SetSSHServerVersionsSpec(spec *ValueSpec) {
  143. if spec == nil {
  144. return
  145. }
  146. sshServerVersionsSpec.Store(spec)
  147. }
  148. // GetSSHServerVersion selects a value based on the previously set spec, or
  149. // returns a default when no spec is set.
  150. func GetSSHServerVersion(seed *prng.Seed) string {
  151. spec, ok := sshServerVersionsSpec.Load().(*ValueSpec)
  152. if !ok {
  153. return ""
  154. }
  155. return spec.GetValue(seed)
  156. }
  157. // SetUserAgentsSpec sets the corresponding value spec.
  158. func SetUserAgentsSpec(spec *ValueSpec) {
  159. if spec == nil {
  160. return
  161. }
  162. userAgentsSpec.Store(spec)
  163. }
  164. // GetUserAgent selects a value based on the previously set spec, or
  165. // returns a default when no spec is set.
  166. func GetUserAgent() string {
  167. spec, ok := userAgentsSpec.Load().(*ValueSpec)
  168. if !ok {
  169. return ""
  170. }
  171. return spec.GetValue(nil)
  172. }
  173. // SetHostNamesSpec sets the corresponding value spec.
  174. func SetHostNamesSpec(spec *ValueSpec) {
  175. if spec == nil {
  176. return
  177. }
  178. hostNamesSpec.Store(spec)
  179. }
  180. // GetHostName selects a value based on the previously set spec, or
  181. // returns a default when no spec is set.
  182. func GetHostName() string {
  183. spec, ok := hostNamesSpec.Load().(*ValueSpec)
  184. if !ok {
  185. return "www.example.org"
  186. }
  187. return spec.GetValue(nil)
  188. }