Skip to content

Commit affe724

Browse files
Marshall/transport interface (#178)
* min transport: add Connect() functionality * obfs4 transport: add Connect() and Prepare() * pkg/core: add core.go for ConjureHMAC function * transport (obfs4): generate shared keys in station * transport client: add keys to ClientTransport * transports (obfs4): move ClientTransport functions to client.go * transports (obfs4): remove ClientTransport functions * reg transport test: use relative path instead of gopath * transports test (min): use relative path instead of gopath * transports internal test: use relative path * min client: check conn.Write for err * min/obfs4 transports: rename dd -> cj, rename Connect * transport interface: change Prepare() -> PrepareKeys() * transport interface: move obfs4 key generation * transport interface: obfs4 key generation * minor fix --------- Co-authored-by: jmwample <[email protected]>
1 parent 107c48f commit affe724

File tree

9 files changed

+127
-31
lines changed

9 files changed

+127
-31
lines changed

application/registration_with_transport_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ func mockReceiveFromDetector() (*pb.ClientToStation, cj.ConjureSharedKeys) {
3636
}
3737

3838
func TestManagerFunctionality(t *testing.T) {
39-
testSubnetPath := os.Getenv("GOPATH") + "/src/github.com/refraction-networking/conjure/application/lib/test/phantom_subnets.toml"
39+
cwd, _ := os.Getwd()
40+
testSubnetPath := cwd + "/lib/test/phantom_subnets.toml"
4041
os.Setenv("PHANTOM_SUBNET_LOCATION", testSubnetPath)
4142

4243
rm := cj.NewRegistrationManager(&cj.RegConfig{})

application/transports/wrapping/internal/tests/tests.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ var SharedSecret = []byte(`6a328b8ec2024dd92dd64332164cc0425ddbde40cb7b81e055bf7
2121
// SetupPhantomConnections registers one session with the provided transport and
2222
// registration manager using a pre-determined kay and phantom subnet file.
2323
func SetupPhantomConnections(manager *dd.RegistrationManager, transport pb.TransportType) (clientToPhantom net.Conn, serverFromPhantom net.Conn, reg *dd.DecoyRegistration) {
24-
testSubnetPath := os.Getenv("GOPATH") + "/src/github.com/refraction-networking/conjure/application/lib/test/phantom_subnets.toml"
24+
cwd, _ := os.Getwd()
25+
testSubnetPath := cwd + "/../internal/tests/phantom_subnets.toml"
2526
return SetupPhantomConnectionsSecret(manager, transport, SharedSecret, testSubnetPath)
2627
}
2728

application/transports/wrapping/min/client.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package min
22

33
import (
44
"fmt"
5+
"io"
6+
"net"
57

68
"github.com/refraction-networking/conjure/application/transports"
9+
core "github.com/refraction-networking/conjure/pkg/core"
710
pb "github.com/refraction-networking/gotapdance/protobuf"
811
"google.golang.org/protobuf/proto"
912
)
@@ -14,7 +17,7 @@ import (
1417
type ClientTransport struct {
1518
// Parameters are fields that will be shared with the station in the registration
1619
Parameters *pb.GenericTransportParams
17-
20+
connectTag []byte
1821
// // state tracks fields internal to the registrar that survive for the lifetime
1922
// // of the transport session without being shared - i.e. local derived keys.
2023
// state any
@@ -63,17 +66,18 @@ func (t *ClientTransport) GetDstPort(seed []byte, params any) (uint16, error) {
6366
return transports.PortSelectorRange(portRangeMin, portRangeMax, seed)
6467
}
6568

66-
// // Connect creates the connection to the phantom address negotiated in the registration phase of
67-
// // Conjure connection establishment.
68-
// func (t *ClientTransport) Connect(ctx context.Context, reg *cj.ConjureReg) (net.Conn, error) {
69-
// // conn, err := reg.getFirstConnection(ctx, reg.TcpDialer, phantoms)
70-
// // if err != nil {
71-
// // return nil, err
72-
// // }
69+
// WrapConn creates the connection to the phantom address negotiated in the registration phase of
70+
// Conjure connection establishment.
71+
func (t *ClientTransport) WrapConn(conn net.Conn) (net.Conn, error) {
72+
// Send hmac(seed, str) bytes to indicate to station (min transport) generated during Prepare(...)
73+
_, err := conn.Write(t.connectTag)
74+
if err != nil {
75+
return nil, err
76+
}
77+
return conn, nil
78+
}
7379

74-
// // // Send hmac(seed, str) bytes to indicate to station (min transport)
75-
// // connectTag := conjureHMAC(reg.keys.SharedSecret, "MinTrasportHMACString")
76-
// // conn.Write(connectTag)
77-
// // return conn, nil
78-
// return nil, nil
79-
// }
80+
func (t *ClientTransport) PrepareKeys(pubkey [32]byte, sharedSecret []byte, dRand io.Reader) error {
81+
t.connectTag = core.ConjureHMAC(sharedSecret, "MinTransportHMACString")
82+
return nil
83+
}

application/transports/wrapping/min/min.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"fmt"
66
"net"
77

8-
dd "github.com/refraction-networking/conjure/application/lib"
8+
cj "github.com/refraction-networking/conjure/application/lib"
99
"github.com/refraction-networking/conjure/application/transports"
1010
pb "github.com/refraction-networking/gotapdance/protobuf"
1111
"google.golang.org/protobuf/types/known/anypb"
@@ -37,7 +37,7 @@ func (Transport) LogPrefix() string { return "MIN" }
3737
// GetIdentifier takes in a registration and returns an identifier for it. This
3838
// identifier should be unique for each registration on a given phantom;
3939
// registrations on different phantoms can have the same identifier.
40-
func (Transport) GetIdentifier(d *dd.DecoyRegistration) string {
40+
func (Transport) GetIdentifier(d *cj.DecoyRegistration) string {
4141
return string(d.Keys.ConjureHMAC("MinTrasportHMACString"))
4242
}
4343

@@ -75,7 +75,7 @@ func (Transport) ParseParams(libVersion uint, data *anypb.Any) (any, error) {
7575
//
7676
// If the returned error is nil or non-nil and non-{ transports.ErrTryAgain,
7777
// transports.ErrNotTransport }, the caller may no longer use data or conn.
78-
func (Transport) WrapConnection(data *bytes.Buffer, c net.Conn, originalDst net.IP, regManager *dd.RegistrationManager) (*dd.DecoyRegistration, net.Conn, error) {
78+
func (Transport) WrapConnection(data *bytes.Buffer, c net.Conn, originalDst net.IP, regManager *cj.RegistrationManager) (*cj.DecoyRegistration, net.Conn, error) {
7979
if data.Len() < minTagLength {
8080
return nil, nil, transports.ErrTryAgain
8181
}

application/transports/wrapping/min/min_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import (
1717
)
1818

1919
func TestSuccessfulWrap(t *testing.T) {
20-
testSubnetPath := os.Getenv("GOPATH") + "/src/github.com/refraction-networking/conjure/application/lib/test/phantom_subnets.toml"
20+
cwd, _ := os.Getwd()
21+
testSubnetPath := cwd + "/../../../lib/test/phantom_subnets.toml"
2122
os.Setenv("PHANTOM_SUBNET_LOCATION", testSubnetPath)
2223

2324
var transport Transport
@@ -108,7 +109,7 @@ func TestTryParamsToDstPort(t *testing.T) {
108109
clv := randomizeDstPortMinVersion
109110
seed, _ := hex.DecodeString("0000000000000000000000000000000000")
110111

111-
cases := []struct{
112+
cases := []struct {
112113
r bool
113114
p uint16
114115
}{{true, 58047}, {false, 443}}

application/transports/wrapping/obfs4/client.go

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ package obfs4
22

33
import (
44
"fmt"
5+
"io"
6+
"net"
57

8+
pt "git.torproject.org/pluggable-transports/goptlib.git"
69
"github.com/refraction-networking/conjure/application/transports"
710
pb "github.com/refraction-networking/gotapdance/protobuf"
11+
"gitlab.com/yawning/obfs4.git/transports/obfs4"
812

913
"google.golang.org/protobuf/proto"
1014
)
@@ -14,6 +18,7 @@ import (
1418
// the station side Transport struct has one instance to be re-used for all sessions.
1519
type ClientTransport struct {
1620
Parameters *pb.GenericTransportParams
21+
keys Obfs4Keys
1722
}
1823

1924
// Name returns a string identifier for the Transport for logging
@@ -59,8 +64,39 @@ func (t *ClientTransport) GetDstPort(seed []byte, params any) (uint16, error) {
5964
return transports.PortSelectorRange(portRangeMin, portRangeMax, seed)
6065
}
6166

62-
// // Connect creates the connection to the phantom address negotiated in the registration phase of
63-
// // Conjure connection establishment.
64-
// func (*ClientTransport) Connect(ctx context.Context, reg *cj.ConjureReg) (net.Conn, error) {
65-
// return nil, nil
66-
// }
67+
// WrapConn creates the connection to the phantom address negotiated in the registration phase of
68+
// Conjure connection establishment.
69+
func (t ClientTransport) WrapConn(conn net.Conn) (net.Conn, error) {
70+
obfsTransport := obfs4.Transport{}
71+
args := pt.Args{}
72+
73+
args.Add("node-id", t.keys.NodeID.Hex())
74+
args.Add("public-key", t.keys.PublicKey.Hex())
75+
args.Add("iat-mode", "1")
76+
77+
c, err := obfsTransport.ClientFactory("")
78+
if err != nil {
79+
return nil, fmt.Errorf("failed to create client factory")
80+
}
81+
82+
parsedArgs, err := c.ParseArgs(&args)
83+
if err != nil {
84+
return nil, fmt.Errorf("failed to parse obfs4 args")
85+
}
86+
87+
d := func(network, address string) (net.Conn, error) {
88+
return conn, nil
89+
}
90+
91+
return c.Dial("tcp", "", d, parsedArgs)
92+
}
93+
94+
func (t *ClientTransport) PrepareKeys(pubkey [32]byte, sharedSecret []byte, dRand io.Reader) error {
95+
// Generate shared keys
96+
var err error
97+
t.keys, err = generateObfs4Keys(dRand)
98+
if err != nil {
99+
return err
100+
}
101+
return nil
102+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package obfs4
2+
3+
import (
4+
"io"
5+
6+
"gitlab.com/yawning/obfs4.git/common/ntor"
7+
"golang.org/x/crypto/curve25519"
8+
)
9+
10+
type Obfs4Keys struct {
11+
PrivateKey *ntor.PrivateKey
12+
PublicKey *ntor.PublicKey
13+
NodeID *ntor.NodeID
14+
}
15+
16+
func generateObfs4Keys(rand io.Reader) (Obfs4Keys, error) {
17+
keys := Obfs4Keys{
18+
PrivateKey: new(ntor.PrivateKey),
19+
PublicKey: new(ntor.PublicKey),
20+
NodeID: new(ntor.NodeID),
21+
}
22+
23+
_, err := rand.Read(keys.PrivateKey[:])
24+
if err != nil {
25+
return keys, err
26+
}
27+
28+
keys.PrivateKey[0] &= 248
29+
keys.PrivateKey[31] &= 127
30+
keys.PrivateKey[31] |= 64
31+
32+
pub, err := curve25519.X25519(keys.PrivateKey[:], curve25519.Basepoint)
33+
if err != nil {
34+
return keys, err
35+
}
36+
copy(keys.PublicKey[:], pub)
37+
38+
_, err = rand.Read(keys.NodeID[:])
39+
return keys, err
40+
}

application/transports/wrapping/obfs4/obfs4.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"net"
77

88
pt "git.torproject.org/pluggable-transports/goptlib.git"
9-
dd "github.com/refraction-networking/conjure/application/lib"
9+
cj "github.com/refraction-networking/conjure/application/lib"
1010
"github.com/refraction-networking/conjure/application/transports"
1111
pb "github.com/refraction-networking/gotapdance/protobuf"
1212
"gitlab.com/yawning/obfs4.git/common/drbg"
@@ -34,7 +34,7 @@ func (Transport) Name() string { return "obfs4" }
3434
func (Transport) LogPrefix() string { return "OBFS4" }
3535

3636
// GetIdentifier implements the station Transport interface
37-
func (Transport) GetIdentifier(r *dd.DecoyRegistration) string {
37+
func (Transport) GetIdentifier(r *cj.DecoyRegistration) string {
3838
return string(r.Keys.Obfs4Keys.PublicKey.Bytes()[:]) + string(r.Keys.Obfs4Keys.NodeID.Bytes()[:])
3939
}
4040

@@ -66,7 +66,7 @@ func (Transport) ParseParams(libVersion uint, data *anypb.Any) (any, error) {
6666
}
6767

6868
// WrapConnection implements the station Transport interface
69-
func (Transport) WrapConnection(data *bytes.Buffer, c net.Conn, phantom net.IP, regManager *dd.RegistrationManager) (*dd.DecoyRegistration, net.Conn, error) {
69+
func (Transport) WrapConnection(data *bytes.Buffer, c net.Conn, phantom net.IP, regManager *cj.RegistrationManager) (*cj.DecoyRegistration, net.Conn, error) {
7070
if data.Len() < ClientMinHandshakeLength {
7171
return nil, nil, transports.ErrTryAgain
7272
}
@@ -119,8 +119,8 @@ func (Transport) WrapConnection(data *bytes.Buffer, c net.Conn, phantom net.IP,
119119
// This function makes the assumption that any identifier with length 52 is an obfs4 registration.
120120
// This may not be strictly true, but any other identifier will simply fail to form a connection and
121121
// should be harmless.
122-
func getObfs4Registrations(regManager *dd.RegistrationManager, darkDecoyAddr net.IP) []*dd.DecoyRegistration {
123-
var regs []*dd.DecoyRegistration
122+
func getObfs4Registrations(regManager *cj.RegistrationManager, darkDecoyAddr net.IP) []*cj.DecoyRegistration {
123+
var regs []*cj.DecoyRegistration
124124

125125
for identifier, r := range regManager.GetRegistrations(darkDecoyAddr) {
126126
if len(identifier) == ntor.PublicKeyLength+ntor.NodeIDLength {

pkg/core/core.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package core
2+
3+
import (
4+
"crypto/hmac"
5+
"crypto/sha256"
6+
)
7+
8+
// ConjureHMAC implements the hmak that can then be used for further hkdf key generation
9+
func ConjureHMAC(key []byte, str string) []byte {
10+
hash := hmac.New(sha256.New, key)
11+
hash.Write([]byte(str))
12+
return hash.Sum(nil)
13+
}

0 commit comments

Comments
 (0)