connectivity/tor/torrcBuilder.go

87 lines
2.3 KiB
Go

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)))
}