Several fixes including vendoring ed25519 code under torutil

This commit is contained in:
Chad Retz 2018-05-16 17:37:42 -05:00
parent db2d028c4a
commit 4d7c47a3d4
17 changed files with 3494 additions and 104 deletions

View File

@ -3,7 +3,7 @@ package control
import (
"strings"
"github.com/cretz/bine/util"
"github.com/cretz/bine/torutil"
)
// SetConf invokes SETCONF.
@ -20,7 +20,7 @@ func (c *Conn) sendSetConf(cmd string, entries []*KeyVal) error {
for _, entry := range entries {
cmd += " " + entry.Key
if entry.ValSet() {
cmd += "=" + util.EscapeSimpleQuotedStringIfNeeded(entry.Val)
cmd += "=" + torutil.EscapeSimpleQuotedStringIfNeeded(entry.Val)
}
}
return c.sendRequestIgnoreResponse(cmd)
@ -35,10 +35,10 @@ func (c *Conn) GetConf(keys ...string) ([]*KeyVal, error) {
data := resp.DataWithReply()
ret := make([]*KeyVal, 0, len(data))
for _, data := range data {
key, val, ok := util.PartitionString(data, '=')
key, val, ok := torutil.PartitionString(data, '=')
entry := &KeyVal{Key: key}
if ok {
if entry.Val, err = util.UnescapeSimpleQuotedStringIfNeeded(val); err != nil {
if entry.Val, err = torutil.UnescapeSimpleQuotedStringIfNeeded(val); err != nil {
return nil, err
}
if len(entry.Val) == 0 {

View File

@ -7,7 +7,7 @@ import (
"strings"
"time"
"github.com/cretz/bine/util"
"github.com/cretz/bine/torutil"
)
// EventCode represents an asynchronous event code (ref control spec 4.1).
@ -120,6 +120,7 @@ func (c *Conn) EventWait(
ctx context.Context, events []EventCode, predicate func(Event) (bool, error),
) (Event, error) {
eventCh := make(chan Event, 10)
defer close(eventCh)
if err := c.AddEventListener(eventCh, events...); err != nil {
return nil, err
}
@ -212,7 +213,7 @@ func (c *Conn) AddEventListener(ch chan<- Event, events ...EventCode) error {
// no longer be listened to. If no events are provided, this is essentially a
// no-op.
func (c *Conn) RemoveEventListener(ch chan<- Event, events ...EventCode) error {
c.removeEventListenerFromMap(ch, events...)
return c.sendSetEvents()
}
@ -280,7 +281,7 @@ func (c *Conn) relayAsyncEvents(resp *Response) {
code, dataArray = resp.Data[0], resp.Data[1:]
} else {
// Otherwise, the reply line has the data
code, data, _ = util.PartitionString(resp.Reply, ' ')
code, data, _ = torutil.PartitionString(resp.Reply, ' ')
}
// Only relay if there are chans
eventCode := EventCode(code)
@ -413,14 +414,14 @@ type CircuitEvent struct {
// ParseCircuitEvent parses the event.
func ParseCircuitEvent(raw string) *CircuitEvent {
event := &CircuitEvent{Raw: raw}
event.CircuitID, raw, _ = util.PartitionString(raw, ' ')
event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ')
var ok bool
event.Status, raw, ok = util.PartitionString(raw, ' ')
event.Status, raw, ok = torutil.PartitionString(raw, ' ')
var attr string
first := true
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "BUILD_FLAGS":
event.BuildFlags = strings.Split(val, ",")
@ -472,19 +473,19 @@ type StreamEvent struct {
// ParseStreamEvent parses the event.
func ParseStreamEvent(raw string) *StreamEvent {
event := &StreamEvent{Raw: raw}
event.StreamID, raw, _ = util.PartitionString(raw, ' ')
event.Status, raw, _ = util.PartitionString(raw, ' ')
event.CircuitID, raw, _ = util.PartitionString(raw, ' ')
event.StreamID, raw, _ = torutil.PartitionString(raw, ' ')
event.Status, raw, _ = torutil.PartitionString(raw, ' ')
event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ')
var ok bool
event.TargetAddress, raw, ok = util.PartitionString(raw, ' ')
if target, port, hasPort := util.PartitionStringFromEnd(event.TargetAddress, ':'); hasPort {
event.TargetAddress, raw, ok = torutil.PartitionString(raw, ' ')
if target, port, hasPort := torutil.PartitionStringFromEnd(event.TargetAddress, ':'); hasPort {
event.TargetAddress = target
event.TargetPort, _ = strconv.Atoi(port)
}
var attr string
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "REASON":
event.Reason = val
@ -494,7 +495,7 @@ func ParseStreamEvent(raw string) *StreamEvent {
event.Source = val
case "SOURCE_ADDR":
event.SourceAddress = val
if source, port, hasPort := util.PartitionStringFromEnd(event.SourceAddress, ':'); hasPort {
if source, port, hasPort := torutil.PartitionStringFromEnd(event.SourceAddress, ':'); hasPort {
event.SourceAddress = source
event.SourcePort, _ = strconv.Atoi(port)
}
@ -521,13 +522,13 @@ type ORConnEvent struct {
// ParseORConnEvent parses the event.
func ParseORConnEvent(raw string) *ORConnEvent {
event := &ORConnEvent{Raw: raw}
event.Target, raw, _ = util.PartitionString(raw, ' ')
event.Target, raw, _ = torutil.PartitionString(raw, ' ')
var ok bool
event.Status, raw, ok = util.PartitionString(raw, ' ')
event.Status, raw, ok = torutil.PartitionString(raw, ' ')
var attr string
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "REASON":
event.Reason = val
@ -554,9 +555,9 @@ type BandwidthEvent struct {
func ParseBandwidthEvent(raw string) *BandwidthEvent {
event := &BandwidthEvent{Raw: raw}
var temp string
temp, raw, _ = util.PartitionString(raw, ' ')
temp, raw, _ = torutil.PartitionString(raw, ' ')
event.BytesRead, _ = strconv.ParseInt(temp, 10, 64)
temp, raw, _ = util.PartitionString(raw, ' ')
temp, raw, _ = torutil.PartitionString(raw, ' ')
event.BytesWritten, _ = strconv.ParseInt(temp, 10, 64)
return event
}
@ -607,23 +608,23 @@ type AddrMapEvent struct {
// ParseAddrMapEvent parses the event.
func ParseAddrMapEvent(raw string) *AddrMapEvent {
event := &AddrMapEvent{Raw: raw}
event.Address, raw, _ = util.PartitionString(raw, ' ')
event.NewAddress, raw, _ = util.PartitionString(raw, ' ')
event.Address, raw, _ = torutil.PartitionString(raw, ' ')
event.NewAddress, raw, _ = torutil.PartitionString(raw, ' ')
var ok bool
// Skip local expiration, use UTC one later
_, raw, ok = util.PartitionString(raw, ' ')
_, raw, ok = torutil.PartitionString(raw, ' ')
var attr string
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "error":
event.ErrorCode = val
case "EXPIRES":
val, _ = util.UnescapeSimpleQuotedString(val)
val, _ = torutil.UnescapeSimpleQuotedString(val)
event.Expires = parseISOTime(val)
case "CACHED":
event.Cached, _ = util.UnescapeSimpleQuotedStringIfNeeded(val)
event.Cached, _ = torutil.UnescapeSimpleQuotedStringIfNeeded(val)
}
}
return event
@ -657,14 +658,14 @@ type StatusEvent struct {
// ParseStatusEvent parses the event.
func ParseStatusEvent(typ EventCode, raw string) *StatusEvent {
event := &StatusEvent{Raw: raw, Type: typ, Arguments: map[string]string{}}
event.Severity, raw, _ = util.PartitionString(raw, ' ')
event.Severity, raw, _ = torutil.PartitionString(raw, ' ')
var ok bool
event.Action, raw, ok = util.PartitionString(raw, ' ')
event.Action, raw, ok = torutil.PartitionString(raw, ' ')
var attr string
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
event.Arguments[key], _ = util.UnescapeSimpleQuotedStringIfNeeded(val)
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
event.Arguments[key], _ = torutil.UnescapeSimpleQuotedStringIfNeeded(val)
}
return event
}
@ -683,9 +684,9 @@ type GuardEvent struct {
// ParseGuardEvent parses the event.
func ParseGuardEvent(raw string) *GuardEvent {
event := &GuardEvent{Raw: raw}
event.Type, raw, _ = util.PartitionString(raw, ' ')
event.Name, raw, _ = util.PartitionString(raw, ' ')
event.Status, raw, _ = util.PartitionString(raw, ' ')
event.Type, raw, _ = torutil.PartitionString(raw, ' ')
event.Name, raw, _ = torutil.PartitionString(raw, ' ')
event.Status, raw, _ = torutil.PartitionString(raw, ' ')
return event
}
@ -717,12 +718,12 @@ type StreamBandwidthEvent struct {
func ParseStreamBandwidthEvent(raw string) *StreamBandwidthEvent {
event := &StreamBandwidthEvent{Raw: raw}
var temp string
temp, raw, _ = util.PartitionString(raw, ' ')
temp, raw, _ = torutil.PartitionString(raw, ' ')
event.BytesRead, _ = strconv.ParseInt(temp, 10, 64)
temp, raw, _ = util.PartitionString(raw, ' ')
temp, raw, _ = torutil.PartitionString(raw, ' ')
event.BytesWritten, _ = strconv.ParseInt(temp, 10, 64)
temp, raw, _ = util.PartitionString(raw, ' ')
temp, _ = util.UnescapeSimpleQuotedString(temp)
temp, raw, _ = torutil.PartitionString(raw, ' ')
temp, _ = torutil.UnescapeSimpleQuotedString(temp)
event.Time = parseISOTime2Frac(temp)
return event
}
@ -743,21 +744,21 @@ func ParseClientsSeenEvent(raw string) *ClientsSeenEvent {
event := &ClientsSeenEvent{Raw: raw}
var temp string
var ok bool
temp, raw, ok = util.PartitionString(raw, ' ')
temp, _ = util.UnescapeSimpleQuotedString(temp)
temp, raw, ok = torutil.PartitionString(raw, ' ')
temp, _ = torutil.UnescapeSimpleQuotedString(temp)
event.TimeStarted = parseISOTime(temp)
strToMap := func(str string) map[string]int {
ret := map[string]int{}
for _, keyVal := range strings.Split(str, ",") {
key, val, _ := util.PartitionString(keyVal, '=')
key, val, _ := torutil.PartitionString(keyVal, '=')
ret[key], _ = strconv.Atoi(val)
}
return ret
}
var attr string
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "CountrySummary":
event.CountrySummary = strToMap(val)
@ -802,16 +803,16 @@ type BuildTimeoutSetEvent struct {
func ParseBuildTimeoutSetEvent(raw string) *BuildTimeoutSetEvent {
event := &BuildTimeoutSetEvent{Raw: raw}
var ok bool
event.Type, raw, ok = util.PartitionString(raw, ' ')
_, raw, ok = util.PartitionString(raw, ' ')
event.Type, raw, ok = torutil.PartitionString(raw, ' ')
_, raw, ok = torutil.PartitionString(raw, ' ')
var attr string
parseFloat := func(val string) float32 {
f, _ := strconv.ParseFloat(val, 32)
return float32(f)
}
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "TOTAL_TIMES":
event.TotalTimes, _ = strconv.Atoi(val)
@ -886,14 +887,14 @@ type CircuitMinorEvent struct {
// ParseCircuitMinorEvent parses the event.
func ParseCircuitMinorEvent(raw string) *CircuitMinorEvent {
event := &CircuitMinorEvent{Raw: raw}
event.CircuitID, raw, _ = util.PartitionString(raw, ' ')
event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ')
var ok bool
event.Event, raw, ok = util.PartitionString(raw, ' ')
event.Event, raw, ok = torutil.PartitionString(raw, ' ')
var attr string
first := true
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "BUILD_FLAGS":
event.BuildFlags = strings.Split(val, ",")
@ -934,11 +935,11 @@ type TransportLaunchedEvent struct {
// ParseTransportLaunchedEvent parses the event.
func ParseTransportLaunchedEvent(raw string) *TransportLaunchedEvent {
event := &TransportLaunchedEvent{Raw: raw}
event.Type, raw, _ = util.PartitionString(raw, ' ')
event.Name, raw, _ = util.PartitionString(raw, ' ')
event.Address, raw, _ = util.PartitionString(raw, ' ')
event.Type, raw, _ = torutil.PartitionString(raw, ' ')
event.Name, raw, _ = torutil.PartitionString(raw, ' ')
event.Address, raw, _ = torutil.PartitionString(raw, ' ')
var temp string
temp, raw, _ = util.PartitionString(raw, ' ')
temp, raw, _ = torutil.PartitionString(raw, ' ')
event.Port, _ = strconv.Atoi(temp)
return event
}
@ -961,8 +962,8 @@ func ParseConnBandwidthEvent(raw string) *ConnBandwidthEvent {
ok := true
var attr string
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "ID":
event.ConnID = val
@ -995,8 +996,8 @@ func ParseCircuitBandwidthEvent(raw string) *CircuitBandwidthEvent {
ok := true
var attr string
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "ID":
event.CircuitID = val
@ -1038,14 +1039,14 @@ func ParseCellStatsEvent(raw string) *CellStatsEvent {
toIntMap := func(val string) map[string]int {
ret := map[string]int{}
for _, v := range strings.Split(val, ",") {
key, val, _ := util.PartitionString(v, ':')
key, val, _ := torutil.PartitionString(v, ':')
ret[key], _ = strconv.Atoi(val)
}
return ret
}
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "ID":
event.CircuitID = val
@ -1091,11 +1092,11 @@ type TokenBucketEmptyEvent struct {
func ParseTokenBucketEmptyEvent(raw string) *TokenBucketEmptyEvent {
event := &TokenBucketEmptyEvent{Raw: raw}
var ok bool
event.BucketName, raw, ok = util.PartitionString(raw, ' ')
event.BucketName, raw, ok = torutil.PartitionString(raw, ' ')
var attr string
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, _ := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, _ := torutil.PartitionString(attr, '=')
switch key {
case "ID":
event.ConnID = val
@ -1132,16 +1133,16 @@ type HSDescEvent struct {
// ParseHSDescEvent parses the event.
func ParseHSDescEvent(raw string) *HSDescEvent {
event := &HSDescEvent{Raw: raw}
event.Action, raw, _ = util.PartitionString(raw, ' ')
event.Address, raw, _ = util.PartitionString(raw, ' ')
event.AuthType, raw, _ = util.PartitionString(raw, ' ')
event.Action, raw, _ = torutil.PartitionString(raw, ' ')
event.Address, raw, _ = torutil.PartitionString(raw, ' ')
event.AuthType, raw, _ = torutil.PartitionString(raw, ' ')
var ok bool
event.HSDir, raw, ok = util.PartitionString(raw, ' ')
event.HSDir, raw, ok = torutil.PartitionString(raw, ' ')
var attr string
first := true
for ok {
attr, raw, ok = util.PartitionString(raw, ' ')
key, val, valOk := util.PartitionString(attr, '=')
attr, raw, ok = torutil.PartitionString(raw, ' ')
key, val, valOk := torutil.PartitionString(attr, '=')
switch key {
case "REASON":
event.Reason = val
@ -1174,8 +1175,8 @@ type HSDescContentEvent struct {
// ParseHSDescContentEvent parses the event.
func ParseHSDescContentEvent(raw string) *HSDescContentEvent {
event := &HSDescContentEvent{Raw: raw}
event.Address, raw, _ = util.PartitionString(raw, ' ')
event.DescID, raw, _ = util.PartitionString(raw, ' ')
event.Address, raw, _ = torutil.PartitionString(raw, ' ')
event.DescID, raw, _ = torutil.PartitionString(raw, ' ')
newlineIndex := strings.Index(raw, "\r\n")
if newlineIndex != -1 {
event.HSDir, event.Descriptor = raw[:newlineIndex], raw[newlineIndex+2:]

View File

@ -3,7 +3,7 @@ package control
import (
"strings"
"github.com/cretz/bine/util"
"github.com/cretz/bine/torutil"
)
// Signal invokes SIGNAL.
@ -30,7 +30,7 @@ func (c *Conn) MapAddresses(addresses ...*KeyVal) ([]*KeyVal, error) {
ret := make([]*KeyVal, 0, len(data))
for _, address := range data {
mappedAddress := &KeyVal{}
mappedAddress.Key, mappedAddress.Val, _ = util.PartitionString(address, '=')
mappedAddress.Key, mappedAddress.Val, _ = torutil.PartitionString(address, '=')
ret = append(ret, mappedAddress)
}
return ret, nil
@ -45,8 +45,8 @@ func (c *Conn) GetInfo(keys ...string) ([]*KeyVal, error) {
ret := make([]*KeyVal, 0, len(resp.Data))
for _, val := range resp.Data {
infoVal := &KeyVal{}
infoVal.Key, infoVal.Val, _ = util.PartitionString(val, '=')
if infoVal.Val, err = util.UnescapeSimpleQuotedStringIfNeeded(infoVal.Val); err != nil {
infoVal.Key, infoVal.Val, _ = torutil.PartitionString(val, '=')
if infoVal.Val, err = torutil.UnescapeSimpleQuotedStringIfNeeded(infoVal.Val); err != nil {
return nil, err
}
ret = append(ret, infoVal)

View File

@ -8,8 +8,8 @@ import (
"strconv"
"strings"
"github.com/cretz/bine/util"
"golang.org/x/crypto/ed25519"
"github.com/cretz/bine/torutil"
"github.com/cretz/bine/torutil/ed25519"
)
// KeyType is a key type for Key in AddOnion.
@ -47,7 +47,7 @@ type Key interface {
// KeyFromString creates a Key for AddOnion based on a response string.
func KeyFromString(str string) (Key, error) {
typ, blob, _ := util.PartitionString(str, ':')
typ, blob, _ := torutil.PartitionString(str, ':')
switch KeyType(typ) {
case KeyTypeNew:
return GenKeyFromBlob(blob), nil
@ -176,7 +176,7 @@ func (c *Conn) AddOnion(req *AddOnionRequest) (*AddOnionResponse, error) {
}
ret := &AddOnionResponse{RawResponse: resp}
for _, data := range resp.Data {
key, val, _ := util.PartitionString(data, '=')
key, val, _ := torutil.PartitionString(data, '=')
switch key {
case "ServiceID":
ret.ServiceID = val
@ -185,7 +185,7 @@ func (c *Conn) AddOnion(req *AddOnionRequest) (*AddOnionResponse, error) {
return nil, err
}
case "ClientAuth":
name, pass, _ := util.PartitionString(val, ':')
name, pass, _ := torutil.PartitionString(val, ':')
if ret.ClientAuths == nil {
ret.ClientAuths = map[string]string{}
}

View File

@ -3,7 +3,7 @@ package control
import (
"strings"
"github.com/cretz/bine/util"
"github.com/cretz/bine/torutil"
)
// ProtocolInfo is the protocol info result of Conn.ProtocolInfo.
@ -42,7 +42,7 @@ func (c *Conn) sendProtocolInfo() (*ProtocolInfo, error) {
// Check data vals
ret := &ProtocolInfo{RawResponse: resp}
for _, piece := range resp.Data {
key, val, ok := util.PartitionString(piece, ' ')
key, val, ok := torutil.PartitionString(piece, ' ')
if !ok {
continue
}
@ -52,7 +52,7 @@ func (c *Conn) sendProtocolInfo() (*ProtocolInfo, error) {
return nil, c.protoErr("Invalid PIVERSION: %v", val)
}
case "AUTH":
methods, cookieFile, _ := util.PartitionString(val, ' ')
methods, cookieFile, _ := torutil.PartitionString(val, ' ')
if !strings.HasPrefix(methods, "METHODS=") {
continue
}
@ -60,15 +60,15 @@ func (c *Conn) sendProtocolInfo() (*ProtocolInfo, error) {
if !strings.HasPrefix(cookieFile, "COOKIEFILE=") {
continue
}
if ret.CookieFile, err = util.UnescapeSimpleQuotedString(cookieFile[11:]); err != nil {
if ret.CookieFile, err = torutil.UnescapeSimpleQuotedString(cookieFile[11:]); err != nil {
continue
}
}
ret.AuthMethods = strings.Split(methods[8:], ",")
case "VERSION":
torVersion, _, _ := util.PartitionString(val, ' ')
torVersion, _, _ := torutil.PartitionString(val, ' ')
if strings.HasPrefix(torVersion, "Tor=") {
ret.TorVersion, err = util.UnescapeSimpleQuotedString(torVersion[4:])
ret.TorVersion, err = torutil.UnescapeSimpleQuotedString(torVersion[4:])
}
}
}

View File

@ -15,7 +15,7 @@ import (
"strconv"
"strings"
"github.com/cretz/bine/util"
"github.com/cretz/bine/torutil"
)
// Process is the interface implemented by Tor processes.
@ -54,7 +54,7 @@ func (e *exeProcessCreator) New(ctx context.Context, args ...string) (Process, e
// when ControlPortWriteToFile is set.
func ControlPortFromFileContents(contents string) (int, error) {
contents = strings.TrimSpace(contents)
_, port, ok := util.PartitionString(contents, ':')
_, port, ok := torutil.PartitionString(contents, ':')
if !ok || !strings.HasPrefix(contents, "PORT=") {
return 0, fmt.Errorf("Invalid port format: %v", contents)
}

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/cretz/bine/tor"
"github.com/cretz/bine/torutil"
)
func TestListenSimpleHTTPV2(t *testing.T) {
@ -17,12 +18,31 @@ func TestListenSimpleHTTPV2(t *testing.T) {
_, err := w.Write([]byte("Test Content"))
ctx.Require.NoError(err)
})
// Check the service ID
ctx.Require.Equal(torutil.OnionServiceIDFromPrivateKey(onion.Key), onion.ID)
defer server.Shutdown(ctx)
// Call /test
byts := httpGet(ctx, client, "http://"+onion.ID+".onion/test")
ctx.Require.Equal("Test Content", string(byts))
}
func TestListenSimpleHTTPV3(t *testing.T) {
ctx := GlobalEnabledNetworkContext(t)
// Create an onion service to listen on random port but show as 80
conf := &tor.ListenConf{RemotePorts: []int{80}, Version3: true}
// _, conf.Key, _ = ed25519.GenerateKey(nil)
client, server, onion := startHTTPServer(ctx, conf, "/test", func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("Test Content"))
ctx.Require.NoError(err)
})
defer server.Shutdown(ctx)
// Check the service ID
ctx.Require.Equal(torutil.OnionServiceIDFromPrivateKey(onion.Key), onion.ID)
// Call /test
byts := httpGet(ctx, client, "http://"+onion.ID+".onion/test")
ctx.Require.Equal("Test Content", string(byts))
}
// Only have to shutdown the HTTP server
func startHTTPServer(
ctx *TestContext,

View File

@ -8,9 +8,9 @@ import (
"net"
"strconv"
"golang.org/x/crypto/ed25519"
"github.com/cretz/bine/control"
"github.com/cretz/bine/torutil/ed25519"
othered25519 "golang.org/x/crypto/ed25519"
)
// OnionService implements net.Listener and net.Addr for an onion service.
@ -21,7 +21,7 @@ type OnionService struct {
// Key is the private key for this service. It is either the set key, the
// generated key, or nil if asked to discard the key. If present, it is
// *crypto/rsa.PrivateKey (1024 bit) when Version3 is false or
// golang.org/x/crypto/ed25519.PrivateKey when Version3 is true.
// github.com/cretz/bine/torutil/ed25519.PrivateKey when Version3 is true.
Key crypto.PrivateKey
// Version3 says whether or not this service is a V3 service.
@ -66,6 +66,7 @@ type ListenConf struct {
// Key is the private key to use. If not present, a key is generated based
// on whether Version3 is true or false. If present, it must be a
// *crypto/rsa.PrivateKey (1024 bit), a
// github.com/cretz/bine/torutil/ed25519.PrivateKey, a
// golang.org/x/crypto/ed25519.PrivateKey, or a
// github.com/cretz/bine/control.Key.
Key crypto.PrivateKey
@ -119,7 +120,7 @@ func (t *Tor) Listen(ctx context.Context, conf *ListenConf) (*OnionService, erro
ctx = context.Background()
}
// Create the service up here and make sure we close it no matter the error within
svc := &OnionService{Tor: t, Key: conf.Key, CloseLocalListenerOnClose: conf.LocalListener == nil}
svc := &OnionService{Tor: t, CloseLocalListenerOnClose: conf.LocalListener == nil}
var err error
// Create the local listener if necessary
@ -159,16 +160,38 @@ func (t *Tor) Listen(ctx context.Context, conf *ListenConf) (*OnionService, erro
} else {
req.Key = control.GenKey(control.KeyAlgoRSA1024)
}
case control.GenKey:
svc.Version3 = conf.Version3
req.Key = key
case *rsa.PrivateKey:
svc.Key = key
svc.Version3 = false
if key.N == nil || key.N.BitLen() != 1024 {
err = fmt.Errorf("RSA key must be 1024 bits")
} else {
req.Key = &control.RSAKey{PrivateKey: key}
}
case *control.RSAKey:
svc.Key = key.PrivateKey
svc.Version3 = false
if key.N == nil || key.N.BitLen() != 1024 {
err = fmt.Errorf("RSA key must be 1024 bits")
} else {
req.Key = key
}
case ed25519.PrivateKey:
svc.Key = key
svc.Version3 = true
req.Key = control.ED25519Key(key)
case othered25519.PrivateKey:
properKey := ed25519.FromCryptoPrivateKey(key)
svc.Key = properKey
svc.Version3 = true
req.Key = control.ED25519Key(properKey)
case control.ED25519Key:
svc.Key = ed25519.PrivateKey(key)
svc.Version3 = true
req.Key = key
default:
err = fmt.Errorf("Unrecognized key type: %T", key)
}

View File

@ -288,7 +288,7 @@ func (t *Tor) EnableNetwork(ctx context.Context, wait bool) error {
if status, _ := evt.(*control.StatusEvent); status != nil && status.Action == "BOOTSTRAP" {
if status.Severity == "NOTICE" && status.Arguments["PROGRESS"] == "100" {
return true, nil
} else if status.Severity != "NOTICE" {
} else if status.Severity == "ERR" {
return false, fmt.Errorf("Failing bootstrapping, Tor warning: %v", status.Arguments["WARNING"])
}
}

2
torutil/doc.go Normal file
View File

@ -0,0 +1,2 @@
// Package torutil has generic utilities shared across the library.
package torutil

View File

@ -0,0 +1,78 @@
// Package ed25519 implements Tor/BitTorrent-like ed25519 keys.
//
// See the following stack overflow post for details on why
// golang.org/x/crypto/ed25519 can't be used:
// https://stackoverflow.com/questions/44810708/ed25519-public-result-is-different
package ed25519
import (
"crypto"
"crypto/rand"
"crypto/sha512"
"errors"
"io"
"github.com/cretz/bine/torutil/ed25519/edwards25519"
"golang.org/x/crypto/ed25519"
)
// Ref: https://stackoverflow.com/questions/44810708/ed25519-public-result-is-different
// PrivateKey is a 64-byte Ed25519 private key.
type PrivateKey []byte
// PublicKey is a 32-byte Ed25519 public key.
type PublicKey []byte
// FromCryptoPrivateKey converts a Go private key to the one in this package.
func FromCryptoPrivateKey(key ed25519.PrivateKey) PrivateKey {
digest := sha512.Sum512(key[:32])
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
return digest[:]
}
// FromCryptoPublicKey converts a Go public key to the one in this package.
func FromCryptoPublicKey(key ed25519.PublicKey) PublicKey {
return PublicKey(key)
}
func (p PrivateKey) Public() crypto.PublicKey {
return p.PublicKey()
}
func (p PrivateKey) PublicKey() PublicKey {
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], p[:])
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
return publicKeyBytes[:]
}
func (p PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
panic("TODO")
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rnd io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
if rnd == nil {
rnd = rand.Reader
}
_, err = io.ReadFull(rnd, privateKey[:32])
if err == nil {
digest := sha512.Sum512(privateKey[:32])
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
privateKey = digest[:]
publicKey = privateKey.PublicKey()
}
return
}

View File

@ -0,0 +1 @@
This is taken from https://github.com/golang/crypto/tree/1a580b3eff7814fc9b40602fd35256c63b50f491/ed25519/internal/edwards25519

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

52
torutil/key.go Normal file
View File

@ -0,0 +1,52 @@
package torutil
import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base32"
"fmt"
"strings"
"github.com/cretz/bine/torutil/ed25519"
"golang.org/x/crypto/sha3"
)
var serviceIDEncoding = base32.StdEncoding.WithPadding(base32.NoPadding)
func OnionServiceIDFromPrivateKey(key crypto.PrivateKey) string {
switch k := key.(type) {
case *rsa.PrivateKey:
return OnionServiceIDFromV2PublicKey(&k.PublicKey)
case ed25519.PrivateKey:
return OnionServiceIDFromV3PublicKey(k.PublicKey())
}
panic(fmt.Sprintf("Unrecognized private key type: %T", key))
}
func OnionServiceIDFromPublicKey(key crypto.PublicKey) string {
switch k := key.(type) {
case *rsa.PublicKey:
return OnionServiceIDFromV2PublicKey(k)
case ed25519.PublicKey:
return OnionServiceIDFromV3PublicKey(k)
}
panic(fmt.Sprintf("Unrecognized private key type: %T", key))
}
func OnionServiceIDFromV2PublicKey(key *rsa.PublicKey) string {
h := sha1.New()
h.Write(x509.MarshalPKCS1PublicKey(key))
return strings.ToLower(serviceIDEncoding.EncodeToString(h.Sum(nil)[:10]))
}
func OnionServiceIDFromV3PublicKey(key ed25519.PublicKey) string {
checkSum := sha3.Sum256(append(append([]byte(".onion checksum"), key...), 0x03))
var keyBytes [35]byte
copy(keyBytes[:], key)
keyBytes[32] = checkSum[0]
keyBytes[33] = checkSum[1]
keyBytes[34] = 0x03
return strings.ToLower(serviceIDEncoding.EncodeToString(keyBytes[:]))
}

View File

@ -1,4 +1,4 @@
package util
package torutil
import (
"fmt"

View File

@ -1,2 +0,0 @@
// Package util has generic utilities shared across the library.
package util