gosmartkeyboard/docs/Authentication.html

160 lines
6.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>GoSmartKeyboard</title>
<link rel="stylesheet" href="google-code-prettify/prettify.css">
<link rel="stylesheet" href="styles/prettify-theme.css">
<script defer src="google-code-prettify/prettify.js"></script>
<script defer src="google-code-prettify/run_prettify.js"></script>
<link rel="stylesheet" href="styles/main.css">
</head>
<!-- Generated by srcweave https://github.com/justinmeiners/srcweave -->
<h1>Keyboard connection authentication<a id="c10"></a></h1>
<p>Keyboarding is a very sensitive activity, so this app naturally needs to encrypt and authenticate connections.</p>
<p>All connections are encrypted using an external TLS proxy (e.g. <a href="https://caddyserver.com">Caddy</a>) outside the
scope of this project.</p>
<p>We perform application level authentication using the system random device. <em class="block-link nocode"><a href="#token-generation-block-30">token generation</a></em></p>
<p>We hash the 32 byte 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.</p>
<div class="code-block">
<span class="block-header">
<strong class="block-title"><em><a id="token-generation-block-30" href="#token-generation-block-30">token generation</a></em></strong></span>
<pre class="prettyprint"><code class="">authToken := [32]byte{}
rand.Read(authToken[:])
authTokenString = base64.StdEncoding.EncodeToString(authToken[:])
hashedID := sha3.Sum256(authToken[:])
</code></pre>
<p class="block-usages"><small>Used by <a href="#provision-token-function-block-36" title="provision token function">1</a> </small></p></div>
<h2>1. Authentication token file<a id="s10:0"></a></h2>
<p>We store the token in a file, which is set
by the environment variable <code>KEYBOARD_AUTH_TOKEN_FILE</code>, but defaults to
&lsquo;XDG_CONFIG_HOME/smartkeyboard/auth-token.&rsquo;</p>
<p>The following is used when we need to get the token file path:</p>
<div class="code-block">
<span class="block-header">
<strong class="block-title"><em><a id="define-authentication-token-file-block-32" href="#define-authentication-token-file-block-32">define authentication token file</a></em></strong></span>
<pre class="prettyprint"><code class=""><em class="block-link nocode" title="EnvironmentVariables.html"><a href="EnvironmentVariables.html#get-authtokenfile-from-environment-block-16">@{get authTokenFile from environment}</a></em>
if authTokenFileIsSet == false {
authTokenFile = filepath.Join(xdg.ConfigHome, "smartkeyboard", "auth-token")
}
</code></pre>
<p class="block-usages"><small>Used by <a href="#check-token-function-block-34" title="check token function">1</a> <a href="#provision-token-function-block-36" title="provision token function">2</a> </small></p></div>
<h2>2. Checking authentication<a id="s10:1"></a></h2>
<p>When a client connects, the <a href="Server.html">websocket endpoint</a> checks the token they send against the stored token.</p>
<p>We use a constant time comparison to avoid timing attacks, although it is not clear if this is necessary in this case.</p>
<div class="code-block">
<span class="block-header">
<strong class="block-title"><em><a id="check-token-function-block-34" href="#check-token-function-block-34">check token function</a></em></strong></span>
<pre class="prettyprint"><code class="">func CheckAuthToken(token string) error {
<em class="block-link nocode"><a href="#define-authentication-token-file-block-32">@{define authentication token file}</a></em>
// compare sha3_256 hash to hash in file
tokenBytes, err := base64.StdEncoding.DecodeString(token)
hashedToken := sha3.Sum256(tokenBytes)
storedToken, err := os.ReadFile(authTokenFile)
if err != nil {
return err
}
if subtle.ConstantTimeCompare(hashedToken[:], storedToken) != 1 {
return errors.New("invalid token")
}
return nil
}
</code></pre>
<p class="block-usages"><small>Used by <a href="#-server-auth-auth.go-block-38" title="/server/auth/auth.go">1</a> </small></p></div>
<div class="code-block">
<span class="block-header">
<strong class="block-title"><em><a id="provision-token-function-block-36" href="#provision-token-function-block-36">provision token function</a></em></strong></span>
<pre class="prettyprint"><code class="">func ProvisionToken() (authTokenString string, failed error){
<em class="block-link nocode"><a href="#define-authentication-token-file-block-32">@{define authentication token file}</a></em>
if _, err := os.Stat(authTokenFile); err == nil {
return "", nil
}
<em class="block-link nocode"><a href="#token-generation-block-30">@{token generation}</a></em>
// create directories if they don't exist
os.MkdirAll(filepath.Dir(authTokenFile), 0700)
fo, err := os.Create(authTokenFile)
defer fo.Close()
if err != nil {
panic(err)
}
fo.Write(hashedID[:])
return authTokenString, nil
}
</code></pre>
<p class="block-usages"><small>Used by <a href="#-server-auth-auth.go-block-38" title="/server/auth/auth.go">1</a> </small></p></div>
<h2>3. Putting it all together<a id="s10:2"></a></h2>
<p>The following is the structure of the authentication package.</p>
<p>Both CheckAuthToken and ProvisionToken are exported.
The former is used by the server on client connect and the latter is called on startup.</p>
<div class="code-block">
<span class="block-header">
<strong class="block-title"><em><a id="-server-auth-auth.go-block-38" href="#-server-auth-auth.go-block-38">/server/auth/auth.go</a></em></strong></span>
<pre class="prettyprint"><code class="">package auth
import(
"os"
"path/filepath"
"errors"
"encoding/base64"
"crypto/rand"
"crypto/subtle"
<em class="block-link nocode" title="Dependencies.html"><a href="Dependencies.html#sha3-import-string-block-7">@{sha3 import string}</a></em>
<em class="block-link nocode" title="Dependencies.html"><a href="Dependencies.html#xdg-import-string-block-5">@{xdg import string}</a></em>
)
//var authToken = ""
<em class="block-link nocode"><a href="#provision-token-function-block-36">@{provision token function}</a></em>
<em class="block-link nocode"><a href="#check-token-function-block-34">@{check token function}</a></em>
</code></pre>
</div>
</body>
</html>