github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/protocol/protocol.go (about)

     1  /*
     2   * Copyright (c) 2016, 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 protocol
    21  
    22  import (
    23  	"crypto/sha256"
    24  	"encoding/json"
    25  
    26  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
    27  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    28  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/osl"
    29  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
    30  )
    31  
    32  const (
    33  	TUNNEL_PROTOCOL_SSH                              = "SSH"
    34  	TUNNEL_PROTOCOL_OBFUSCATED_SSH                   = "OSSH"
    35  	TUNNEL_PROTOCOL_UNFRONTED_MEEK                   = "UNFRONTED-MEEK-OSSH"
    36  	TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS             = "UNFRONTED-MEEK-HTTPS-OSSH"
    37  	TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET    = "UNFRONTED-MEEK-SESSION-TICKET-OSSH"
    38  	TUNNEL_PROTOCOL_FRONTED_MEEK                     = "FRONTED-MEEK-OSSH"
    39  	TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP                = "FRONTED-MEEK-HTTP-OSSH"
    40  	TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH              = "QUIC-OSSH"
    41  	TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH = "FRONTED-MEEK-QUIC-OSSH"
    42  	TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH          = "TAPDANCE-OSSH"
    43  	TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH           = "CONJURE-OSSH"
    44  
    45  	TUNNEL_PROTOCOLS_ALL = "All"
    46  
    47  	SERVER_ENTRY_SOURCE_EMBEDDED   = "EMBEDDED"
    48  	SERVER_ENTRY_SOURCE_REMOTE     = "REMOTE"
    49  	SERVER_ENTRY_SOURCE_DISCOVERY  = "DISCOVERY"
    50  	SERVER_ENTRY_SOURCE_TARGET     = "TARGET"
    51  	SERVER_ENTRY_SOURCE_OBFUSCATED = "OBFUSCATED"
    52  	SERVER_ENTRY_SOURCE_EXCHANGED  = "EXCHANGED"
    53  
    54  	CAPABILITY_SSH_API_REQUESTS            = "ssh-api-requests"
    55  	CAPABILITY_UNTUNNELED_WEB_API_REQUESTS = "handshake"
    56  
    57  	CLIENT_CAPABILITY_SERVER_REQUESTS = "server-requests"
    58  
    59  	PSIPHON_API_HANDSHAKE_REQUEST_NAME = "psiphon-handshake"
    60  	PSIPHON_API_CONNECTED_REQUEST_NAME = "psiphon-connected"
    61  	PSIPHON_API_STATUS_REQUEST_NAME    = "psiphon-status"
    62  	PSIPHON_API_OSL_REQUEST_NAME       = "psiphon-osl"
    63  	PSIPHON_API_ALERT_REQUEST_NAME     = "psiphon-alert"
    64  
    65  	PSIPHON_API_ALERT_DISALLOWED_TRAFFIC = "disallowed-traffic"
    66  	PSIPHON_API_ALERT_UNSAFE_TRAFFIC     = "unsafe-traffic"
    67  
    68  	// PSIPHON_API_CLIENT_VERIFICATION_REQUEST_NAME may still be used by older Android clients
    69  	PSIPHON_API_CLIENT_VERIFICATION_REQUEST_NAME = "psiphon-client-verification"
    70  
    71  	PSIPHON_API_CLIENT_SESSION_ID_LENGTH = 16
    72  
    73  	PSIPHON_SSH_API_PROTOCOL = "ssh"
    74  	PSIPHON_WEB_API_PROTOCOL = "web"
    75  
    76  	PACKET_TUNNEL_CHANNEL_TYPE            = "tun@psiphon.ca"
    77  	RANDOM_STREAM_CHANNEL_TYPE            = "random@psiphon.ca"
    78  	TCP_PORT_FORWARD_NO_SPLIT_TUNNEL_TYPE = "direct-tcpip-no-split-tunnel@psiphon.ca"
    79  
    80  	// Reject reason codes are returned in SSH open channel responses.
    81  	//
    82  	// Values 0xFE000000 to 0xFFFFFFFF are reserved for "PRIVATE USE" (see
    83  	// https://tools.ietf.org/rfc/rfc4254.html#section-5.1).
    84  	CHANNEL_REJECT_REASON_SPLIT_TUNNEL = 0xFE000000
    85  
    86  	PSIPHON_API_HANDSHAKE_AUTHORIZATIONS = "authorizations"
    87  
    88  	CONJURE_TRANSPORT_MIN_OSSH   = "Min-OSSH"
    89  	CONJURE_TRANSPORT_OBFS4_OSSH = "Obfs4-OSSH"
    90  )
    91  
    92  var SupportedServerEntrySources = []string{
    93  	SERVER_ENTRY_SOURCE_EMBEDDED,
    94  	SERVER_ENTRY_SOURCE_REMOTE,
    95  	SERVER_ENTRY_SOURCE_DISCOVERY,
    96  	SERVER_ENTRY_SOURCE_TARGET,
    97  	SERVER_ENTRY_SOURCE_OBFUSCATED,
    98  	SERVER_ENTRY_SOURCE_EXCHANGED,
    99  }
   100  
   101  func AllowServerEntrySourceWithUpstreamProxy(source string) bool {
   102  	return source == SERVER_ENTRY_SOURCE_EMBEDDED ||
   103  		source == SERVER_ENTRY_SOURCE_REMOTE
   104  }
   105  
   106  type TunnelProtocols []string
   107  
   108  func (t TunnelProtocols) Validate() error {
   109  	for _, p := range t {
   110  		if !common.Contains(SupportedTunnelProtocols, p) {
   111  			return errors.Tracef("invalid tunnel protocol: %s", p)
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  func (t TunnelProtocols) PruneInvalid() TunnelProtocols {
   118  	u := make(TunnelProtocols, 0)
   119  	for _, p := range t {
   120  		if common.Contains(SupportedTunnelProtocols, p) {
   121  			u = append(u, p)
   122  		}
   123  	}
   124  	return u
   125  }
   126  
   127  var SupportedTunnelProtocols = TunnelProtocols{
   128  	TUNNEL_PROTOCOL_SSH,
   129  	TUNNEL_PROTOCOL_OBFUSCATED_SSH,
   130  	TUNNEL_PROTOCOL_UNFRONTED_MEEK,
   131  	TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS,
   132  	TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET,
   133  	TUNNEL_PROTOCOL_FRONTED_MEEK,
   134  	TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP,
   135  	TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH,
   136  	TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH,
   137  	TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH,
   138  	TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH,
   139  }
   140  
   141  var DefaultDisabledTunnelProtocols = TunnelProtocols{
   142  	TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH,
   143  	TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH,
   144  	TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH,
   145  }
   146  
   147  func TunnelProtocolUsesTCP(protocol string) bool {
   148  	return protocol != TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH &&
   149  		protocol != TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH
   150  }
   151  
   152  func TunnelProtocolUsesSSH(protocol string) bool {
   153  	return true
   154  }
   155  
   156  func TunnelProtocolUsesObfuscatedSSH(protocol string) bool {
   157  	return protocol != TUNNEL_PROTOCOL_SSH
   158  }
   159  
   160  func TunnelProtocolUsesMeek(protocol string) bool {
   161  	return TunnelProtocolUsesMeekHTTP(protocol) ||
   162  		TunnelProtocolUsesMeekHTTPS(protocol) ||
   163  		TunnelProtocolUsesFrontedMeekQUIC(protocol)
   164  }
   165  
   166  func TunnelProtocolUsesFrontedMeek(protocol string) bool {
   167  	return protocol == TUNNEL_PROTOCOL_FRONTED_MEEK ||
   168  		protocol == TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP ||
   169  		protocol == TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH
   170  }
   171  
   172  func TunnelProtocolUsesMeekHTTP(protocol string) bool {
   173  	return protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK ||
   174  		protocol == TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP
   175  }
   176  
   177  func TunnelProtocolUsesMeekHTTPS(protocol string) bool {
   178  	return protocol == TUNNEL_PROTOCOL_FRONTED_MEEK ||
   179  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS ||
   180  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET
   181  }
   182  
   183  func TunnelProtocolUsesObfuscatedSessionTickets(protocol string) bool {
   184  	return protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET
   185  }
   186  
   187  func TunnelProtocolUsesQUIC(protocol string) bool {
   188  	return protocol == TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH ||
   189  		protocol == TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH
   190  }
   191  
   192  func TunnelProtocolUsesFrontedMeekQUIC(protocol string) bool {
   193  	return protocol == TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH
   194  }
   195  
   196  func TunnelProtocolUsesRefractionNetworking(protocol string) bool {
   197  	return protocol == TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH ||
   198  		protocol == TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH
   199  }
   200  
   201  func TunnelProtocolUsesTapDance(protocol string) bool {
   202  	return protocol == TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH
   203  }
   204  
   205  func TunnelProtocolUsesConjure(protocol string) bool {
   206  	return protocol == TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH
   207  }
   208  
   209  func TunnelProtocolIsResourceIntensive(protocol string) bool {
   210  	return TunnelProtocolUsesMeek(protocol) ||
   211  		TunnelProtocolUsesQUIC(protocol) ||
   212  		TunnelProtocolUsesRefractionNetworking(protocol)
   213  }
   214  
   215  func TunnelProtocolIsCompatibleWithFragmentor(protocol string) bool {
   216  	return protocol == TUNNEL_PROTOCOL_SSH ||
   217  		protocol == TUNNEL_PROTOCOL_OBFUSCATED_SSH ||
   218  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK ||
   219  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS ||
   220  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET ||
   221  		protocol == TUNNEL_PROTOCOL_FRONTED_MEEK ||
   222  		protocol == TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP ||
   223  		protocol == TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH
   224  }
   225  
   226  func TunnelProtocolRequiresTLS12SessionTickets(protocol string) bool {
   227  	return protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET
   228  }
   229  
   230  func TunnelProtocolSupportsPassthrough(protocol string) bool {
   231  	return protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS ||
   232  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET
   233  }
   234  
   235  func TunnelProtocolSupportsUpstreamProxy(protocol string) bool {
   236  	return !TunnelProtocolUsesQUIC(protocol)
   237  }
   238  
   239  func TunnelProtocolMayUseServerPacketManipulation(protocol string) bool {
   240  	return protocol == TUNNEL_PROTOCOL_SSH ||
   241  		protocol == TUNNEL_PROTOCOL_OBFUSCATED_SSH ||
   242  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK ||
   243  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS ||
   244  		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET
   245  }
   246  
   247  func IsValidClientTunnelProtocol(
   248  	clientProtocol string,
   249  	listenerProtocol string,
   250  	serverProtocols TunnelProtocols) bool {
   251  
   252  	if !common.Contains(serverProtocols, clientProtocol) {
   253  		return false
   254  	}
   255  
   256  	// If the client reports the same tunnel protocol as the listener, the value
   257  	// is valid.
   258  
   259  	if clientProtocol == listenerProtocol {
   260  		return true
   261  	}
   262  
   263  	// When the server is running multiple fronted protocols, and the client
   264  	// reports a fronted protocol, the client's reported tunnel protocol is
   265  	// presumed to be valid since some CDNs forward several protocols to the same
   266  	// server port; in this case the listener port is not sufficient to
   267  	// distinguish these protocols.
   268  
   269  	if !TunnelProtocolUsesFrontedMeek(clientProtocol) {
   270  		return false
   271  	}
   272  
   273  	frontedProtocolCount := 0
   274  	for _, protocol := range serverProtocols {
   275  		if TunnelProtocolUsesFrontedMeek(protocol) {
   276  			frontedProtocolCount += 1
   277  			if frontedProtocolCount > 1 {
   278  				return true
   279  			}
   280  		}
   281  	}
   282  
   283  	return false
   284  }
   285  
   286  const (
   287  	TLS_VERSION_12 = "TLSv1.2"
   288  	TLS_VERSION_13 = "TLSv1.3"
   289  
   290  	TLS_PROFILE_IOS_111     = "iOS-11.1"
   291  	TLS_PROFILE_IOS_121     = "iOS-12.1"
   292  	TLS_PROFILE_IOS_13      = "iOS-13"
   293  	TLS_PROFILE_IOS_14      = "iOS-14"
   294  	TLS_PROFILE_CHROME_58   = "Chrome-58"
   295  	TLS_PROFILE_CHROME_62   = "Chrome-62"
   296  	TLS_PROFILE_CHROME_70   = "Chrome-70"
   297  	TLS_PROFILE_CHROME_72   = "Chrome-72"
   298  	TLS_PROFILE_CHROME_83   = "Chrome-83"
   299  	TLS_PROFILE_CHROME_96   = "Chrome-96"
   300  	TLS_PROFILE_CHROME_102  = "Chrome-102"
   301  	TLS_PROFILE_FIREFOX_55  = "Firefox-55"
   302  	TLS_PROFILE_FIREFOX_56  = "Firefox-56"
   303  	TLS_PROFILE_FIREFOX_65  = "Firefox-65"
   304  	TLS_PROFILE_FIREFOX_99  = "Firefox-99"
   305  	TLS_PROFILE_FIREFOX_102 = "Firefox-102"
   306  	TLS_PROFILE_RANDOMIZED  = "Randomized-v2"
   307  )
   308  
   309  var SupportedTLSProfiles = TLSProfiles{
   310  	TLS_PROFILE_IOS_111,
   311  	TLS_PROFILE_IOS_121,
   312  	TLS_PROFILE_IOS_13,
   313  	TLS_PROFILE_IOS_14,
   314  	TLS_PROFILE_CHROME_58,
   315  	TLS_PROFILE_CHROME_62,
   316  	TLS_PROFILE_CHROME_70,
   317  	TLS_PROFILE_CHROME_72,
   318  	TLS_PROFILE_CHROME_83,
   319  	TLS_PROFILE_CHROME_96,
   320  	TLS_PROFILE_CHROME_102,
   321  	TLS_PROFILE_FIREFOX_55,
   322  	TLS_PROFILE_FIREFOX_56,
   323  	TLS_PROFILE_FIREFOX_65,
   324  	TLS_PROFILE_FIREFOX_99,
   325  	TLS_PROFILE_FIREFOX_102,
   326  	TLS_PROFILE_RANDOMIZED,
   327  }
   328  
   329  var legacyTLSProfiles = TLSProfiles{
   330  	"iOS-Safari-11.3.1",
   331  	"Android-6.0",
   332  	"Android-5.1",
   333  	"Chrome-57",
   334  	"Randomized",
   335  	"TLS-1.3-Randomized",
   336  }
   337  
   338  func TLSProfileIsRandomized(tlsProfile string) bool {
   339  	return tlsProfile == TLS_PROFILE_RANDOMIZED
   340  }
   341  
   342  func TLS12ProfileOmitsSessionTickets(tlsProfile string) bool {
   343  	if tlsProfile == TLS_PROFILE_IOS_111 ||
   344  		tlsProfile == TLS_PROFILE_IOS_121 {
   345  		return true
   346  	}
   347  	return false
   348  }
   349  
   350  type TLSProfiles []string
   351  
   352  func (profiles TLSProfiles) Validate(customTLSProfiles []string) error {
   353  
   354  	for _, p := range profiles {
   355  		if !common.Contains(SupportedTLSProfiles, p) &&
   356  			!common.Contains(customTLSProfiles, p) &&
   357  			!common.Contains(legacyTLSProfiles, p) {
   358  			return errors.Tracef("invalid TLS profile: %s", p)
   359  		}
   360  	}
   361  	return nil
   362  }
   363  
   364  func (profiles TLSProfiles) PruneInvalid(customTLSProfiles []string) TLSProfiles {
   365  	q := make(TLSProfiles, 0)
   366  	for _, p := range profiles {
   367  		if common.Contains(SupportedTLSProfiles, p) ||
   368  			common.Contains(customTLSProfiles, p) {
   369  			q = append(q, p)
   370  		}
   371  	}
   372  	return q
   373  }
   374  
   375  type LabeledTLSProfiles map[string]TLSProfiles
   376  
   377  func (labeledProfiles LabeledTLSProfiles) Validate(customTLSProfiles []string) error {
   378  	for _, profiles := range labeledProfiles {
   379  		err := profiles.Validate(customTLSProfiles)
   380  		if err != nil {
   381  			return errors.Trace(err)
   382  		}
   383  	}
   384  	return nil
   385  }
   386  
   387  func (labeledProfiles LabeledTLSProfiles) PruneInvalid(customTLSProfiles []string) LabeledTLSProfiles {
   388  	l := make(LabeledTLSProfiles)
   389  	for label, profiles := range labeledProfiles {
   390  		l[label] = profiles.PruneInvalid(customTLSProfiles)
   391  	}
   392  	return l
   393  }
   394  
   395  const (
   396  	QUIC_VERSION_GQUIC39       = "gQUICv39"
   397  	QUIC_VERSION_GQUIC43       = "gQUICv43"
   398  	QUIC_VERSION_GQUIC44       = "gQUICv44"
   399  	QUIC_VERSION_OBFUSCATED    = "OBFUSCATED"
   400  	QUIC_VERSION_V1            = "QUICv1"
   401  	QUIC_VERSION_RANDOMIZED_V1 = "RANDOMIZED-QUICv1"
   402  	QUIC_VERSION_OBFUSCATED_V1 = "OBFUSCATED-QUICv1"
   403  	QUIC_VERSION_DECOY_V1      = "DECOY-QUICv1"
   404  )
   405  
   406  // The value of SupportedQUICVersions is conditionally compiled based on
   407  // whether gQUIC is enabled. SupportedQUICv1Versions are the supported QUIC
   408  // versions that are based on QUICv1.
   409  
   410  var SupportedQUICv1Versions = QUICVersions{
   411  	QUIC_VERSION_V1,
   412  	QUIC_VERSION_RANDOMIZED_V1,
   413  	QUIC_VERSION_OBFUSCATED_V1,
   414  	QUIC_VERSION_DECOY_V1,
   415  }
   416  
   417  var legacyQUICVersions = QUICVersions{
   418  	"IETF-draft-24",
   419  }
   420  
   421  func QUICVersionHasRandomizedClientHello(version string) bool {
   422  	return version == QUIC_VERSION_RANDOMIZED_V1
   423  }
   424  
   425  func QUICVersionIsObfuscated(version string) bool {
   426  	return version == QUIC_VERSION_OBFUSCATED ||
   427  		version == QUIC_VERSION_OBFUSCATED_V1 ||
   428  		version == QUIC_VERSION_DECOY_V1
   429  }
   430  
   431  func QUICVersionUsesPathMTUDiscovery(version string) bool {
   432  	return version != QUIC_VERSION_GQUIC39 &&
   433  		version != QUIC_VERSION_GQUIC43 &&
   434  		version != QUIC_VERSION_GQUIC44 &&
   435  		version != QUIC_VERSION_OBFUSCATED
   436  }
   437  
   438  type QUICVersions []string
   439  
   440  func (versions QUICVersions) Validate() error {
   441  	for _, v := range versions {
   442  		if !common.Contains(SupportedQUICVersions, v) &&
   443  			!common.Contains(legacyQUICVersions, v) {
   444  			return errors.Tracef("invalid QUIC version: %s", v)
   445  		}
   446  	}
   447  	return nil
   448  }
   449  
   450  func (versions QUICVersions) PruneInvalid() QUICVersions {
   451  	u := make(QUICVersions, 0)
   452  	for _, v := range versions {
   453  		if common.Contains(SupportedQUICVersions, v) {
   454  			u = append(u, v)
   455  		}
   456  	}
   457  	return u
   458  }
   459  
   460  type LabeledQUICVersions map[string]QUICVersions
   461  
   462  func (labeledVersions LabeledQUICVersions) Validate() error {
   463  	for _, versions := range labeledVersions {
   464  		err := versions.Validate()
   465  		if err != nil {
   466  			return errors.Trace(err)
   467  		}
   468  	}
   469  	return nil
   470  }
   471  
   472  func (labeledVersions LabeledQUICVersions) PruneInvalid() LabeledQUICVersions {
   473  	l := make(LabeledQUICVersions)
   474  	for label, versions := range labeledVersions {
   475  		l[label] = versions.PruneInvalid()
   476  	}
   477  	return l
   478  }
   479  
   480  type HandshakeResponse struct {
   481  	SSHSessionID             string              `json:"ssh_session_id"`
   482  	Homepages                []string            `json:"homepages"`
   483  	UpgradeClientVersion     string              `json:"upgrade_client_version"`
   484  	PageViewRegexes          []map[string]string `json:"page_view_regexes"`
   485  	HttpsRequestRegexes      []map[string]string `json:"https_request_regexes"`
   486  	EncodedServerList        []string            `json:"encoded_server_list"`
   487  	ClientRegion             string              `json:"client_region"`
   488  	ClientAddress            string              `json:"client_address"`
   489  	ServerTimestamp          string              `json:"server_timestamp"`
   490  	ActiveAuthorizationIDs   []string            `json:"active_authorization_ids"`
   491  	TacticsPayload           json.RawMessage     `json:"tactics_payload"`
   492  	UpstreamBytesPerSecond   int64               `json:"upstream_bytes_per_second"`
   493  	DownstreamBytesPerSecond int64               `json:"downstream_bytes_per_second"`
   494  	Padding                  string              `json:"padding"`
   495  }
   496  
   497  type ConnectedResponse struct {
   498  	ConnectedTimestamp string `json:"connected_timestamp"`
   499  	Padding            string `json:"padding"`
   500  }
   501  
   502  type StatusResponse struct {
   503  	InvalidServerEntryTags []string `json:"invalid_server_entry_tags"`
   504  	Padding                string   `json:"padding"`
   505  }
   506  
   507  type OSLRequest struct {
   508  	ClearLocalSLOKs bool             `json:"clear_local_sloks"`
   509  	SeedPayload     *osl.SeedPayload `json:"seed_payload"`
   510  }
   511  
   512  type SSHPasswordPayload struct {
   513  	SessionId          string   `json:"SessionId"`
   514  	SshPassword        string   `json:"SshPassword"`
   515  	ClientCapabilities []string `json:"ClientCapabilities"`
   516  }
   517  
   518  type MeekCookieData struct {
   519  	MeekProtocolVersion  int    `json:"v"`
   520  	ClientTunnelProtocol string `json:"t"`
   521  	EndPoint             string `json:"e"`
   522  }
   523  
   524  type RandomStreamRequest struct {
   525  	UpstreamBytes   int `json:"u"`
   526  	DownstreamBytes int `json:"d"`
   527  }
   528  
   529  type AlertRequest struct {
   530  	Reason     string   `json:"reason"`
   531  	Subject    string   `json:"subject"`
   532  	ActionURLs []string `json:"action"`
   533  }
   534  
   535  func DeriveSSHServerKEXPRNGSeed(obfuscatedKey string) (*prng.Seed, error) {
   536  	// By convention, the obfuscatedKey will often be a hex-encoded 32 byte value,
   537  	// but this isn't strictly required or validated, so we use SHA256 to map the
   538  	// obfuscatedKey to the necessary 32-byte seed value.
   539  	seed := prng.Seed(sha256.Sum256([]byte(obfuscatedKey)))
   540  	return prng.NewSaltedSeed(&seed, "ssh-server-kex")
   541  }
   542  
   543  func DeriveSSHServerVersionPRNGSeed(obfuscatedKey string) (*prng.Seed, error) {
   544  	seed := prng.Seed(sha256.Sum256([]byte(obfuscatedKey)))
   545  	return prng.NewSaltedSeed(&seed, "ssh-server-version")
   546  }
   547  
   548  func DeriveBPFServerProgramPRNGSeed(obfuscatedKey string) (*prng.Seed, error) {
   549  	seed := prng.Seed(sha256.Sum256([]byte(obfuscatedKey)))
   550  	return prng.NewSaltedSeed(&seed, "bpf-server-program")
   551  }