values.go 5.4 KB

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