summaryrefslogtreecommitdiff
path: root/transports
diff options
context:
space:
mode:
authorYawning Angel <yawning@torproject.org>2014-10-01 19:00:30 +0000
committerYawning Angel <yawning@torproject.org>2014-10-01 19:00:30 +0000
commit6cd81ec42f203585c59e610dc16728cb0a5d1455 (patch)
tree28e16d3446e40c2ad6a1b2d5aa46006704a616fb /transports
parent213495d3b9f6d41771751f5ab76711a225f05d9b (diff)
Change the bridge line format to be more compact.
Instead of "node-id" and "public-key" that are Base16 encoded, use "cert" which contains the "node-id" and "public-key" in Base64 encoded form. This is more compact and cuts the length down by 49 characters.
Diffstat (limited to 'transports')
-rw-r--r--transports/obfs4/obfs4.go51
-rw-r--r--transports/obfs4/statefile.go56
2 files changed, 85 insertions, 22 deletions
diff --git a/transports/obfs4/obfs4.go b/transports/obfs4/obfs4.go
index b862e5a..256f549 100644
--- a/transports/obfs4/obfs4.go
+++ b/transports/obfs4/obfs4.go
@@ -57,6 +57,7 @@ const (
privateKeyArg = "private-key"
seedArg = "drbg-seed"
iatArg = "iat-mode"
+ certArg = "cert"
biasCmdArg = "obfs4-distBias"
@@ -122,8 +123,7 @@ func (t *Transport) ServerFactory(stateDir string, args *pt.Args) (base.ServerFa
// Store the arguments that should appear in our descriptor for the clients.
ptArgs := pt.Args{}
- ptArgs.Add(nodeIDArg, st.nodeID.Hex())
- ptArgs.Add(publicKeyArg, st.identityKey.Public().Hex())
+ ptArgs.Add(certArg, st.cert.String())
ptArgs.Add(iatArg, strconv.Itoa(st.iatMode))
// Initialize the replay filter.
@@ -154,15 +154,39 @@ func (cf *obfs4ClientFactory) Transport() base.Transport {
func (cf *obfs4ClientFactory) ParseArgs(args *pt.Args) (interface{}, error) {
var err error
- // Handle the arguments.
- nodeIDStr, ok := args.Get(nodeIDArg)
- if !ok {
- return nil, fmt.Errorf("missing argument '%s'", nodeIDArg)
- }
var nodeID *ntor.NodeID
- if nodeID, err = ntor.NodeIDFromHex(nodeIDStr); err != nil {
- return nil, err
+ var publicKey *ntor.PublicKey
+
+ // The "new" (version >= 0.0.3) bridge lines use a unified "cert" argument
+ // for the Node ID and Public Key.
+ certStr, ok := args.Get(certArg)
+ if ok {
+ var cert *obfs4ServerCert
+ if cert, err = serverCertFromString(certStr); err != nil {
+ return nil, err
+ }
+ nodeID, publicKey = cert.unpack()
+ } else {
+ // The "old" style (version <= 0.0.2) bridge lines use separate Node ID
+ // and Public Key arguments in Base16 encoding and are a UX disaster.
+ nodeIDStr, ok := args.Get(nodeIDArg)
+ if !ok {
+ return nil, fmt.Errorf("missing argument '%s'", nodeIDArg)
+ }
+ if nodeID, err = ntor.NodeIDFromHex(nodeIDStr); err != nil {
+ return nil, err
+ }
+
+ publicKeyStr, ok := args.Get(publicKeyArg)
+ if !ok {
+ return nil, fmt.Errorf("missing argument '%s'", publicKeyArg)
+ }
+ if publicKey, err = ntor.PublicKeyFromHex(publicKeyStr); err != nil {
+ return nil, err
+ }
}
+
+ // IAT config is common across the two bridge line formats.
iatStr, ok := args.Get(iatArg)
if !ok {
return nil, fmt.Errorf("missing argument '%s'", iatArg)
@@ -173,15 +197,6 @@ func (cf *obfs4ClientFactory) ParseArgs(args *pt.Args) (interface{}, error) {
return nil, fmt.Errorf("invalid iat-mode '%d'", iatMode)
}
- publicKeyStr, ok := args.Get(publicKeyArg)
- if !ok {
- return nil, fmt.Errorf("missing argument '%s'", publicKeyArg)
- }
- var publicKey *ntor.PublicKey
- if publicKey, err = ntor.PublicKeyFromHex(publicKeyStr); err != nil {
- return nil, err
- }
-
// Generate the session key pair before connectiong to hide the Elligator2
// rejection sampling from network observers.
sessionKey, err := ntor.NewKeypair(true)
diff --git a/transports/obfs4/statefile.go b/transports/obfs4/statefile.go
index 1e00f32..0838180 100644
--- a/transports/obfs4/statefile.go
+++ b/transports/obfs4/statefile.go
@@ -28,12 +28,14 @@
package obfs4
import (
+ "encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
+ "strings"
"git.torproject.org/pluggable-transports/goptlib.git"
"git.torproject.org/pluggable-transports/obfs4.git/common/csrand"
@@ -44,6 +46,9 @@ import (
const (
stateFile = "obfs4_state.json"
bridgeFile = "obfs4_bridgeline.txt"
+
+ certSuffix = "=="
+ certLength = ntor.NodeIDLength + ntor.PublicKeyLength
)
type jsonServerState struct {
@@ -54,11 +59,55 @@ type jsonServerState struct {
IATMode int `json:"iat-mode"`
}
+type obfs4ServerCert struct {
+ raw []byte
+}
+
+func (cert *obfs4ServerCert) String() string {
+ return strings.TrimSuffix(base64.StdEncoding.EncodeToString(cert.raw), certSuffix)
+}
+
+func (cert *obfs4ServerCert) unpack() (*ntor.NodeID, *ntor.PublicKey) {
+ if len(cert.raw) != certLength {
+ panic(fmt.Sprintf("cert length %d is invalid", len(cert.raw)))
+ }
+
+ nodeID, _ := ntor.NewNodeID(cert.raw[:ntor.NodeIDLength])
+ pubKey, _ := ntor.NewPublicKey(cert.raw[ntor.NodeIDLength:])
+
+ return nodeID, pubKey
+}
+
+func serverCertFromString(encoded string) (*obfs4ServerCert, error) {
+ decoded, err := base64.StdEncoding.DecodeString(encoded + certSuffix)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode cert: %s", err)
+ }
+
+ if len(decoded) != certLength {
+ return nil, fmt.Errorf("cert length %d is invalid", len(decoded))
+ }
+
+ return &obfs4ServerCert{raw: decoded}, nil
+}
+
+func serverCertFromState(st *obfs4ServerState) *obfs4ServerCert {
+ cert := new(obfs4ServerCert)
+ cert.raw = append(st.nodeID.Bytes()[:], st.identityKey.Public().Bytes()[:]...)
+ return cert
+}
+
type obfs4ServerState struct {
nodeID *ntor.NodeID
identityKey *ntor.Keypair
drbgSeed *drbg.Seed
iatMode int
+
+ cert *obfs4ServerCert
+}
+
+func (st *obfs4ServerState) clientString() string {
+ return fmt.Sprintf("%s=%s %s=%d", certArg, st.cert, iatArg, st.iatMode)
}
func serverStateFromArgs(stateDir string, args *pt.Args) (*obfs4ServerState, error) {
@@ -112,6 +161,7 @@ func serverStateFromJSONServerState(stateDir string, js *jsonServerState) (*obfs
return nil, fmt.Errorf("invalid iat-mode '%d'", js.IATMode)
}
st.iatMode = js.IATMode
+ st.cert = serverCertFromState(st)
// Generate a human readable summary of the configured endpoint.
if err = newBridgeFile(stateDir, st); err != nil {
@@ -190,10 +240,8 @@ func newBridgeFile(stateDir string, st *obfs4ServerState) (err error) {
"# <PORT> - The TCP/IP port of your obfs4 bridge.\n" +
"# <FINGERPRINT> - The bridge's fingerprint.\n\n"
- bridgeLine := fmt.Sprintf("Bridge obfs4 <IP ADDRESS>:<PORT> <FINGERPRINT> node-id=%s public-key=%s iat-mode=%d\n",
- st.nodeID.Hex(),
- st.identityKey.Public().Hex(),
- st.iatMode)
+ bridgeLine := fmt.Sprintf("Bridge obfs4 <IP ADDRESS>:<PORT> <FINGERPRINT> %s\n",
+ st.clientString())
tmp := []byte(prefix + bridgeLine)
if err = ioutil.WriteFile(path.Join(stateDir, bridgeFile), tmp, 0600); err != nil {