Added explanation of server auth and started authentication tests
This commit is contained in:
parent
cb27d192c2
commit
8e5f0ef73f
98
security/Authentication.md
Normal file
98
security/Authentication.md
Normal file
@ -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}
|
||||||
|
}
|
||||||
|
---
|
||||||
|
```
|
16
smartkeyboard/auth/auth_test.go
Normal file
16
smartkeyboard/auth/auth_test.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user