-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d306e90
commit d86be25
Showing
6 changed files
with
221 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package vote | ||
|
||
import ( | ||
"crypto/hmac" | ||
"crypto/sha256" | ||
"encoding/base64" | ||
"encoding/binary" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
) | ||
|
||
type voteMessage struct { | ||
Payload string `json:"payload"` | ||
Signature string `json:"signature"` | ||
} | ||
|
||
type votePayload struct { | ||
ServiceName string `json:"serviceName"` | ||
Username string `json:"username"` | ||
Timestamp int64 `json:"timestamp"` | ||
Challenge string `json:"challenge"` | ||
UUID string `json:"uuid"` | ||
} | ||
|
||
type voteResponse struct { | ||
Status string `json:"status"` | ||
Error string `json:"error"` | ||
} | ||
|
||
func sendHandshake(w io.Writer, challenge string) error { | ||
_, err := w.Write([]byte(fmt.Sprintf("VOTIFIER 2 %s\n", challenge))) | ||
|
||
return err | ||
} | ||
|
||
func readPayload(r io.Reader, challenge string) error { | ||
var ( | ||
identifier int16 | ||
messageLength int16 | ||
) | ||
|
||
if err := binary.Read(r, binary.BigEndian, &identifier); err != nil { | ||
return err | ||
} | ||
|
||
if identifier != 0x733A { | ||
return fmt.Errorf("invalid identifier: %X", identifier) | ||
} | ||
|
||
if err := binary.Read(r, binary.BigEndian, &messageLength); err != nil { | ||
return err | ||
} | ||
|
||
data := make([]byte, messageLength) | ||
|
||
if _, err := r.Read(data); err != nil { | ||
return err | ||
} | ||
|
||
var message voteMessage | ||
|
||
if err := json.Unmarshal(data, &message); err != nil { | ||
return err | ||
} | ||
|
||
var payload votePayload | ||
|
||
if err := json.Unmarshal([]byte(message.Payload), &payload); err != nil { | ||
return err | ||
} | ||
|
||
if payload.Challenge != challenge { | ||
return fmt.Errorf("unexpected challenge: %s", payload.Challenge) | ||
} | ||
|
||
hash := hmac.New(sha256.New, []byte(conf.Votifier.Token)) | ||
hash.Write([]byte(message.Payload)) | ||
|
||
if base64.StdEncoding.EncodeToString(hash.Sum(nil)) != message.Signature { | ||
return fmt.Errorf("invalid message signature (is the token wrong?): %s", message.Signature) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func writeResponse(w io.Writer) error { | ||
data, err := json.Marshal(voteResponse{ | ||
Status: "ok", | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = w.Write(append(data, '\n')) | ||
|
||
return err | ||
} | ||
|
||
func writeError(w io.Writer, message string) error { | ||
data, err := json.Marshal(voteResponse{ | ||
Status: "error", | ||
Error: message, | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = w.Write(append(data, '\n')) | ||
|
||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package vote | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
|
||
"main/src/config" | ||
) | ||
|
||
var ( | ||
socket net.Listener = nil | ||
conf *config.Config = nil | ||
) | ||
|
||
// Listen creates a new TCP socket server using the address specified in the configuration file. | ||
func Listen(c *config.Config) (err error) { | ||
conf = c | ||
|
||
socket, err = net.Listen("tcp", fmt.Sprintf("%s:%d", c.Votifier.Host, c.Votifier.Port)) | ||
|
||
return | ||
} | ||
|
||
// Close closes the socket server. | ||
func Close() error { | ||
return socket.Close() | ||
} | ||
|
||
// AcceptConnections should be started in a Goroutine and accepts new connections from the socket server. | ||
func AcceptConnections() { | ||
for { | ||
conn, err := socket.Accept() | ||
|
||
if err != nil { | ||
continue | ||
} | ||
|
||
go handleConnection(conn) | ||
} | ||
} | ||
|
||
func handleConnection(conn net.Conn) { | ||
defer conn.Close() | ||
|
||
challenge := generateChallenge() | ||
|
||
if err := sendHandshake(conn, challenge); err != nil { | ||
writeError(conn, err.Error()) | ||
|
||
return | ||
} | ||
|
||
if err := readPayload(conn, challenge); err != nil { | ||
writeError(conn, err.Error()) | ||
|
||
return | ||
} | ||
|
||
if err := writeResponse(conn); err != nil { | ||
writeError(conn, err.Error()) | ||
|
||
return | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package vote | ||
|
||
import ( | ||
"crypto/rand" | ||
"encoding/hex" | ||
) | ||
|
||
func generateChallenge() string { | ||
data := make([]byte, 8) | ||
|
||
if _, err := rand.Read(data); err != nil { | ||
panic(err) | ||
} | ||
|
||
return hex.EncodeToString(data) | ||
} |