summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Whited <sam@samwhited.com>2022-03-11 12:39:28 -0500
committerSam Whited <sam@samwhited.com>2022-03-11 12:41:34 -0500
commit724b17ec5b25b57b477fd936ad8e52a4f18761e5 (patch)
tree404c39ac564833df5a0fdef14650ba70355a2d99
parent263e27bfa13147a84377790fce14f5a5b5845994 (diff)
obfsvpn: support gateway certificates
Support creating dialers and listeners from the newer gateway certificates, as well as setting the cert argument when connecting. Signed-off-by: Sam Whited <sam@samwhited.com>
-rw-r--r--dialer.go55
-rw-r--r--listener.go13
2 files changed, 66 insertions, 2 deletions
diff --git a/dialer.go b/dialer.go
index a7bd075..444e0c2 100644
--- a/dialer.go
+++ b/dialer.go
@@ -2,6 +2,7 @@ package obfsvpn
import (
"context"
+ "encoding/base64"
"fmt"
"net"
"strconv"
@@ -16,6 +17,11 @@ const (
ptArgNode = "node-id"
ptArgKey = "public-key"
ptArgMode = "iat-mode"
+ ptArgCert = "cert"
+)
+
+const (
+ certLength = ntor.NodeIDLength + ntor.PublicKeyLength
)
// IATMode determines the amount of time sent between packets.
@@ -42,8 +48,49 @@ type Dialer struct {
cf base.ClientFactory
}
-// NewDialer creates a dialer from existing pluggable transport arguments.
-func NewDialer(args pt.Args) (*Dialer, error) {
+func packCert(node *ntor.NodeID, public *ntor.PublicKey) string {
+ cert := make([]byte, 0, certLength)
+ cert = append(cert, node[:]...)
+ cert = append(cert, public[:]...)
+
+ return base64.RawStdEncoding.EncodeToString(cert)
+}
+
+func unpackCert(cert string) (*ntor.NodeID, *ntor.PublicKey, error) {
+ if l := base64.RawStdEncoding.DecodedLen(len(cert)); l != certLength {
+ return nil, nil, fmt.Errorf("cert length %d is invalid", l)
+ }
+ decoded, err := base64.RawStdEncoding.DecodeString(cert)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ nodeID, err := ntor.NewNodeID(decoded[:ntor.NodeIDLength])
+ if err != nil {
+ return nil, nil, err
+ }
+ pubKey, err := ntor.NewPublicKey(decoded[ntor.NodeIDLength:])
+ if err != nil {
+ return nil, nil, err
+ }
+ return nodeID, pubKey, nil
+}
+
+// NewDialerCert creates a dialer from a node certificate.
+func NewDialerCert(cert string) (*Dialer, error) {
+ nodeID, publicKey, err := unpackCert(cert)
+
+ if err != nil {
+ return nil, err
+ }
+ return &Dialer{
+ NodeID: nodeID,
+ PublicKey: publicKey,
+ }, nil
+}
+
+// NewDialerArgs creates a dialer from existing pluggable transport arguments.
+func NewDialerArgs(args pt.Args) (*Dialer, error) {
cf, err := (&obfs4.Transport{}).ClientFactory("")
if err != nil {
return nil, err
@@ -93,12 +140,16 @@ func (d *Dialer) Dial(ctx context.Context, network, address string) (net.Conn, e
}
// Args returns the dialers options as pluggable transport arguments.
+// The args include valid args for the "new" (version >= 0.0.3) bridge lines
+// that use a unified "cert" argument as well as the legacy lines that use a
+// separate Node ID and Public Key.
func (d *Dialer) Args() pt.Args {
if d.ptArgs == nil {
d.ptArgs = make(pt.Args)
d.ptArgs.Add(ptArgNode, d.NodeID.Hex())
d.ptArgs.Add(ptArgKey, d.PublicKey.Hex())
d.ptArgs.Add(ptArgMode, strconv.Itoa(int(d.IATMode)))
+ d.ptArgs.Add(ptArgCert, packCert(d.NodeID, d.PublicKey))
}
return d.ptArgs
}
diff --git a/listener.go b/listener.go
index 67a25e7..4163cb9 100644
--- a/listener.go
+++ b/listener.go
@@ -25,6 +25,19 @@ type ListenConfig struct {
StateDir string
}
+// NewListenConfigCert creates a listener config by unpacking the node ID from
+// its certificate.
+// The private key must still be specified.
+func NewListenConfigCert(cert string) (*ListenConfig, error) {
+ nodeID, _, err := unpackCert(cert)
+ if err != nil {
+ return nil, err
+ }
+ return &ListenConfig{
+ NodeID: nodeID,
+ }, nil
+}
+
// Listen announces on the local network address.
//
// See func net.Dial for a description of the network and address parameters.