prefix

package
v0.7.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 13, 2023 License: Apache-2.0 Imports: 14 Imported by: 1

README

Prefix Transport

TLDR - This transport allows up to prepend conjure connections with bytes that look like the initialization of other protocols. This can help to circumvent blocking in some areas and better understand censorship regimes, but is generally a short term solution.

The Prefix_Min transport is a strictly improved version of the existing Min transport and we suggest migration.

Description

This package implements the prefix transport for the conjure refraction-networking system. The prefix transport operates in much the same way as the min transport, sending a tag in the fist packet signalling to the station that the flow has knowledge of a secret shared with the station by a previous registration.

Integrating the Prefix Transport

Though the client dialer allows the use of TrasnportType for compatibility reasons, the prefix transport requires use of the newer Client Transport interface (TransportConfig in the dialer) which is implemented by the prefix.ClientTransport object.

Usage Example:

// basic - default prefix is Random.
t := transports.NewWithParams("prefix", nil)

dialer = &tapdanceDialer{TransportConfig: t}
conn, err := dialer.Dial("tcp", "1.1.1.1:443")
// ...
// Options included
var prefixID int32 = prefix.OpenSSH2
var randomizePhantomPort = true
var flushPolicy = prefix.FlushAfterTag

params = &pb.PrefixTransportParams{
  RandomizeDstPort: &randomizePhantomPort,
  PrefixId: &prefixID
  CustomFlushPolicy: &flushPolicy
}

t, err := transports.NewWithParams("prefix", params)
if err != nil {
  panic(err)
}

dialer = &tapdanceDialer{ TransportConfig: t}
conn, err := dialer.Dial("tcp", "1.1.1.1:443")
// ...
Prefixes Supported by Default

All Prefixes include an obfuscated tag that indicates to the station that this is in fact a registered client and shared the registration identifier as a secure value obfuscated to uniform random.

  • Min - minimum prefix prepends no bytes other than obfuscated tag, similar to the existing Min transport
  • GetLong - Plain text HTTP 1.1 GET Header for a root path
  • PostLong - Plain text HTTP 1.1 POST Header for a root path
  • HTTPResp - Plain text HTTP 1.1 Response Header with a 200 (success) return code
  • TLSClientHello - TLS ClientHello header up to random field
  • TLSServerHello - TLS ServerHello header up to random field
  • TLSAlertWarning - TLS header indicating a fatal alert
  • TLSAlertFatal - TLS header indicating a fatal alert
  • DNSOverTCP - DNS over TCP header
  • OpenSSH2 - OpenSSH banner header version 8.9.p1
Ports

Prefixes have default ports associated with them, but also allow port randomization. In the prefix Transport specifically the parameter to control that is RandomizeDstPort. If this is not set, then the connection will set the port to the the fixed port associated with the chosen prefix (e.g. TLS prefixes will use 443, HTTP will use 80, etc.). If Randomize is selected, the port is chosen from a destination port randomly from the range [1024 - 65535].

Prefix Write Buffer Flush

Given that the prefix support is intended to be relatively flexible wrt. the way that prefixes can be expressed we have added a parameter to give the user basic control over the places where the write buffer gets flushed. Each existing prefix haa a default flush policy that makes the most sense for the individual prefix.

Currently there are only two positions where potential flushes will be inserted, after the prefix and/or after the obfuscated tag.

[prefix_bytes] |    | [obfuscated_tag] |    | [client bytes .... -> ]
                 ^                        ^
            maybe flush              maybe flush

Currently the default policy for the existing (partial packet) prefixes is no added flushes. However, for different prefixes in the future (e.g a complete TLS ClientHello packet) it would make sense to flush after the prefix.

⚠ Sharp Edges ⚠

In general this transport will not properly mimic the protocols that are sent as a prefix and should not be expected to do so.

Comparing the Min transport and the min prefix

The min transport is designed to send a uniform random encoding of the client`s session identifier before the initial packet in a connection. Howveer, on reconnect the same value is sent for the session identifier. Meaning that connections made by re-using registrations will always start with the same 32 bytes.

In contrast the Min prefix uses a 64 byte obfuscated tag that will be random on every connection, even when re-using a registration. Beyond this the tag encoding scheme is built to be modular, and capable of supporting new obfuscation techniques as necessary in the future. Currently obfuscation is done by deriving a shared key using ECDHE an then encrypts the plaintext under that key using AES CTR. The elligator representative for the clients public key is prepended to the returned byte array. This means that the result length will likely be: 32 + len(plaintext).

// Min Transport
[32B elligator encoded session indicator]

// Min Prefix
[32B elligator encoded client ECDHE Pub] [32B session indicator]

Adding a Prefix / Bidirectional Registration Prefix Overrides

TODO: 🚧 In order to add a prefix ...

🚧 Road-Map

Planned Features

  • Randomization - indicate segments of the prefix to be filled from a random source.

  • Server Side Prefix Override From File - file format shared between station and Reg server describing available prefixes outside of defaults.

These features are not necessarily planned or landing imminently, they are simply things that would be nice to have.

  • TagEncodings - Allow the tag to (by prefix configuration) be encoded using an encoder expected by the station, Base64 for example.

  • StreamEncodings - Allow the Stream of client bytes to (by configuration) encoded / encrypted using a scheme expected by the station, AES or Base64 for example.

  • Prefix Revocation - If there is a prefix that is known to be blocked and we don't want clients to use it, but we still want them to roll a random prefix, how do we do this?

Documentation

Index

Constants

View Source
const (
	// DefaultFlush uses the flush pattern defined by the chosen prefix
	DefaultFlush int32 = iota
	// NoAddedFlush no flushes when writing prefix and tag
	NoAddedFlush
	// FlushAfterPrefix flush after the prefix before the tag (if possible), but not after tag
	// before client data is sent over the connection
	FlushAfterPrefix
)

Variables

View Source
var (
	// ErrUnknownPrefix indicates that the provided Prefix ID is unknown to the transport object.
	ErrUnknownPrefix = errors.New("unknown / unsupported prefix")

	// ErrBadParams indicates that the parameters provided to a call on the server side do not make
	// sense in the context that they are provided and the registration will be ignored.
	ErrBadParams = errors.New("bad parameters provided")

	// ErrIncorrectPrefix indicates that tryFindRegistration found a valid registration based on
	// the obfuscated tag, however the prefix that it matched was not the prefix indicated in the
	// registration.
	ErrIncorrectPrefix = errors.New("found connection for unexpected prefix")

	// ErrIncorrectTransport indicates that tryFindRegistration found a valid registration based on
	// the obfuscated tag, however the prefix that it matched was not the prefix indicated in the
	// registration.
	ErrIncorrectTransport = errors.New("found registration w/ incorrect transport type")
)
View Source
var DefaultPrefixes = map[PrefixID]Prefix{}

DefaultPrefixes provides the prefixes supported by default for use when by the client.

Functions

This section is empty.

Types

type ClientParams

type ClientParams struct {
	RandomizeDstPort bool
	FlushPolicy      int32
	PrefixID         int32
}

ClientParams are parameters available to a calling library to configure the Prefix transport outside of the specific Prefix

func DefaultParams added in v0.7.2

func DefaultParams() *ClientParams

DefaultParams returns the default parameters for the transport

func (*ClientParams) GetParams added in v0.7.2

func (c *ClientParams) GetParams() any

func (*ClientParams) String added in v0.7.2

func (c *ClientParams) String() string

type ClientTransport

type ClientTransport struct {
	Prefix        Prefix
	TagObfuscator transports.Obfuscator
	// contains filtered or unexported fields
}

ClientTransport implements the client side transport interface for the Min transport. The significant difference is that there is an instance of this structure per client session, where the station side Transport struct has one instance to be re-used for all sessions.

External libraries must set parameters through SetParams using PrefixTransportParams.

func (*ClientTransport) GetDstPort

func (t *ClientTransport) GetDstPort(seed []byte) (uint16, error)

GetDstPort returns the destination port that the client should open the phantom connection to

func (*ClientTransport) GetParams

func (t *ClientTransport) GetParams() (proto.Message, error)

GetParams returns a generic protobuf with any parameters from both the registration and the transport.

func (*ClientTransport) ID

ID provides an identifier that will be sent to the conjure station during the registration so that the station knows what transport to expect connecting to the chosen phantom.

func (*ClientTransport) Name

func (t *ClientTransport) Name() string

Name returns the human-friendly name of the transport, implementing the Transport interface.

func (ClientTransport) ParseParams

func (t ClientTransport) ParseParams(data *anypb.Any) (any, error)

ParseParams gives the specific transport an option to parse a generic object into parameters provided by the station in the registration response during registration.

func (*ClientTransport) Prepare added in v0.6.0

func (t *ClientTransport) Prepare(ctx context.Context, dialer func(ctx context.Context, network, laddr, raddr string) (net.Conn, error)) error

Prepare lets the transport use the dialer to prepare. This is called before GetParams to let the transport prepare stuff such as nat traversal.

func (*ClientTransport) PrepareKeys

func (t *ClientTransport) PrepareKeys(pubkey [32]byte, sharedSecret []byte, hkdf io.Reader) error

PrepareKeys provides an opportunity for the transport to integrate the station public key as well as bytes from the deterministic random generator associated with the registration that this ClientTransport is attached to.

func (*ClientTransport) SetParams

func (t *ClientTransport) SetParams(p any) error

SetParams allows the caller to set parameters associated with the transport, returning an error if the provided generic message is not compatible or the parameters are otherwise invalid

func (*ClientTransport) SetSessionParams added in v0.7.5

func (t *ClientTransport) SetSessionParams(incoming *anypb.Any, unchecked ...bool) error

SetSessionParams allows the session to apply updated params that are only used within an individual dial, returning an error if the provided generic message is not compatible. the variadic bool parameter is used to indicate whether the client should sanity check the params or just apply them. This is useful in cases where the registrar may provide options to the client that it is able to handle, but are outside of the clients sanity checks. (see prefix transport for an example)

func (*ClientTransport) String

func (t *ClientTransport) String() string

String returns a string identifier for the Transport for logging (including string formatters)

func (*ClientTransport) WrapConn

func (t *ClientTransport) WrapConn(conn net.Conn) (net.Conn, error)

WrapConn gives the transport the opportunity to perform a handshake and wrap / transform the incoming and outgoing bytes send by the implementing client.

type Prefix

type Prefix interface {
	Bytes() []byte
	FlushPolicy() int32
	ID() PrefixID
	DstPort([]byte) uint16
}

Prefix struct used by, selected by, or given to the client. This interface allows for non-uniform behavior like a rand prefix for example.

func TryFromID

func TryFromID(id PrefixID) (Prefix, error)

TryFromID returns a Prefix based on the Prefix ID. This is useful for non-static prefixes like the random prefix

type PrefixID

type PrefixID int

PrefixID provide an integer Identifier for each individual prefixes allowing clients to indicate to the station the prefix they intend to connect with.

const (
	Rand PrefixID = -1 + iota
	Min
	GetLong
	PostLong
	HTTPResp
	TLSClientHello
	TLSServerHello
	TLSAlertWarning
	TLSAlertFatal
	DNSOverTCP
	OpenSSH2
)

func (PrefixID) Name

func (id PrefixID) Name() string

Name returns the human-friendly name of the prefix.

type Transport

type Transport struct {
	SupportedPrefixes map[PrefixID]prefix
	TagObfuscator     transports.Obfuscator
	Privkey           [32]byte
}

Transport provides a struct implementing the Transport, WrappingTransport, PortRandomizingTransport, and FixedPortTransport interfaces.

func Default

func Default(privkey [32]byte, filepath ...string) (*Transport, error)

Default Given a private key this builds the server side transport with the DEFAULT set of supported prefixes. The optional filepath specifies a file from which to read extra prefixes. If provided only the first variadic string will be used to attempt to parse prefixes. There can be no colliding PrefixIDs - file defined prefixes take precedent over defaults, and within the file first defined takes precedence.

func DefaultSet

func DefaultSet() *Transport

DefaultSet builds a hollow version of the transport with the DEFAULT set of supported prefixes. This is useful in instances where we just need to check whether the prefix ID is known, not actually handle any major operations (tryFindReg / WrapConn)

func New

func New(privkey [32]byte, filepath ...string) (*Transport, error)

New Given a private key this builds the server side transport with an EMPTY set of supported prefixes. The optional filepath specifies a file from which to read extra prefixes. If provided only the first variadic string will be used to attempt to parse prefixes. There can be no colliding PrefixIDs - within the file first defined takes precedence.

func (Transport) GetDstPort

func (t Transport) GetDstPort(libVersion uint, seed []byte, params any) (uint16, error)

GetDstPort Given the library version, a seed, and a generic object containing parameters the transport should be able to return the destination port that a clients phantom connection will attempt to reach

func (Transport) GetIdentifier

func (Transport) GetIdentifier(d transports.Registration) string

GetIdentifier takes in a registration and returns an identifier for it. This identifier should be unique for each registration on a given phantom; registrations on different phantoms can have the same identifier.

func (Transport) GetProto

func (Transport) GetProto() pb.IPProto

GetProto returns the next layer protocol that the transport uses. Implements the Transport interface.

func (Transport) LogPrefix

func (Transport) LogPrefix() string

LogPrefix returns the prefix used when including this transport in logs, implementing the Transport interface.

func (Transport) Name

func (Transport) Name() string

Name returns the human-friendly name of the transport, implementing the Transport interface..

func (Transport) ParamStrings

func (t Transport) ParamStrings(p any) []string

ParamStrings returns an array of tag string that will be added to tunStats when a proxy session is closed.

func (Transport) ParseParams

func (t Transport) ParseParams(libVersion uint, data *anypb.Any) (any, error)

ParseParams gives the specific transport an option to parse a generic object into parameters provided by the client during registration. This Transport was written after RandomizeDstPort was added, so it should not be usable by clients who don't support destination port randomization.

func (Transport) WrapConnection

func (t Transport) WrapConnection(data *bytes.Buffer, c net.Conn, originalDst net.IP, regManager transports.RegManager) (transports.Registration, net.Conn, error)

WrapConnection attempts to wrap the given connection in the transport. It takes the information gathered so far on the connection in data, attempts to identify itself, and if it positively identifies itself wraps the connection in the transport, returning a connection that's ready to be used by others.

If the returned error is nil or non-nil and non-{ transports.ErrTryAgain, transports.ErrNotTransport }, the caller may no longer use data or conn.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL