forked from openprivacy/connectivity
Better Torrc Builder
This commit is contained in:
parent
6f2a158504
commit
ba56a6cb48
|
@ -3,6 +3,7 @@ package testing
|
||||||
import (
|
import (
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -10,9 +11,13 @@ import (
|
||||||
|
|
||||||
func TestLaunchTor(t *testing.T) {
|
func TestLaunchTor(t *testing.T) {
|
||||||
log.SetLevel(log.LevelDebug)
|
log.SetLevel(log.LevelDebug)
|
||||||
|
|
||||||
|
controlPort := rand.Intn(1000) + 9052
|
||||||
|
password := "examplehashedpassword"
|
||||||
|
|
||||||
// Create the tor data directory if it doesn't already exist..
|
// Create the tor data directory if it doesn't already exist..
|
||||||
os.MkdirAll("../tmp/data/tor", 0700)
|
os.MkdirAll("../tmp/data/tor", 0700)
|
||||||
err := tor.GenerateTorrc("examplehashedpassword", "../tmp/data/tor/torrc")
|
err := tor.NewTorrc().WithHashedPassword(password).WithControlPort(controlPort).Build("../tmp/data/tor/torrc")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create torrc file: %v", err)
|
t.Fatalf("failed to create torrc file: %v", err)
|
||||||
|
@ -21,7 +26,7 @@ func TestLaunchTor(t *testing.T) {
|
||||||
// Get the current working director, clean the paths to remove relative references
|
// Get the current working director, clean the paths to remove relative references
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
t.Logf("Launching bundled tor at %v", path.Clean(wd+"/../tmp/tor"))
|
t.Logf("Launching bundled tor at %v", path.Clean(wd+"/../tmp/tor"))
|
||||||
acn, err := tor.NewTorACNWithAuth(path.Clean(wd+"/../tmp/data"), path.Clean(wd+"/../tmp/tor"), 9051, tor.HashedPasswordAuthenticator{Password: "examplehashedpassword"})
|
acn, err := tor.NewTorACNWithAuth(path.Clean(wd+"/../tmp/data"), path.Clean(wd+"/../tmp/tor"), controlPort, tor.HashedPasswordAuthenticator{Password: password})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tor failed to start: %v", err)
|
t.Fatalf("tor failed to start: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
package tor
|
package tor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/cretz/bine/control"
|
"github.com/cretz/bine/control"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,39 +81,3 @@ type NullAuthenticator struct {
|
||||||
func (n NullAuthenticator) Authenticate(controlport *control.Conn) error {
|
func (n NullAuthenticator) Authenticate(controlport *control.Conn) error {
|
||||||
return errors.New("null authenticator provided, this control port is unsafe, start a new tor process instead")
|
return errors.New("null authenticator provided, this control port is unsafe, start a new tor process instead")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateHashedPassword calculates a hash in the same waay tha tor --hash-password does
|
|
||||||
// this function takes a salt as input which is not great from an api-misuse perspective, but
|
|
||||||
// we make it private.
|
|
||||||
func generateHashedPassword(salt [8]byte, password string) string {
|
|
||||||
c := 96
|
|
||||||
count := (16 + (c & 15)) << ((c >> 4) + 6)
|
|
||||||
tmp := append(salt[:], []byte(password)...)
|
|
||||||
slen := len(tmp)
|
|
||||||
d := sha1.New()
|
|
||||||
for count != 0 {
|
|
||||||
if count > slen {
|
|
||||||
d.Write(tmp)
|
|
||||||
count -= slen
|
|
||||||
} else {
|
|
||||||
d.Write(tmp[:count])
|
|
||||||
count = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hashed := d.Sum([]byte{})
|
|
||||||
return fmt.Sprintf("16:%s%s%s", strings.ToUpper(hex.EncodeToString(salt[:])),
|
|
||||||
strings.ToUpper(hex.EncodeToString([]byte{byte(c)})),
|
|
||||||
strings.ToUpper(hex.EncodeToString(hashed)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateTorrc generates a Tor config file with the control port bieng guarded by a hash of the given
|
|
||||||
// password.
|
|
||||||
func GenerateTorrc(password string, path string) error {
|
|
||||||
var salt [8]byte
|
|
||||||
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hashedpassword := generateHashedPassword(salt, password)
|
|
||||||
contents := fmt.Sprintf("SOCKSPort 9050\nControlPort 9051\nHashedControlPassword %s", hashedpassword)
|
|
||||||
return ioutil.WriteFile(path, []byte(contents), 0600)
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ func TestGenerateHashedPassword(t *testing.T) {
|
||||||
|
|
||||||
func TestGenerateTorrc(t *testing.T) {
|
func TestGenerateTorrc(t *testing.T) {
|
||||||
path := "./torrc.test"
|
path := "./torrc.test"
|
||||||
err := GenerateTorrc("examplehashedpassword", path)
|
password := "examplehashedpassword"
|
||||||
|
err := NewTorrc().WithHashedPassword(password).Build(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Torrc file could not be written")
|
t.Errorf("Torrc file could not be written")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package tor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TorrcBuilder is a a helper for building torrc files
|
||||||
|
type TorrcBuilder struct {
|
||||||
|
lines []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTorrc creates a new torrc builder
|
||||||
|
func NewTorrc() *TorrcBuilder {
|
||||||
|
return &TorrcBuilder{
|
||||||
|
lines: []string{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSocksPort sets the SOCKS port of the tor process
|
||||||
|
func (tb *TorrcBuilder) WithSocksPort(port int) *TorrcBuilder {
|
||||||
|
tb.lines = append(tb.lines, fmt.Sprintf("SocksPort %v", port))
|
||||||
|
return tb
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithControlPort sets the control port of the tor process
|
||||||
|
func (tb *TorrcBuilder) WithControlPort(port int) *TorrcBuilder {
|
||||||
|
tb.lines = append(tb.lines, fmt.Sprintf("ControlPort %v", port))
|
||||||
|
return tb
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOnionTrafficOnly ensures that the tor process only routes tor onion traffic.
|
||||||
|
func (tb *TorrcBuilder) WithOnionTrafficOnly() *TorrcBuilder {
|
||||||
|
for i, line := range tb.lines {
|
||||||
|
if strings.HasPrefix(line, "SocksPort") {
|
||||||
|
tb.lines[i] = fmt.Sprintf("%v OnionTrafficOnly", line)
|
||||||
|
return tb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tb
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHashedPassword sets a password for the control port.
|
||||||
|
func (tb *TorrcBuilder) WithHashedPassword(password string) *TorrcBuilder {
|
||||||
|
var salt [8]byte
|
||||||
|
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
|
||||||
|
panic("no randomness")
|
||||||
|
}
|
||||||
|
hashedpassword := generateHashedPassword(salt, password)
|
||||||
|
tb.lines = append(tb.lines, fmt.Sprintf("HashedControlPassword %s", hashedpassword))
|
||||||
|
return tb
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build finalizes the torrc contents and write a file
|
||||||
|
func (tb *TorrcBuilder) Build(path string) error {
|
||||||
|
return ioutil.WriteFile(path, []byte(strings.Join(tb.lines, "\n")), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateHashedPassword calculates a hash in the same way tha tor --hash-password does
|
||||||
|
// this function takes a salt as input which is not great from an api-misuse perspective, but
|
||||||
|
// we make it private.
|
||||||
|
func generateHashedPassword(salt [8]byte, password string) string {
|
||||||
|
c := 96
|
||||||
|
count := (16 + (c & 15)) << ((c >> 4) + 6)
|
||||||
|
tmp := append(salt[:], []byte(password)...)
|
||||||
|
slen := len(tmp)
|
||||||
|
d := sha1.New()
|
||||||
|
for count != 0 {
|
||||||
|
if count > slen {
|
||||||
|
d.Write(tmp)
|
||||||
|
count -= slen
|
||||||
|
} else {
|
||||||
|
d.Write(tmp[:count])
|
||||||
|
count = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashed := d.Sum([]byte{})
|
||||||
|
return fmt.Sprintf("16:%s%s%s", strings.ToUpper(hex.EncodeToString(salt[:])),
|
||||||
|
strings.ToUpper(hex.EncodeToString([]byte{byte(c)})),
|
||||||
|
strings.ToUpper(hex.EncodeToString(hashed)))
|
||||||
|
}
|
Loading…
Reference in New Issue