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 }