summaryrefslogtreecommitdiff
path: root/vendor/github.com/cretz/bine/control/cmd_authenticate.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cretz/bine/control/cmd_authenticate.go')
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_authenticate.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/vendor/github.com/cretz/bine/control/cmd_authenticate.go b/vendor/github.com/cretz/bine/control/cmd_authenticate.go
new file mode 100644
index 0000000..c43d864
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_authenticate.go
@@ -0,0 +1,108 @@
+package control
+
+import (
+ "crypto/hmac"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/hex"
+ "io/ioutil"
+ "strings"
+)
+
+// Authenticate authenticates with the Tor instance using the "best" possible
+// authentication method if not already authenticated and sets the Authenticated
+// field. The password argument is optional, and will only be used if the
+// "SAFECOOKIE" and "NULL" authentication methods are not available and
+// "HASHEDPASSWORD" is.
+func (c *Conn) Authenticate(password string) error {
+ if c.Authenticated {
+ return nil
+ }
+ // Determine the supported authentication methods, and the cookie path.
+ pi, err := c.ProtocolInfo()
+ if err != nil {
+ return err
+ }
+ // Get the bytes to pass to with authenticate
+ var authBytes []byte
+ if pi.HasAuthMethod("NULL") {
+ // No auth bytes
+ } else if pi.HasAuthMethod("SAFECOOKIE") {
+ if pi.CookieFile == "" {
+ return c.protoErr("Invalid (empty) COOKIEFILE")
+ }
+ cookie, err := ioutil.ReadFile(pi.CookieFile)
+ if err != nil {
+ return c.protoErr("Failed to read COOKIEFILE: %v", err)
+ } else if len(cookie) != 32 {
+ return c.protoErr("Invalid cookie file length: %v", len(cookie))
+ }
+
+ // Send an AUTHCHALLENGE command, and parse the response.
+ var clientNonce [32]byte
+ if _, err := rand.Read(clientNonce[:]); err != nil {
+ return c.protoErr("Failed to generate clientNonce: %v", err)
+ }
+ resp, err := c.SendRequest("AUTHCHALLENGE %s %s", "SAFECOOKIE", hex.EncodeToString(clientNonce[:]))
+ if err != nil {
+ return err
+ }
+ splitResp := strings.Split(resp.Reply, " ")
+ if len(splitResp) != 3 || !strings.HasPrefix(splitResp[1], "SERVERHASH=") ||
+ !strings.HasPrefix(splitResp[2], "SERVERNONCE=") {
+ return c.protoErr("Invalid AUTHCHALLENGE response")
+ }
+ serverHash, err := hex.DecodeString(splitResp[1][11:])
+ if err != nil {
+ return c.protoErr("Failed to decode ServerHash: %v", err)
+ }
+ if len(serverHash) != 32 {
+ return c.protoErr("Invalid ServerHash length: %d", len(serverHash))
+ }
+ serverNonce, err := hex.DecodeString(splitResp[2][12:])
+ if err != nil {
+ return c.protoErr("Failed to decode ServerNonce: %v", err)
+ }
+ if len(serverNonce) != 32 {
+ return c.protoErr("Invalid ServerNonce length: %d", len(serverNonce))
+ }
+
+ // Validate the ServerHash.
+ m := hmac.New(sha256.New, []byte("Tor safe cookie authentication server-to-controller hash"))
+ m.Write(cookie)
+ m.Write(clientNonce[:])
+ m.Write(serverNonce)
+ dervServerHash := m.Sum(nil)
+ if !hmac.Equal(serverHash, dervServerHash) {
+ return c.protoErr("invalid ServerHash: mismatch")
+ }
+
+ // Calculate the ClientHash, and issue the AUTHENTICATE.
+ m = hmac.New(sha256.New, []byte("Tor safe cookie authentication controller-to-server hash"))
+ m.Write(cookie)
+ m.Write(clientNonce[:])
+ m.Write(serverNonce)
+ authBytes = m.Sum(nil)
+ } else if pi.HasAuthMethod("HASHEDPASSWORD") {
+ // Despite the name HASHEDPASSWORD, the raw password is actually sent. According to the code, this can either be
+ // a QuotedString, or base16 encoded, so go with the later since it's easier to handle.
+ if password == "" {
+ return c.protoErr("password auth needs a password")
+ }
+ authBytes = []byte(password)
+ } else {
+ return c.protoErr("No supported authentication methods")
+ }
+ // Send it
+ if err = c.sendAuthenticate(authBytes); err == nil {
+ c.Authenticated = true
+ }
+ return err
+}
+
+func (c *Conn) sendAuthenticate(byts []byte) error {
+ if len(byts) == 0 {
+ return c.sendRequestIgnoreResponse("AUTHENTICATE")
+ }
+ return c.sendRequestIgnoreResponse("AUTHENTICATE %v", hex.EncodeToString(byts))
+}