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