From 8e5f0ef73fa7a17a1215e55429b17faf186f90ef Mon Sep 17 00:00:00 2001 From: Kevin F Date: Thu, 8 Sep 2022 13:33:57 -0500 Subject: [PATCH] Added explanation of server auth and started authentication tests --- security/Authentication.md | 98 +++++++++++++++++++++++++++++++++ smartkeyboard/auth/auth_test.go | 16 ++++++ 2 files changed, 114 insertions(+) create mode 100644 security/Authentication.md create mode 100644 smartkeyboard/auth/auth_test.go diff --git a/security/Authentication.md b/security/Authentication.md new file mode 100644 index 0000000..11aee88 --- /dev/null +++ b/security/Authentication.md @@ -0,0 +1,98 @@ +# Keyboard connection authentication + +Keyboarding obviously has a ton of sensitive data, so we need to both encrypt and +authenticate the connections. + +All connections are encrypted using an external TLS proxy (e.g. Caddy) outside the +scope of this project, but we perform application level authentication using two +randomly generated UUIDv4s in a manner similar to a passphrase. @{token generation} + +We hash the token using sha3-256 to avoid accidentally exposing the token to a +readonly attacker. + + +## Generating the authentication token + +``` go +--- token generation +id := uuid.New() + uuid.New() +hashedID := sha3.Sum256([]byte(id)) +--- +``` + +## Storing the authentication token + +We then store the token in a file, which is set +by the environment variable `KEYBOARD_AUTH_TOKEN_FILE`, but defaults to +'XDG_CONFIG_HOME/smartkeyboard/auth-token.' @{defineProvisionTokenFile} + + +``` go +--- store token +if err := os.WriteFile(KEYBOARD_AUTH_TOKEN_FILE, hashedID, 0666); err != nil { + log.Fatal(err) +} +--- +``` + +## Checking authentication + +When a client connects, the [websocket server](Server.md) checks the token they send against the stored token. + +``` go +--- check token +func checkAuthToken(token string) error { + hashedToken := sha3.Sum256([]byte(token)) + storedToken, err := os.ReadFile(KEYBOARD_AUTH_TOKEN_FILE) + if err != nil { + return err + } + + if subtle.ConstantTimeCompare(hashedToken[:], storedToken) != 1 { + return errors.New("invalid token") + } + + return nil +} +--- +``` + + +--- /auth/auth.go +package main + +@{auth imports} +@{check token} +--- + +--- auth imports + +import( + //"github.com/google/uuid" + //"encoding/base64" + "golang.org/x/crypto/sha3" + "os" + "errors" + "crypto/subtle" +) +--- + +``` go +--- defineProvisionTokenFile +provisionTokenFile, keyboardAuthExists := os.Getenv("KEYBOARD_AUTH_TOKEN_FILE") + +if keyboardAuthExists == false { + provisionTokenFile = filepath.Join(xdg.ConfigHome, "smartkeyboard", "auth-token") +} +--- +``` + +```go +--- provisionToken +func provisionToken() (error){ + @{defineProvisionTokenFile} + @{token generation} + @{store token} +} +--- +``` \ No newline at end of file diff --git a/smartkeyboard/auth/auth_test.go b/smartkeyboard/auth/auth_test.go new file mode 100644 index 0000000..cf55172 --- /dev/null +++ b/smartkeyboard/auth/auth_test.go @@ -0,0 +1,16 @@ +package main + +import ( + "testing" +) + +func TestAuthPasswordHashBad(t *testing.T) { + t.Log("TestAuthPasswordHash") + expectedResult := error(nil) // OK + password := "wrong password" + + result := checkAuthToken(password) + if result != expectedResult { + t.Errorf("Expected %s, got %s", expectedResult, result) + } +}