mirror of
https://github.com/rwinkhart/sshyp-labs.git
synced 2026-08-28 04:46:48 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11f1fa2a0b | ||
|
|
2e3eb2fea3 | ||
|
|
4b0f45c5e2 | ||
|
|
619ea52f7a | ||
|
|
d63d77e69f | ||
|
|
a908beaa92 | ||
|
|
e40dd66d38 | ||
|
|
a22b803288 | ||
|
|
6e5a3b8f5b | ||
|
|
626ddcce30 | ||
|
|
0985a168db | ||
|
|
f62122d79f | ||
|
|
ab691c16d8 | ||
|
|
ada8a20a8f | ||
|
|
efcfe248ba | ||
|
|
e075f70ccf | ||
|
|
029032c74c | ||
|
|
b722321a6e | ||
|
|
4fa5b11fec | ||
|
|
044bf2b205 | ||
|
|
9b44f438d4 | ||
|
|
df9d32c715 | ||
|
|
e84e9569f3 | ||
|
|
cfe9dd1eef | ||
|
|
cb1aca70bc | ||
|
|
80425bec2e | ||
|
|
d72c4a5c12 | ||
|
|
d339e4c734 | ||
|
|
240939178e | ||
|
|
b98122f4d8 | ||
|
|
7be5509fa7 | ||
|
|
8413c305c2 | ||
|
|
25a5731936 | ||
|
|
286645dd31 | ||
|
|
ec633c5104 | ||
|
|
9540e9874f | ||
|
|
0020ac313b | ||
|
|
ee8d875095 | ||
|
|
5d1eeb1f75 | ||
|
|
769f522b2a | ||
|
|
018912cfc5 | ||
|
|
d53fc304d5 | ||
|
|
adcb13fd89 | ||
|
|
2421bc2ff3 | ||
|
|
0a4efc2002 | ||
|
|
90884b4cd7 | ||
|
|
bc09ec5e30 | ||
|
|
e9a1728c6f | ||
|
|
08bc5dec87 | ||
|
|
23be7f8c30 | ||
|
|
0bbcda37f2 | ||
|
|
19ff50a5db | ||
|
|
ac035db597 | ||
|
|
fb267085eb | ||
|
|
90c3de83b5 | ||
|
|
a635207cee | ||
|
|
b9b06a4800 | ||
|
|
4e76b641a6 | ||
|
|
ed40b01e85 | ||
|
|
c8e54cef43 | ||
|
|
8fe7675ca3 | ||
|
|
45fd0631ca | ||
|
|
d4ed9b0da1 | ||
|
|
0953f60d0b | ||
|
|
580d511a50 | ||
|
|
da6b1658a4 | ||
|
|
5df741726f | ||
|
|
ea020c4bf1 | ||
|
|
3568601520 | ||
|
|
e9ee114452 | ||
|
|
8c1a3c340c | ||
|
|
0ad86a1040 | ||
|
|
931d7a0494 | ||
|
|
f0eb398f07 | ||
|
|
1216d06975 | ||
|
|
8e45b15fee | ||
|
|
6ee49d6b95 | ||
|
|
7b03cfdc5e | ||
|
|
45caab270e | ||
|
|
1c6150afe4 | ||
|
|
8916f9b2e2 | ||
|
|
c0281a4dc3 | ||
|
|
37efe8f96a | ||
|
|
2271281683 | ||
|
|
c5c3600e6d | ||
|
|
a97aa6052a | ||
|
|
d133406de9 | ||
|
|
25f608dd1e | ||
|
|
a957d80a67 | ||
|
|
521714fd12 | ||
|
|
3c80d72a36 | ||
|
|
68c370be55 | ||
|
|
bd09ab32cd | ||
|
|
755b1183e3 | ||
|
|
9c67f41199 | ||
|
|
951cb5adc5 | ||
|
|
4227a264c8 | ||
|
|
d19cc1ed2c | ||
|
|
04ab10fdd9 | ||
|
|
fd5266351e | ||
|
|
73947dd7ef | ||
|
|
fcf43986a8 | ||
|
|
dfb90ca294 | ||
|
|
42ef63c0d9 | ||
|
|
939d07e804 | ||
|
|
be41d32f78 | ||
|
|
ba2ac73965 | ||
|
|
ebb420d30f | ||
|
|
8cf5728c42 | ||
|
|
9e0c121843 |
@@ -0,0 +1,4 @@
|
||||
/extra/conversion-scripts/gpgToRCW/1output
|
||||
/extra/conversion-scripts/gpgToRCW/gpgToRCW
|
||||
/extra/conversion-scripts/upgradeRCW/1output
|
||||
/extra/conversion-scripts/upgradeRCW/upgradeRCW
|
||||
@@ -1,17 +1,23 @@
|
||||
# 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).
|
||||
More recently, this repository has become a dumping ground for assets related to sshyp and its successor, MUTN.
|
||||
|
||||
# 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)
|
||||
[export-to-libmutton](https://github.com/rwinkhart/sshyp-labs/wiki/export%E2%80%90to%E2%80%90libmutton): export sshyp entries for usage in libmutton-based password managers
|
||||
|
||||
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.
|
||||
[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.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[config]
|
||||
input = export
|
||||
output = /usr/lib/sshyp/export-to-libmutton
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from os import path, remove, system, walk
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
from sshyp import decrypt
|
||||
|
||||
if __name__ == "__main__":
|
||||
_pasture = path.expanduser('~/.local/share/sshyp')
|
||||
|
||||
_gpg_id = input('\nWhat is the ID of the GPG key you would like to use for re-encryption?\n\nID: ')
|
||||
|
||||
# remove previous export if it exists
|
||||
if Path(f"{_pasture}.export").exists():
|
||||
rmtree(f"{_pasture}.export")
|
||||
|
||||
# prompt for unlock and display do not close warning
|
||||
decrypt(None)
|
||||
print('\nentry conversion may take some time (especially on slower devices) - do not terminate this process!\n')
|
||||
|
||||
# iterate over entires, exporting them in the libmutton format
|
||||
if path.isdir(_pasture):
|
||||
for _dirPath, _dirNames, _filenames in walk(_pasture):
|
||||
for _filename in sorted(_filenames):
|
||||
# ensure parent directory within export tree exists
|
||||
Path(_dirPath.replace(_pasture, _pasture + '.export')).mkdir(0o700, parents=True, exist_ok=True)
|
||||
|
||||
# remove the .gpg file extension, if it is present
|
||||
# prevents saving with the extension, as libmutton does not require it
|
||||
_og_extension = ''
|
||||
if _filename.endswith('.gpg'):
|
||||
_og_extension = '.gpg'
|
||||
_filename = _filename[:-4]
|
||||
|
||||
# decrypt to export directory, append .gpg to decrypted file to differentiate from new file
|
||||
system(f"gpg -d '{_dirPath}/{_filename}{_og_extension}' > '{_dirPath.replace(_pasture, _pasture + '.export')}/{_filename}.gpg'")
|
||||
|
||||
_old_contents, _new_contents = open(f"{_dirPath.replace(_pasture, _pasture + '.export')}/{_filename}.gpg", 'r').readlines(), ''
|
||||
|
||||
# extract sshyp-mfa data for new reserved line
|
||||
_secret = '\n'
|
||||
if len(_old_contents) >= 5:
|
||||
if _old_contents[4].startswith('otpauth://'):
|
||||
# check if sshyp-mfa data is standard TOTP or Steam
|
||||
if _old_contents[4][10:].startswith('steam'):
|
||||
print("\n\u001b[38;5;3mlibmutton uses a different form of secret key for Steam, and as such, your Steam TOTP secret must be migrated manually\u001b[0m\n")
|
||||
else:
|
||||
# extract TOTP secret
|
||||
_secret = _old_contents[4].split('?secret=')[1].split('&issuer=')[0]+'\n'
|
||||
|
||||
# temporarily expand _old_contents to avoid index errors (will be trimmed later)
|
||||
while len(_old_contents) < 6:
|
||||
_old_contents.append('\n')
|
||||
|
||||
# extract any possible notes from _old_contents (as a string)
|
||||
if _secret == '\n':
|
||||
_old_notes = ''.join(_old_contents[3:])
|
||||
else:
|
||||
# skip second notes line if sshyp-mfa data was found
|
||||
# skip first notes line if blank line
|
||||
if _old_contents[3] in ('\n', ' '):
|
||||
_old_contents[3] = ''
|
||||
_old_notes = _old_contents[3] + ''.join(_old_contents[5:])
|
||||
|
||||
# set _new_contents
|
||||
_new_contents = _old_contents[0] + _old_contents[1] + _secret + _old_contents[2] + _old_notes
|
||||
|
||||
# remove any trailing whitespace
|
||||
for _num in reversed(range(len(_new_contents))):
|
||||
if _new_contents[_num] in ('\n', '', ' '):
|
||||
_new_contents = _new_contents[:-1]
|
||||
else:
|
||||
break
|
||||
|
||||
# write and encrypt the output file
|
||||
open(f"{_dirPath.replace(_pasture, _pasture + '.export')}/{_filename}.gpg", 'w').writelines(_new_contents)
|
||||
system(f"gpg -er {str(_gpg_id)} -o '{_dirPath.replace(_pasture, _pasture + '.export')}/{_filename}' '{_dirPath.replace(_pasture, _pasture + '.export')}/{_filename}.gpg'")
|
||||
remove(f"{_dirPath.replace(_pasture, _pasture + '.export')}/{_filename}.gpg")
|
||||
else:
|
||||
print(f"\n\u001b[38;5;9merror: no entry folder ({_pasture}) found\u001b[0m\n")
|
||||
s_exit(2)
|
||||
|
||||
@@ -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})
|
||||
Executable
+5
@@ -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)
|
||||
@@ -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
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/bin/sh
|
||||
|
||||
version='1.5.1.3'
|
||||
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
|
||||
printf '[config]\ninput = copy -m\noutput = /system/lib/sshyp/sshyp-mfa\n' > ./output/haikutemp/lib/sshyp/extensions/sshyp-mfa.ini
|
||||
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' 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
|
||||
printf '[config]\ninput = copy -m\noutput = /data/data/com.termux/files/usr/lib/sshyp/sshyp-mfa\n' > ./output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/extensions/sshyp-mfa.ini
|
||||
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
|
||||
@@ -0,0 +1,3 @@
|
||||
[config]
|
||||
input = copy -m
|
||||
output = /usr/lib/sshyp/sshyp-mfa
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/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
|
||||
home = expanduser("~")
|
||||
|
||||
|
||||
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))
|
||||
_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:
|
||||
_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.getboolean('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':
|
||||
_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 + "'"))
|
||||
elif 'WAYLAND_DISPLAY' in environ: # Wayland clipboard detection
|
||||
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
|
||||
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)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 353 KiB |
Executable → Regular
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
@@ -1 +0,0 @@
|
||||
89fbbcf15e7fb786db4e2551dc3234615c57f77e
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
# This script generates portable release binaries for the following platforms:
|
||||
# - Linux (x86_64_v1)
|
||||
# - Linux (aarch64)
|
||||
# - Windows (x86_64_v1)
|
||||
# - Windows (aarch64)
|
||||
|
||||
mkdir -p ./1output
|
||||
GOOS=linux CGO_ENABLED=0 GOAMD64=v1 go build -o ./1output/gpgToRCW-linux-x86_64_v1 -ldflags="-s -w" -trimpath .
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o ./1output/gpgToRCW-linux-aarch64 -ldflags="-s -w" -trimpath .
|
||||
GOOS=windows CGO_ENABLED=0 GOAMD64=v1 go build -o ./1output/gpgToRCW-windows-x86_64_v1.exe -ldflags="-s -w" -trimpath .
|
||||
GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build -o ./1output/gpgToRCW-windows-aarch64.exe -ldflags="-s -w" -trimpath .
|
||||
@@ -0,0 +1,19 @@
|
||||
module gpgToRCW
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/rwinkhart/go-boilerplate v0.3.1
|
||||
github.com/rwinkhart/libmutton v0.5.0
|
||||
github.com/rwinkhart/rcw v0.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
)
|
||||
|
||||
replace golang.org/x/sys => github.com/rwinkhart/sys v0.41.0
|
||||
|
||||
replace github.com/Microsoft/go-winio => github.com/rwinkhart/go-winio v0.1.1
|
||||
@@ -0,0 +1,12 @@
|
||||
github.com/rwinkhart/go-boilerplate v0.3.1 h1:vkVRuptO2s1yPzzwpvtiiB0c/hPB6yr0p1mzZ2Myb9E=
|
||||
github.com/rwinkhart/go-boilerplate v0.3.1/go.mod h1:ES13A2r9fnCVfyezwMBgY/RgA4pOIudOUXz3Jk/ikes=
|
||||
github.com/rwinkhart/libmutton v0.5.0 h1:Du/o5Hyb26uE6lubqBISN/ZF9JZ5oObHVveQjAqrP0E=
|
||||
github.com/rwinkhart/libmutton v0.5.0/go.mod h1:peh8A1f3gnaF97m8J06U28JloKv04Kw8WtFCH+Pcq6s=
|
||||
github.com/rwinkhart/rcw v0.3.0 h1:OdgA++IGcDrZqN6FzyYnzbz04fL8Q1MHvlgPjhtgJM4=
|
||||
github.com/rwinkhart/rcw v0.3.0/go.mod h1:UUY7kSku6kV2X1RWPUcK0Re+rQ6RvngWI8DVZJ11cqk=
|
||||
github.com/rwinkhart/sys v0.41.0 h1:pHB6HphVC132UXYZ6yeOk62abqgl+pyl5ZZnsk4iUKg=
|
||||
github.com/rwinkhart/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/rwinkhart/go-boilerplate/other"
|
||||
"github.com/rwinkhart/libmutton/global"
|
||||
)
|
||||
|
||||
// decryptGPG decrypts a GPG-encrypted file and returns the contents as a slice of (trimmed) strings.
|
||||
func decryptGPG(targetLocation string) []string {
|
||||
cmd := exec.Command("gpg", "--pinentry-mode", "loopback", "-q", "-d", targetLocation)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
other.PrintError("Failed to decrypt \""+targetLocation+"\" - Ensure it is a valid GPG-encrypted file and that you entered your passphrase correctly", global.ErrorDecryption)
|
||||
}
|
||||
return strings.Split(string(output), "\n")
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/rwinkhart/go-boilerplate/back"
|
||||
"github.com/rwinkhart/go-boilerplate/front"
|
||||
"github.com/rwinkhart/go-boilerplate/other"
|
||||
"github.com/rwinkhart/libmutton/global"
|
||||
"github.com/rwinkhart/libmutton/synccommon"
|
||||
"github.com/rwinkhart/rcw/wrappers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// get info from user
|
||||
var rcwPassphrase []byte
|
||||
for {
|
||||
rcwPassphrase = front.InputSecret("RCW passphrase:")
|
||||
if !bytes.Equal(rcwPassphrase, front.InputSecret("Confirm RCW passphrase:")) {
|
||||
fmt.Println(back.AnsiError + "Passphrases do not match" + back.AnsiReset)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// convert the entries
|
||||
fmt.Print("\nRe-encrypting entries. Please wait; do not force close this process.\n\nGPG may prompt you for a passphrase for decryption.\n\n")
|
||||
var outputDir = global.EntryRoot + "-new"
|
||||
entries, folders, err := synccommon.WalkEntryDir()
|
||||
if err != nil {
|
||||
other.PrintError("Failed to walk entry directory: "+err.Error(), back.ErrorRead)
|
||||
}
|
||||
for _, folder := range folders {
|
||||
err := os.MkdirAll(outputDir+strings.ReplaceAll(folder, "/", global.PathSeparator), 0700)
|
||||
if err != nil {
|
||||
other.PrintError("Failed to create directory: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
}
|
||||
var decLines []string
|
||||
for _, entry := range entries {
|
||||
entry = strings.TrimSuffix(entry, ".gpg")
|
||||
decLines = decryptGPG(global.GetRealPath(entry))
|
||||
encBytes := wrappers.Encrypt([]byte(strings.Join(decLines, "\n")), rcwPassphrase, true, false)
|
||||
err := os.WriteFile(outputDir+strings.ReplaceAll(entry, "/", global.PathSeparator), encBytes, 0600)
|
||||
if err != nil {
|
||||
other.PrintError("Failed to write to file: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
}
|
||||
|
||||
// swap the new directory with the old one
|
||||
err = os.Rename(global.EntryRoot, global.EntryRoot+"-old")
|
||||
if err != nil {
|
||||
other.PrintError("Failed to rename old directory: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
err = os.Rename(outputDir, global.EntryRoot)
|
||||
if err != nil {
|
||||
other.PrintError("Failed to rename new directory: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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")
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
# This script generates portable release binaries for the following platforms:
|
||||
# - Linux (x86_64_v1)
|
||||
# - Linux (aarch64)
|
||||
# - Windows (x86_64_v1)
|
||||
# - Windows (aarch64)
|
||||
|
||||
version=0.1.X-0.2.X
|
||||
|
||||
mkdir -p ./1output
|
||||
GOOS=linux CGO_ENABLED=0 GOAMD64=v1 go build -o "./1output/upgradeRCW-$version-linux-x86_64_v1" -ldflags="-s -w" -trimpath .
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o "./1output/upgradeRCW-$version-linux-aarch64" -ldflags="-s -w" -trimpath .
|
||||
GOOS=windows CGO_ENABLED=0 GOAMD64=v1 go build -o "./1output/upgradeRCW-$version-windows-x86_64_v1.exe" -ldflags="-s -w" -trimpath .
|
||||
GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build -o "./1output/upgradeRCW-$version-windows-aarch64.exe" -ldflags="-s -w" -trimpath .
|
||||
@@ -0,0 +1,22 @@
|
||||
module upgradeRCW
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/rwinkhart/convertroman v0.2.0 // DECOY TO TRICK GO INTO USING TWO VERSIONS OF RCW
|
||||
github.com/rwinkhart/go-boilerplate v0.3.1
|
||||
github.com/rwinkhart/libmutton v0.5.0
|
||||
github.com/rwinkhart/rcw v0.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/rwinkhart/convertroman => github.com/rwinkhart/rcw v0.1.2
|
||||
|
||||
replace golang.org/x/sys => github.com/rwinkhart/sys v0.41.0
|
||||
|
||||
replace github.com/Microsoft/go-winio => github.com/rwinkhart/go-winio v0.1.1
|
||||
@@ -0,0 +1,14 @@
|
||||
github.com/rwinkhart/go-boilerplate v0.3.1 h1:vkVRuptO2s1yPzzwpvtiiB0c/hPB6yr0p1mzZ2Myb9E=
|
||||
github.com/rwinkhart/go-boilerplate v0.3.1/go.mod h1:ES13A2r9fnCVfyezwMBgY/RgA4pOIudOUXz3Jk/ikes=
|
||||
github.com/rwinkhart/libmutton v0.5.0 h1:Du/o5Hyb26uE6lubqBISN/ZF9JZ5oObHVveQjAqrP0E=
|
||||
github.com/rwinkhart/libmutton v0.5.0/go.mod h1:peh8A1f3gnaF97m8J06U28JloKv04Kw8WtFCH+Pcq6s=
|
||||
github.com/rwinkhart/rcw v0.1.2 h1:JFNRx5Uc0VKZMbur05VbZBhfUtOp2KBjZ6UDeaCRYFI=
|
||||
github.com/rwinkhart/rcw v0.1.2/go.mod h1:LQ2/YJFQlkW+JkgrLpZ51NQP/unIEzPTWsLOpOuKdKU=
|
||||
github.com/rwinkhart/rcw v0.3.0 h1:OdgA++IGcDrZqN6FzyYnzbz04fL8Q1MHvlgPjhtgJM4=
|
||||
github.com/rwinkhart/rcw v0.3.0/go.mod h1:UUY7kSku6kV2X1RWPUcK0Re+rQ6RvngWI8DVZJ11cqk=
|
||||
github.com/rwinkhart/sys v0.41.0 h1:pHB6HphVC132UXYZ6yeOk62abqgl+pyl5ZZnsk4iUKg=
|
||||
github.com/rwinkhart/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
oldwrap "github.com/rwinkhart/convertroman/wrappers"
|
||||
newwrap "github.com/rwinkhart/rcw/wrappers"
|
||||
|
||||
"github.com/rwinkhart/go-boilerplate/back"
|
||||
"github.com/rwinkhart/go-boilerplate/front"
|
||||
"github.com/rwinkhart/go-boilerplate/other"
|
||||
"github.com/rwinkhart/libmutton/global"
|
||||
"github.com/rwinkhart/libmutton/synccommon"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// get info from user
|
||||
var oldRCWPassphrase, newRCWPassphrase []byte
|
||||
for {
|
||||
oldRCWPassphrase = front.InputSecret("Old RCW passphrase:")
|
||||
if !bytes.Equal(oldRCWPassphrase, front.InputSecret("Confirm old RCW passphrase:")) {
|
||||
fmt.Println(back.AnsiError + "Passphrases do not match" + back.AnsiReset)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
for {
|
||||
newRCWPassphrase = front.InputSecret("New RCW passphrase:")
|
||||
if !bytes.Equal(newRCWPassphrase, front.InputSecret("Confirm new RCW passphrase:")) {
|
||||
fmt.Println(back.AnsiError + "Passphrases do not match" + back.AnsiReset)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// convert the entries
|
||||
fmt.Print("\nRe-encrypting entries. Please wait; do not force close this process.\n\n")
|
||||
var outputDir = global.EntryRoot + "-new"
|
||||
entries, folders, err := synccommon.WalkEntryDir()
|
||||
if err != nil {
|
||||
other.PrintError("Failed to walk entry directory: "+err.Error(), back.ErrorRead)
|
||||
}
|
||||
for _, folder := range folders {
|
||||
err := os.MkdirAll(outputDir+strings.ReplaceAll(folder, "/", global.PathSeparator), 0700)
|
||||
if err != nil {
|
||||
other.PrintError("Failed to create directory: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
}
|
||||
for _, entry := range entries {
|
||||
oldEncBytes, err := os.ReadFile(global.GetRealPath(entry))
|
||||
if err != nil {
|
||||
other.PrintError("Failed to read file: "+err.Error(), back.ErrorRead)
|
||||
}
|
||||
decBytes, err := oldwrap.Decrypt(oldEncBytes, oldRCWPassphrase)
|
||||
if err != nil {
|
||||
other.PrintError("Failed to decrypt file: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
newEncBytes := newwrap.Encrypt(decBytes, newRCWPassphrase, true, false)
|
||||
err = os.WriteFile(outputDir+strings.ReplaceAll(entry, "/", global.PathSeparator), newEncBytes, 0600)
|
||||
if err != nil {
|
||||
other.PrintError("Failed to write to file: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
}
|
||||
|
||||
// swap the new directory with the old one
|
||||
err = os.Rename(global.EntryRoot, global.EntryRoot+"-old")
|
||||
if err != nil {
|
||||
other.PrintError("Failed to rename old directory: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
err = os.Rename(outputDir, global.EntryRoot)
|
||||
if err != nil {
|
||||
other.PrintError("Failed to rename new directory: "+err.Error(), back.ErrorWrite)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
Require mutn
|
||||
Require gpg
|
||||
|
||||
Set Theme { "name": "GruvboxDarkMUTNCustom", "black": "#282828", "red": "#cc241d", "green": "#98971a", "yellow": "#d79921", "blue": "#458588", "magenta": "#b16286", "cyan": "#689d6a", "white": "#d5c4a1", "brightBlack": "#928374", "brightRed": "#fb4934", "brightGreen": "#b8bb26", "brightYellow": "#fabd2f", "brightBlue": "#83a598", "brightMagenta": "#d3869b", "brightCyan": "#8ec07c", "brightWhite": "#ebdbb2", "background": "#272727", "foreground": "#d5c4a1", "selection": "#665c54", "cursor": "#ebdbb2" }
|
||||
|
||||
Set Padding 5
|
||||
Set Framerate 30
|
||||
Set Height 675
|
||||
Set Width 1290
|
||||
Set Shell zsh
|
||||
|
||||
Output mutn-demo.gif
|
||||
|
||||
Sleep 1s
|
||||
Type "# start of loop | viewing my entry list :)"
|
||||
Sleep 800ms
|
||||
Backspace@25ms 42
|
||||
Type "mutn"
|
||||
Sleep 250ms
|
||||
Enter
|
||||
Sleep 2.25s
|
||||
Type "# syncing entries with the server :o"
|
||||
Sleep 500ms
|
||||
Backspace@25ms 36
|
||||
Type "mutn sync"
|
||||
Enter
|
||||
Sleep 1.25s
|
||||
Type "clear"
|
||||
Sleep 1.5s
|
||||
Enter
|
||||
Type "# reading my todo list :^)"
|
||||
Sleep 500ms
|
||||
Backspace@25ms 26
|
||||
Type "mutn /notes/personal/todo"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "example"
|
||||
Enter
|
||||
Sleep 2s
|
||||
Type "clear"
|
||||
Sleep 2.5s
|
||||
Enter
|
||||
Type "# changing my github password :p"
|
||||
Sleep 500ms
|
||||
Backspace@25ms 32
|
||||
Type "mutn /development/github gen -u"
|
||||
Enter
|
||||
Sleep 500ms
|
||||
Type "69"
|
||||
Sleep 500ms
|
||||
Enter
|
||||
Sleep 500ms
|
||||
Type "y"
|
||||
Sleep 500ms
|
||||
Enter
|
||||
Sleep 2s
|
||||
Enter
|
||||
Type "# copying the new password :]"
|
||||
Sleep 500ms
|
||||
Backspace@25ms 29
|
||||
Up
|
||||
Sleep 500ms
|
||||
Backspace 6
|
||||
Sleep 500ms
|
||||
Type "copy"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Enter
|
||||
Type "# darn, I need my totp token :/"
|
||||
Sleep 800ms
|
||||
Backspace@25ms 31
|
||||
Up
|
||||
Sleep 500ms
|
||||
Type " -t"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Enter
|
||||
Type "as long as the process runs, the token will be refreshed | let's paste it: "
|
||||
Sleep 250ms
|
||||
Paste
|
||||
Sleep 1s
|
||||
Backspace@25ms 81
|
||||
Ctrl+c
|
||||
Sleep 1s
|
||||
Type "clear"
|
||||
Sleep 1.5s
|
||||
Enter
|
||||
Type "# editing my todo list :|"
|
||||
Sleep 500ms
|
||||
Backspace@25ms 25
|
||||
Type "mutn /notes/personal/todo edit -n"
|
||||
Sleep 500ms
|
||||
Enter
|
||||
Escape
|
||||
Sleep 1s
|
||||
Type "jj"
|
||||
Sleep 500ms
|
||||
Type "2dd"
|
||||
Sleep 1s
|
||||
Type ":wq"
|
||||
Sleep 500ms
|
||||
Enter
|
||||
Sleep 2s
|
||||
Type "clear"
|
||||
Sleep 2.5s
|
||||
Enter
|
||||
Type "# checking up on my homework :'("
|
||||
Sleep 500ms
|
||||
Backspace@25ms 32
|
||||
Type "mutn /notes/school/homework"
|
||||
Enter
|
||||
Sleep 2s
|
||||
Type "clear"
|
||||
Sleep 2.5s
|
||||
Enter
|
||||
Type "# giving my school notes a better name ;3"
|
||||
Sleep 500ms
|
||||
Backspace@25ms 41
|
||||
Type "mutn /notes/school/world\ history/MUTN\ public\ debut edit -r"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "/notes/school/world history/the best day ever"
|
||||
Enter
|
||||
Sleep 1.5s
|
||||
Type "mutn"
|
||||
Sleep 250ms
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "# end of loop! (:"
|
||||
Sleep 3s
|
||||
@@ -0,0 +1,17 @@
|
||||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
lIYEZpmhGhYJKwYBBAHaRw8BAQdAtMOex5PpSNmm45m78oWwiyL7+HzpMXEfsJuo
|
||||
sVbQVkn+BwMCxhrqdwXRs/T/zvz/OO16ugFb9USURoP2Q9qxob5xHu/pitT2BKJ0
|
||||
AmqFgJ7d+XBhUYUgyghFs4ghnbJ+idc3M97gaUu3LTWVTN4z4WCDP7RFbGlibXV0
|
||||
dG9uLTE3MjEzNDQyODIgKGdwZy1saWJtdXR0b24pIDxnaXRodWIuY29tL3J3aW5r
|
||||
aGFydC9saWJtdXR0b24+iJMEExYKADsWIQRKkDJcKDRJ8GqC6UGj5HQatDKA/QUC
|
||||
ZpmhGgIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRCj5HQatDKA/YPC
|
||||
AP0a0uWYfPFt7ql6uqTF8ybH1CWlrcCdKH32Yt0TbzMFuQEAoILlIqeSznySva/v
|
||||
V/vEoKjghrlOxYS1XooR2VmLXg6ciwRmmaEaEgorBgEEAZdVAQUBAQdAjCCCC4Vq
|
||||
t6VILdm8aL8A1Lxld3QznrjastwbS5xGyw0DAQgH/gcDAiY9vEXhi4Uq/x3t0G8S
|
||||
iIbkoNaAe9r8jI/ju2FaqdDSo/kPGm/BdsifQqdfq8OuWIGYTsCItSk3nkfc6lYm
|
||||
zdIVFahsMVW9ATz8Jd8Gnp2IeAQYFgoAIBYhBEqQMlwoNEnwaoLpQaPkdBq0MoD9
|
||||
BQJmmaEaAhsMAAoJEKPkdBq0MoD9ulcBAN1jS5Nby4s1wGCGBfMFj4AxTBSvoWB9
|
||||
CUponolEwl5MAP9VS6scwf/sOkpohn3d9PBnCioGxR4+N4JNzrBQFE7ECQ==
|
||||
=bGiv
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
„^ůRb dJ@ WwÔU=KŃ.ĆŃ‹ÝÖ>Ź":–0bÉţÔ•"0'‘fŰv�—šŔř=~ť#đÓźô•©ťáćvPšŘ
|
||||
ŰE˘z Ö{6'1ˇţpÔŻ Đ4®ŘÄ4SçŇ›ÖZú`dÎ�)ć$Šj|ÂŘŤg�_|ŤAłěâă7ż=‘§Ě/8č'žÜ†ěÇﱫ$$ă�¶ #»šTd;$]Ţ*?űŹÍż[ţTüĹ_ćW™á˛ĄŃ‰N:(Z«€łÝ°ÜµNąZü$Z`ë_扸x&4ť+ѵŮh˙x|üKF}źaŤ9NŤ+saĆkşKŕúÝéŇnw>EŚËë0“+
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
„^ůRb dJ@Näë'”Éäňń¦GÝ|ŤÉźj˙ĄÜh@#
Bő–<0ď5Á”˘GË}�=ÖúG2
|
||||
]ź?wµ˝ýÔs„ß¶D†qŦ?!hÄ�„&ľm,nÔM Í:V%`Ľ©ß(Woł05‘z'§NgdNÔą´_‹Ó2DXĎF’AÉ„ZUĐĆŐÖzÍ.>\Ä�_yJ‚Jßz“Z
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
„^ùRb dJ@Vž„»)›fåäð:€ü¨Ýq7€¦C5�#¿WI“ß½0Ì—"æl6í>êr�9†I°ùæz*€ŒãMøUý̵V"dÔÚ[à“¹à«òÑÿqÔÀ0 $¾ó/ë^®þt½Ÿu]‹õ
|
||||
&ZaBqH:l}sR–b.ov?È!¢Ø¬C¢ü¥ÁƦdzŒÒ=Á§Ûá!v”Et²;PDÚxO•8ÝSï$%þíï|E^Ã/Á›Ü!Œ§¾¬ˆý6° ÙÌÑHÝä$JúCNÞ¹fEÈâí÷ŠV·U¼…�JÖtL¡{�ã“6‡è²—V*s]À×QTi]®Ö´cJON¹[¨O:NÍÙÒ:Ü#B´:™�A:¼úÌ.*}–R̽w©‘ 7H6×zí¸¼ÆÁýD7°Ú¶ñ¢‘u(9í‰Ð°ßaŠóì?
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
„^ùRb dJ@Ô†W¢Ï›7¹ù¾áíN¨ËGïÑÄë²Ûö"õ×
0 Þâ,Ö“ÝœoØr͘è˜!ÚrÿL³ÁITdEL*ˆK7BèÒ7ÿh4¢ó�ÔW >Û:A+ä?Aý~qÓ�+í»©#‚ F¸Áú!¦z`ánOò¦òG“XÕ‘G]Ú¦õ
|
||||
@£ínEz˜%’ØþóGRY’™AÖLÏ%y›åÕ-c
|
||||
@@ -0,0 +1,2 @@
|
||||
„^ùRb dJ@Ê‹Èaâ¥Þ!á¾4Ùäó†uÄä‘Ìß™ÎH¯ýp>t0Ų½—'ef¾I�päS•p¤!©þh¦ùN%šÃ).Û¯}µdÀªÌ¾c:êo?Wœ�eÔX £)«zÒKåf‰N(üt#df¡Á�™‡#±˜€ÙÆÜ�Eð¤Ðˆè_5I~�Nñ&%�c|AÃ
|
||||
¬Ã¾/tSUOo`Á¼>G‘«�6&k¥É¢-ö
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
„^щRb dJ@�WЎе кЇzЖ¬pУv•YЉЮџрсD®ЖЊдiоІ
v0 dОВ]Њ)тт,jњЄЁвиЯ‹LVQ- ѓП:З¤ЯР¬"}‘Ф=Ѓрv'6Ър§Ф_ §AГокУҐЙѓp,шAvЂhї0‡„gW†JqDїЇ]и„fPдЂЮЯЦT-Щ¬"ґ«’с41ї6ъW1уz^MЯвоў[соФСЎb€OПаA „ 7–жH5
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
||||
„^ùRb dJ@J�oä�pëÁ�È<õÔMßÑv§�‰µ¦ßJ¿'ª˜Z0åÞ5äö0ƒ UsÞA–Ø0&îS:ojÄb¡R!²�´ŸÏÀÙ`]Œ(áLØŽ÷TËäÔV Ó®ÌF¶ã ÚŒüt¦TOr‰Ô¦ìVàïu€«?çC49·áåö²Æ >…ˆ�1µ%GŠ|�Ã=¹‘_x^*OÎRüœ²¶?FA®?ùRà
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
„^שRb dJ@Bנֹמ!z,tgeֳ�›¢dW—}5J�oJ� ²Tָ0¼
3L* �˜<תuw¼#£ˆd,M:zָg›µ)˜w��»›�9�5�®ל{װ“ ײtBד·#½V–Dמה(C*u�ֵZןQ°�TC§,�g—�„ֶ�ם]ם—;“£ֽ:ָd×�זVְ£ur£נ´ר2ט”7!�&oםם¶מboָ[& ₪�R¸,־rgq<ֶ"V<הװ«�j„ֲ�²M�‚בע¦Mן{QO?�h¿9װjz°P^
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
@@ -0,0 +1,14 @@
|
||||
# MUTN VHS Tape Creation
|
||||
1. Ensure `mutn` is in your path
|
||||
2. If `zsh` is not installed, either install it or change the shell in `cassette.tape` (for some reason the "Up" function only works in `zsh` for me)
|
||||
3. Import example.asc (if not done already) with password `example`
|
||||
4. Set up a client-server pair using the `libmutton` entries folder provided in this repo
|
||||
5. Run the following "reset" command from within this folder (it's okay if it throws errors; this is expected if running before using `vhs`):
|
||||
- `rm -rf ~/.local/share/libmutton && cp -r ./libmutton ~/.local/share/libmutton && mutn sync && mutn /notes/school/world\ history/the\ best\ day\ ever shear; rm ~/.local/share/libmutton/notes/school/ccnp\ networking/{chapter\ 4,chapter\ 5} && gpgconf --kill gpg-agent`
|
||||
6. Run `vhs ./cassette.tape`
|
||||
7. View mutn-demo.gif! Use the above "reset" command between each recording.
|
||||
|
||||
# Read for Production
|
||||
Before posting for production, I convert the GIF to an animated WEBP (to save space) using libwebp's `gif2webp`:
|
||||
|
||||
`gif2webp -min_size -q 100 -m 6 ./mutn-demo.gif -o ./mutn-demo.webp`
|
||||
@@ -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
|
||||
@@ -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.3/extensions/sshyp-mfa/sshyp-mfa.py
|
||||
ini = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.1.3/extensions/sshyp-mfa/sshyp-mfa.ini
|
||||
@@ -0,0 +1,11 @@
|
||||
[sshyp-mfa]
|
||||
desc = reads mfa/2fa secrets from sshyp entries to generate and copy totp/Steam otp keys to the clipboard
|
||||
usage = sshyp /<entry name> copy -m<br><br>to add mfa data to a sshyp entry, insert it into the SECOND notes line for a given entry using the following format:<br><br> otpauth://<OTP METHOD>/<ACCOUNT NAME, NOT USED>?secret=<SECRET>&issuer=<ISSUER, NOT USED>&algorithm=<ALGORITHM>&digits=<DIGITS>&period=<REFRESH PERIOD><br><br>what to put in each of the above spaces:<br><br> <OTP METHOD> is almost always 'totp', but in the case of Steam, it needs to be set to 'steam'.<br> <ACCOUNT NAME, NOT USED> is a part of the Authenticator backup format, but it is not used in sshyp-mfa. Set to anything.<br> <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).<br> <ISSUER, NOT USED> is a part of the Authenticator backup format, but it is not used in sshyp-mfa. Set to anything.<br> <ALGORITHM> refers to the algorithm used to generate your MFA key based on your secret. This is almost always 'sha1'.<br> <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'.<br> <REFRESH PERIOD> refers to the interval at which a new MFA key needs to be generated. This is almost always '30', for 30 seconds.<br><br>examples:<br><br> GitHub (standard 6-digit totp):<br> otpauth://totp/MyNameIsBob?secret=YUGBSG65SG9SDBSDF56SBFVSC86SBVD6&issuer=GitHub&algorithm=sha1&digits=6&period=30<br><br> Steam (5-character totp):<br> otpauth://steam/SteamUser?secret=VGVG34GH2GJHVCK7HGVS7&issuer=Steam&algorithm=sha1&digits=5&period=30<br><br>for more information, visit:<br><br> https://github.com/rwinkhart/sshyp-labs/wiki/sshyp-mfa
|
||||
exe = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.1.3/extensions/sshyp-mfa/sshyp-mfa.py
|
||||
ini = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.1.3/extensions/sshyp-mfa/sshyp-mfa.ini
|
||||
|
||||
[export-to-libmutton]
|
||||
desc = exports sshyp entries to ~/.local/share/sshyp.export for use with libmutton v0.3.X
|
||||
usage = sshyp export<br><br>this extension does not export to a format compatible with libmutton v0.4.X or later!!<br><br>for use with libmutton v0.4.X or later, run the output of this extension through the gpgToRCW binaries provided at https://github.com/rwinkhart/sshyp-labs/releases/tag/v2.0.0
|
||||
exe = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/extensions/export-to-libmutton/export-to-libmutton.py
|
||||
ini = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/extensions/export-to-libmutton/export-to-libmutton.ini
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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 +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/>.
|
||||
Reference in New Issue
Block a user