Skip to content

Commit

Permalink
Merge pull request #646 from adotkhan/prefix
Browse files Browse the repository at this point in the history
OSSH Prefix update
  • Loading branch information
rod-hynes authored Jul 7, 2023
2 parents 6d01ade + 04b513d commit 4de7aad
Show file tree
Hide file tree
Showing 16 changed files with 449 additions and 120 deletions.
9 changes: 9 additions & 0 deletions psiphon/common/fragmentor/fragmentor.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,15 @@ func (c *Conn) GetReplay() (*prng.Seed, bool) {
return seed, c.isReplay
}

// Stops the fragmentor from fragmenting any further writes.
func (c *Conn) StopFragmenting() {

c.writeMutex.Lock()
defer c.writeMutex.Unlock()

c.bytesToFragment = 0
}

func (c *Conn) Write(buffer []byte) (int, error) {

c.writeMutex.Lock()
Expand Down
5 changes: 3 additions & 2 deletions psiphon/common/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,12 @@ type UnderlyingTCPAddrSource interface {
GetUnderlyingTCPAddrs() (*net.TCPAddr, *net.TCPAddr, bool)
}

// FragmentorReplayAccessor defines the interface for accessing replay properties
// FragmentorAccessor defines the interface for accessing properties
// of a fragmentor Conn.
type FragmentorReplayAccessor interface {
type FragmentorAccessor interface {
SetReplay(*prng.PRNG)
GetReplay() (*prng.Seed, bool)
StopFragmenting()
}

// HTTPRoundTripper is an adapter that allows using a function as a
Expand Down
107 changes: 102 additions & 5 deletions psiphon/common/obfuscator/obfuscatedSshConn.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ package obfuscator

import (
"bytes"
"context"
"encoding/binary"
std_errors "errors"
"io"
"io/ioutil"
"net"
"time"

"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
Expand Down Expand Up @@ -60,6 +62,8 @@ const (
type ObfuscatedSSHConn struct {
net.Conn
mode ObfuscatedSSHConnMode
runCtx context.Context
stopRunning context.CancelFunc
obfuscator *Obfuscator
readDeobfuscate func([]byte)
writeObfuscate func([]byte)
Expand Down Expand Up @@ -129,6 +133,7 @@ func NewObfuscatedSSHConn(
obfuscatorSeedTransformerParameters *transforms.ObfuscatorSeedTransformerParameters,
clientPrefixSpec *OSSHPrefixSpec,
serverPrefixSepcs transforms.Specs,
osshPrefixSplitConfig *OSSHPrefixSplitConfig,
minPadding, maxPadding *int,
seedHistory *SeedHistory,
irregularLogger func(
Expand All @@ -151,6 +156,7 @@ func NewObfuscatedSSHConn(
IsOSSH: true,
Keyword: obfuscationKeyword,
ClientPrefixSpec: clientPrefixSpec,
OSSHPrefixSplitConfig: osshPrefixSplitConfig,
PaddingPRNGSeed: obfuscationPaddingPRNGSeed,
MinPadding: minPadding,
MaxPadding: maxPadding,
Expand All @@ -163,7 +169,7 @@ func NewObfuscatedSSHConn(
writeObfuscate = obfuscator.ObfuscateClientToServer
writeState = OBFUSCATION_WRITE_STATE_CLIENT_SEND_PREAMBLE

if obfuscator.prefixHeader != nil {
if obfuscator.osshPrefixHeader != nil {
// Client expects prefix with terminator from the server.
readState = OBFUSCATION_READ_STATE_CLIENT_READ_PREFIX
}
Expand Down Expand Up @@ -200,9 +206,13 @@ func NewObfuscatedSSHConn(
return nil, errors.Trace(err)
}

runCtx, stopRunning := context.WithCancel(context.Background())

return &ObfuscatedSSHConn{
Conn: conn,
mode: mode,
runCtx: runCtx,
stopRunning: stopRunning,
obfuscator: obfuscator,
readDeobfuscate: readDeobfuscate,
writeObfuscate: writeObfuscate,
Expand All @@ -224,6 +234,7 @@ func NewClientObfuscatedSSHConn(
obfuscationPaddingPRNGSeed *prng.Seed,
obfuscatorSeedTransformerParameters *transforms.ObfuscatorSeedTransformerParameters,
prefixSpec *OSSHPrefixSpec,
osshPrefixSplitConfig *OSSHPrefixSplitConfig,
minPadding, maxPadding *int) (*ObfuscatedSSHConn, error) {

return NewObfuscatedSSHConn(
Expand All @@ -234,6 +245,7 @@ func NewClientObfuscatedSSHConn(
obfuscatorSeedTransformerParameters,
prefixSpec,
nil,
osshPrefixSplitConfig,
minPadding, maxPadding,
nil,
nil)
Expand All @@ -258,11 +270,18 @@ func NewServerObfuscatedSSHConn(
nil, nil,
nil,
serverPrefixSpecs,
nil,
nil, nil,
seedHistory,
irregularLogger)
}

// IsOSSHPrefixedStream returns true if client wrote a prefix to the Obfuscated SSH stream,
// or the server read a prefixed Obfuscated SSH stream.
func (conn *ObfuscatedSSHConn) IsOSSHPrefixStream() bool {
return conn.obfuscator.osshPrefixHeader != nil
}

// GetDerivedPRNG creates a new PRNG with a seed derived from the
// ObfuscatedSSHConn padding seed and distinguished by the salt, which should
// be a unique identifier for each usage context.
Expand All @@ -274,6 +293,27 @@ func (conn *ObfuscatedSSHConn) GetDerivedPRNG(salt string) (*prng.PRNG, error) {
return conn.obfuscator.GetDerivedPRNG(salt)
}

// SetOSSHPrefixSplitConfig sets the OSSHPrefixSplitConfig for the server.
// This must be called before any data is written.
func (conn *ObfuscatedSSHConn) SetOSSHPrefixSplitConfig(minDelay, maxDelay time.Duration) error {
if conn.mode != OBFUSCATION_CONN_MODE_SERVER {
return errors.TraceNew("SetOSSHPrefixSplitConfig() is only valid for server connections")
}
if conn.writeState != OBFUSCATION_WRITE_STATE_SERVER_SEND_PREFIX_AND_IDENTIFICATION_LINE_PADDING {
return errors.TraceNew("SetOSSHPrefixSplitConfig() must be called before any data is written")
}
seed, err := conn.obfuscator.GetDerivedPRNGSeed("obfuscated-ssh-prefix-split")
if err != nil {
return errors.Trace(err)
}
conn.obfuscator.osshPrefixSplitConfig = &OSSHPrefixSplitConfig{
Seed: seed,
MinDelay: minDelay,
MaxDelay: maxDelay,
}
return nil
}

// GetMetrics implements the common.MetricsSource interface.
func (conn *ObfuscatedSSHConn) GetMetrics() common.LogFields {
logFields := make(common.LogFields)
Expand Down Expand Up @@ -318,6 +358,11 @@ func (conn *ObfuscatedSSHConn) Write(buffer []byte) (int, error) {
return len(buffer), nil
}

func (conn *ObfuscatedSSHConn) Close() error {
conn.stopRunning()
return conn.Conn.Close()
}

// readAndTransform reads and transforms the downstream bytes stream
// while in an obfucation state. It parses the stream of bytes read
// looking for the first SSH_MSG_NEWKEYS packet sent from the peer,
Expand Down Expand Up @@ -499,9 +544,35 @@ func (conn *ObfuscatedSSHConn) transformAndWrite(buffer []byte) error {
// identification line padding (server) are injected before any standard SSH traffic.
if conn.writeState == OBFUSCATION_WRITE_STATE_CLIENT_SEND_PREAMBLE {

preamble := conn.obfuscator.SendPreamble()
preamble, prefixLen := conn.obfuscator.SendPreamble()

_, err := conn.Conn.Write(preamble)
// Writes the prefix first, then the rest of the preamble after a delay.
_, err := conn.Conn.Write(preamble[:prefixLen])
if err != nil {
return errors.Trace(err)
}

// Adds random delay defined by OSSH prefix split config.
if config := conn.obfuscator.osshPrefixSplitConfig; config != nil {
rng := prng.NewPRNGWithSeed(config.Seed)
delay := rng.Period(config.MinDelay, config.MaxDelay)

timer := time.NewTimer(delay)

var err error
select {
case <-conn.runCtx.Done():
err = conn.runCtx.Err()
case <-timer.C:
}
timer.Stop()

if err != nil {
return errors.Trace(err)
}
}

_, err = conn.Conn.Write(preamble[prefixLen:])
if err != nil {
return errors.Trace(err)
}
Expand All @@ -512,8 +583,34 @@ func (conn *ObfuscatedSSHConn) transformAndWrite(buffer []byte) error {

var buffer bytes.Buffer

if preamble := conn.obfuscator.SendPreamble(); preamble != nil {
_, err := buffer.Write(preamble)
if preamble, prefixLen := conn.obfuscator.SendPreamble(); preamble != nil {
// Prefix bytes are written to the underlying conn immediately, skipping the buffer.
_, err := conn.Conn.Write(preamble[:prefixLen])
if err != nil {
return errors.Trace(err)
}

// Adds random delay defined by OSSH prefix split config.
if config := conn.obfuscator.osshPrefixSplitConfig; config != nil {
rng := prng.NewPRNGWithSeed(config.Seed)
delay := rng.Period(config.MinDelay, config.MaxDelay)

timer := time.NewTimer(delay)

var err error
select {
case <-conn.runCtx.Done():
err = conn.runCtx.Err()
case <-timer.C:
}
timer.Stop()

if err != nil {
return errors.Trace(err)
}
}

_, err = buffer.Write(preamble[prefixLen:])
if err != nil {
return errors.Trace(err)
}
Expand Down
Loading

0 comments on commit 4de7aad

Please sign in to comment.