gosmartkeyboard/security/Authentication.md

100 lines
2.2 KiB
Markdown
Raw Normal View History

# Keyboard connection authentication
Keyboarding is a very sensitive activity, so this app naturally needs to encrypt and authenticate connections.
All connections are encrypted using an external TLS proxy (e.g. [Caddy](https://caddyserver.com)) 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. Since the token is very high entropy, we do not need a salt or
KDF.
``` go
--- token generation
authToken = uuid.New().String() + uuid.New().String()
hashedID := sha3.Sum256([]byte(authToken))
---
```
## Authentication token file
We 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.'
The following is used when we need to get the token file path:
``` go
--- define authentication token file
@{get authTokenFile from environment}
if authTokenFileIsSet == false {
authTokenFile = filepath.Join(xdg.ConfigHome, "smartkeyboard", "auth-token")
}
---
```
## 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 {
@{define authentication token file}
// compare sha3_256 hash to hash in file
hashedToken := sha3.Sum256([]byte(token))
storedToken, err := os.ReadFile(authTokenFile)
if err != nil {
return err
}
if subtle.ConstantTimeCompare(hashedToken[:], storedToken) != 1 {
return errors.New("invalid token")
}
return nil
}
---
--- /auth/auth.go
package auth
import(
"os"
"path/filepath"
"errors"
"crypto/subtle"
@{sha3 import string}
@{uuid import string}
@{xdg import string}
)
var authToken = ""
func provisionToken() (error){
@{define authentication token file}
if _, err := os.Stat(authTokenFile); err == nil {
return nil
}
@{token generation}
fo, err := os.Create(authTokenFile)
if err != nil {
panic(err)
}
fo.Write(hashedID[:])
fo.Close()
return nil
}
@{check token}
---
```