13 Commits
5 changed files with 121 additions and 27 deletions
+7 -5
View File
@@ -4,16 +4,18 @@ Extensions for the [sshyp password manager](https://github.com/rwinkhart/sshyp).
When new functionality is desired that goes outside of sshyp's primary goals or requires venturing outside of the Python standard library, said functionality is implemented as an extension.
# Installation
For sshyp v1.5.0+, sshyp extensions should be installed from the `sshyp tweak` menu's "extension management" option.
For most sshyp-supported platforms, sshyp extensions should be installed from the `sshyp tweak` menu's "extension management" option.
**Do NOT use the packages from the releases page! These are only for older, unsupported releases of sshyp!**
For **Haiku and Termux _ONLY_**, use the packages from the [releases page](https://github.com/rwinkhart/sshyp-labs/releases).
# Available Extensions
[sshyp-mfa](https://github.com/rwinkhart/sshyp-labs/wiki/sshyp-mfa): read mfa data from sshyp entries to generate and copy totp keys to the clipboard (optional Steam support)
[sshyp-mfa](https://github.com/rwinkhart/sshyp-labs/wiki/sshyp-mfa): read mfa data from sshyp entries to generate and copy totp keys to the clipboard (includes Steam support)
[password-pasture](https://github.com/rwinkhart/sshyp-labs/wiki/password-pasture): a HIGHLY experimental GTK4 sshyp GUI - very incomplete (last updated for sshyp v1.1.x)
# Acknowledgements
sshyp-mfa relies on [ValvePython/steam](https://github.com/ValvePython/steam) for Steam support.
sshyp-mfa's TOTP support is partially derrived from [susam/mintotp](https://github.com/susam/mintotp).
sshyp-mfa's Steam support is partially derrived from [ValvePython/steam](https://github.com/ValvePython/steam).
These packages are _not_ required as dependencies; the necessary code from each is included in sshyp-mfa.
+80
View File
@@ -0,0 +1,80 @@
#!/bin/sh
version='1.5.0.2'
if [ -z "$2" ]; then
revision=1
else
revision="$2"
fi
_create_hpkg() {
printf '\npackaging for Haiku...\n'
mkdir -p output/haikutemp/lib/sshyp/extensions
printf "name sshyp_mfa
version "$version"-"$revision"
architecture any
summary \"An MFA (TOTP/Steam) key generator for the sshyp password manager\"
description \"sshyp-mfa is an extension for the sshyp password manager that reads MFA data from sshyp entries and generates generic TOTP and Steam keys.\"
packager \"Randall Winkhart <idgr at tutanota dot com>\"
vendor \"Randall Winkhart\"
licenses {
\"GNU GPL v3\"
}
copyrights {
\"2021-2023 Randall Winkhart\"
}
provides {
sshyp_mfa = "$version"
}
requires {
sshyp_client
}
urls {
\"https://github.com/rwinkhart/sshyp-labs\"
}
" > output/haikutemp/.PackageInfo
cp ./sshyp-mfa.py output/haikutemp/lib/sshyp/sshyp-mfa
cp ./sshyp-mfa.ini output/haikutemp/lib/sshyp/extensions/
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' output/haikutemp/lib/sshyp/sshyp-mfa
cd output/haikutemp
package create -b HAIKU-sshyp_mfa-"$version"-"$revision"_all.hpkg
package add HAIKU-sshyp_mfa-"$version"-"$revision"_all.hpkg lib
cd ../..
mv output/haikutemp/HAIKU-sshyp_mfa-"$version"-"$revision"_all.hpkg output/
rm -rf output/haikutemp
printf "\nHaiku packaging complete\n\n"
} &&
_create_termux() {
printf '\npackaging for Termux...\n'
mkdir -p output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/DEBIAN \
output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/extensions
printf "Package: sshyp-mfa
Version: $version
Section: utils
Architecture: all
Maintainer: Randall Winkhart <idgr at tutanota dot com>
Description: An MFA (TOTP/Steam) key generator for the sshyp password manager
Depends: sshyp-client
Priority: optional
Installed-Size: 100
" > output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/DEBIAN/control
cp ./sshyp-mfa.py output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/sshyp-mfa
cp ./sshyp-mfa.ini output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/extensions/
dpkg-deb --build --root-owner-group output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/
mv output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux.deb output/TERMUX-sshyp-mfa_"$version"-"$revision"_all.deb
rm -rf output/termuxtemp
printf '\nTermux packaging complete\n\n'
} &&
case "$1" in
haiku)
_create_hpkg
;;
termux)
_create_termux
;;
*)
printf '\nusage: package.sh [target] <revision>\n\ntargets: haiku termux\n\n'
;;
esac
+27 -20
View File
@@ -1,9 +1,11 @@
#!/usr/bin/env python3
from base64 import b32decode
from configparser import ConfigParser
from hmac import new as hmac_new
from os import environ, listdir, uname
from os.path import expanduser, isdir, isfile
from sshyp import decrypt, whitelist_verify
from struct import pack, unpack
from subprocess import PIPE, Popen, run
from sys import argv, exit as s_exit
from time import sleep, strftime, time
@@ -11,22 +13,37 @@ home = expanduser("~")
def totp(_secret, _algo, _digits, _period): # uses provided information to generate a standard totp key
from hmac import new as new_mac
from struct import pack, unpack
_secret = b32decode(_secret.upper() + '=' * ((8 - len(_secret)) % 8))
_counter = pack('>Q', int(time() / _period))
_mac = new_mac(_secret, _counter, _algo).digest()
_offset = _mac[-1] & 0x0f
_binary = unpack('>L', _mac[_offset:_offset + 4])[0] & 0x7fffffff
_hmac = hmac_new(_secret, _counter, _algo).digest()
_offset = _hmac[-1] & 0x0f
_binary = unpack('>L', _hmac[_offset:_offset + 4])[0] & 0x7fffffff
return str(_binary)[-_digits:].zfill(_digits)
def steam_otp(_secret): # uses provided information to generate a Steam-compatible otp
_hmac = hmac_new(bytes(_secret), msg=pack('>Q', int(time()//30)), digestmod='sha1').digest()
_start = ord(_hmac[19:20]) & 0xF
_codeint = unpack('>I', _hmac[_start:_start+4])[0] & 0x7fffffff
_charset = '23456789BCDFGHJKMNPQRTVWXY'
_code = ''
for _ in range(5):
_codeint, _i = divmod(_codeint, len(_charset))
_code += _charset[_i]
return _code
def mfa_read_shortcut(): # extracts MFA info from the user-specified sshyp entry
if not isfile(f"{directory}{arguments[0]}.gpg"):
print(f"\n\u001b[38;5;9merror: entry ({arguments[0]}) does not exist\u001b[0m\n")
s_exit(1)
if quick_unlock_enabled == 'true':
_mfa_data = decrypt(directory + arguments[0], _quick_pass=whitelist_verify(port, username_ssh, ip, device_id))
_mfa_data = decrypt(directory + arguments[0],
_quick_pass=whitelist_verify(sshyp_data.get('SSHYNC', 'port'),
sshyp_data.get('SSHYNC', 'user'),
sshyp_data.get('SSHYNC', 'ip'),
listdir(f"{home}/.config/sshyp/devices")[0],
sshyp_data.get('SSHYNC', 'identity_file')))
else:
_mfa_data = decrypt(directory + arguments[0])
try:
@@ -51,12 +68,8 @@ if __name__ == '__main__':
# user data fetcher
sshyp_data = ConfigParser()
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
directory = f"{home}/.local/share/sshyp/"
quick_unlock_enabled = sshyp_data.get('CLIENT-ONLINE', 'quick_unlock_enabled')
username_ssh = sshyp_data.get('SSHYNC', 'user')
ip = sshyp_data.get('SSHYNC', 'ip')
port = sshyp_data.get('SSHYNC', 'port')
directory = sshyp_data.get('SSHYNC', 'local_dir')
device_id = listdir(f"{home}/.config/sshyp/devices")[0]
# main process: runs functions to generate MFA key, then continuously copies up-to-date MFA key to clipboard
try:
@@ -68,18 +81,13 @@ if __name__ == '__main__':
if copied is None:
copied = 1
if mfa_data[0] == 'steam':
try:
from steam.guard import generate_twofactor_code as steam_totp
_mfa_key = steam_totp(b32decode(mfa_data[1]))
except ModuleNotFoundError:
print('\n\u001b[38;5;9merror: steam module not found\n\ninstall with "pip install -U \'steam[client]\'"\u001b[0m\n')
s_exit(6)
_mfa_key = steam_otp(b32decode(mfa_data[1]))
else:
_mfa_key = totp(mfa_data[1], mfa_data[2], mfa_data[3], mfa_data[4])
if 'WSL_DISTRO_NAME' in environ: # WSL clipboard detection
run(('powershell.exe', '-c', 'Set-Clipboard', _mfa_key))
run(('powershell.exe', '-c', "Set-Clipboard '" + _mfa_key + "'"))
elif 'WAYLAND_DISPLAY' in environ: # Wayland clipboard detection
run(('wl-copy', _mfa_key))
run('wl-copy', stdin=Popen(('printf', _mfa_key), stdout=PIPE).stdout)
elif uname()[0] == 'Haiku': # Haiku clipboard detection
run(('clipboard', '-c', _mfa_key))
elif uname()[0] == 'Darwin': # MacOS clipboard detection
@@ -90,5 +98,4 @@ if __name__ == '__main__':
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', _mfa_key), stdout=PIPE).stdout)
sleep(1)
except KeyboardInterrupt:
print('\n')
s_exit(0)
+2 -2
View File
@@ -1,5 +1,5 @@
[sshyp-mfa]
desc = read mfa data from sshyp entries to generate and copy totp keys to the clipboard (optional Steam support)
usage = sshyp /<entry name> copy -m
exe = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.1/extensions/sshyp-mfa/sshyp-mfa.py
ini = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.1/extensions/sshyp-mfa/sshyp-mfa.ini
exe = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.2/extensions/sshyp-mfa/sshyp-mfa.py
ini = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.2/extensions/sshyp-mfa/sshyp-mfa.ini
+5
View File
@@ -0,0 +1,5 @@
[sshyp-mfa]
desc = read mfa data from sshyp entries to generate and copy totp keys to the clipboard (optional Steam support)
usage = sshyp /<entry name> copy -m
exe = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.1.1/extensions/sshyp-mfa/sshyp-mfa.py
ini = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.1.1/extensions/sshyp-mfa/sshyp-mfa.ini