diff --git a/Building.md b/Building.md index df646b4..0c39c1f 100644 --- a/Building.md +++ b/Building.md @@ -9,7 +9,7 @@ ## Installing srcweave 1. Install sbcl (steel bank common lisp) -2. Install quicklisp (instructions: [https://quicklisp.org/beta/](https://quicklisp.org/beta/]) +2. Install quicklisp (instructions: [https://quicklisp.org/beta/](https://quicklisp.org/beta/)) 3. `git clone https://github.com/justinmeiners/srcweave` 4. `cd srcweave` 5. `make; make install` \ No newline at end of file diff --git a/Makefile b/Makefile index b944159..edb4d67 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ weave: - srcweave --formatter srcweave-format --weave docs/ ReadMe.md security/Authentication.md EnvironmentVariables.md Dependencies.md server/Server.md server/Streaming.md \ - server/XdotoolCommands.md ThreatModel.md Client.md - util/removefencedcode.py + srcweave --formatter srcweave-format --weave docs/ ReadMe.md Building.md Dependencies.md EnvironmentVariables.md LICENSE.md \ + security/Authentication.md security/ThreatModel.md Plumbing.md server/Server.md server/Streaming.md server/Sendkeys.md server/XdotoolCommands.md \ + Client.md tools/Tools.md tools/Editor.md tools/Input.md tools/RawCapture.md + util/clean.py tangle: - srcweave --formatter srcweave-format --tangle smartkeyboard/ ReadMe.md security/Authentication.md EnvironmentVariables.md Dependencies.md \ + srcweave --formatter srcweave-format --tangle smartkeyboard/ ReadMe.md Plumbing.md security/Authentication.md EnvironmentVariables.md Dependencies.md \ server/Server.md server/Streaming.md server/Sendkeys.md server/XdotoolCommands.md ThreatModel.md Client.md tools/Tools.md tools/Editor.md tools/Input.md \ tools/RawCapture.md clean: @@ -15,12 +16,10 @@ clean: build: tangle - cd smartkeyboard/server && go mod init keyboard.voidnet.tech - cd smartkeyboard/server && go mod tidy - - cd smartkeyboard/server && go build -o ../../bin/keyboard + - cd smartkeyboard/server && go build -tags osusergo,netgo -o ../../bin/keyboard - cd smartkeyboard/client && go mod init keyboard.voidnet.tech - cd smartkeyboard/client && go mod tidy - - cd smartkeyboard/client && go build -o ../../bin/keyboard-client - - + - cd smartkeyboard/client && go build -tags osusergo,netgo -o ../../bin/keyboard-client test: tangle -cd smartkeyboard && go mod init keyboard.voidnet.tech diff --git a/Plumbing.md b/Plumbing.md new file mode 100644 index 0000000..8a564c2 --- /dev/null +++ b/Plumbing.md @@ -0,0 +1,12 @@ +# Miscelanious plumbing functions + +## Detect version command, print version, and exit + +``` go +--- handle version command +if len(os.Args) > 1 && os.Args[1] == "version" { + fmt.Println("Keyboard version: @{version}") + os.Exit(0) +} +--- +``` \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md index a8c2764..3e73b5c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -5,6 +5,11 @@ Copyright [Kevin Froman](https://chaoswebs.net/) [Licensed under GPLv3](LICENSE. Work in progress +--- version +0.0.1 +--- + + # Introduction GoSmartKeyboard is a daemon that allows you to have a more powerful keyboarding experience. It can be used with a secondary device, such as an Android phone or a raspberry pi, or it can run locally. A seperate client binary is provided that reads from a FIFO (named pipe) and sends the data to the server. This allows you to use any program that can write to a FIFO as a source of keyboard input. @@ -34,6 +39,7 @@ Examples of what you can do: * On-the-fly spell checking or translation * On-the-fly encryption (ex: PGP sign every message you type), isolated from the perhaps untrusted computer * Easy layout configuration +* Key logging * Delay keystrokes by a few dozen or so milliseconds to reduce [key stroke timing biometrics](https://en.wikipedia.org/wiki/Keystroke_dynamics) @@ -51,61 +57,42 @@ markdown book is actually the source code # Running +## Installation + +The server and client are both single static binaries. The only requirement is Linux. This software has been tested +with typical US keyboards in QWERTY and Colemak layouts. It should work with any keyboard layout, though. + +### Keyboard weirdness + +Not all keyboards are equal, per the [Linux kernel documentation](https://www.kernel.org/doc/html/latest/input/event-codes.html#ev-key), +some keyboards do not autorepeat keys. Autorepeat behavior was also found inconsistent during testing and seems to mess up the rawcapture tool. + ## Server -`sudo KEYBOARD_TCP_BIND_ADDRESS=0.0 KEYBOARD_TCP_BIND_PORT=8080 ./keyboard` +`sudo KEYBOARD_TCP_BIND_ADDRESS=127.1 KEYBOARD_TCP_BIND_PORT=8080 ./keyboard` + +You could also run sudoless by giving your user access to uinput, but it would minimally if at all be more secure. + +On first run it will output your authentication token. Store it in a safe place such as your password manager. + +It is highly recommended to use SSH forwarding (preferred) or a reverse https proxy to access the server. + +### SSH example + +To connect with ssh, run this on the client: + +`ssh -R 8080:localhost:8080 user@myserver` -# Server Entrypoint +### Socket file +It is more secure and mildly more efficient to use a unix socket file. To do this, set the environment variable `KEYBOARD_UNIX_SOCKET_PATH` to the path of the socket file. The server will create the file if it does not exist. The socket is useful for reverse proxies or SSH forwarding. +## Client -Right out of the gate, we make sure a token is provisioned. In the future we will use the system keyring. +`KEYBOARD_AUTH=your_token_here KEYBOARD_FIFO=keyboard_control_file ./keyboard-client "ws://myserver:8080/sendkeys` -Then we can start the web server and listen for websocket connections. +From here you can use any program that can write to a FIFO to send keystrokes to the server. For example, you could use `cat` to send a file to the server, or `cowsay` to send a cow message to the server. -``` go +### Tools ---- entrypoint - - func main(){ - - tokenBase64, _ := auth.ProvisionToken() - if len(tokenBase64) > 0 { - fmt.Println("This is your authentication token, it will only be shown once: " + tokenBase64) - } - - - server.StartServer() - } - ---- - - ---- /server/main.go - package main - - import( - "fmt" - "keyboard.voidnet.tech/server" - "keyboard.voidnet.tech/auth" - ) - - - @{entrypoint} - ---- - - - ---- set network bind globals - - var string unixSocketPath - var bool unixSocketPathExists - var string tcpBindAddress - var bool tcpBindAddressExists - var string tcpBindPort - var bool tcpBindPortExists - ---- -``` \ No newline at end of file diff --git a/ThreatModel.md b/ThreatModel.md deleted file mode 100644 index 61abd57..0000000 --- a/ThreatModel.md +++ /dev/null @@ -1,6 +0,0 @@ -# GoSmartKeyboard Threat Model - - -GoSmartKeyboard assumes that it is running behind a reverse proxy that provides TLS termination. This is a common setup for web applications, and is the default configuration for the [Caddy](https://caddyserver.com/) web server. Alternatively you could use SSH port forwarding to tunnel the traffic to the server. - -The server daemon is intended to be used on a single-user system. The goal is to prevent against well funded attackers without physical access to the machine from authenticating to the service. To prevent this, a 256 bit random token is generated and stored in a file. The token is then displayed to the user, and they are expected to copy it to store it safely. The token cannot be recovered because only a sha256 hash of the token is stored on disk. \ No newline at end of file diff --git a/security/ThreatModel.md b/security/ThreatModel.md index e69de29..61abd57 100644 --- a/security/ThreatModel.md +++ b/security/ThreatModel.md @@ -0,0 +1,6 @@ +# GoSmartKeyboard Threat Model + + +GoSmartKeyboard assumes that it is running behind a reverse proxy that provides TLS termination. This is a common setup for web applications, and is the default configuration for the [Caddy](https://caddyserver.com/) web server. Alternatively you could use SSH port forwarding to tunnel the traffic to the server. + +The server daemon is intended to be used on a single-user system. The goal is to prevent against well funded attackers without physical access to the machine from authenticating to the service. To prevent this, a 256 bit random token is generated and stored in a file. The token is then displayed to the user, and they are expected to copy it to store it safely. The token cannot be recovered because only a sha256 hash of the token is stored on disk. \ No newline at end of file diff --git a/server/Sendkeys.md b/server/Sendkeys.md index 81988d6..b61a7c3 100644 --- a/server/Sendkeys.md +++ b/server/Sendkeys.md @@ -1,7 +1,6 @@ # uinput streaming approach - ``` go --- do streaming keylogger approach diff --git a/server/Server.md b/server/Server.md index b0cbd20..37a98df 100644 --- a/server/Server.md +++ b/server/Server.md @@ -4,6 +4,50 @@ The server has two jobs, to authenticate and to accept a stream of key presses f For efficiency and security we support use of a unix socket, but tcp can be used instead. In the case of TCP, the server will listen on 127.1 by default but can be configured to listen on a different address and port. In any case, it is highly recommended to run the server behind a reverse proxy supporting HTTPS such as nginx or caddy. +# Server Entrypoint + +Before main execution, both the server and client check for a version command line argument. If it is present, the program will print the version and exit. + +First, we make sure a token is provisioned. In the future we will use the system keyring. + +Then we can start the web server and listen for websocket connections. + +``` go + +--- entrypoint + + func main(){ + @{handle version command} + tokenBase64, _ := auth.ProvisionToken() + if len(tokenBase64) > 0 { + fmt.Println("This is your authentication token, it will only be shown once: " + tokenBase64) + } + + + server.StartServer() + } + +--- + + +--- /server/main.go + package main + + import( + "fmt" + "os" + "keyboard.voidnet.tech/server" + "keyboard.voidnet.tech/auth" + ) + + + @{entrypoint} + +--- + +``` + + # Picking a socket type and setting the listener diff --git a/util/removefencedcode.py b/util/clean.py similarity index 75% rename from util/removefencedcode.py rename to util/clean.py index 51c51de..a2fb251 100755 --- a/util/removefencedcode.py +++ b/util/clean.py @@ -2,12 +2,15 @@ # Remove fenced code blocks from generated HTML files # We need to do this because srcweave does not support fenced code blocks +# We also adjust links to use .html + import glob for f in glob.glob('docs/*.html'): with open(f, 'r') as fin: lines = fin.readlines() + with open(f, 'w') as fout: for line in lines: if line.startswith('

```'): continue - fout.write(line) \ No newline at end of file + fout.write(line.replace('.md', '.html').replace('.MD', '.html')) \ No newline at end of file