78 Commits
Author SHA1 Message Date
RandyTheSilly 25a5731936 Add pointer for future sshyp release v1.5.1(.1) 2023-10-19 18:39:13 -04:00
RandyTheSilly 286645dd31 sshyp-mfa - Add support for quick-unlock with custom ssh keys 2023-10-19 18:36:12 -04:00
RandyTheSilly ec633c5104 sshyp-mfa - Removed extra blank line printed after a keyboard interrupt 2023-09-24 15:47:02 -04:00
RandyTheSilly 9540e9874f Fixed typo in Termux package name 2023-07-12 20:25:02 -04:00
RandyTheSilly 0020ac313b Fixed typo in packaging version number 2023-07-12 20:19:53 -04:00
RandyTheSilly ee8d875095 Fixed typo in Haiku packaging 2023-07-12 20:05:27 -04:00
RandyTheSilly 5d1eeb1f75 Update pointer for release v1.5.0.2 2023-07-12 19:41:48 -04:00
Randall Winkhart 769f522b2a Update README.md 2023-07-12 19:38:21 -04:00
RandyTheSilly 018912cfc5 sshyp-mfa - Changed Termux package to depend on sshyp-client 2023-07-10 12:25:43 -04:00
RandyTheSilly d53fc304d5 sshyp-mfa - Added temporary packaging script for Haiku and Termux 2023-07-10 11:17:41 -04:00
RandyTheSilly adcb13fd89 sshyp-mfa - Fixed inability to work in offline mode 2023-07-10 10:49:16 -04:00
RandyTheSilly 2421bc2ff3 Update pointer for v1.5.0 2023-07-07 08:38:23 -04:00
Randall Winkhart 0a4efc2002 Merge pull request #4 from rwinkhart/ext-manager
Merge ext-manager into main
2023-07-06 13:27:55 -04:00
RandyTheSilly 90884b4cd7 Added error message when generating Steam TOTP keys without dependency 2023-07-06 13:21:40 -04:00
RandyTheSilly bc09ec5e30 sshyp-mfa - Removed file extension expectation 2023-07-06 10:10:30 -04:00
RandyTheSilly e9a1728c6f Update variable names and file extensions to be more clear 2023-07-06 08:57:47 -04:00
RandyTheSilly 08bc5dec87 sshyp-mfa - Call extension directly, rather than through a symlink 2023-06-27 15:17:54 -04:00
Randall Winkhart 23be7f8c30 Delete password-pasture directory 2023-06-27 14:52:44 -04:00
Randall Winkhart 0bbcda37f2 Delete sshyp-mfa directory 2023-06-27 14:52:35 -04:00
Randall Winkhart 19ff50a5db Delete version 2023-06-27 14:51:21 -04:00
RandyTheSilly ac035db597 Re-organized repo for upcoming v1.5.0 extension management changes 2023-06-27 14:49:03 -04:00
RandyTheSilly fb267085eb sshyp-mfa - Compatibility with new tmpfs-less operation 2023-06-18 00:49:33 -04:00
RandyTheSilly 90c3de83b5 sshyp-mfa - Compatibility with new sshyp.decrypt() 2023-06-15 21:01:36 -04:00
RandyTheSilly a635207cee sshyp-mfa - Fixed clipboard typos 2023-06-15 19:25:59 -04:00
RandyTheSilly b9b06a4800 sshyp-mfa - Removed unused imports 2023-06-15 19:05:05 -04:00
RandyTheSilly 4e76b641a6 Updated compatible versions on refresh script 2023-06-13 10:13:52 -04:00
RandyTheSilly ed40b01e85 Support for new sshyp configuration file 2023-05-16 11:06:29 -04:00
RandyTheSilly c8e54cef43 sshyp-mfa - Use printf for X11 clipboard 2023-05-15 13:43:46 -04:00
RandyTheSilly 8fe7675ca3 sshyp-mfa - Added MacOS clipboard support 2023-05-15 13:39:56 -04:00
RandyTheSilly 45fd0631ca sshyp-mfa - Replaced subprocess lists with tuples 2023-05-15 13:33:21 -04:00
RandyTheSilly d4ed9b0da1 sshyp-mfa - Partial packaging parity with sshyp v1.4.2 2023-05-08 09:03:52 -04:00
RandyTheSilly 0953f60d0b sshyp-mfa - Added WSL clipboard support 2023-05-05 11:40:29 -04:00
RandyTheSilly 580d511a50 Updated packaging script to reflect changes in the sshyp packaging script 2023-03-29 16:37:52 -04:00
RandyTheSilly da6b1658a4 sshyp-mfa - Correct revision number for Alpine Linux conventions 2023-03-04 22:28:35 -05:00
RandyTheSilly 5df741726f sshyp-mfa - Specify config file location for Fedora and FreeBSD packages 2023-03-04 22:16:14 -05:00
RandyTheSilly ea020c4bf1 sshyp-mfa - Updated copyright data 2023-03-04 22:10:33 -05:00
RandyTheSilly 3568601520 sshyp-mfa - Updated date in man page 2023-03-04 22:08:34 -05:00
RandyTheSilly e9ee114452 Update documentation for release v1.4.0.1 2023-03-04 21:31:56 -05:00
RandyTheSilly 8c1a3c340c Removed unused import statement 2023-03-01 10:26:28 -05:00
RandyTheSilly 0ad86a1040 sshyp-mfa - Removed interactive entry prompt and updated help text 2023-03-01 10:21:45 -05:00
RandyTheSilly 931d7a0494 sshyp-mfa - Optimized argument handling 2023-02-26 18:04:02 -05:00
RandyTheSilly f0eb398f07 sshyp-mfa - Added extension config file for sshyp v1.4.0 integration 2023-02-26 16:52:16 -05:00
RandyTheSilly 1216d06975 Bump version for v1.3.0.1 2022-12-14 12:37:37 -05:00
RandyTheSilly 8e45b15fee Removed use of os.system, implemented new clipboard functionality from sshyp v1.3.0 2022-12-07 23:28:34 -05:00
RandyTheSilly 6ee49d6b95 sshyp-mfa - sshyp v1.3.0 compatibility (no longer pass gpg to decrypt()) 2022-12-07 23:15:17 -05:00
RandyTheSilly 7b03cfdc5e Fixed Alpine APKBUILD sha512 information 2022-11-06 15:11:49 -05:00
RandyTheSilly 45caab270e Fixed Debian/Termux packaging 2022-11-06 14:53:12 -05:00
RandyTheSilly 1c6150afe4 Only load rarely used imports when needed 2022-11-06 11:03:06 -05:00
RandyTheSilly 8916f9b2e2 Fixed license location in Haiku 2022-11-06 10:51:42 -05:00
RandyTheSilly c0281a4dc3 Removed executable bits where they are not needed 2022-11-06 10:40:51 -05:00
RandyTheSilly 37efe8f96a Brought packaging in-line with sshyp 2022-11-06 10:37:50 -05:00
RandyTheSilly 2271281683 Removed unnecessary blank lines in packaging script 2022-11-03 22:52:07 -04:00
RandyTheSilly c5c3600e6d Fix symlink at /usr/bin/sshyp not being created in generic package 2022-11-01 12:02:17 -04:00
RandyTheSilly a97aa6052a Added source for build scripts 2022-11-01 11:54:13 -04:00
RandyTheSilly d133406de9 Move to /usr/lib/sshyp for sshyp v1.2.0 compatibility 2022-11-01 11:49:24 -04:00
RandyTheSilly 25f608dd1e Update license to GPL-3.0-only 2022-10-26 11:49:47 -04:00
RandyTheSilly a957d80a67 Packaging parity with sshyp 2022-10-26 11:45:53 -04:00
RandyTheSilly 521714fd12 sshyp-mfa - Pass device_id to whitelist_verify 2022-10-24 15:32:51 -04:00
RandyTheSilly 3c80d72a36 Support for quick-unlock in sshyp 1.2.0 2022-10-20 16:38:17 -04:00
RandyTheSilly 68c370be55 Added support for updated sshyp decrypt() function (still not fully functional) 2022-10-19 15:52:51 -04:00
RandyTheSilly bd09ab32cd password-pasture - Added delete button functionality 2022-08-11 19:06:34 -04:00
RandyTheSilly 755b1183e3 password-pasture - Added copy button functionality 2022-08-11 17:08:13 -04:00
RandyTheSilly 9c67f41199 password-pasture - Entries are now sorted alphabetically 2022-08-11 15:09:34 -04:00
RandyTheSilly 951cb5adc5 password-pasture - User entries are now displayed on the entries page 2022-08-10 19:34:13 -04:00
RandyTheSilly 4227a264c8 password-pasture - General code clean-up 2022-08-09 21:58:10 -04:00
RandyTheSilly d19cc1ed2c password-pasture - Allow build script to use all cores 2022-08-08 05:07:35 -04:00
RandyTheSilly 04ab10fdd9 password-pasture - Hide debug button 2022-08-08 01:21:34 -04:00
RandyTheSilly fd5266351e password-pasture - Initial demo of entry selector 2022-08-08 01:12:30 -04:00
RandyTheSilly 73947dd7ef password-pasture - Re-design to fit better on the PinePhone at 200% scaling 2022-08-04 04:00:16 -04:00
RandyTheSilly fcf43986a8 password-pasture - Remove artwork (already exists in extra/) 2022-08-04 03:23:25 -04:00
RandyTheSilly dfb90ca294 password-pasture (GUI) - Initial commit 2022-08-04 03:22:01 -04:00
RandyTheSilly 42ef63c0d9 sshyp-mfa - Added Alpine Linux packaging 2022-08-01 20:48:27 -04:00
RandyTheSilly 939d07e804 Corrected generic package archival format 2022-08-01 05:31:21 -04:00
RandyTheSilly be41d32f78 sshyp-mfa - Updated Haiku packaging for compatibility with new shebang 2022-08-01 05:21:32 -04:00
RandyTheSilly ba2ac73965 Switched shebang to '#!/usr/bin/env python3' for greater compatibility 2022-08-01 04:52:09 -04:00
RandyTheSilly ebb420d30f extra/artwork: added a retro-themed sshyp gif 2022-07-21 05:09:02 -04:00
RandyTheSilly 8cf5728c42 Updated refresh script (changes gpg id, optimizes entries) to take advantage of sshyp v1.1.1 2022-07-21 01:49:30 -04:00
RandyTheSilly 9e0c121843 No longer remove leading new lines from notes (interfered with sshyp-mfa) 2022-07-16 22:36:02 -04:00
23 changed files with 641 additions and 433 deletions
+1
View File
@@ -0,0 +1 @@
*
Executable → Regular
View File
Executable → Regular
+10 -8
View File
@@ -1,17 +1,19 @@
# sshyp-labs
Experimental extensions for the sshyp password manager.
Extensions for the [sshyp password manager](https://github.com/rwinkhart/sshyp).
sshyp-labs is currently the home of sshyp-mfa, and soon to be the home of password-pasture (sshyp-gui).
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 most sshyp-supported platforms, sshyp extensions should be installed from the `sshyp tweak` menu's "extension management" option.
For **Haiku and Termux _ONLY_**, use the packages from the [releases page](https://github.com/rwinkhart/sshyp-labs/releases).
# Available Extensions
sshyp-mfa - [installation and usage instructions](https://github.com/rwinkhart/sshyp-labs/wiki/sshyp-mfa)
[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 is a unique approach to generating multi-factor authentication keys. Upon running `sshyp-mfa <target entry>`,
an MFA key will be generated and copied to your clipboard. sshyp-mfa will continue to run in the background and copy a
new key to your clipboard every time the actively copied one expires. Never worry about a TOTP timer expiring, again!
At any time, sshyp-mfa can be closed with ctrl+c to stop this process.
[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 optionally depends 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).
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
git add -f LICENSE README.md commit.sh extensions extra pointers .gitignore
git commit -m "$1"
git push
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.23)
project(password_pasture C)
set(CMAKE_C_STANDARD 17)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK4 REQUIRED gtk4)
include_directories(${GTK4_INCLUDE_DIRS})
link_directories(${GTK4_LIBRARY_DIRS})
add_definitions(${GTK4_CFLAGS_OTHER})
add_executable(password_pasture main.c)
target_link_libraries(password_pasture ${GTK4_LIBRARIES})
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env sh
rm -rf CMakeFiles cmake_install.cmake CMakeCache.txt Makefile
cmake ./
sleep 10 # prevents clock skew
make -j$(nproc)
+380
View File
@@ -0,0 +1,380 @@
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <stddef.h>
#include <gtk/gtk.h>
GtkWidget *label_selected; GtkListBox *list_box;
char entries[1024][1024]; int entry_array_count = 0;
void str_remove(char *str, const char *sub) {
char *p, *q, *r;
if (*sub && (q = r = strstr(str, sub)) != 0) {
size_t len = strlen(sub);
while ((r = strstr(p = r + len, sub)) != 0) {
while (p < r)
*q++ = *p++;
}
while ((*q++ = *p++) != '\0')
continue;
}
}
void gen_entry_array(char *path, size_t size, const char *path_orig) {
DIR *dir;
struct dirent *entry;
size_t len = strlen(path);
if (!(dir = opendir(path))) {
return; }
while ((entry = readdir(dir)) != 0) {
char *name = entry->d_name;
if (entry->d_type == DT_DIR) {
if (!strcmp(name, ".") || !strcmp(name, "..")) {
continue; }
path[len] = '/';
strcpy(path + len + 1, name);
gen_entry_array(path, size, path_orig);
path[len] = '\0';
} else {
char path_filtered[1024];
strcpy(path_filtered, path);
str_remove(path_filtered, path_orig);
size_t name_len = strlen(name);
name[name_len-4] = 0;
char sshyp_path[1024]; strcpy(sshyp_path, path_filtered); strcat(sshyp_path, "/"); strcat(sshyp_path, name);
strcpy(entries[entry_array_count], sshyp_path);
entry_array_count++;
}
}
closedir(dir);
}
void password_convert(GtkEntryBuffer *buffer) {
const char *password = gtk_entry_buffer_get_text(buffer);
char *sub1 = "gpg --pinentry-mode loopback --batch --passphrase-fd 0 --armor -qd --output /dev/null ~/.config/sshyp/lock.gpg <<< '", *sub2 = "'", *command = (char *) malloc(2048);
strcpy(command, sub1); strcat(command, password); strcat(command, sub2);
system(command);
free(command);
}
void gpg_lock() {
system("gpgconf --reload gpg-agent");
}
void password_prompt(GtkWindow *window) {
GtkWidget *dialog, *dialog_header, *box_header; // main window and header bar
GtkWidget *button_unlock, *button_cancel, *button_lock; // header bar buttons
GtkWidget *content_area, *password_entry; // content area and widgets
dialog = gtk_dialog_new();
password_entry = gtk_entry_new();
gtk_entry_set_visibility(GTK_ENTRY(password_entry), FALSE);
gtk_entry_set_placeholder_text(GTK_ENTRY(password_entry), "passphrase");
dialog_header = gtk_header_bar_new();
gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE);
box_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_halign(box_header, GTK_ALIGN_CENTER);
button_unlock = gtk_button_new_with_label("unlock");
button_cancel = gtk_button_new_with_label("cancel");
button_lock = gtk_button_new_from_icon_name("changes-prevent");
gtk_widget_set_size_request(button_lock, 20, -1);
gtk_box_append(GTK_BOX(box_header), button_unlock);
gtk_box_append(GTK_BOX(box_header), button_cancel);
gtk_box_append(GTK_BOX(box_header), button_lock);
gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), box_header);
gtk_window_set_title(GTK_WINDOW(dialog), "");
gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50);
// add password entry to dialog content area
content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
gtk_box_append(GTK_BOX(content_area), password_entry);
gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header);
gtk_widget_show(dialog);
// button actions
g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(password_convert), gtk_entry_get_buffer(GTK_ENTRY(password_entry)));
g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(gtk_window_close), dialog);
g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog);
g_signal_connect(button_lock, "clicked", G_CALLBACK(gpg_lock), 0);
g_signal_connect_swapped(button_lock, "clicked", G_CALLBACK(gtk_window_close), dialog);
}
void copy_item(GtkWidget *button) {
char *command = malloc(2048); const char *button_label = gtk_button_get_label(GTK_BUTTON(button));
strcpy(command, "sshyp copy ");
strcat(command, button_label); strcat(command, " ");
strcat(command, gtk_label_get_text((GtkLabel *) label_selected));
system(command);
free(command);
}
void copy_edit_field_prompt(GtkWidget *button, gpointer *window) {
GtkWidget *dialog, *dialog_header; // main window and header bar
GtkWidget *button_cancel; // header bar buttons
GtkWidget *content_area, *button_pwd, *button_usr, *button_url, *button_nte, *button_mfa; // content area and widgets
int mode; // copy/edit mode flag
// set mode based on button pressed, copy = 1, edit = 2
const char *button_label = gtk_button_get_icon_name(GTK_BUTTON(button));
if (strcmp(button_label, "edit-copy") == 0) {
mode = 1;
} else {
mode = 2;
}
dialog = gtk_dialog_new();
dialog_header = gtk_header_bar_new();
gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE);
button_cancel = gtk_button_new_with_label("cancel");
gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), button_cancel);
gtk_window_set_title(GTK_WINDOW(dialog), "");
gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50);
// add options to dialog content area
content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
button_pwd = gtk_button_new_with_label("password");
button_usr = gtk_button_new_with_label("username");
button_url = gtk_button_new_with_label("url");
button_nte = gtk_button_new_with_label("note");
button_mfa = gtk_button_new_with_label("mfa");
gtk_box_append(GTK_BOX(content_area), button_pwd);
gtk_box_append(GTK_BOX(content_area), button_usr);
gtk_box_append(GTK_BOX(content_area), button_url);
gtk_box_append(GTK_BOX(content_area), button_nte);
gtk_box_append(GTK_BOX(content_area), button_mfa);
gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header);
gtk_widget_show(dialog);
// button actions
if (mode == 1) {
g_signal_connect(button_pwd, "clicked", G_CALLBACK(copy_item), 0);
g_signal_connect(button_usr, "clicked", G_CALLBACK(copy_item), 0);
g_signal_connect(button_url, "clicked", G_CALLBACK(copy_item), 0);
g_signal_connect(button_nte, "clicked", G_CALLBACK(copy_item), 0);
g_signal_connect(button_mfa, "clicked", G_CALLBACK(copy_item), 0);
}
g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog);
g_signal_connect_swapped(button_pwd, "clicked", G_CALLBACK(gtk_window_close), dialog);
g_signal_connect_swapped(button_usr, "clicked", G_CALLBACK(gtk_window_close), dialog);
g_signal_connect_swapped(button_url, "clicked", G_CALLBACK(gtk_window_close), dialog);
g_signal_connect_swapped(button_nte, "clicked", G_CALLBACK(gtk_window_close), dialog);
g_signal_connect_swapped(button_mfa, "clicked", G_CALLBACK(gtk_window_close), dialog);
}
void set_selection_label(GtkWidget *button, gpointer *label) {
const char *button_label = gtk_button_get_label(GTK_BUTTON(button));
gtk_label_set_text(GTK_LABEL(label), button_label);
}
void entry_list_gen() {
GtkWidget *entry_button;
GtkWidget *iter = gtk_widget_get_first_child ((GtkWidget *) list_box);
while (iter != 0) {
GtkWidget *next = gtk_widget_get_next_sibling (iter);
gtk_list_box_remove (list_box, iter);
iter = next;
}
char path[1024]; char *user_home = getenv("HOME"); strcpy(path, user_home); strcat(path, "/.local/share/sshyp");
char path_orig[1024]; strcpy(path_orig, path); strcat(path_orig, "/");
gen_entry_array(path, sizeof path, path_orig);
// bubble sort entries array
char *temp = (char *) malloc(1024);
for(int i=0; i<entry_array_count; i++){
for(int j=0; j<entry_array_count-1-i; j++){
if(strcmp(entries[j], entries[j+1]) > 0){
strcpy(temp, entries[j]);
strcpy(entries[j], entries[j+1]);
strcpy(entries[j+1], temp);
}
}
}
free(temp);
strcpy(entries[entry_array_count], "\0");
for (int i_2 = 0; strcmp(entries[i_2], "\0") != 0; i_2++) {
entry_button = gtk_button_new_with_label(entries[i_2]);
gtk_label_set_xalign(GTK_LABEL(gtk_button_get_child(GTK_BUTTON(entry_button))), GTK_JUSTIFY_LEFT);
gtk_button_set_has_frame(GTK_BUTTON(entry_button), FALSE);
g_signal_connect(entry_button, "clicked", G_CALLBACK(set_selection_label), (gpointer *)label_selected);
gtk_list_box_append(list_box, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
gtk_list_box_append(list_box, entry_button);
}
}
void sshyp_sync() {
system("sshyp sync");
entry_array_count = 0;
entry_list_gen();
}
void delete_entry() {
char *command = (char *) malloc(2048);
strcpy(command, "sshyp shear "); strcat(command, gtk_label_get_text(GTK_LABEL(label_selected)));
system(command);
free(command);
entry_array_count = 0;
entry_list_gen();
}
void are_they_sure(GtkWindow *window) {
GtkWidget *dialog, *dialog_header, *box_header; // main window and header bar
GtkWidget *button_delete, *button_cancel; // header bar buttons
GtkWidget *content_area, *label_sure; // content area and widgets
dialog = gtk_dialog_new();
dialog_header = gtk_header_bar_new();
gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE);
box_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_halign(box_header, GTK_ALIGN_CENTER);
button_delete = gtk_button_new_with_label("delete");
button_cancel = gtk_button_new_with_label("cancel");
gtk_box_append(GTK_BOX(box_header), button_delete);
gtk_box_append(GTK_BOX(box_header), button_cancel);
gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), box_header);
gtk_window_set_title(GTK_WINDOW(dialog), "");
gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50);
// add label to dialog content area
content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
gtk_widget_set_halign(content_area, GTK_ALIGN_CENTER);
char *label_text = malloc(2048);
strcpy(label_text, "this will delete:\n");
strcat(label_text, gtk_label_get_text(GTK_LABEL(label_selected)));
strcat(label_text, "\nare you sure?");
label_sure = gtk_label_new(label_text);
free(label_text);
gtk_label_set_justify(GTK_LABEL(label_sure), GTK_JUSTIFY_CENTER);
gtk_box_append(GTK_BOX(content_area), label_sure);
// button actions
g_signal_connect(button_delete, "clicked", G_CALLBACK(delete_entry), 0);
g_signal_connect_swapped(button_delete, "clicked", G_CALLBACK(gtk_window_close), dialog);
g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog);
gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header);
gtk_widget_show(dialog);
}
static void activate(GtkApplication* app) {
GtkWidget *window, *header_bar; // main window and header bar
GtkWidget *button_sync, *button_unlock; // header bar buttons
GtkWidget *stack, *stack_switcher; GtkStackPage *page_info; // stack widgets
GtkWidget *box_info, *sshyp_logo, *label_home1; // info page widgets
GtkWidget *box_browse_page, *box_browse_controls; // browse page boxes
GtkWidget *button_shear, *button_read, *button_edit, *button_copy; // browse buttons
GtkWidget *scrollable; // scrollable entry list container
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "");
gtk_window_set_default_size(GTK_WINDOW(window), 360, 720);
header_bar = gtk_header_bar_new();
gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(header_bar), FALSE);
// !!start main stack stuff!!
// create the stack
stack = gtk_stack_new();
gtk_stack_set_transition_type(GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
// attach the stack to the main window
gtk_window_set_child(GTK_WINDOW(window), stack);
// browse
button_shear = gtk_button_new_from_icon_name("edit-delete");
gtk_widget_set_hexpand(button_shear, FALSE);
gtk_widget_set_size_request(button_shear, 50, -1);
g_signal_connect_swapped(button_shear, "clicked", G_CALLBACK(are_they_sure), window);
button_read = gtk_button_new_from_icon_name("mail-read");
gtk_widget_set_hexpand(button_read, TRUE);
button_edit = gtk_button_new_from_icon_name("document-edit");
gtk_widget_set_hexpand(button_edit, TRUE);
g_signal_connect(button_edit, "clicked", G_CALLBACK(copy_edit_field_prompt), (gpointer *) window);
button_copy = gtk_button_new_from_icon_name("edit-copy");
gtk_widget_set_hexpand(button_copy, TRUE);
g_signal_connect(button_copy, "clicked", G_CALLBACK(copy_edit_field_prompt), (gpointer *) window);
box_browse_page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
label_selected = gtk_label_new("no entry selected");
gtk_widget_set_hexpand(label_selected, TRUE);
gtk_widget_set_halign(label_selected, GTK_ALIGN_CENTER);
box_browse_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_valign(box_browse_controls, GTK_ALIGN_START);
gtk_widget_set_size_request(box_browse_controls, -1, 50);
gtk_box_append(GTK_BOX(box_browse_controls), button_shear);
gtk_box_append(GTK_BOX(box_browse_controls), button_read);
gtk_box_append(GTK_BOX(box_browse_controls), button_edit);
gtk_box_append(GTK_BOX(box_browse_controls), button_copy);
gtk_box_append(GTK_BOX(box_browse_page), label_selected);
gtk_box_append(GTK_BOX(box_browse_page), box_browse_controls);
// !!start scrollable list!!
scrollable = gtk_scrolled_window_new();
list_box = (GtkListBox *) gtk_list_box_new();
gtk_widget_set_vexpand(GTK_WIDGET(list_box), TRUE);
entry_list_gen();
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrollable), GTK_WIDGET(list_box));
// !!end scrollable list!!
gtk_box_append(GTK_BOX(box_browse_page), GTK_WIDGET(scrollable));
// info
box_info = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
sshyp_logo = gtk_image_new_from_file("PLACEHOLDER");
gtk_image_set_pixel_size(GTK_IMAGE(sshyp_logo), 300);
label_home1 = gtk_label_new("password pasture\nexperimental gui client for sshyp");
gtk_label_set_justify(GTK_LABEL(label_home1), GTK_JUSTIFY_CENTER);
gtk_label_set_markup(GTK_LABEL(label_home1), "<span size='large'><b>password pasture</b></span>\n<span size='small'>experimental gui client for sshyp</span>");
gtk_box_append(GTK_BOX(box_info), sshyp_logo);
gtk_box_append(GTK_BOX(box_info), label_home1);
stack_switcher = gtk_stack_switcher_new();
page_info = gtk_stack_add_titled(GTK_STACK(stack), box_info, "info", "info");
gtk_stack_page_set_icon_name(GTK_STACK_PAGE(page_info), "help-about");
gtk_stack_add_titled(GTK_STACK(stack), box_browse_page, "browse", "browse");
gtk_stack_switcher_set_stack(GTK_STACK_SWITCHER(stack_switcher), GTK_STACK(stack));
gtk_header_bar_pack_start(GTK_HEADER_BAR(header_bar), stack_switcher);
// !!end main stack stuff!!
// header bar buttons
button_sync = gtk_button_new_from_icon_name("view-refresh");
g_signal_connect(button_sync, "clicked", G_CALLBACK(sshyp_sync), 0);
gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_sync);
button_unlock = gtk_button_new_from_icon_name("changes-allow");
g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(password_prompt), window);
gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_unlock);
//GtkWidget *button_debug = gtk_button_new_from_icon_name("applications-science");
//g_signal_connect(button_debug, "clicked", G_CALLBACK(0), 0);
//gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_debug);
gtk_window_set_titlebar(GTK_WINDOW(window), header_bar);
gtk_widget_show(window);
}
int main(int argc,
char **argv) {
GtkApplication *app = gtk_application_new("org.rwinkhart.sshyp", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate), 0);
int status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
@@ -0,0 +1,3 @@
arch: base-devel gtk4 cmake make gcc
alpine: build-base gtk4.0 gtk4.0-dev cmake make gcc binutils abuild
+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
+3
View File
@@ -0,0 +1,3 @@
[config]
input = copy -m
output = /usr/lib/sshyp/sshyp-mfa
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
from base64 import b32decode
from configparser import ConfigParser
from os import environ, listdir, uname
from os.path import expanduser, isdir, isfile
from sshyp import decrypt, whitelist_verify
from subprocess import PIPE, Popen, run
from sys import argv, exit as s_exit
from time import sleep, strftime, time
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
return str(_binary)[-_digits:].zfill(_digits)
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(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:
_type = _mfa_data[4].split('otpauth://')[1].split('/')[0]
_secret = _mfa_data[4].split('?secret=')[1].split('&issuer=')[0]
_algo = _mfa_data[4].split('&algorithm=')[1].split('&digits=')[0]
_digits = int(_mfa_data[4].split('&digits=')[1].split('&period=')[0])
_period = int(_mfa_data[4].split('&period=')[1])
return _type, _secret, _algo, _digits, _period
except IndexError:
print(f"\n\u001b[38;5;9merror: entry ({arguments[0]}) does not contain valid mfa data\u001b[0m\n")
s_exit(1)
if __name__ == '__main__':
# argument fetcher
arguments = argv[1:]
if len(arguments) < 1 or not arguments[0].startswith('/'):
print("\nsshyp-mfa extension usage: sshyp </entry name> copy -m\n\nrun 'man sshyp-mfa' for more information\n")
s_exit(1)
# 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')
# main process: runs functions to generate MFA key, then continuously copies up-to-date MFA key to clipboard
try:
mfa_data, copied = mfa_read_shortcut(), None
print('\nmfa key copied to clipboard\n\nuntil this process is closed, your clipboard will be automatically '
'updated with the newest mfa key')
while True:
if str(int(strftime('%S'))/mfa_data[4]).endswith('.0') or copied is None:
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)
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))
elif 'WAYLAND_DISPLAY' in environ: # Wayland clipboard detection
run(('wl-copy', _mfa_key))
elif uname()[0] == 'Haiku': # Haiku clipboard detection
run(('clipboard', '-c', _mfa_key))
elif uname()[0] == 'Darwin': # MacOS clipboard detection
run('pbcopy', stdin=Popen(('printf', _mfa_key), stdout=PIPE).stdout)
elif isdir("/data/data/com.termux"): # Termux (Android) clipboard detection
run(('termux-clipboard-set', _mfa_key))
else: # X11 clipboard detection
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', _mfa_key), stdout=PIPE).stdout)
sleep(1)
except KeyboardInterrupt:
s_exit(0)
Executable → Regular
View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

@@ -1,41 +0,0 @@
#!/bin/python3
from os import path, remove, system, walk
from pathlib import Path
from shutil import move, rmtree
if __name__ == "__main__":
gpg_id = input('\nWhat is the ID of the GPG key you would like to use for re-encryption?\n\nID: ')
directory = path.expanduser('~/.local/share/sshyp')
if Path(f"{directory}.new").exists():
rmtree(f"{directory}.new")
if Path(f"{directory}.old").exists():
rmtree(f"{directory}.old")
if path.isdir(directory):
for dirPath, dirNames, filenames in walk(directory):
for filename in sorted(filenames):
Path(dirPath.replace(directory, directory + '.new')).mkdir(0o700, parents=True, exist_ok=True)
system(f"gpg -d '{dirPath}/{filename}' "
f"> '{dirPath.replace(directory, directory + '.new')}/{filename[:-4]}'")
_old_contents, _new_contents = \
open(f"{dirPath.replace(directory, directory + '.new')}/{filename[:-4]}", 'r').readlines(), ''
for _num in range(len(_old_contents)):
if _num == 3:
if _old_contents[_num] == ' ' or _old_contents[_num] == '\n':
pass
else:
_new_contents += _old_contents[_num]
else:
_new_contents += _old_contents[_num]
for _num in reversed(range(len(_new_contents))):
if _new_contents[_num] == '\n' or _new_contents[_num] == '':
_new_contents = _new_contents[:-1]
else:
break
open(f"{dirPath.replace(directory, directory + '.new')}/{filename[:-4]}", 'w').writelines(_new_contents)
system(f"gpg -qr {str(gpg_id)} -e '{dirPath.replace(directory, directory + '.new')}/{filename[:-4]}'")
remove(f"{dirPath.replace(directory, directory + '.new')}/{filename[:-4]}")
else:
print(f"\nError: no entry folder ({directory}) found\n")
move(directory, f"{directory}.old")
move(f"{directory}.new", directory)
@@ -1,4 +1,4 @@
#!/bin/python3
#!/usr/bin/env python3
from os import path, remove, system, walk
from pathlib import Path
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
from os import path, system, uname, walk
from pathlib import Path
from shutil import move, rmtree
from sshyp import decrypt, encrypt, optimized_edit, shm_gen
if __name__ == "__main__":
directory, tmp_dir = path.expanduser('~/.local/share/sshyp'), path.expanduser('~/.config/sshyp/tmp/')
if uname()[0] == 'Haiku': # set proper gpg command for OS
gpg = 'gpg --pinentry-mode loopback'
else:
gpg = 'gpg'
system(f"{gpg} -k")
gpg_id = input('\ngpg id for re-encryption: ')
if Path(f"{directory}.new").exists():
rmtree(f"{directory}.new")
if Path(f"{directory}.old").exists():
rmtree(f"{directory}.old")
if path.isdir(directory):
print('\nconverting... please wait, do not force close this process')
for dirPath, dirNames, filenames in walk(directory):
for filename in sorted(filenames):
Path(dirPath.replace(directory, directory + '.new')).mkdir(0o700, parents=True, exist_ok=True)
_shm_folder, _shm_entry = shm_gen()
decrypt(f"{dirPath}/{filename[:-4]}", _shm_folder, _shm_entry, gpg)
_new_lines = optimized_edit(open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines(), None, -1)
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_new_lines)
encrypt(f"{dirPath.replace(directory, directory + '.new', 1)}/{filename[:-4]}",
_shm_folder, _shm_entry, gpg, gpg_id)
move(directory, f"{directory}.old")
move(f"{directory}.new", directory)
else:
print(f"\nError: no entry folder ({directory}) found\n")
+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.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
-92
View File
@@ -1,92 +0,0 @@
#!/bin/python3
from base64 import b32decode
from hmac import new as new_mac
from os import environ, path, system, uname
from pathlib import Path
from shutil import rmtree
from sshync import get_profile
from sshyp import decrypt, entry_list_gen, shm_gen
from struct import pack, unpack
from sys import argv, exit as s_exit
from time import sleep, strftime, time
try:
from steam.guard import generate_twofactor_code as steam_totp
except (ModuleNotFoundError, ImportError):
pass
def totp(_secret, _algo, _digits, _period): # uses provided information to generate a standard totp key
_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
return str(_binary)[-_digits:].zfill(_digits)
def mfa_read_shortcut(): # reads and extracts MFA info from the user-specified sshyp entry
if not Path(f"{directory}{argument}.gpg").exists():
print(f"\n\u001b[38;5;9merror: entry ({argument}) does not exist\u001b[0m\n")
s_exit(1)
_shm_folder, _shm_entry = shm_gen()
decrypt(directory + argument, _shm_folder, _shm_entry, gpg)
try:
_mfa_data = open(f"{path.expanduser('~/.config/sshyp/tmp/')}{_shm_folder}/{_shm_entry}", 'r').readlines()
_type = _mfa_data[4].split('otpauth://')[1].split('/')[0]
_secret = _mfa_data[4].split('?secret=')[1].split('&issuer=')[0]
_algo = _mfa_data[4].split('&algorithm=')[1].split('&digits=')[0]
_digits = int(_mfa_data[4].split('&digits=')[1].split('&period=')[0])
_period = int(_mfa_data[4].split('&period=')[1])
rmtree(f"{path.expanduser('~/.config/sshyp/tmp/')}{_shm_folder}")
return _type, _secret, _algo, _digits, _period
except IndexError:
print(f"\n\u001b[38;5;9merror: entry ({argument}) does not contain valid mfa data\u001b[0m\n")
rmtree(f"{path.expanduser('~/.config/sshyp/tmp/')}{_shm_folder}")
s_exit(1)
if __name__ == '__main__':
# argument fetcher
argument_list = argv
if not len(argv) == 1 and not argv[1].strip().startswith('/'):
print(f"\n\u001b[38;5;9merror: invalid argument - run 'man sshyp-mfa' for usage information\u001b[0m\n")
s_exit(1)
# user data fetcher
ssh_info = get_profile(path.expanduser('~/.config/sshyp/sshyp.sshync'))
directory = str(ssh_info[3].replace('\n', ''))
if uname()[0] == 'Haiku': # set proper gpg command for OS
gpg = 'gpg --pinentry-mode loopback'
else:
gpg = 'gpg'
# main process; runs functions to generate MFA key, then continuously copies up-to-date MFA key to clipboard
try:
if len(argument_list) == 1:
entry_list_gen()
argument_list.append(input('entry to read: '))
argument = ' '.join(argument_list[1:]).replace('/', '', 1)
mfa_data, copied = mfa_read_shortcut(), None
print('\nmfa key copied to clipboard\n\nuntil this process is closed, your clipboard will be automatically '
'updated with the newest mfa key')
while True:
if str(int(strftime('%S'))/mfa_data[4]).endswith('.0') or copied is None:
if copied is None:
copied = 1
if mfa_data[0] == 'steam':
_mfa_key = steam_totp(b32decode(mfa_data[1]))
else:
_mfa_key = totp(mfa_data[1], mfa_data[2], mfa_data[3], mfa_data[4])
if uname()[0] == 'Haiku': # Haiku clipboard detection
system(f"clipboard -c '{_mfa_key}'")
elif Path("/data/data/com.termux").exists(): # Termux (Android) clipboard detection
system(f"termux-clipboard-set '{_mfa_key}'")
elif environ.get('WAYLAND_DISPLAY') == 'wayland-0': # Wayland clipboard detection
system(f"wl-copy '{_mfa_key}'")
else: # X11 clipboard detection
system(f"echo -n '{_mfa_key}' | xclip -sel c")
sleep(1)
except KeyboardInterrupt:
print('\n')
s_exit()
-44
View File
@@ -1,44 +0,0 @@
.TH sshyp-mfa 1 "12 July 2022" "rolling" "sshyp-mfa man page"
.SH NAME
sshyp-mfa \- An MFA (TOTP/Steam) key generator for the sshyp password manager.
.SH SYNOPSIS
sshyp-mfa [/<entry name>]
.SH 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.
.SH EXAMPLES
Viewing the entry database:
sshyp-mfa
Generating and copying an MFA key for an existing entry saved as ~/.local/share/sshyp-mfa/development/github.gpg
sshyp-mfa /development/github
.SH SETUP
sshyp-mfa requires a pre-existing functional sshyp setup.
MFA keys are generated off of MFA data placed in the second line of a sshyp entry's notes field. This data needs to be added manually, through sshyp, and must be in Authenticator backup format.
It might be easiest to import all of your MFA keys into Authenticator, export them into plain text, then add all of the exported MFA data into the second notes line in their respective sshyp entries.
.SH FORMAT
Format: otpauth://<OTP METHOD>/<ACCOUNT NAME, NOT USED>?secret=<SECRET>&issuer=<ISSUER, NOT USED>&algorithm=<ALGORITHM>&digits=<DIGITS>&period=<REFRESH PERIOD>
Help! What do I put in each of those spaces?
<OTP METHOD> is almost always 'totp', but in the case of Steam, it needs to be set to 'steam'.
<ACCOUNT NAME, NOT USED> is a part of the Authenticator backup format, but it is not used in sshyp-mfa. Set to anything.
<SECRET> refers to the secret used to generate your MFA key. This is usually directly provided by the issuer, but it is sometimes hidden and more easily retrieved by copying it from a QR-compatible MFA app (such as Aegis).
<ISSUER, NOT USED> is a part of the Authenticator backup format, but it is not used in sshyp-mfa. Set to anything.
<ALGORITHM> refers to the algorithm used to generate your MFA key based on your secret. This is almost always 'sha1'.
<DIGITS> refers to the intended length of your MFA key. This is almost always '6', but in the case of Steam, it needs to be set to '5'.
<REFRESH PERIOD> refers to the interval at which a new MFA key needs to be generated. This is almost always '30', for 30 seconds.
Example configuration (for most services, see above for differences regarding Steam):
otpauth://totp/MyNameIsBob?secret=YUGBSG65SG9SDBSDF56SBFVSC86SBVD6&issuer=Github&algorithm=sha1&digits=6&period=30
.SH AUTHOR
Randall Winkhart (https://github.com/rwinkhart)
-231
View File
@@ -1,231 +0,0 @@
#!/bin/bash
# This script packages sshyp-mfa (from source) for various UNIX(-like) environments.
# Dependencies (Arch Linux): dpkg (packaging for Debian/Termux), freebsd-pkg (packaging for FreeBSD)
# Dependencies (Fedora) (can only package for self): rpmdevtools
# NOTE It is recommended to instead use the latest officially packaged and tagged release.
echo -e '\nOptions (please enter the number only):'
echo -e '\nPackage Formats:\n\n1. Haiku\n2. Debian&Ubuntu Linux\n3. Fedora Linux\n4. FreeBSD\n5. Termux\n6. Generic (used for PKGBUILD/APKBUILD)'
echo -e '\nBuild Scripts:\n\n7. Arch Linux (PKGBUILD)'
echo -e '\nOther:\n\n8. All (generates all distribution packages (excluding Haiku and Fedora, as these must be packaged on their respective distributions) and build scripts)\n'
read -n 1 -r -p "Distribution: " distro
echo -e '\n\nThe value entered in this field will only affect the version reported to the package manager. The latest source is used regardless.\n'
read -r -p "Version number: " version
echo -e '\nThe value entered in this field will only affect the revision number for build scripts.\n'
read -r -p "Revision number: " revision
if [ "$distro" == "7" ] || [ "$distro" == "8" ]; then
echo -e '\nOptions (please enter the number only):'
echo -e '\n1. GitHub Release Tag\n2. Local\n'
read -r -p "Source (for build scripts): " source
if [ "$source" == "1" ]; then
source='https://github.com/rwinkhart/sshyp-labs/releases/download/v$pkgver/sshyp-mfa-$pkgver.tar.xz'
else
source=local://sshyp-mfa-"$version".tar.xz
fi
fi
mkdir -p packages
if [ "$distro" == "1" ]; then
echo -e '\nPackaging for Haiku...\n'
mkdir -p packages/haikutemp/documentation/{man/man1,packages/sshyp-mfa}
echo "name sshypmfa
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-2022 Randall Winkhart\"
}
provides {
sshypmfa = "$version"
cmd:sshypmfa
}
requires {
sshyp
python3
}
urls {
\"https://github.com/rwinkhart/sshyp-labs\"
}
" > packages/haikutemp/.PackageInfo
cp -r bin packages/haikutemp/
ln -s /bin/sshyp-mfa.py packages/haikutemp/bin/sshyp-mfa
cp -r share/licenses/sshyp-mfa/ packages/haikutemp/documentation/packages/
cp extra/manpage packages/haikutemp/documentation/man/man1/sshyp-mfa.1
gzip packages/haikutemp/documentation/man/man1/sshyp-mfa.1
cd packages/haikutemp
package create -b sshyp-mfa-"$version"-"$revision"_all.hpkg
package add sshyp-mfa-"$version"-"$revision"_all.hpkg bin documentation
cd ../..
mv packages/haikutemp/sshyp-mfa-"$version"-"$revision"_all.hpkg packages/
rm -rf packages/haikutemp
echo -e "\nHaiku packaging complete.\n"
fi
if [ "$distro" == "2" ] || [ "$distro" == "8" ]; then
echo -e '\nPackaging for Debian...\n'
mkdir -p packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all/{DEBIAN,usr/share/man/man1}
echo "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, python3
Priority: optional
Installed-Size: 100
" > packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all/DEBIAN/control
cp -r bin packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/
ln -s /usr/bin/sshyp-mfa.py packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/bin/sshyp-mfa
cp -r share packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/
cp extra/manpage packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/share/man/man1/sshyp-mfa.1
gzip packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/share/man/man1/sshyp-mfa.1
dpkg-deb --build --root-owner-group packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all/
mv packages/debiantemp/sshyp-mfa_"$version"-"$revision"_all.deb packages/
rm -rf packages/debiantemp
echo -e "\nDebian packaging complete.\n"
fi
if [ "$distro" == "5" ] || [ "$distro" == "8" ]; then
echo -e '\nPackaging for Termux...\n'
mkdir -p packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/{data/data/com.termux/files/usr/share/man/man1,DEBIAN}
echo "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, python3
Priority: optional
Installed-Size: 100
" > packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/DEBIAN/control
cp -r bin packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
ln -s /data/data/com.termux/files/usr/bin/sshyp-mfa.py packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin/sshyp-mfa
cp -r share packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
cp extra/manpage packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp-mfa.1
gzip packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp-mfa.1
dpkg-deb --build --root-owner-group packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/
mv packages/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux.deb packages/
rm -rf packages/termuxtemp
echo -e "\nTermux packaging complete.\n"
fi
if [ "$distro" == "3" ] || [ "$distro" == "4" ] || [ "$distro" == "6" ] || [ "$distro" == "7" ] || [ "$distro" == "8" ]; then
echo -e '\nPackaging as generic...\n'
mkdir -p packages/generictemp/usr/share/man/man1
cp -r bin packages/generictemp/usr/
ln -s /usr/bin/sshyp-mfa.py packages/generictemp/usr/bin/sshyp-mfa
cp -r share packages/generictemp/usr/
cp extra/manpage packages/generictemp/usr/share/man/man1/sshyp-mfa.1
gzip packages/generictemp/usr/share/man/man1/sshyp-mfa.1
tar -C packages/generictemp -cvf packages/sshyp-mfa-"$version".tar.xz usr/
rm -rf packages/generictemp
sha512="$(sha512sum packages/sshyp-mfa-"$version".tar.xz | awk '{print $1;}')"
echo -e "\nsha512 sum:\n$sha512"
echo -e "\nGeneric packaging complete.\n"
fi
if [ "$distro" == "3" ]; then
echo -e '\nPackaging for Fedora...\n'
rm -rf ~/rpmbuild
rpmdev-setuptree
cp packages/sshyp-mfa-"$version".tar.xz ~/rpmbuild/SOURCES
echo "Name: sshyp-mfa
Version: "$version"
Release: "$revision"
Summary: An MFA (TOTP/Steam) key generator for the sshyp password manager
BuildArch: noarch
License: GPLv3
URL: https://github.com/rwinkhart/sshyp-labs
Source0: sshyp-mfa-"$version".tar.xz
Requires: sshyp python
%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.
%install
tar xf %{_sourcedir}/sshyp-mfa-"$version".tar.xz -C %{_sourcedir}
cp -r %{_sourcedir}/usr %{buildroot}
%files
/usr/bin/sshyp-mfa
/usr/bin/sshyp-mfa.py
%license /usr/share/licenses/sshyp-mfa/license
%doc /usr/share/man/man1/sshyp-mfa.1.gz
" > ~/rpmbuild/SPECS/sshyp-mfa.spec
rpmbuild -bb ~/rpmbuild/SPECS/sshyp-mfa.spec
mv ~/rpmbuild/RPMS/noarch/* packages/
rm -rf ~/rpmbuild
echo -e "\nFedora packaging complete.\n"
fi
if [ "$distro" == "4" ] || [ "$distro" == "8" ]; then
echo -e '\nPackaging for FreeBSD...\n'
mkdir -p packages/FreeBSDtemp/bin
tar xf packages/sshyp-mfa-"$version".tar.xz -C packages/FreeBSDtemp
echo "name: sshyp-mfa
version: \""$version"\"
abi = \"FreeBSD:13:*\";
arch = \"freebsd:13:*\";
origin: security/sshyp-mfa
comment: \"a sshyp extension\"
desc: \"an MFA (TOTP/Steam) key generator for the sshyp password manager\"
maintainer: <idgr at tutanota dot com>
www: https://github.com/rwinkhart/sshyp-labs
prefix: /
\"deps\" : {
\"sshyp\" : {
\"origin\" : \"security/sshyp\"
},
\"python\" : {
\"origin\" : \"lang/python\"
},
},
" > packages/FreeBSDtemp/+MANIFEST
echo "/usr/bin/sshyp-mfa
/usr/bin/sshyp-mfa.py
/usr/share/licenses/sshyp-mfa/license
/usr/share/man/man1/sshyp-mfa.1.gz
" > packages/FreeBSDtemp/plist
pkg create -m packages/FreeBSDtemp/ -r packages/FreeBSDtemp/ -p packages/FreeBSDtemp/plist -o packages/
rm -rf packages/FreeBSDtemp
echo -e "\nFreeBSD packaging complete.\n"
fi
if [ "$distro" == "7" ] || [ "$distro" == "8" ]; then
echo -e '\nGenerating PKGBUILD...'
echo "# Maintainer: Randall Winkhart <idgr at tutanota dot com>
pkgname=sshyp-mfa
pkgver="$version"
pkgrel="$revision"
pkgdesc='An MFA (TOTP/Steam) key generator for the sshyp password manager'
url='https://github.com/rwinkhart/sshyp-labs'
arch=('any')
license=('GPL3')
depends=(sshyp python)
source=(\""$source"\")
sha512sums=('"$sha512"')
package() {
tar xf sshyp-mfa-"\"\$pkgver\"".tar.xz -C "\"\${pkgdir}\""
}
" > packages/PKGBUILD
echo -e "\nPKGBUILD generated.\n"
fi
-1
View File
@@ -1 +0,0 @@
steam[client]
@@ -1,15 +0,0 @@
sshyp-mfa is a FOSS extension for the sshyp password manager.
Copyright (C) 2022 Randall Winkhart idgr@tutanota.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.