Compare commits

...

49 Commits

Author SHA1 Message Date
Russ Magee bf395f14f5 Updates of dependencies 2026-04-10 23:50:40 -07:00
Russ Magee 6fabdb3a48 Bumped version 2026-04-02 18:29:00 -07:00
Russ Magee 41516ae5a6 Changed license from MIT to MIT NON-AI. 2026-04-02 18:17:01 -07:00
Russ Magee 3552c415dc Use external spinsult pkg 2026-02-23 18:50:02 -08:00
Russ Magee 36f6684f4f Resync/master 2026-02-05 19:35:41 -07:00
Russ Magee 43392e2715 Updated to use hopscotch v0.3.0; Bumped version 2026-02-05 19:33:19 -07:00
Russ Magee d9188faa3c Comment about observed issue with MSYS2 paths outside of install tree 2025-12-19 23:07:03 -08:00
Russtopia c626e988fe Update 'README.md' with info on MINGW_ROOT env var for using xc in MSYS2 environment 2025-12-09 00:42:25 -08:00
Russ Magee 3b55f4a9f4 Bumped version 2025-12-08 22:47:53 -08:00
Russ Magee a05e5948b6 Fixes for xc to work in MSYS2/MINGW 2025-12-08 22:44:54 -08:00
Russ Magee f9a0f282b7 Updated go.{mod,sum}, ci script 2025-12-08 22:31:03 -08:00
Russtopia 4b1e203dd8 Added terminal screenshot to README.md 2025-11-23 12:24:58 -08:00
Russ Magee 88635f2cef Fix for existing go.mod before generating new go.mod 2025-10-18 18:19:56 -07:00
Russ Magee af148acdd6 Added go.{mod,sum}; updated copyright dates 2025-10-14 03:16:02 -06:00
Russ Magee 6739466ce3 Added capslock capabilities analysis 2025-10-13 22:53:50 -06:00
Russ Magee 003230ef62 Removed stale import and log msg 2025-04-20 11:22:15 -07:00
Russ Magee 7731466c8c Fixed rekey selection of HMAC algs (was always using alg 0, SHA256) 2025-04-20 11:10:06 -07:00
Russ Magee a929fdc211 Fix term check to work for MSYS64/CYGWIN64 2024-11-30 18:46:15 -08:00
Russ Magee fc66a0557a Merge branch 'master' of https://gogs.blitter.com/RLabs/xs 2024-11-22 02:45:44 -08:00
Russ Magee bd3f90d308 Bumped version 2024-11-22 02:44:33 -08:00
Russtopia 8c1f90aaff Merge branch 'keepalive-only-shellmode' of RLabs/xs into master 2024-11-22 02:35:04 -08:00
Russ Magee efa01ee0e1 Fix for Issue #40: file copies are aborted 2024-11-21 22:19:47 -08:00
Russ Magee 4aea95fa3c Re-instated isatty check 2024-10-27 14:29:59 -07:00
Russ Magee 8e96e4fb32 Use GOOS in makefile rather than MSYS for Windows MSYS/CYGWIN detection 2024-10-27 12:37:49 -07:00
Russ Magee f07aa457b3 Fixes for MSYS2 and CYGWIN term mode; removed mintty_wrapper.sh 2024-10-27 12:17:34 -07:00
Russ Magee b12c8fe562 Bumped version 2024-10-27 12:12:07 -07:00
Russ Magee e5b6422d70 Fixes for MSYS2 and CYGWIN term mode; removed mintty_wrapper.sh 2024-10-27 11:59:17 -07:00
Russ Magee 12409319e7 Removed go.mod, go.sum 2024-07-15 02:06:26 -07:00
Russtopia bfcd097a14 Bump version to v0.9.11 2024-05-02 00:48:50 -07:00
Russtopia 136f37e209 Update 'README.md' 2024-05-01 23:58:44 -07:00
Russtopia ec9b4fe2f4 Merge branch 'whirlpool-hash' of RLabs/xs into master 2024-05-01 23:49:36 -07:00
Russtopia aa33a3b8a0 Merge branch 'log-listener-ipaddr' of RLabs/xs into master 2024-05-01 23:48:18 -07:00
Russ Magee 7e4aeba93a Add remote IP to net.Listener Accept logmsg 2024-05-01 23:46:43 -07:00
Russ Magee 91bb0778b2 Addition of WHIRLPOOL hash 2024-05-01 23:43:24 -07:00
Russ Magee 952279a108 Moved esc seq table out of copyBuffer to avoid redecls 2024-04-27 22:01:43 -07:00
Russ Magee dbaa8b5b62 Ensure auth fails if server is somehow built for unsupported platform 2024-03-30 00:48:46 -07:00
Russ Magee 77c9b8654f Left GOPROXY alone 2024-03-02 14:46:15 -08:00
Russ Magee e42645a2b3 Refreshed go.{mod,sum} and bumped semver in Makefile 2024-03-01 23:34:14 -08:00
Russ Magee 057a3c01c7 Updated go.{mod,sum} 2024-02-25 21:14:20 -08:00
Russ Magee 540cb8ff3a gofmt 2024-02-25 21:14:00 -08:00
Russ Magee ae67ee6201 Fixed CI script 2024-01-29 21:47:03 -08:00
Russ Magee 8827d67cc6 unified refs to authtoken file to a const string 2024-01-29 21:37:07 -08:00
Russtopia 17d7bc01ef Update 'README.md'
Updated references to .xs_id location
2024-01-29 19:03:13 -08:00
Russ Magee 89ad0e0998 Fixed missed authtoken file ref in auth.go 2024-01-29 18:56:21 -08:00
Russ Magee 713f44086a Bumped version 2024-01-29 18:43:24 -08:00
Russ Magee 08cccb6929 Moved .xs_id to ~/.config/xs 2024-01-29 18:40:26 -08:00
Russ Magee 6212119621 Added max bounds for chaff, rekey intervals and random jitter for rekey interval 2023-12-03 19:22:05 -08:00
Russtopia faf8769ac4 Merge branch 'remodulate-on-rekey' of RLabs/xs into master
Add optional cipher/hmac algo remodulate on rekey
2023-12-02 02:00:17 -08:00
Russ Magee 32b669192b Add optional cipher/hmac algo remodulate on rekey 2023-12-02 01:58:30 -08:00
27 changed files with 2801 additions and 447 deletions

50
LICENSE
View File

@ -1,21 +1,37 @@
MIT License MIT NON-AI License
Copyright (c) 2017 - 2021 Russell Magee (xs/xsd/xsnet/xspasswd) Copyright (c) 2017 - 2026 Russell Magee (xs/xsd/xsnet/xspasswd)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy of the software and associated documentation files (the "Software"), to deal in
of this software and associated documentation files (the "Software"), to deal the Software without restriction, including without limitation the
in the Software without restriction, including without limitation the rights rights to use, copy, modify, merge, publish, distribute, sublicense,
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell and/or sell copies of the Software, and to permit persons to whom the
copies of the Software, and to permit persons to whom the Software is Software is furnished to do so, subject to the following conditions.
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included
copies or substantial portions of the Software. in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR In addition, the following restrictions apply:
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1. The Software and any modifications made to it may not be used for the
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER purpose of training or improving machine learning algorithms, including
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, but not limited to artificial intelligence, natural language processing,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE or data mining. This condition applies to any derivatives,
SOFTWARE. modifications, or updates based on the Software code. Any usage of the
Software in an AI-training dataset is considered a breach of this License.
2. The Software may not be included in any dataset used for training or
improving machine learning algorithms, including but not limited to
artificial intelligence, natural language processing, or data mining.
3. Any person or organization found to be in violation of these
restrictions will be subject to legal action and may be held liable for
any damages resulting from such use.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,4 +1,4 @@
VERSION := 0.9.6 VERSION := 0.9.18
.PHONY: lint vis clean common client server passwd\ .PHONY: lint vis clean common client server passwd\
subpkgs install uninstall reinstall scc subpkgs install uninstall reinstall scc
@ -34,7 +34,7 @@ GIT_COMMIT := $(shell git rev-list -1 HEAD)
BUILDOPTS :=$(BUILDOPTS)"$(GOBUILDOPTS) -ldflags \"-X main.version=$(VERSION)$(MTAG)$(VTAG) -X main.gitCommit=$(GIT_COMMIT)\"" BUILDOPTS :=$(BUILDOPTS)"$(GOBUILDOPTS) -ldflags \"-X main.version=$(VERSION)$(MTAG)$(VTAG) -X main.gitCommit=$(GIT_COMMIT)\""
#endif #endif
SUBPKGS = logger spinsult xsnet SUBPKGS = logger xsnet
TOOLS = xs xsd TOOLS = xs xsd
SUBDIRS = $(LIBS) $(TOOLS) SUBDIRS = $(LIBS) $(TOOLS)
@ -43,13 +43,10 @@ ifeq ($(GOOS),)
endif endif
ifeq ($(GOOS),windows) ifeq ($(GOOS),windows)
ifeq ($(MSYSTEM),MSYS) INSTPREFIX = /usr
WIN_MSYS=1 else
endif
endif
INSTPREFIX = /usr/local INSTPREFIX = /usr/local
endif
all: common client server all: common client server
@ -73,7 +70,7 @@ tools:
common: common:
$(GO) build . $(GO) build .
go install . go install -a .
client: common client: common
@ -81,7 +78,7 @@ client: common
server: common server: common
ifeq ($(MSYSTEM),MSYS) ifeq ($(GOOS),windows)
echo "Build of xsd server for Windows not yet supported" echo "Build of xsd server for Windows not yet supported"
else else
$(MAKE) BUILDOPTS=$(BUILDOPTS) -C xsd $(MAKE) BUILDOPTS=$(BUILDOPTS) -C xsd
@ -106,13 +103,10 @@ lint:
reinstall: uninstall install reinstall: uninstall install
install: install:
echo "WIN_MSYS:" $(WIN_MSYS) ifeq ($(GOOS),windows)
ifdef WIN_MSYS cp xs/xs $(INSTPREFIX)/bin/xs
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xs cp xs/xs $(INSTPREFIX)/bin/xc
cp xs/mintty_wrapper.sh $(INSTPREFIX)/bin/xc @echo "Install of xsd server for Windows not yet supported"
cp xs/xs $(INSTPREFIX)/bin/_xs
cp xs/xs $(INSTPREFIX)/bin/_xc
echo "Install of xsd server for Windows not yet supported"
else else
cp xs/xs $(INSTPREFIX)/bin cp xs/xs $(INSTPREFIX)/bin
cd $(INSTPREFIX)/bin && ln -s xs xc && cd - cd $(INSTPREFIX)/bin && ln -s xs xc && cd -
@ -120,8 +114,7 @@ else
endif endif
uninstall: uninstall:
rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc \ rm -f $(INSTPREFIX)/bin/xs $(INSTPREFIX)/bin/xc
$(INSTPREFIX)/bin/_xs $(INSTPREFIX)/bin/_xc
ifndef $(WIN_MSYS) ifndef $(WIN_MSYS)
rm -f $(INSTPREFIX)/sbin/xsd rm -f $(INSTPREFIX)/sbin/xsd
endif endif

View File

@ -3,6 +3,7 @@
# XS # XS
![last build status](https://bacillus.blitter.com/onPush-xs-build/lastStatusIcon) ![last build status](https://bacillus.blitter.com/onPush-xs-build/lastStatusIcon)
![terminal screenshot on MSYS64](https://blitter.com/~russtopia/files/xs-msys64.png)
-- --
XS (**X**perimental **S**hell) is a simple alternative to ssh (<5% total SLOCC) written from scratch in Go. XS (**X**perimental **S**hell) is a simple alternative to ssh (<5% total SLOCC) written from scratch in Go.
@ -44,10 +45,12 @@ Currently supported session algorithms:
* Blowfish-64 * Blowfish-64
* CryptMTv1 (64bit) (https://eprint.iacr.org/2005/165.pdf) * CryptMTv1 (64bit) (https://eprint.iacr.org/2005/165.pdf)
* ChaCha20 (https://github.com/aead/chacha20) * ChaCha20 (https://github.com/aead/chacha20)
* HOPSCOTCH (https://gogs.blitter.com/Russtopia/hopscotch)
[HMAC] [HMAC]
* HMAC-SHA256 * HMAC-SHA256
* HMAC-SHA512 * HMAC-SHA512
* WHIRLPOOL
*** ***
**A Note on 'cryptographic agility'** **A Note on 'cryptographic agility'**
@ -92,18 +95,6 @@ KYBER IND-CCA-2 KEM
As of this time (Oct 2018) Kyber is one of the candidate algorithms submitted to the [NIST post-quantum cryptography project](https://csrc.nist.gov/Projects/Post-Quantum-Cryptography). The authors recommend using it in "... so-called hybrid mode in combination with established "pre-quantum" security; for example in combination with elliptic-curve Diffie-Hellman." THIS PROJECT DOES NOT DO THIS (in case you didn't notice yet, THIS PROJECT IS EXPERIMENTAL.) As of this time (Oct 2018) Kyber is one of the candidate algorithms submitted to the [NIST post-quantum cryptography project](https://csrc.nist.gov/Projects/Post-Quantum-Cryptography). The authors recommend using it in "... so-called hybrid mode in combination with established "pre-quantum" security; for example in combination with elliptic-curve Diffie-Hellman." THIS PROJECT DOES NOT DO THIS (in case you didn't notice yet, THIS PROJECT IS EXPERIMENTAL.)
### Dependencies:
* Recent version of go (tested, at various times, with go-1.9 to go-1.12.4)
* [github.com/mattn/go-isatty](http://github.com/mattn/go-isatty) //terminal tty detection
* [github.com/kr/pty](http://github.com/kr/pty) //unix pty control (server pty connections)
* [github.com/jameskeane/bcrypt](http://github.com/jameskeane/bcrypt) //password storage/auth
* [blitter.com/go/goutmp](https://gogs.blitter.com/RLabs/goutmp) // wtmp/lastlog C bindings for user accounting
* [https://gitlab.com/yawning/kyber](https://gogs.blitter.com/RLabs/kyber) // golang Kyber KEM
* [https://gitlab.com/yawning/kyber](https://gogs.blitter.com/RLabs/newhope) // golang NEWHOPE,NEWHOPE-SIMPLE KEX
* [blitter.com/go/mtwist](https://gogs.blitter.com/RLabs/mtwist) // 64-bit Mersenne Twister PRNG
* [blitter.com/go/cryptmt](https://gogs.blitter.com/RLabs/cryptmt) // CryptMTv1 stream cipher
### Installing ### Installing
@ -197,15 +188,17 @@ or is interrupted.
### Setting up an 'authtoken' for scripted (password-free) logins ### Setting up an 'authtoken' for scripted (password-free) logins
Use the -g option of xs to request a token from the remote server, which will return a Use the -g option of xs to request a token from the remote server, which will return a
hostname:token string. Place this string into $HOME/.xs_id to allow logins without hostname:token string. Place this string into $HOME/.config/xs/.xs_id to allow logins without
entering a password (obviously, $HOME/.xs_id on both server and client for the user entering a password (obviously, $HOME/.config/xs/.xs_id on both server and client for the user
should *not* be world-readable.) should *not* be world-readable.)
``` ```
$ xs -g user@host.net >~/.xs_id $ xs -g user@host.net >>~/.config/xs/.xs_id
``` ```
[enter password blindly, authtoken entry will be stored in ~/.xs_id] [enter password blindly, authtoken entry will be stored in ~/.config/xs/.xs_id]
NOTE you may need to remove older entries for the same host if this is not the first time you have added
it to your .xs_id file.
### File Copying using xc ### File Copying using xc
@ -218,6 +211,12 @@ If no leading / is specified in src-or-dest-path, it is assumed to be relative t
remote user. File operations are all performed as the remote user, so account permissions apply remote user. File operations are all performed as the remote user, so account permissions apply
as expected. as expected.
When running under MSYS2, one must set the MINGW_ROOT environment variable to assist in
determining how to convert Windows paths to UNIX-style paths. This should be the installation path
of one's MSYS2 environment (eg., _C:/msys2_). Go's stdlib, under the hood, still uses Windows
style paths (drive letters and all) to locate other executables and _xc_ uses _tar_ as part of the copy
functionality.
Local (client) to remote (server) copy: Local (client) to remote (server) copy:
``` ```
$ xc fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir $ xc fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir

38
auth.go Executable file → Normal file
View File

@ -2,7 +2,7 @@ package xs
// Package xs - a secure terminal client/server written from scratch in Go // Package xs - a secure terminal client/server written from scratch in Go
// //
// Copyright (c) 2017-2020 Russell Magee // Copyright (c) 2017-2025 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this // Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution) // distribution)
// //
@ -23,6 +23,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"blitter.com/go/xs/xsnet"
"github.com/jameskeane/bcrypt" "github.com/jameskeane/bcrypt"
passlib "gopkg.in/hlandau/passlib.v1" passlib "gopkg.in/hlandau/passlib.v1"
) )
@ -52,7 +53,7 @@ func VerifyPass(ctx *AuthCtx, user, password string) (bool, error) {
} else if runtime.GOOS == "freebsd" { } else if runtime.GOOS == "freebsd" {
pwFileName = "/etc/master.passwd" pwFileName = "/etc/master.passwd"
} else { } else {
pwFileName = "unsupported" return false, errors.New("Unsupported platform")
} }
pwFileData, e := ctx.reader(pwFileName) pwFileData, e := ctx.reader(pwFileName)
if e != nil { if e != nil {
@ -154,7 +155,7 @@ func AuthUserByPasswd(ctx *AuthCtx, username string, auth string, fname string)
// ------------- End xs-local passwd auth routine(s) ----------- // ------------- End xs-local passwd auth routine(s) -----------
// AuthUserByToken checks user login information against an auth token. // AuthUserByToken checks user login information against an auth token.
// Auth tokens are stored in each user's $HOME/.xs_id and are requested // Auth tokens are stored in each user's $HOME/.config/xs/.xs_id and are requested
// via the -g option. // via the -g option.
// The function also check system /etc/passwd to cross-check the user // The function also check system /etc/passwd to cross-check the user
// actually exists. // actually exists.
@ -172,9 +173,9 @@ func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth st
return false return false
} }
b, e := ctx.reader(fmt.Sprintf("%s/.xs_id", u.HomeDir)) b, e := ctx.reader(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE))
if e != nil { if e != nil {
log.Printf("INFO: Cannot read %s/.xs_id\n", u.HomeDir) log.Printf("INFO: Cannot read %s/%s\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)
return false return false
} }
@ -214,18 +215,39 @@ func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth st
return return
} }
func GroomFsPath(path string) (ret string) {
pathRoot := os.Getenv("MINGW_ROOT")
if pathRoot != "" {
ret = path[len(pathRoot):]
ret = strings.ReplaceAll(ret, "\\", "/")
} else {
ret = path
}
//fmt.Printf("groomed fspath:%v\n", ret)
return
}
func GetTool(tool string) (ret string) { func GetTool(tool string) (ret string) {
ret = "/bin/" + tool cmdSuffix := ""
pathRoot := os.Getenv("MINGW_ROOT")
if pathRoot != "" {
cmdSuffix = ".exe"
}
//fmt.Printf("pathRoot:%v cmdSuffix:%v\n", pathRoot, cmdSuffix)
ret = pathRoot + "/bin/" + tool + cmdSuffix
_, err := os.Stat(ret) _, err := os.Stat(ret)
if err == nil { if err == nil {
return ret return ret
} }
ret = "/usr/bin/" + tool ret = pathRoot + "/usr/bin/" + tool + cmdSuffix
_, err = os.Stat(ret) _, err = os.Stat(ret)
if err == nil { if err == nil {
return ret return ret
} }
ret = "/usr/local/bin/" + tool ret = pathRoot + "/usr/local/bin/" + tool + cmdSuffix
_, err = os.Stat(ret) _, err = os.Stat(ret)
if err == nil { if err == nil {
return ret return ret

View File

@ -5,7 +5,7 @@
export GOPATH="${HOME}/go" export GOPATH="${HOME}/go"
export PATH=/usr/local/bin:/usr/bin:/usr/lib/ccache/bin:/bin:$GOPATH/bin export PATH=/usr/local/bin:/usr/bin:/usr/lib/ccache/bin:/bin:$GOPATH/bin
unset GO111MODULE unset GO111MODULE
export GOPROXY="direct" #export GOPROXY="direct"
#!# GOCACHE will be phased out in v1.12. [github.com/golang/go/issues/26809] #!# GOCACHE will be phased out in v1.12. [github.com/golang/go/issues/26809]
#!export GOCACHE="${HOME}/.cache/go-build" #!export GOCACHE="${HOME}/.cache/go-build"
@ -25,6 +25,16 @@ echo "Building most recent push on branch $branch"
git checkout "$branch" git checkout "$branch"
ls ls
#!############
#!stage "GoMod"
#!############
#!go clean -modcache
#!
#!rm -f go.{mod,sum}
#!go mod init blitter.com/go/xs
#!go mod tidy
#!echo "---"
############ ############
stage "Build" stage "Build"
############ ############
@ -32,26 +42,29 @@ echo "Invoking 'make clean' ..."
make clean make clean
echo "Invoking 'make all' ..." echo "Invoking 'make all' ..."
make all make all
echo "---"
############ ############
stage "Lint" stage "Lint"
############ ############
make lint make lint
echo "---"
############ ############
stage "UnitTests" stage "UnitTests"
############ ############
go test -v . go test -v .
echo "---"
############ ############
stage "Test(Authtoken)" stage "Test(Authtoken)"
############ ############
if [ -f ~/.xs_id ]; then if [ -f ~/.config/xs/.xs_id ]; then
echo "Clearing test user $USER ~/.xs_id file ..." echo "Clearing test user $USER .xs_id file ..."
mv ~/.xs_id ~/.xs_id.bak mv ~/.config/xs/.xs_id ~/.config/xs/.xs_id.bak
fi fi
echo "Setting dummy authtoken in ~/.xs_id ..." echo "Setting dummy authtoken in .xs_id ..."
echo "localhost:${USER}:asdfasdfasdf" >~/.xs_id echo "localhost:${USER}:asdfasdfasdf" >~/.config/xs/.xs_id
echo "Performing remote command on @localhost via authtoken login ..." echo "Performing remote command on @localhost via authtoken login ..."
tokentest=$(timeout 10 xs -x "echo -n FOO" @localhost) tokentest=$(timeout 10 xs -x "echo -n FOO" @localhost)
if [ "${tokentest}" != "FOO" ]; then if [ "${tokentest}" != "FOO" ]; then
@ -61,6 +74,7 @@ else
echo "client cmd performed OK." echo "client cmd performed OK."
unset tokentest unset tokentest
fi fi
echo "---"
############ ############
stage "Test(xc S->C)" stage "Test(xc S->C)"
@ -85,22 +99,25 @@ else
echo "FAILED!" echo "FAILED!"
exit $stat exit $stat
fi fi
echo "---"
############ ############
stage "Test(xc C->S)" stage "Test(xc C->S)"
############ ############
echo "TODO ..." echo "TODO ..."
if [ -f ~/.xs_id.bak ]; then if [ -f ~/.config/xs/.xs_id.bak ]; then
echo "Restoring test user $USER ~/.xs_id file ..." echo "Restoring test user $USER .xs_id file ..."
mv ~/.xs_id.bak ~/.xs_id mv ~/.config/xs/.xs_id.bak ~/.config/xs/.xs_id
fi fi
echo "---"
############ ############
stage "Artifacts" stage "Artifacts"
############ ############
echo -n "Creating tarfile ..." echo -n "Creating tarfile ..."
tar -cz --exclude=.git --exclude=cptest -f ${BACILLUS_ARTFDIR}/xs.tgz . tar -cz --exclude=.git --exclude=cptest -f ${BACILLUS_ARTFDIR}/xs.tgz .
echo "---"
############ ############
stage "Cleanup" stage "Cleanup"

2295
capabilities.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// Package xs - a secure terminal client/server written from scratch in Go // Package xs - a secure terminal client/server written from scratch in Go
// //
// Copyright (c) 2017-2020 Russell Magee // Copyright (c) 2017-2025 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this // Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution) // distribution)
// //

49
go.mod
View File

@ -1,38 +1,37 @@
module blitter.com/go/xs module blitter.com/go/xs
go 1.20 go 1.25.3
require ( require (
blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c blitter.com/go/cryptmt v1.0.3
blitter.com/go/cryptmt v1.0.2
blitter.com/go/goutmp v1.0.6 blitter.com/go/goutmp v1.0.6
blitter.com/go/groestl v0.0.0-20220410000905-c4decbf31d64 blitter.com/go/herradurakex v1.0.1
blitter.com/go/herradurakex v1.0.0 blitter.com/go/hopscotch v0.4.0
blitter.com/go/hopscotch v0.1.1 blitter.com/go/kyber v1.0.0
blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9 blitter.com/go/newhope v1.0.0
blitter.com/go/mtwist v1.0.1 blitter.com/go/spinsult v0.9.1
blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/creack/pty v1.1.18 github.com/creack/pty v1.1.24
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f
github.com/klauspost/cpuid/v2 v2.2.5 github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004
github.com/klauspost/reedsolomon v1.11.8
github.com/kuking/go-frodokem v1.0.2 github.com/kuking/go-frodokem v1.0.2
github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-isatty v0.0.20
github.com/pkg/errors v0.9.1 github.com/xtaci/kcp-go v4.3.4+incompatible
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 golang.org/x/crypto v0.49.0
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b golang.org/x/sys v0.42.0
github.com/tjfoc/gmsm v1.4.1
github.com/xtaci/kcp-go v5.4.20+incompatible
golang.org/x/crypto v0.13.0
golang.org/x/net v0.15.0
golang.org/x/sys v0.12.0
gopkg.in/hlandau/easymetric.v1 v1.0.0
gopkg.in/hlandau/measurable.v1 v1.0.1
gopkg.in/hlandau/passlib.v1 v1.0.11 gopkg.in/hlandau/passlib.v1 v1.0.11
) )
require ( require (
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c // indirect
golang.org/x/text v0.13.0 // indirect blitter.com/go/mtwist v1.0.2 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/reedsolomon v1.13.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
golang.org/x/net v0.51.0 // indirect
gopkg.in/hlandau/easymetric.v1 v1.0.0 // indirect
gopkg.in/hlandau/measurable.v1 v1.0.1 // indirect
) )

77
go.sum
View File

@ -1,21 +1,21 @@
blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c h1:LcnFFg6MCIJHf26P7eOUST45fNLHJI5erq0gWZaDLCo= blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c h1:LcnFFg6MCIJHf26P7eOUST45fNLHJI5erq0gWZaDLCo=
blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c/go.mod h1:EMJtRcf22WCtHGiXCw+NB/Sb/PYcXtUgUql6LDEwyXo= blitter.com/go/chacha20 v0.0.0-20200130200441-214e4085f54c/go.mod h1:EMJtRcf22WCtHGiXCw+NB/Sb/PYcXtUgUql6LDEwyXo=
blitter.com/go/cryptmt v1.0.2 h1:ZcLhQk7onUssXyQwG3GdXDXctCVnNL+b7aFuvwOdKXc= blitter.com/go/cryptmt v1.0.3 h1:C1j55/TV8301jROxn83Zlm+qNH3/XUSzBoTrbBGD8gw=
blitter.com/go/cryptmt v1.0.2/go.mod h1:tdME2J3O4agaDAYIYNQzzuB28yVGnPSMmV3a/ucSU84= blitter.com/go/cryptmt v1.0.3/go.mod h1:otZPP0Vps15DRZNo2zD4RLym+IT6XnbtI1HS412BxHM=
blitter.com/go/goutmp v1.0.6 h1:jRKRw2WalVBza4T50etAfbvT2xp9G5uykIHTvyB5r0k= blitter.com/go/goutmp v1.0.6 h1:jRKRw2WalVBza4T50etAfbvT2xp9G5uykIHTvyB5r0k=
blitter.com/go/goutmp v1.0.6/go.mod h1:DnK/uLBu1/1yLFiuVlmwvWErzAWVp+pDv7t6ZaQRLNc= blitter.com/go/goutmp v1.0.6/go.mod h1:DnK/uLBu1/1yLFiuVlmwvWErzAWVp+pDv7t6ZaQRLNc=
blitter.com/go/groestl v0.0.0-20220410000905-c4decbf31d64 h1:SH6cZ4JiOTmWGeVd5hCgt8gsMvfPPHWpEwNdxfsBugM= blitter.com/go/herradurakex v1.0.1 h1:7smv+RiG6PQ2hHebT/uSjIKcisp/lx5PSCBn8fISHWA=
blitter.com/go/groestl v0.0.0-20220410000905-c4decbf31d64/go.mod h1:YMdIR/gCtFwU/a09jyWAwUu2J9CQejUFwkfD+PyVg+4= blitter.com/go/herradurakex v1.0.1/go.mod h1:m3+vYZX+2dDjdo+n/HDnXEYJX9pwmNeQLgAfJM8mtxw=
blitter.com/go/herradurakex v1.0.0 h1:6XaxY+JLT1HUWPF0gYJnjX3pVjrw4YhYZEzZ1U0wkyc= blitter.com/go/hopscotch v0.4.0 h1:alWU1HRJhgofqA/0n7cW2McascbrH06As1SZ8etvPcM=
blitter.com/go/herradurakex v1.0.0/go.mod h1:m3+vYZX+2dDjdo+n/HDnXEYJX9pwmNeQLgAfJM8mtxw= blitter.com/go/hopscotch v0.4.0/go.mod h1:hCz7oE31KjaO9M6+s2DcyVNlAA8saE/AaVYKFs7hl1I=
blitter.com/go/hopscotch v0.1.1 h1:hh809THr3I52J5G5QozNhDSd+qGwXWGqLh3FJBGrp+o= blitter.com/go/kyber v1.0.0 h1:xniqw15FUrmR1bTqwH57cIZ0Ko2kYcINnSRE3ESzA9M=
blitter.com/go/hopscotch v0.1.1/go.mod h1:hCz7oE31KjaO9M6+s2DcyVNlAA8saE/AaVYKFs7hl1I= blitter.com/go/kyber v1.0.0/go.mod h1:xE277hhExsmBIaVT7oFgNfkXC3ioHaZCTreNJHSCwqw=
blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9 h1:D45AnrNphtvczBXRp5JQicZRTgaK/Is5bgPDDvRKhTc= blitter.com/go/mtwist v1.0.2 h1:4zmpKNynrRuFF8JAPdhBN8TaJB+quU5d2i7KBgFtVng=
blitter.com/go/kyber v0.0.0-20200130200857-6f2021cb88d9/go.mod h1:SK6QfGG72lIfKW1Td0wH7f0wwN5nSIhV3K+wvzGNjrw= blitter.com/go/mtwist v1.0.2/go.mod h1:Y/0x0EsFMUKK1+tdkoCW7H88eF7CTOycUMsTHcfCoZE=
blitter.com/go/mtwist v1.0.1 h1:PxmoWexfMpLmc8neHP/PcRc3s17ct7iz4d5W/qJVt04= blitter.com/go/newhope v1.0.0 h1:oUn35Ei30AGmLeqjNIG6DA7YNaK7fncBx4ptTnNrzmo=
blitter.com/go/mtwist v1.0.1/go.mod h1:aU82Nx8+b1v8oZRNqImfEDzDTPim81rY0ACKAIclV18= blitter.com/go/newhope v1.0.0/go.mod h1:ywoxfDBqInPsqtnxYsmS4SYMJ5D/kNcrFgpvI+Xcun0=
blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae h1:YBBaCcdYRrI1btsmcMTv1VMPmaSXXz0RwKOTgMJYSRU= blitter.com/go/spinsult v0.9.1 h1:j98oB6JyyWVCt8M6sAszuXwGsNTJWno96bnQu1CECzo=
blitter.com/go/newhope v0.0.0-20200130200750-192fc08a8aae/go.mod h1:ywoxfDBqInPsqtnxYsmS4SYMJ5D/kNcrFgpvI+Xcun0= blitter.com/go/spinsult v0.9.1/go.mod h1:/EchWRYRoJvzxpR6Pt6tSpQ9X4bjhF4BOppXYsZa16I=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
@ -23,10 +23,11 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSi
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
@ -47,22 +48,25 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f h1:UWGE8Vi+1Agt0lrvnd7UsmvwqWKRzb9byK9iQmsbY0Y= github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f h1:UWGE8Vi+1Agt0lrvnd7UsmvwqWKRzb9byK9iQmsbY0Y=
github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s= github.com/jameskeane/bcrypt v0.0.0-20120420032655-c3cd44c1e20f/go.mod h1:u+9Snq0w+ZdYKi8BBoaxnEwWu0fY4Kvu9ByFpM51t1s=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004 h1:G+9t9cEtnC9jFiTxyptEKuNIAbiN5ZCQzX2a74lj3xg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004/go.mod h1:KmHnJWQrgEvbuy0vcvj00gtMqbvNn1L+3YUZLK/B92c=
github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/reedsolomon v1.11.8/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/reedsolomon v1.13.3 h1:01GwnO2xoCSaM0ShP4qwl+FsHg3csFShC6Tu/RS1ji0=
github.com/klauspost/reedsolomon v1.13.3/go.mod h1:yjqqjgMTQkBUHSG97/rm4zipffCNbCiZcB3kTqr++sQ=
github.com/kuking/go-frodokem v1.0.2 h1:sxdguENCyr6WnLbJ/cjz0AYCW75H1b+E6zXY2ldZnUU= github.com/kuking/go-frodokem v1.0.2 h1:sxdguENCyr6WnLbJ/cjz0AYCW75H1b+E6zXY2ldZnUU=
github.com/kuking/go-frodokem v1.0.2/go.mod h1:83ZX1kHOd72ouCsvbffCqJIj7Ih83MQTAjH2QbqzLZk= github.com/kuking/go-frodokem v1.0.2/go.mod h1:83ZX1kHOd72ouCsvbffCqJIj7Ih83MQTAjH2QbqzLZk=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI=
@ -71,18 +75,15 @@ github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg= github.com/xtaci/kcp-go v4.3.4+incompatible h1:T56s9GLhx+KZUn5T8aO2Didfa4uTYvjeVIRLt6uYdhE=
github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/kcp-go v4.3.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -93,8 +94,8 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -104,14 +105,11 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -139,7 +137,8 @@ gopkg.in/hlandau/measurable.v1 v1.0.1 h1:wH5UZKCRUnRr1iD+xIZfwhtxhmr+bprRJttqA1R
gopkg.in/hlandau/measurable.v1 v1.0.1/go.mod h1:6N+SYJGMTmetsx7wskULP+juuO+++tsHJkAgzvzsbuM= gopkg.in/hlandau/measurable.v1 v1.0.1/go.mod h1:6N+SYJGMTmetsx7wskULP+juuO+++tsHJkAgzvzsbuM=
gopkg.in/hlandau/passlib.v1 v1.0.11 h1:vKeHwGRdWBD9mm4bJ56GAAdBXpFUYvg/BYYkmphjnmA= gopkg.in/hlandau/passlib.v1 v1.0.11 h1:vKeHwGRdWBD9mm4bJ56GAAdBXpFUYvg/BYYkmphjnmA=
gopkg.in/hlandau/passlib.v1 v1.0.11/go.mod h1:wxGAv2CtQHlzWY8NJp+p045yl4WHyX7v2T6XbOcmqjM= gopkg.in/hlandau/passlib.v1 v1.0.11/go.mod h1:wxGAv2CtQHlzWY8NJp+p045yl4WHyX7v2T6XbOcmqjM=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -2,7 +2,7 @@ package xs
// Package xs - a secure terminal client/server written from scratch in Go // Package xs - a secure terminal client/server written from scratch in Go
// //
// Copyright (c) 2017-2020 Russell Magee // Copyright (c) 2017-2025 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this // Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution) // distribution)
// //

View File

@ -1,24 +0,0 @@
.PHONY: info clean lib
ifeq ($(GARBLE),y)
GO = garble -literals -tiny -debugdir=garbled
else
GO = go
endif
all: lib
clean:
go clean .
lib: info
$(GO) build .
go install .
ifneq ($(MSYSTEM),)
info:
@echo "Building for Windows (MSYS)"
else
info:
@echo "Building for Linux"
endif

View File

@ -1,55 +0,0 @@
// A golang translation of a 'Shakespeare insult generator'
// Originally from http://www.mainstrike.com/mstservices/handy/insult.html
package spinsult
import (
"math/rand"
"time"
)
var (
r *rand.Rand
phrase1 = [...]string{
"artless", "bawdy", "beslubbering", "bootless", "churlish", "clouted",
"cockered", "craven", "currish", "dankish", "dissembling", "droning", "errant", "fawning",
"fobbing", "frothy", "froward", "gleeking", "goatish", "gorbellied", "impertinent",
"infectious", "jarring", "loggerheaded", "lumpish", "mammering", "mangled", "mewling",
"paunchy", "pribbling", "puking", "puny", "qualling", "rank", "reeky", "roguish", "ruttish",
"saucy", "spleeny", "spongy", "surly", "tottering", "unmuzzled", "vain", "venomed",
"villainous", "warped", "wayward", "weedy", "yeasty"}
phrase2 = [...]string{"base-court", "bat-fowling", "beef-witted", "beetle-headed",
"boil-brained", "clapper-clawed", "clay-brained", "common-kissing", "crook-pated",
"dismal-dreaming", "dizzy-eyed", "doghearted", "dread-bolted", "earth-vexing",
"elf-skinned", "fat-kidneyed", "fen-sucked", "flap-mouthed", "fly-bitten",
"folly-fallen", "fool-born", "full-gorged", "guts-griping", "half-faced", "hasty-witted",
"hedge-born", "hell-hated", "idle-headed", "ill-breeding", "ill-nurtured", "knotty-pated",
"milk-livered", "motley-minded", "onion-eyed", "plume-plucked", "pottle-deep",
"pox-marked", "reeling-ripe", "rough-hewn", "rude-growing", "rump-fed", "shard-borne",
"sheep-biting", "spur-galled", "swag-bellied", "tardy-gaited", "tickle-brained",
"toad-spotted", "urchin-snouted", "weather-bitten"}
phrase3 = [...]string{"apple-john", "baggage", "barnacle", "bladder", "boar-pig", "bugbear",
"bum-bailey", "canker-blossom", "clack-dish", "clotpole", "codpiece", "coxcomb", "death-token",
"dewberry", "flap-dragon", "flax-wench", "flirt-gill", "foot-licker", "fustilarian",
"giglet", "gudgeon", "haggard", "harpy", "hedge-pig", "horn-beast", "hugger-mugger",
"joithead", "lewdster", "lout", "maggot-pie", "malt-worm", "mammet", "measle", "minnow",
"miscreant", "moldwarp", "mumble-news", "nut-hook", "pigeon-egg", "pignut", "pumpion",
"puttock", "ratsbane", "scut", "skainsmate", "strumpet", "varlet", "vassal", "wagtail",
"whey-face"}
)
func GetSentence() (ret string) {
return "Thou " + Get()
}
func Get() (ret string) {
if r == nil {
r = rand.New(rand.NewSource(time.Now().UnixNano()))
}
ret = phrase1[r.Int()%len(phrase1)] + " " +
phrase2[r.Int()%len(phrase2)] + " " +
phrase3[r.Int()%len(phrase3)] + "!"
return
}

View File

@ -1,52 +0,0 @@
//To show coverage for tests:
//
//1. go test -coverprofile=cov.out
//2. go tool cover -func=cov.out
//3. go tool cover -html=cov.out
//4. Profit!!
//
// For heatmap coverage, change step 1 to:
//2. go test -covermode=count -coverprofile=cov.out
//
// ref: https://blog.golang.org/cover
package spinsult
import (
"fmt"
"math/rand"
"testing"
)
func Test1Get(t *testing.T) {
//if testing.Short() {
// t.Skip("skipping test in short mode.")
//}
r = rand.New(rand.NewSource(42))
out := Get()
if out != "mammering doghearted codpiece!" {
t.Fail()
}
}
func Test2Get(t *testing.T) {
//if testing.Short() {
// t.Skip("skipping test in short mode.")
//}
out := Get()
if out != "dankish common-kissing coxcomb!" {
t.Fail()
}
out = GetSentence()
if out != "Thou wayward crook-pated fustilarian!" {
t.Fail()
}
}
// Example of calling Get() for a random insult.
func ExampleGet() {
r = rand.New(rand.NewSource(42))
out := GetSentence()
fmt.Println(out)
//Output: Thou mammering doghearted codpiece!
}

View File

@ -1,4 +1,4 @@
// +build freebsd //go:build freebsd
package xs package xs

View File

@ -1,10 +1,11 @@
// +build linux //go:build linux
package xs package xs
import ( import (
"errors" "errors"
"io" "io"
"os"
unix "golang.org/x/sys/unix" unix "golang.org/x/sys/unix"
) )
@ -30,7 +31,8 @@ type State struct {
// MakeRaw put the terminal connected to the given file descriptor into raw // MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be // mode and returns the previous state of the terminal so that it can be
// restored. // restored.
func MakeRaw(fd uintptr) (*State, error) { func MakeRaw(f *os.File) (*State, error) {
fd := f.Fd()
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil { if err != nil {
return nil, err return nil, err
@ -56,8 +58,8 @@ func MakeRaw(fd uintptr) (*State, error) {
// GetState returns the current state of a terminal which may be useful to // GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal. // restore the terminal after a signal.
func GetState(fd uintptr) (*State, error) { func GetState(f *os.File) (*State, error) {
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) termios, err := unix.IoctlGetTermios(int(f.Fd()), ioctlReadTermios)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -67,9 +69,9 @@ func GetState(fd uintptr) (*State, error) {
// Restore restores the terminal connected to the given file descriptor to a // Restore restores the terminal connected to the given file descriptor to a
// previous state. // previous state.
func Restore(fd uintptr, state *State) error { func Restore(f *os.File, state *State) error {
if state != nil { if state != nil {
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.termios) return unix.IoctlSetTermios(int(f.Fd()), ioctlWriteTermios, &state.termios)
} else { } else {
return errors.New("nil State") return errors.New("nil State")
} }
@ -78,7 +80,8 @@ func Restore(fd uintptr, state *State) error {
// ReadPassword reads a line of input from a terminal without local echo. This // ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice // is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n. // returned does not include the \n.
func ReadPassword(fd uintptr) ([]byte, error) { func ReadPassword(f *os.File) ([]byte, error) {
fd := f.Fd()
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios) termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -1,4 +1,4 @@
// +build windows //go:build windows
// Note the terminal manipulation functions herein are mostly stubs. They // Note the terminal manipulation functions herein are mostly stubs. They
// don't really do anything and the xs demo client depends on a wrapper // don't really do anything and the xs demo client depends on a wrapper
@ -15,10 +15,12 @@
package xs package xs
import ( import (
"io" "bufio"
"fmt"
"log"
"os"
"os/exec" "os/exec"
"os/signal"
"golang.org/x/sys/windows"
) )
type State struct { type State struct {
@ -27,67 +29,84 @@ type State struct {
// MakeRaw put the terminal connected to the given file descriptor into raw // MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be // mode and returns the previous state of the terminal so that it can be
// restored. // restored.
func MakeRaw(fd uintptr) (*State, error) { func MakeRaw(f *os.File) (*State, error) {
// This doesn't really work. The exec.Command() runs a sub-shell cmd := exec.Command("stty", "-echo", "raw")
// so the stty mods don't affect the client process. cmd.Stdin = f
cmd := exec.Command("stty", "-echo raw") err := cmd.Run()
cmd.Run() if err != nil {
log.Fatal(err)
return &State{}, err
}
// MSYS2/CYGWIN: wintty needs CTRL-C caught
// ----------------------------------------
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
go func() {
for sig := range c {
_ = sig
//fmt.Println(sig)
}
}()
// ----------------------------------------
return &State{}, nil return &State{}, nil
} }
// GetState returns the current state of a terminal which may be useful to // GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal. // restore the terminal after a signal.
func GetState(fd uintptr) (*State, error) { func GetState(f *os.File) (*State, error) {
return &State{}, nil return &State{}, nil
} }
// Restore restores the terminal connected to the given file descriptor to a // Restore restores the terminal connected to the given file descriptor to a
// previous state. // previous state.
func Restore(fd uintptr, state *State) error { func Restore(f *os.File, state *State) error {
cmd := exec.Command("stty", "echo cooked") cmd := exec.Command("stty", "sane")
cmd.Run() cmd.Stdin = f
err := cmd.Run()
if err != nil {
log.Fatal(err)
return nil
}
return nil return nil
} }
// ReadPassword reads a line of input from a terminal without local echo. This // ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice // is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n. // returned does not include the \n.
func ReadPassword(fd uintptr) ([]byte, error) { func ReadPassword(f *os.File) (pw []byte, err error) {
return readPasswordLine(passwordReader(fd)) sttycmd, err := exec.LookPath("stty")
} if err != nil {
return nil, err
// passwordReader is an io.Reader that reads from a specific file descriptor. } else {
type passwordReader windows.Handle //fmt.Printf("stty found at: %v\n", sttycmd)
cmdOff := exec.Command(sttycmd, "-echo")
func (r passwordReader) Read(buf []byte) (int, error) { cmdOff.Stdin = f //os.Stdin
return windows.Read(windows.Handle(r), buf) cmdOff.Stdout = nil //os.Stdout
} cmdOff.Stderr = nil //os.Stderr
err = cmdOff.Run()
// readPasswordLine reads from reader until it finds \n or io.EOF.
// The slice returned does not include the \n.
// readPasswordLine also ignores any \r it finds.
func readPasswordLine(reader io.Reader) ([]byte, error) {
var buf [1]byte
var ret []byte
for {
n, err := reader.Read(buf[:])
if n > 0 {
switch buf[0] {
case '\n':
return ret, nil
case '\r':
// remove \r from passwords on Windows
default:
ret = append(ret, buf[0])
}
continue
}
if err != nil { if err != nil {
if err == io.EOF && len(ret) > 0 { return nil, err
return ret, nil }
}
return ret, err //fmt.Printf("Enter password:")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
err = scanner.Err()
if err != nil {
return nil, err
}
pw = scanner.Bytes()
fmt.Println()
cmdOn := exec.Command(sttycmd, "echo")
cmdOn.Stdin = f //os.Stdin
cmdOn.Stdout = nil //os.Stdout
cmdOn.Stderr = nil //os.Stderr
err = cmdOn.Run()
if err != nil {
return nil, err
} }
} }
return
} }

View File

@ -1,39 +0,0 @@
#!/bin/bash
#
## This wrapper may be used within the MSYS/mintty Windows
## shell environment to have a functioning xs client with
## working 'raw' mode and hidden password entry.
##
## mintty uses named pipes and ptys to get a more POSIX-like
## terminal (incl. VT/ANSI codes) rather than the dumb Windows
## console interface; however Go on Windows does not have functioning
## MSYS/mintty code to set raw, echo etc. modes.
##
## Someday it would be preferable to put native Windows term mode
## code into the client build, but this is 'good enough' for now
## (with the exception of tty rows/cols not being set based on
## info from the server).
##
## INSTALLATION
## --
## Build the client, put it somewhere in your $PATH with this
## wrapper and edit the name of the client binary
## eg.,
## $ cp hkexsh.exe /usr/bin/.hkexsh.exe
## $ cp mintty_wrapper.sh /usr/bin/hkexsh
####
trap cleanup EXIT ERR
cleanup() {
stty sane
}
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
if [ ${1}x == "-hx" ]; then
_${me} -h
else
stty -echo raw icrnl
_${me} $@
fi

149
xs/xs.go
View File

@ -1,6 +1,6 @@
// xs client // xs client
// //
// Copyright (c) 2017-2020 Russell Magee // Copyright (c) 2017-2025 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this // Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution) // distribution)
// //
@ -33,9 +33,10 @@ import (
xs "blitter.com/go/xs" xs "blitter.com/go/xs"
"blitter.com/go/xs/logger" "blitter.com/go/xs/logger"
"blitter.com/go/xs/spinsult" "blitter.com/go/spinsult"
"blitter.com/go/xs/xsnet" "blitter.com/go/xs/xsnet"
isatty "github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
//isatty "github.com/mattn/go-isatty"
) )
var ( var (
@ -102,6 +103,14 @@ type (
escSeqs map[byte]escHandler escSeqs map[byte]escHandler
) )
var (
escs = escSeqs{
'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) },
't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) },
'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) },
}
)
// Copy copies from src to dst until either EOF is reached // Copy copies from src to dst until either EOF is reached
// on src or an error occurs. It returns the number of bytes // on src or an error occurs. It returns the number of bytes
// copied and the first error encountered while copying, if any. // copied and the first error encountered while copying, if any.
@ -149,11 +158,6 @@ func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err er
// or tunnel traffic indicator - note we cannot just spawn a goroutine // or tunnel traffic indicator - note we cannot just spawn a goroutine
// here, as copyBuffer() returns after each burst of data. Scope must // here, as copyBuffer() returns after each burst of data. Scope must
// outlive individual copyBuffer calls). // outlive individual copyBuffer calls).
escs := escSeqs{
'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) },
't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) },
'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + bob + "\x1b[39;49m")) },
}
/* /*
// If the reader has a WriteTo method, use it to do the copy. // If the reader has a WriteTo method, use it to do the copy.
@ -290,7 +294,14 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
captureStderr = true captureStderr = true
cmd = xs.GetTool("tar") cmd = xs.GetTool("tar")
args = []string{"-cz", "-f", "/dev/stdout"} //fmt.Printf("GetTool found cmd:%v\n", cmd)
/* Explicit -f /dev/stdout doesn't work in MINGW/MSYS64
* as '/dev/stdout' doesn't actually appear in the /dev/ filesystem...?
* And it appears not to actually be required as without -f stdout is
* implied. -rlm 2025-12-07
*/
//args = []string{"-cz", "-f", "/dev/stdout"}
args = []string{"-cz"}
files = strings.TrimSpace(files) files = strings.TrimSpace(files)
// Awesome fact: tar actually can take multiple -C args, and // Awesome fact: tar actually can take multiple -C args, and
// changes to the dest dir *as it sees each one*. This enables // changes to the dest dir *as it sees each one*. This enables
@ -306,6 +317,7 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
// remote destDir. // remote destDir.
for _, v := range strings.Split(files, " ") { for _, v := range strings.Split(files, " ") {
v, _ = filepath.Abs(v) // #nosec v, _ = filepath.Abs(v) // #nosec
v = xs.GroomFsPath(v)
dirTmp, fileTmp := path.Split(v) dirTmp, fileTmp := path.Split(v)
if dirTmp == "" { if dirTmp == "" {
args = append(args, fileTmp) args = append(args, fileTmp)
@ -318,7 +330,8 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS) bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS)
displayOpts := " -pre " //nolint:goconst,nolintlint displayOpts := " -pre " //nolint:goconst,nolintlint
cmd = xs.GetTool("bash") cmd = xs.GetTool("bash")
args = []string{"-c", xs.GetTool("tar") + " -cz -f /dev/stdout "} //args = []string{"-c", xs.GetTool("tar") + " -cz -f /dev/stdout "}
args = []string{"-c", xs.GetTool("tar") + " -cz "}
files = strings.TrimSpace(files) files = strings.TrimSpace(files)
// Awesome fact: tar actually can take multiple -C args, and // Awesome fact: tar actually can take multiple -C args, and
// changes to the dest dir *as it sees each one*. This enables // changes to the dest dir *as it sees each one*. This enables
@ -334,6 +347,7 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
// remote destDir. // remote destDir.
for _, v := range strings.Split(files, " ") { for _, v := range strings.Split(files, " ") {
v, _ = filepath.Abs(v) // #nosec v, _ = filepath.Abs(v) // #nosec
v = xs.GroomFsPath(v)
dirTmp, fileTmp := path.Split(v) dirTmp, fileTmp := path.Split(v)
if dirTmp == "" { if dirTmp == "" {
args[1] = args[1] + fileTmp + " " args[1] = args[1] + fileTmp + " "
@ -382,6 +396,8 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
c.Stderr = os.Stderr c.Stderr = os.Stderr
} }
//fmt.Printf("cmd:%v args:%v\n", cmdName, cmdArgs)
// Start the command (no pty) // Start the command (no pty)
err = c.Start() // returns immediately err = c.Start() // returns immediately
///////////// /////////////
@ -584,7 +600,7 @@ func usageCp() {
// //
// TODO: do this from the server side and have client just emit that // TODO: do this from the server side and have client just emit that
func rejectUserMsg() string { func rejectUserMsg() string {
return "Begone, " + spinsult.GetSentence() + "\r\n" return "Begone, thou " + spinsult.Get() + "!\r\n"
} }
// Transmit request to server for it to set up the remote end of a tunnel // Transmit request to server for it to set up the remote end of a tunnel
@ -610,6 +626,12 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
// Whether fancyArg is src or dst file depends on flag.Args() index; // Whether fancyArg is src or dst file depends on flag.Args() index;
// fancyArg as last flag.Args() element denotes dstFile // fancyArg as last flag.Args() element denotes dstFile
// fancyArg as not-last flag.Args() element denotes srcFile // fancyArg as not-last flag.Args() element denotes srcFile
/* rlm:2025-12-10 This breaks if srcPath is outside of MSYS2 tree, as srcPath
appears to silently be converted to an absolute winpath eg.,
/c/users/RM/... -> C:/users/RM/...
and the colon (:) in this breaks the logic below.
*/
var fancyUser, fancyHost, fancyPath string var fancyUser, fancyHost, fancyPath string
for i, arg := range a { for i, arg := range a {
if strings.Contains(arg, ":") || strings.Contains(arg, "@") { if strings.Contains(arg, ":") || strings.Contains(arg, "@") {
@ -689,23 +711,24 @@ func sendSessionParams(conn io.Writer /* *xsnet.Conn*/, rec *xs.Session) (e erro
// TODO: reduce gocyclo // TODO: reduce gocyclo
func main() { //nolint: funlen, gocyclo func main() { //nolint: funlen, gocyclo
var ( var (
isInteractive bool isInteractive bool
vopt bool vopt bool
gopt bool // true: login via password, asking server to generate authToken gopt bool // true: login via password, asking server to generate authToken
dbg bool dbg bool
shellMode bool // true: act as shell, false: file copier shellMode bool // true: act as shell, false: file copier
cipherAlg string cipherAlg string
hmacAlg string hmacAlg string
kexAlg string kexAlg string
server string server string
port uint port uint
cmdStr string cmdStr string
tunSpecStr string // lport1:rport1[,lport2:rport2,...] tunSpecStr string // lport1:rport1[,lport2:rport2,...]
rekeySecs uint rekeySecs uint
copySrc []byte remodRequested bool // true: when rekeying, switch to random cipher/hmac alg
copyDst string copySrc []byte
copyQuiet bool copyDst string
copyLimitBPS uint copyQuiet bool
copyLimitBPS uint
authCookie string authCookie string
chaffEnabled bool chaffEnabled bool
@ -725,11 +748,11 @@ func main() { //nolint: funlen, gocyclo
C_TWOFISH_128 C_TWOFISH_128
C_BLOWFISH_64 C_BLOWFISH_64
C_CRYPTMT1 C_CRYPTMT1
C_HOPSCOTCH
C_CHACHA20_12`) C_CHACHA20_12`)
flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC`"+` flag.StringVar(&hmacAlg, "m", "H_SHA256", "session `HMAC`"+`
H_SHA256 H_SHA256
H_SHA512`) H_SHA512
H_WHIRLPOOL`)
flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg`"+` flag.StringVar(&kexAlg, "k", "KEX_HERRADURA512", "KEx `alg`"+`
KEX_HERRADURA256 KEX_HERRADURA256
KEX_HERRADURA512 KEX_HERRADURA512
@ -745,8 +768,9 @@ func main() { //nolint: funlen, gocyclo
KEX_FRODOKEM_976AES KEX_FRODOKEM_976AES
KEX_FRODOKEM_976SHAKE`) KEX_FRODOKEM_976SHAKE`)
flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP") //nolint:lll flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP") //nolint:lll
flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll
flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`") flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`")
flag.BoolVar(&remodRequested, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)")
//nolint:gocritic,nolintlint // flag.StringVar(&authCookie, "a", "", "auth cookie") //nolint:gocritic,nolintlint // flag.StringVar(&authCookie, "a", "", "auth cookie")
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts") flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd
@ -885,7 +909,7 @@ func main() { //nolint: funlen, gocyclo
if !gopt { if !gopt {
// See if we can log in via an auth token // See if we can log in via an auth token
u, _ := user.Current() u, _ := user.Current()
ab, aerr := os.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir)) ab, aerr := os.ReadFile(fmt.Sprintf("%s/%s", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE))
if aerr == nil { if aerr == nil {
for _, line := range strings.Split(string(ab), "\n") { for _, line := range strings.Split(string(ab), "\n") {
line += "\n" line += "\n"
@ -903,7 +927,7 @@ func main() { //nolint: funlen, gocyclo
_, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]") _, _ = fmt.Fprintln(os.Stderr, "[no authtoken, use -g to request one from server]")
} }
} else { } else {
log.Printf("[cannot read %s/.xs_id]\n", u.HomeDir) log.Printf("[cannot read %s/%s]\n", u.HomeDir, xsnet.XS_ID_AUTHTOKFILE)
} }
} }
runtime.GC() runtime.GC()
@ -967,7 +991,13 @@ func main() { //nolint: funlen, gocyclo
if kcpMode != "unused" { if kcpMode != "unused" {
proto = "kcp" proto = "kcp"
} }
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode)
remodExtArg := ""
if remodRequested {
remodExtArg = "OPT_REMOD"
}
// Pass opt to Dial() via extensions arg
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode, remodExtArg)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
exitWithStatus(XSNetDialFailed) exitWithStatus(XSNetDialFailed)
@ -978,29 +1008,13 @@ func main() { //nolint: funlen, gocyclo
// === Shell terminal mode (Shell vs. Copy) setup // === Shell terminal mode (Shell vs. Copy) setup
// Set stdin in raw mode if it's an interactive session
// TODO: send flag to server side indicating this
// affects shell command used
var oldState *xs.State
defer conn.Close() defer conn.Close()
// === From this point on, conn is a secure encrypted channel // === From this point on, conn is a secure encrypted channel
if shellMode { // === BEGIN Login phase
if isatty.IsTerminal(os.Stdin.Fd()) {
oldState, err = xs.MakeRaw(os.Stdin.Fd())
if err != nil {
panic(err)
}
// #gv:s/label=\"main\$1\"/label=\"deferRestore\"/
// TODO:.gv:main:1:deferRestore
defer restoreTermState(oldState)
} else {
log.Println("NOT A TTY")
}
}
// === Login phase var oldState *xs.State
// Start login timeout here and disconnect if user/pass phase stalls // Start login timeout here and disconnect if user/pass phase stalls
// iloginImpatience := time.AfterFunc(20*time.Second, func() { // iloginImpatience := time.AfterFunc(20*time.Second, func() {
@ -1017,7 +1031,7 @@ func main() { //nolint: funlen, gocyclo
// No auth token, prompt for password // No auth token, prompt for password
fmt.Printf("Gimme cookie:") fmt.Printf("Gimme cookie:")
} }
ab, e := xs.ReadPassword(os.Stdin.Fd()) ab, e := xs.ReadPassword(os.Stdin)
if !gopt { if !gopt {
fmt.Printf("\r\n") fmt.Printf("\r\n")
} }
@ -1032,6 +1046,25 @@ func main() { //nolint: funlen, gocyclo
// Security scrub // Security scrub
runtime.GC() runtime.GC()
// === END Login phase
// === Terminal mode adjustment for session
if shellMode {
if isatty.IsTerminal(os.Stdin.Fd()) ||
isatty.IsCygwinTerminal(os.Stdin.Fd()) {
oldState, err = xs.MakeRaw(os.Stdin)
if err != nil {
panic(err)
}
// #gv:s/label=\"main\$1\"/label=\"deferRestore\"/
// TODO:.gv:main:1:deferRestore
defer restoreTermState(oldState)
} else {
log.Println("NOT A TTY")
}
}
// === Session param and TERM setup // === Session param and TERM setup
// Set up session params and send over to server // Set up session params and send over to server
@ -1063,10 +1096,6 @@ func main() { //nolint: funlen, gocyclo
fmt.Fprintln(os.Stderr, rejectUserMsg()) fmt.Fprintln(os.Stderr, rejectUserMsg())
rec.SetStatus(GeneralProtocolErr) rec.SetStatus(GeneralProtocolErr)
} else { } else {
// === Set up connection keepalive to server
conn.StartupKeepAlive() // goroutine, returns immediately
defer conn.ShutdownKeepAlive()
// === Set up chaffing to server // === Set up chaffing to server
conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
if chaffEnabled { if chaffEnabled {
@ -1097,6 +1126,10 @@ func main() { //nolint: funlen, gocyclo
// === Session entry (shellMode or copyMode) // === Session entry (shellMode or copyMode)
if shellMode { if shellMode {
// === Set up connection keepalive to server
conn.StartupKeepAlive() // goroutine, returns immediately
defer conn.ShutdownKeepAlive()
// === (shell) launch tunnels // === (shell) launch tunnels
launchTuns(&conn /*remoteHost,*/, tunSpecStr) launchTuns(&conn /*remoteHost,*/, tunSpecStr)
doShellMode(isInteractive, &conn, oldState, rec) doShellMode(isInteractive, &conn, oldState, rec)
@ -1135,7 +1168,7 @@ func localUserName(u *user.User) string {
} }
func restoreTermState(oldState *xs.State) { func restoreTermState(oldState *xs.State) {
_ = xs.Restore(os.Stdin.Fd(), oldState) _ = xs.Restore(os.Stdin, oldState)
} }
// exitWithStatus wraps os.Exit() plus does any required pprof housekeeping // exitWithStatus wraps os.Exit() plus does any required pprof housekeeping

View File

@ -1,6 +1,6 @@
// xsd server // xsd server
// //
// Copyright (c) 2017-2020 Russell Magee // Copyright (c) 2017-2025 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this // Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution) // distribution)
// //
@ -121,10 +121,6 @@ func runClientToServerCopyAs(who, ttype string, conn *xsnet.Conn, fpath string,
c.Stdout = os.Stdout c.Stdout = os.Stdout
c.Stderr = os.Stderr c.Stderr = os.Stderr
// === Set up connection keepalive to client
conn.StartupKeepAlive() // goroutine, returns immediately
defer conn.ShutdownKeepAlive()
if chaffing { if chaffing {
conn.StartupChaff() conn.StartupChaff()
} }
@ -221,10 +217,6 @@ func runServerToClientCopyAs(who, ttype string, conn *xsnet.Conn, srcPath string
c.Stderr = stdErrBuffer c.Stderr = stdErrBuffer
//c.Stderr = nil //c.Stderr = nil
// === Set up connection keepalive to client
conn.StartupKeepAlive() // goroutine, returns immediately
defer conn.ShutdownKeepAlive()
if chaffing { if chaffing {
conn.StartupChaff() conn.StartupChaff()
} }
@ -380,11 +372,11 @@ func runShellAs(who, hname, ttype, cmd string, interactive bool, //nolint:funlen
if chaffing { if chaffing {
conn.StartupChaff() conn.StartupChaff()
// #gv:s/label=\"runShellAs\$4\"/label=\"deferChaffShutdown\"/
defer func() {
conn.ShutdownChaff()
}()
} }
// #gv:s/label=\"runShellAs\$4\"/label=\"deferChaffShutdown\"/
defer func() {
conn.ShutdownChaff()
}()
// ..and the pty to stdout. // ..and the pty to stdout.
// This may take some time exceeding that of the // This may take some time exceeding that of the
@ -530,12 +522,14 @@ func main() { //nolint:funlen,gocyclo
var dbg bool var dbg bool
var laddr string var laddr string
var rekeySecs uint var rekeySecs uint
var remodSupported bool // true: when rekeying, switch to random cipher/hmac alg
var useSystemPasswd bool var useSystemPasswd bool
flag.BoolVar(&vopt, "v", false, "show version") flag.BoolVar(&vopt, "v", false, "show version")
flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`") flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`")
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") //nolint:gomnd,lll flag.BoolVar(&remodSupported, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)")
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") //nolint:gomnd,lll
flag.StringVar(&kcpMode, "K", "unused", `set to one of ["KCP_NONE","KCP_AES", "KCP_BLOWFISH", "KCP_CAST5", "KCP_SM4", "KCP_SALSA20", "KCP_SIMPLEXOR", "KCP_TEA", "KCP_3DES", "KCP_TWOFISH", "KCP_XTEA"] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP`) //nolint:lll flag.StringVar(&kcpMode, "K", "unused", `set to one of ["KCP_NONE","KCP_AES", "KCP_BLOWFISH", "KCP_CAST5", "KCP_SM4", "KCP_SALSA20", "KCP_SIMPLEXOR", "KCP_TEA", "KCP_3DES", "KCP_TWOFISH", "KCP_XTEA"] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP`) //nolint:lll
flag.BoolVar(&useSysLogin, "L", false, "use system login") flag.BoolVar(&useSysLogin, "L", false, "use system login")
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts") flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
@ -565,12 +559,12 @@ func main() { //nolint:funlen,gocyclo
C_TWOFISH_128 C_TWOFISH_128
C_BLOWFISH_64 C_BLOWFISH_64
C_CRYPTMT1 C_CRYPTMT1
C_HOPSCOTCH
C_CHACHA20_12`) C_CHACHA20_12`)
flag.Var(&aHMACAlgs, "aH", "Allowed `HMAC`s (eg. '-aH HMACAlgA -aH HMACAlgB ...')"+` flag.Var(&aHMACAlgs, "aH", "Allowed `HMAC`s (eg. '-aH HMACAlgA -aH HMACAlgB ...')"+`
H_all H_all
H_SHA256 H_SHA256
H_SHA512`) H_SHA512
H_WHIRLPOOL`)
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>") flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to <`file`>")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>") flag.StringVar(&memprofile, "memprofile", "", "write memory profile to <`file`>")
@ -702,6 +696,22 @@ func main() { //nolint:funlen,gocyclo
} else { } else {
log.Println("Accepted client") log.Println("Accepted client")
// Only enable cipher alg changes on re-key if we were told
// to support it (launching xsd with -R), *and* the client
// proposes to use it.
if !remodSupported {
if (conn.Opts() & xsnet.CORemodulateShields) != 0 {
logger.LogDebug("[client proposed cipher/hmac remod, but we don't support it.]")
conn.Close()
continue
}
} else {
if conn.Opts()&xsnet.CORemodulateShields != 0 {
logger.LogDebug("[cipher/hmac remodulation active]")
} else {
logger.LogDebug("[cipher/hmac remodulation inactive]")
}
}
conn.RekeyHelper(rekeySecs) conn.RekeyHelper(rekeySecs)
// Set up chaffing to client // Set up chaffing to client
@ -823,7 +833,7 @@ func main() { //nolint:funlen,gocyclo
hname := goutmp.GetHost(addr.String()) hname := goutmp.GetHost(addr.String())
logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) //nolint:errcheck logger.LogNotice(fmt.Sprintf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname)) //nolint:errcheck
token := GenAuthToken(string(rec.Who()), string(rec.ConnHost())) token := GenAuthToken(string(rec.Who()), string(rec.ConnHost()))
tokenCmd := fmt.Sprintf("echo %q | tee -a ~/.xs_id", token) tokenCmd := fmt.Sprintf("echo %q | tee -a ~/%s", token, xsnet.XS_ID_AUTHTOKFILE)
cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) cmdStatus, runErr := runShellAs(string(rec.Who()), hname, string(rec.TermType()), tokenCmd, false, hc, chaffEnabled)
// Returned hopefully via an EOF or exit/logout; // Returned hopefully via an EOF or exit/logout;
// Clear current op so user can enter next, or EOF // Clear current op so user can enter next, or EOF

19
xsnet/chan.go Normal file → Executable file
View File

@ -23,6 +23,7 @@ import (
"blitter.com/go/cryptmt" "blitter.com/go/cryptmt"
"blitter.com/go/hopscotch" "blitter.com/go/hopscotch"
"github.com/aead/chacha20/chacha" "github.com/aead/chacha20/chacha"
whirlpool "github.com/jzelinskie/whirlpool"
"golang.org/x/crypto/blowfish" "golang.org/x/crypto/blowfish"
"golang.org/x/crypto/twofish" "golang.org/x/crypto/twofish"
@ -57,9 +58,18 @@ func expandKeyMat(keymat []byte, blocksize int) []byte {
return keymat return keymat
} }
/* (Re-)initialize the keystream and hmac state for an xsnet.Conn, returning // Choose a cipher and hmac alg from supported sets, given two uint8 values
a cipherStream and hash func getNewStreamAlgs(cb uint8, hb uint8) (config uint32) {
*/ // Get new cipher and hash algs (clamped to valid values) based on
// the input rekeying data
c := (cb % CAlgNoneDisallowed)
h := (hb % HmacNoneDisallowed)
config = uint32(h)<<8 | uint32(c)
return
}
// (Re-)initialize the keystream and hmac state for an xsnet.Conn, returning
// a cipherStream and hash
func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) { func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) {
var key []byte var key []byte
var block cipher.Block var block cipher.Block
@ -146,6 +156,9 @@ func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err er
if !halg.Available() { if !halg.Available() {
log.Fatal("hash not available!") log.Fatal("hash not available!")
} }
case HmacWHIRLPOOL:
log.Printf("[hash HmacWHIRLPOOL (%d)]\n", hopts)
mc = whirlpool.New()
default: default:
log.Printf("[invalid hmac (%d)]\n", hopts) log.Printf("[invalid hmac (%d)]\n", hopts)
fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts) fmt.Printf("DOOFUS SET A VALID HMAC ALG (%d)\n", hopts)

19
xsnet/consts.go Normal file → Executable file
View File

@ -119,8 +119,27 @@ type CSCipherAlg uint32
const ( const (
HmacSHA256 = iota HmacSHA256 = iota
HmacSHA512 HmacSHA512
HmacWHIRLPOOL
HmacNoneDisallowed HmacNoneDisallowed
) )
// Conn opts outside of basic kex/cipher/hmac connect config
const (
CONone = iota
CORemodulateShields // if set, rekeying also reselects random cipher/hmac alg
)
type COValue uint32
// Available HMACs for hkex.Conn // Available HMACs for hkex.Conn
type CSHmacAlg uint32 type CSHmacAlg uint32
// Some bounds-checking consts
const (
REKEY_SECS_MIN = 1
REKEY_SECS_MAX = 28800 // 8 hours
CHAFF_FREQ_MSECS_MIN = 1
CHAFF_FREQ_MSECS_MAX = 300000 // 5 minutes
)
const XS_ID_AUTHTOKFILE = ".config/xs/.xs_id"

0
xsnet/kcp.go Normal file → Executable file
View File

75
xsnet/net.go Normal file → Executable file
View File

@ -39,7 +39,6 @@ import (
"net" "net"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
hkex "blitter.com/go/herradurakex" hkex "blitter.com/go/herradurakex"
@ -177,6 +176,8 @@ func (h *CSHmacAlg) String() string {
return "H_SHA256" return "H_SHA256"
case HmacSHA512: case HmacSHA512:
return "H_SHA512" return "H_SHA512"
case HmacWHIRLPOOL:
return "H_WHIRLPOOL"
default: default:
return "H_ERR_UNK" return "H_ERR_UNK"
} }
@ -241,7 +242,7 @@ func (hc *Conn) SetConnOpts(copts uint32) {
// //
// Consumers of this lib may use this for protocol-level options not part // Consumers of this lib may use this for protocol-level options not part
// of the KEx or encryption info used by the connection. // of the KEx or encryption info used by the connection.
func (hc Conn) Opts() uint32 { func (hc *Conn) Opts() uint32 {
return hc.opts return hc.opts
} }
@ -363,6 +364,13 @@ func (hc *Conn) applyConnExtensions(extensions ...string) {
log.Println("[extension arg = H_SHA512]") log.Println("[extension arg = H_SHA512]")
hc.cipheropts &= (0xFFFF00FF) hc.cipheropts &= (0xFFFF00FF)
hc.cipheropts |= (HmacSHA512 << 8) hc.cipheropts |= (HmacSHA512 << 8)
case "H_WHIRLPOOL":
log.Println("[extension arg = H_WHIRLPOOL]")
hc.cipheropts &= (0xFFFF00FF)
hc.cipheropts |= (HmacWHIRLPOOL << 8)
case "OPT_REMOD":
log.Println("[extension arg = OPT_REMOD]")
hc.opts |= CORemodulateShields
//default: //default:
// log.Printf("[Dial ext \"%s\" ignored]\n", s) // log.Printf("[Dial ext \"%s\" ignored]\n", s)
} }
@ -1112,7 +1120,7 @@ func (hl *HKExListener) Accept() (hc Conn, err error) {
return Conn{}, err return Conn{}, err
} }
logger.LogDebug(fmt.Sprintln("[net.Listener Accepted]")) logger.LogDebug(fmt.Sprintf("[net.Listener Accepted %v]\n", c.RemoteAddr()))
} }
// Read KEx alg proposed by client // Read KEx alg proposed by client
var kexAlg KEXAlg var kexAlg KEXAlg
@ -1351,6 +1359,11 @@ func (hc *Conn) Read(b []byte) (n int, err error) {
//logger.LogDebug(fmt.Sprintf("[Got rekey [%02x %02x %02x ...]\n", //logger.LogDebug(fmt.Sprintf("[Got rekey [%02x %02x %02x ...]\n",
// payloadBytes[0], payloadBytes[1], payloadBytes[2])) // payloadBytes[0], payloadBytes[1], payloadBytes[2]))
rekeyData := payloadBytes rekeyData := payloadBytes
if (hc.opts & CORemodulateShields) != 0 {
hc.Lock()
hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1])
hc.Unlock()
}
hc.r, hc.rm, err = hc.getStream(rekeyData) hc.r, hc.rm, err = hc.getStream(rekeyData)
case CSOTermSize: case CSOTermSize:
fmt.Sscanf(string(payloadBytes), "%d %d", &hc.Rows, &hc.Cols) fmt.Sscanf(string(payloadBytes), "%d %d", &hc.Rows, &hc.Cols)
@ -1585,27 +1598,61 @@ func (hc *Conn) StartupChaff() {
} }
func (hc *Conn) ShutdownChaff() { func (hc *Conn) ShutdownChaff() {
hc.Lock()
hc.chaff.shutdown = true hc.chaff.shutdown = true
hc.Unlock()
log.Println("Chaffing SHUTDOWN") log.Println("Chaffing SHUTDOWN")
} }
func (hc *Conn) SetupChaff(msecsMin uint, msecsMax uint, szMax uint) { func (hc *Conn) SetupChaff(msecsMin uint, msecsMax uint, szMax uint) {
// Enforce bounds on chaff frequency and pkt size
hc.Lock()
if hc.chaff.msecsMin < CHAFF_FREQ_MSECS_MIN {
hc.chaff.msecsMin = CHAFF_FREQ_MSECS_MIN
}
if hc.chaff.msecsMax > CHAFF_FREQ_MSECS_MAX {
hc.chaff.msecsMax = CHAFF_FREQ_MSECS_MAX
}
hc.Unlock()
hc.chaff.msecsMin = msecsMin //move these to params of chaffHelper() ? hc.chaff.msecsMin = msecsMin //move these to params of chaffHelper() ?
hc.chaff.msecsMax = msecsMax hc.chaff.msecsMax = msecsMax
hc.chaff.szMax = szMax hc.chaff.szMax = szMax
} }
func (hc *Conn) ShutdownRekey() { func (hc *Conn) ShutdownRekey() {
hc.Lock()
hc.rekey = 0 hc.rekey = 0
hc.Unlock()
} }
func (hc *Conn) RekeyHelper(intervalSecs uint) { func (hc *Conn) RekeyHelper(intervalSecs uint) {
if intervalSecs < REKEY_SECS_MIN {
intervalSecs = REKEY_SECS_MIN
}
if intervalSecs > REKEY_SECS_MAX {
intervalSecs = REKEY_SECS_MAX
}
go func() { go func() {
hc.Lock()
hc.rekey = intervalSecs hc.rekey = intervalSecs
hc.Unlock()
for { for {
if hc.rekey != 0 { hc.Lock()
rekey := hc.rekey
hc.Unlock()
if rekey != 0 {
jitter := rand.Intn(int(rekey)) / 4
rekey = rekey - uint(jitter)
if rekey < 1 {
rekey = 1
}
//logger.LogDebug(fmt.Sprintf("[rekeyHelper Loop]\n")) //logger.LogDebug(fmt.Sprintf("[rekeyHelper Loop]\n"))
time.Sleep(time.Duration(hc.rekey) * time.Second) time.Sleep(time.Duration(rekey) * time.Second)
// Send rekey to other end // Send rekey to other end
rekeyData := make([]byte, 64) rekeyData := make([]byte, 64)
@ -1615,6 +1662,9 @@ func (hc *Conn) RekeyHelper(intervalSecs uint) {
//logger.LogDebug("[+rekeyHelper]") //logger.LogDebug("[+rekeyHelper]")
_, err = hc.WritePacket(rekeyData, CSORekey) _, err = hc.WritePacket(rekeyData, CSORekey)
hc.Lock() hc.Lock()
if (hc.opts & CORemodulateShields) != 0 {
hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1])
}
hc.w, hc.wm, err = hc.getStream(rekeyData) hc.w, hc.wm, err = hc.getStream(rekeyData)
//logger.LogDebug("[-rekeyHelper]") //logger.LogDebug("[-rekeyHelper]")
hc.Unlock() hc.Unlock()
@ -1635,7 +1685,10 @@ func (hc *Conn) chaffHelper() {
var nextDuration int var nextDuration int
for { for {
//logger.LogDebug(fmt.Sprintf("[chaffHelper Loop]\n")) //logger.LogDebug(fmt.Sprintf("[chaffHelper Loop]\n"))
if !hc.chaff.shutdown { hc.Lock()
shutdown := hc.chaff.shutdown
hc.Unlock()
if !shutdown {
var bufTmp []byte var bufTmp []byte
bufTmp = make([]byte, rand.Intn(int(hc.chaff.szMax))) bufTmp = make([]byte, rand.Intn(int(hc.chaff.szMax)))
min := int(hc.chaff.msecsMin) min := int(hc.chaff.msecsMin)
@ -1646,7 +1699,9 @@ func (hc *Conn) chaffHelper() {
//logger.LogDebug("[-chaffHelper]") //logger.LogDebug("[-chaffHelper]")
if err != nil { if err != nil {
log.Println("[ *** error - chaffHelper shutting down *** ]") log.Println("[ *** error - chaffHelper shutting down *** ]")
hc.Lock()
hc.chaff.shutdown = true hc.chaff.shutdown = true
hc.Unlock()
break break
} }
} else { } else {
@ -1670,7 +1725,9 @@ func (hc *Conn) ShutdownKeepAlive() {
} }
func (hc *Conn) ResetKeepAlive() { func (hc *Conn) ResetKeepAlive() {
hc.Lock()
hc.keepalive = 3 hc.keepalive = 3
hc.Unlock()
log.Println("KeepAlive RESET") log.Println("KeepAlive RESET")
} }
@ -1689,7 +1746,9 @@ func (hc *Conn) keepaliveHelper() {
break break
} }
time.Sleep(time.Duration(nextDuration) * time.Millisecond) time.Sleep(time.Duration(nextDuration) * time.Millisecond)
hc.Lock()
hc.keepalive -= 1 hc.keepalive -= 1
hc.Unlock()
//logger.LogDebug(fmt.Sprintf("[keepAlive is now %d]\n", hc.keepalive)) //logger.LogDebug(fmt.Sprintf("[keepAlive is now %d]\n", hc.keepalive))
//if rand.Intn(8) == 0 { //if rand.Intn(8) == 0 {
@ -1702,7 +1761,9 @@ func (hc *Conn) keepaliveHelper() {
hc.ShutdownKeepAlive() hc.ShutdownKeepAlive()
if hc.Pproc != 0 { if hc.Pproc != 0 {
//fmt.Printf("[pid %d needs to be killed]\n", hc.Pproc) //fmt.Printf("[pid %d needs to be killed]\n", hc.Pproc)
syscall.Kill(hc.Pproc, syscall.SIGABRT) //nolint:errcheck //syscall.Kill(hc.Pproc, syscall.SIGABRT) //nolint:errcheck
//exec.Command("taskkill", "/f", "/pid", strconv.Itoa(hc.Pproc)).Run()
hc.kill()
} }
break break
} }

13
xsnet/net_linux.go Executable file
View File

@ -0,0 +1,13 @@
//go:build linux
// +build linux
package xsnet
import (
"syscall"
)
func (hc *Conn) kill() {
syscall.Kill(hc.Pproc, syscall.SIGABRT) //nolint:errcheck
}

13
xsnet/net_windows.go Executable file
View File

@ -0,0 +1,13 @@
//go:build windows
// +build windows
package xsnet
import (
"os/exec"
"strconv"
)
func (hc *Conn) kill() {
exec.Command("taskkill", "/f", "/pid", strconv.Itoa(hc.Pproc)).Run()
}

0
xsnet/tun.go Normal file → Executable file
View File

View File

@ -1,7 +1,7 @@
// Util to generate/store passwords for users in a file akin to /etc/passwd // Util to generate/store passwords for users in a file akin to /etc/passwd
// suitable for the xs server, using bcrypt. // suitable for the xs server, using bcrypt.
// //
// Copyright (c) 2017-2020 Russell Magee // Copyright (c) 2017-2025 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this // Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution) // distribution)
// //