├── .gitignore ├── .gitmodules ├── license.md ├── makefile ├── readme.md └── src ├── dragonfail_error.h ├── handy.h ├── main.c ├── sshram.c └── sshram.h /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sub/argoat"] 2 | path = sub/argoat 3 | url = https://github.com/nullgemm/argoat.git 4 | [submodule "sub/chrono"] 5 | path = sub/chrono 6 | url = https://github.com/nullgemm/chrono.git 7 | [submodule "sub/dragonfail"] 8 | path = sub/dragonfail 9 | url = https://github.com/nullgemm/dragonfail.git 10 | [submodule "sub/cifra"] 11 | path = sub/cifra 12 | url = https://github.com/ctz/cifra.git 13 | [submodule "sub/phc-winner-argon2"] 14 | path = sub/phc-winner-argon2 15 | url = https://github.com/P-H-C/phc-winner-argon2.git 16 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | NAME = sshram 2 | CC = gcc 3 | FLAGS = -std=c99 -pedantic -g 4 | FLAGS+= -Wall -Wno-unused-parameter -Wextra -Werror=vla -Werror 5 | VALGRIND = --show-leak-kinds=all --track-origins=yes --leak-check=full 6 | CACHEGRIND = --tool=cachegrind --branch-sim=yes 7 | 8 | #CMD = ./$(NAME) -e ../ssh/id_ed25519.pub ../ssh/id_ed25519.pub.chachapoly 9 | CMD = ./$(NAME) ../ssh/id_ed25519 10 | 11 | BIND = bin 12 | OBJD = obj 13 | SRCD = src 14 | SUBD = sub 15 | TESTD = tests 16 | 17 | INCL = -I$(SRCD) 18 | INCL+= -I$(SUBD)/ctypes 19 | INCL+= -I$(SUBD)/argoat/src 20 | INCL+= -I$(SUBD)/chrono/src 21 | INCL+= -I$(SUBD)/cifra/src 22 | INCL+= -I$(SUBD)/dragonfail/src 23 | INCL+= -I$(SUBD)/testoasterror/src 24 | INCL+= -I$(SUBD)/phc-winner-argon2/include 25 | 26 | FINAL = $(SRCD)/main.c 27 | 28 | TESTS = $(TESTD)/main.c 29 | TESTS+= $(SUBD)/testoasterror/src/testoasterror.c 30 | 31 | SRCS = $(SRCD)/sshram.c 32 | SRCS+= $(SUBD)/argoat/src/argoat.c 33 | SRCS+= $(SUBD)/chrono/src/chrono_posix.c 34 | SRCS+= $(SUBD)/cifra/src/chacha20poly1305.c 35 | SRCS+= $(SUBD)/cifra/src/chacha20.c 36 | SRCS+= $(SUBD)/cifra/src/poly1305.c 37 | SRCS+= $(SUBD)/cifra/src/blockwise.c 38 | SRCS+= $(SUBD)/dragonfail/src/dragonfail.c 39 | SRCS+= $(SUBD)/phc-winner-argon2/libargon2.a 40 | 41 | FINAL_OBJS:= $(patsubst %.c,$(OBJD)/%.o,$(FINAL)) 42 | SRCS_OBJS := $(patsubst %.c,$(OBJD)/%.o,$(SRCS)) 43 | TESTS_OBJS:= $(patsubst %.c,$(OBJD)/%.o,$(TESTS)) 44 | 45 | LINK = -lpthread 46 | 47 | # aliases 48 | .PHONY: final 49 | final: $(BIND)/$(NAME) 50 | tests: $(BIND)/tests 51 | 52 | # generic compiling command 53 | $(SUBD)/phc-winner-argon2/libargon2.a: 54 | @echo "building $@" 55 | @cd $(SUBD)/phc-winner-argon2 && make 56 | 57 | $(OBJD)/%.o: %.c 58 | @echo "building object $@" 59 | @mkdir -p $(@D) 60 | @$(CC) $(INCL) $(FLAGS) -c -o $@ $< 61 | 62 | # final executable 63 | $(BIND)/$(NAME): $(SRCS_OBJS) $(FINAL_OBJS) 64 | @echo "compiling executable $@" 65 | @mkdir -p $(@D) 66 | @$(CC) -o $@ $^ $(LINK) 67 | 68 | run: 69 | @cd $(BIND) && $(CMD) 70 | 71 | # tests executable 72 | $(BIND)/tests: $(SRCS_OBJS) $(TESTS_OBJS) 73 | @echo "compiling tests" 74 | @mkdir -p $(@D) 75 | @$(CC) -o $@ $^ $(LINK) 76 | 77 | check: 78 | @cd $(BIND) && ./tests 79 | 80 | # tools 81 | leak: leakgrind 82 | leakgrind: $(BIND)/$(NAME) 83 | @rm -f valgrind.log 84 | @cd $(BIND) && valgrind $(VALGRIND) 2> ../valgrind.log $(CMD) 85 | @less valgrind.log 86 | 87 | leakcheck: leakgrindcheck 88 | leakgrindcheck: $(BIND)/tests 89 | @rm -f valgrind.log 90 | @cd $(BIND) && valgrind $(VALGRIND) 2> ../valgrind.log $(CMD) 91 | @less valgrind.log 92 | 93 | cache: cachegrind 94 | cachegrind: $(BIND)/$(NAME) 95 | @rm -f cachegrind.log 96 | @cd $(BIND) && valgrind $(CACHEGRIND) 2> ../cachegrind.log $(CMD) 97 | @less cachegrind.log 98 | 99 | cachecheck: cachegrindcheck 100 | cachegrindcheck: $(BIND)/tests 101 | @rm -f cachegrind.log 102 | @cd $(BIND) && valgrind $(CACHEGRIND) 2> ../cachegrind.log $(CMD) 103 | @less cachegrind.log 104 | 105 | clean: 106 | @echo "cleaning" 107 | @rm -rf $(BIND) $(OBJD) valgrind.log cachegrind.log 108 | @cd $(SUBD)/phc-winner-argon2 && make clean 109 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SSHram 2 | SSHram is a simple tool for SSH private key management. 3 | 4 | # What you need 5 | - A storage medium (your computer's drive, a USB key...) 6 | - Your SSH private key, secured by ChaCha20-Poly1305 7 | - SSHram's binary 8 | 9 | # How it works 10 | - You plug your USB key in a trusted computer and run the SSHram binary 11 | - Your SSH private key is decoded and stored in a non-swappable memory area 12 | - A Unix named pipe is created inside the `~/.ssh/` folder in place of the key 13 | - If the access is confirmed, SSHram sends the private key over the named pipe 14 | 15 | # Why it works 16 | - SSHram stores the decoded SSH private key in locked memory. 17 | This means the operating system cannot swap it out of RAM, 18 | and guarantees the key will never be written on drives. 19 | - Named pipes are virtual files serving as handles to Unix pipes. 20 | Because they are not actual files, everything is handled in memory, 21 | and the private SSH key is never written to the drive the pipe resides on. 22 | - Because Linux is a good operating system, most applications are compatible 23 | with named pipes by default, and these can be used in place of real files. 24 | 25 | # Purpose 26 | The goal of SSHram is to assist the user in hiding the tracks of a private key. 27 | Some people want to use the same SSH private key on different trusted devices, 28 | but are worried their key could be reconstructed afterwards by some evil user 29 | (if the device is lost, stolen or stealthily accessed with bad intentions). 30 | 31 | SSHram is meant to mitigate the most obvious attacks known to be feasible in 32 | this scenario (data theft, sector `grep`ing, RAM cold-reading...) or if the 33 | device holding the private key itself gets compromised. 34 | 35 | # Limitations 36 | The goal of SSHram is *not* to provide any kind of extra security to the user 37 | if the target computer SSHram is executed on is not considered trustworthy. 38 | 39 | SSH cannot be secured if the computer it runs on is not secure itself, 40 | and SSHram (or any other SSH helper program) is useless in this case. 41 | 42 | # Getting started 43 | ## Recommended key generation practices 44 | - Generate your private key offline, in a clean ramdisk, on trusted hardware 45 | (You could use a trusted Linux installation image booted from another USB key). 46 | - Generate your key using trusted cryptography. Avoid algorithms designed 47 | with the help of some government or including shady constants. 48 | - Use multiple rounds of key derivation. It helps slowing attacks down without 49 | turning your data into an advertisement as much as overkill crypto stength does. 50 | ``` 51 | ssh-keygen -t ed25519 -a 100 52 | ``` 53 | 54 | ## Encoding a private key 55 | Once your SSH key pair is generated, your must encode the SSH private key. 56 | For this, run `sshram` with the `--encode` argument and follow the instructions: 57 | ``` 58 | sshram -e id_ed25519 id_ed25519.chachapoly 59 | ``` 60 | 61 | After the private key was encoded, overwrite the plain-text version and test: 62 | ``` 63 | mv id_ed25519.chachapoly id_ed25519 64 | sshram id_ed25519 65 | ``` 66 | 67 | You can now try to connect to a server using this keypair; it will require 68 | multiple key transmissions though, as described at SSHram's startup. 69 | 70 | ## Arguments 71 | SSHram accepts other arguments than `--encode`, get the full list with `--help`: 72 | ``` 73 | sshram -h 74 | ``` 75 | 76 | ## SSH agent 77 | It is possible to use SSHram with an SSH agent without extra setup, 78 | thus completing private key management with passphrase management: 79 | ``` 80 | eval $(ssh-agent) 81 | ssh-add ~/.ssh/id_ed25519 82 | ``` 83 | -------------------------------------------------------------------------------- /src/dragonfail_error.h: -------------------------------------------------------------------------------- 1 | #ifndef H_DRAGONFAIL_ERROR 2 | #define H_DRAGONFAIL_ERROR 3 | 4 | enum dgn_error 5 | { 6 | DGN_OK, // do not remove 7 | 8 | SSHRAM_ERR_ARG_NAME, 9 | SSHRAM_ERR_ARG_DECODED, 10 | SSHRAM_ERR_ARG_DECODED_OPEN, 11 | SSHRAM_ERR_ARG_ENCODED, 12 | SSHRAM_ERR_ARG_ENCODED_OPEN, 13 | 14 | SSHRAM_ERR_RNG, 15 | SSHRAM_ERR_ARGON2, 16 | 17 | SSHRAM_ERR_MALLOC, 18 | SSHRAM_ERR_MLOCK, 19 | 20 | SSHRAM_ERR_ENV, 21 | SSHRAM_ERR_FGETS, 22 | SSHRAM_ERR_FSEEK, 23 | SSHRAM_ERR_FTELL, 24 | SSHRAM_ERR_FREAD, 25 | SSHRAM_ERR_FWRITE, 26 | SSHRAM_ERR_TERMIOS, 27 | 28 | SSHRAM_ERR_ENC_PASS_LEN, 29 | SSHRAM_ERR_ENC_PASS_MATCH, 30 | 31 | SSHRAM_ERR_DEC_CHACHAPOLY, 32 | SSHRAM_ERR_DEC_PATH_LEN, 33 | SSHRAM_ERR_DEC_PASUNEPIPE, 34 | SSHRAM_ERR_DEC_MKFIFO, 35 | SSHRAM_ERR_DEC_PIPE_FOPEN, 36 | SSHRAM_ERR_DEC_PIPE_FWRITE, 37 | SSHRAM_ERR_DEC_PIPE_FCLOSE, 38 | SSHRAM_ERR_DEC_PIPE_UNLINK, 39 | SSHRAM_ERR_DEC_INOTIFY_INIT, 40 | SSHRAM_ERR_DEC_INOTIFY_ADD_WATCH, 41 | SSHRAM_ERR_DEC_INOTIFY_READ, 42 | SSHRAM_ERR_DEC_INOTIFY_READ_INT, 43 | SSHRAM_ERR_DEC_SIGACTION, 44 | 45 | DGN_SIZE, // do not remove 46 | }; 47 | 48 | //#define DRAGONFAIL_SKIP 49 | #define DRAGONFAIL_BASIC_LOG 50 | #define DRAGONFAIL_THROW_BASIC_LOG 51 | #define DRAGONFAIL_THROW_DEBUG_LOG 52 | //#define DRAGONFAIL_ABORT 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/handy.h: -------------------------------------------------------------------------------- 1 | #ifndef H_SSHRAM_HANDY 2 | #define H_SSHRAM_HANDY 3 | 4 | #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) 5 | #define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) 6 | 7 | #include "ext/handy.h" 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | 3 | #include "argoat.h" 4 | #include "dragonfail.h" 5 | #include "sshram.h" 6 | 7 | #include 8 | #include 9 | 10 | #define ARG_COUNT 11 11 | 12 | // arguments handling 13 | void arg_unflagged(void* data, char** pars, const int pars_count) 14 | { 15 | struct config* config = (struct config*) data; 16 | 17 | if (pars_count < 1) 18 | { 19 | config->action = SSHRAM_ACTION_EXIT; 20 | return; 21 | } 22 | 23 | if (pars_count > 1) 24 | { 25 | dgn_throw(SSHRAM_ERR_ARG_ENCODED); 26 | return; 27 | } 28 | 29 | if (config->key_name == NULL) 30 | { 31 | config->key_name = basename(pars[0]); 32 | } 33 | 34 | if (config->action == SSHRAM_ACTION_ENCODE) 35 | { 36 | config->file_encoded = fopen(pars[0], "w+"); 37 | } 38 | else 39 | { 40 | config->file_encoded = fopen(pars[0], "r"); 41 | } 42 | 43 | if (config->file_encoded == NULL) 44 | { 45 | dgn_throw(SSHRAM_ERR_ARG_ENCODED_OPEN); 46 | return; 47 | } 48 | } 49 | 50 | void arg_help(void* data, char** pars, const int pars_count) 51 | { 52 | printf( 53 | "usage:\n" 54 | " sshram [arguments] [encoded file]\n" 55 | "\n" 56 | "arguments:\n" 57 | " -e [decoded file]\n" 58 | " --encode [decoded file]\n" 59 | " specify a plaintext SSH private key [decoded file] to encode in [encoded file]\n" 60 | "\n" 61 | " -h\n" 62 | " --help\n" 63 | " print this help message\n" 64 | "\n" 65 | " -k\n" 66 | " --keep\n" 67 | " do not remove the pipe after execution\n" 68 | " (progams using SSH will freeze until EOF is sent!)\n" 69 | "\n" 70 | " -n [pipe name]\n" 71 | " --name [pipe name]\n" 72 | " override the pipe name (the file name of [encoded file] is used by default)\n" 73 | "\n" 74 | " -v\n" 75 | " --verbose\n" 76 | " print debugging information, including plaintext private key and password hash\n" 77 | ); 78 | } 79 | 80 | void arg_encode(void* data, char** pars, const int pars_count) 81 | { 82 | if (pars_count != 1) 83 | { 84 | dgn_throw(SSHRAM_ERR_ARG_DECODED); 85 | return; 86 | } 87 | 88 | struct config* config = (struct config*) data; 89 | 90 | config->file_decoded = fopen(pars[0], "r"); 91 | 92 | if (config->file_decoded == NULL) 93 | { 94 | dgn_throw(SSHRAM_ERR_ARG_DECODED_OPEN); 95 | return; 96 | } 97 | 98 | config->action = SSHRAM_ACTION_ENCODE; 99 | } 100 | 101 | void arg_keep(void* data, char** pars, const int pars_count) 102 | { 103 | struct config* config = (struct config*) data; 104 | 105 | config->keep_pipe = true; 106 | } 107 | 108 | void arg_name(void* data, char** pars, const int pars_count) 109 | { 110 | if (pars_count != 1) 111 | { 112 | dgn_throw(SSHRAM_ERR_ARG_NAME); 113 | return; 114 | } 115 | 116 | struct config* config = (struct config*) data; 117 | 118 | config->key_name = pars[0]; 119 | } 120 | 121 | void arg_verbose(void* data, char** pars, const int pars_count) 122 | { 123 | struct config* config = (struct config*) data; 124 | 125 | config->verbose = true; 126 | } 127 | 128 | // errors initialization 129 | void log_init(char** log) 130 | { 131 | log[DGN_OK] = 132 | "out-of-bounds log message"; 133 | log[SSHRAM_ERR_ARG_NAME] = 134 | "couldn't set the pipe name (please give exactly one)"; 135 | log[SSHRAM_ERR_ARG_DECODED] = 136 | "couldn't get a decoded file name (please give exactly one)"; 137 | log[SSHRAM_ERR_ARG_DECODED_OPEN] = 138 | "couldn't open a decoded file"; 139 | log[SSHRAM_ERR_ARG_ENCODED] = 140 | "couldn't get an encoded file name (please give exactly one)"; 141 | log[SSHRAM_ERR_ARG_ENCODED_OPEN] = 142 | "couldn't open an encoded file"; 143 | 144 | log[SSHRAM_ERR_RNG] = 145 | "End-Of-File was received as input"; 146 | log[SSHRAM_ERR_ARGON2] = 147 | "couldn't hash password (Argon2 returned an error)"; 148 | 149 | log[SSHRAM_ERR_MALLOC] = 150 | "couldn't allocate memmory"; 151 | log[SSHRAM_ERR_MLOCK] = 152 | "couldn't lock memory"; 153 | 154 | log[SSHRAM_ERR_ENV] = 155 | "couldn't get environment"; 156 | log[SSHRAM_ERR_FGETS] = 157 | "couldn't get user input"; 158 | log[SSHRAM_ERR_FSEEK] = 159 | "couldn't move file cursor"; 160 | log[SSHRAM_ERR_FTELL] = 161 | "couldn't get file cursor position or private key too short"; 162 | log[SSHRAM_ERR_FREAD] = 163 | "couldn't read file"; 164 | log[SSHRAM_ERR_FWRITE] = 165 | "couldn't write file"; 166 | log[SSHRAM_ERR_TERMIOS] = 167 | "termios error"; 168 | 169 | log[SSHRAM_ERR_ENC_PASS_LEN] = 170 | "password is not long enough (please use 16 bytes or more)"; 171 | log[SSHRAM_ERR_ENC_PASS_MATCH] = 172 | "passwords did not match"; 173 | 174 | log[SSHRAM_ERR_DEC_CHACHAPOLY] = 175 | "couldn't decode file"; 176 | log[SSHRAM_ERR_DEC_PATH_LEN] = 177 | "constructed file path did not have the expected length"; 178 | log[SSHRAM_ERR_DEC_PASUNEPIPE] = 179 | "the pipe path points to a file that is not a pipe"; 180 | log[SSHRAM_ERR_DEC_MKFIFO] = 181 | "couldn't create a new pipe"; 182 | log[SSHRAM_ERR_DEC_PIPE_FOPEN] = 183 | "couldn't open the pipe"; 184 | log[SSHRAM_ERR_DEC_PIPE_FWRITE] = 185 | "couldn't write to the pipe"; 186 | log[SSHRAM_ERR_DEC_PIPE_FCLOSE] = 187 | "couldn't close the pipe"; 188 | log[SSHRAM_ERR_DEC_PIPE_UNLINK] = 189 | "couldn't remove the pipe"; 190 | log[SSHRAM_ERR_DEC_INOTIFY_INIT] = 191 | "couldn't initialize inotify"; 192 | log[SSHRAM_ERR_DEC_INOTIFY_ADD_WATCH] = 193 | "couldn't add an inotify watch"; 194 | log[SSHRAM_ERR_DEC_INOTIFY_READ] = 195 | "couldn't read inotify events"; 196 | log[SSHRAM_ERR_DEC_INOTIFY_READ_INT] = 197 | "received SIGINT during inotify read"; 198 | log[SSHRAM_ERR_DEC_SIGACTION] = 199 | "couldn't set SIGINT handler"; 200 | } 201 | 202 | // sshram startup 203 | int main(int argc, char** argv) 204 | { 205 | // init sshram config 206 | struct config config = 207 | { 208 | .action = SSHRAM_ACTION_DECODE, 209 | .file_encoded = NULL, 210 | .file_decoded = NULL, 211 | .key_name = NULL, 212 | .keep_pipe = false, 213 | .verbose = false, 214 | }; 215 | 216 | // init error handling 217 | log_init(dgn_init()); 218 | 219 | // handle args 220 | char* unflagged; 221 | 222 | struct argoat_sprig sprigs[ARG_COUNT] = 223 | { 224 | {NULL, 1, &config, arg_unflagged}, 225 | {"encode", 1, &config, arg_encode}, 226 | {"e", 1, &config, arg_encode}, 227 | {"help", 0, NULL, arg_help}, 228 | {"h", 0, NULL, arg_help}, 229 | {"keep", 0, &config, arg_keep}, 230 | {"k", 0, &config, arg_keep}, 231 | {"name", 1, &config, arg_name}, 232 | {"n", 1, &config, arg_name}, 233 | {"verbose",0, &config, arg_verbose}, 234 | {"v", 0, &config, arg_verbose}, 235 | }; 236 | 237 | struct argoat args = 238 | { 239 | sprigs, 240 | ARG_COUNT, 241 | &unflagged, 242 | 0, 243 | 1, 244 | }; 245 | 246 | argoat_graze(&args, argc, argv); 247 | 248 | if (dgn_catch()) 249 | { 250 | return 1; 251 | } 252 | 253 | // run core program 254 | switch (config.action) 255 | { 256 | case SSHRAM_ACTION_ENCODE: 257 | { 258 | sshram_encode(&config); 259 | fclose(config.file_decoded); 260 | fclose(config.file_encoded); 261 | break; 262 | } 263 | case SSHRAM_ACTION_DECODE: 264 | { 265 | // avoid printing '^C' on SIGINT if possible 266 | struct termios ctx_a; 267 | struct termios ctx_b; 268 | int err_termios = tcgetattr(fileno(stdin), &ctx_a); 269 | 270 | if (err_termios != 0) 271 | { 272 | dgn_throw(SSHRAM_ERR_TERMIOS); 273 | break; 274 | } 275 | 276 | ctx_b = ctx_a; 277 | ctx_b.c_lflag &= ~ECHO; 278 | tcsetattr(fileno(stdout), TCSAFLUSH, &ctx_b); 279 | 280 | sshram_decode(&config); 281 | 282 | tcsetattr(fileno(stdout), TCSAFLUSH, &ctx_a); 283 | fclose(config.file_encoded); 284 | break; 285 | } 286 | case SSHRAM_ACTION_EXIT: 287 | default: 288 | { 289 | break; 290 | } 291 | } 292 | 293 | if (dgn_catch()) 294 | { 295 | return 1; 296 | } 297 | 298 | return 0; 299 | } 300 | -------------------------------------------------------------------------------- /src/sshram.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | 3 | #include "argon2.h" 4 | #include "chacha20poly1305.h" 5 | #include "chrono.h" 6 | #include "dragonfail.h" 7 | #include "handy.h" 8 | #include "sshram.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | static volatile sig_atomic_t decode_run = 1; 25 | 26 | static void sigint_handler(int sig) 27 | { 28 | decode_run = 0; 29 | } 30 | 31 | void sshram_rng(uint8_t* out, size_t len) 32 | { 33 | int fd = open("/dev/random", O_RDONLY); 34 | 35 | if (fd == -1) 36 | { 37 | dgn_throw(SSHRAM_ERR_RNG); 38 | return; 39 | } 40 | 41 | ssize_t ok = read(fd, out, len); 42 | 43 | if (ok == -1) 44 | { 45 | dgn_throw(SSHRAM_ERR_RNG); 46 | return; 47 | } 48 | 49 | ok = close(fd); 50 | 51 | if (ok == -1) 52 | { 53 | dgn_throw(SSHRAM_ERR_RNG); 54 | return; 55 | } 56 | } 57 | 58 | char* getpassword(char* s, int size, FILE* stream) 59 | { 60 | struct termios ctx_a; 61 | struct termios ctx_b; 62 | char* err_pass; 63 | int ok; 64 | 65 | ok = tcgetattr(fileno(stream), &ctx_a); 66 | 67 | if (ok != 0) 68 | { 69 | return NULL; 70 | } 71 | 72 | ctx_b = ctx_a; 73 | ctx_b.c_lflag &= ~ECHO; 74 | 75 | ok = tcsetattr(fileno(stream), TCSAFLUSH, &ctx_b); 76 | 77 | if (ok != 0) 78 | { 79 | return NULL; 80 | } 81 | 82 | err_pass = fgets(s, size, stream); 83 | 84 | if (err_pass == NULL) 85 | { 86 | return NULL; 87 | } 88 | 89 | ok = tcsetattr(fileno(stream), TCSAFLUSH, &ctx_a); 90 | 91 | if (ok != 0) 92 | { 93 | return NULL; 94 | } 95 | 96 | printf("\n"); 97 | 98 | return err_pass; 99 | } 100 | 101 | void sshram_encode(struct config* config) 102 | { 103 | // init timers 104 | uint64_t times[16]; 105 | 106 | chrono_init(times); 107 | 108 | for (int i = 0; i < 16; ++i) 109 | { 110 | chrono_start(i); 111 | } 112 | 113 | int err_mlock; 114 | char* err_pass; 115 | 116 | // get password 117 | char pass[257] = {0}; 118 | 119 | err_mlock = mlock(pass, 257); 120 | 121 | if (err_mlock != 0) 122 | { 123 | dgn_throw(SSHRAM_ERR_MLOCK); 124 | return; 125 | } 126 | 127 | printf("Please enter a password (16-256 bytes, not that of your SSH private key!): "); 128 | 129 | err_pass = getpassword(pass, 257, stdin); 130 | 131 | if (err_pass != pass) 132 | { 133 | mem_clean(pass, 257); 134 | munlock(pass, 257); 135 | 136 | dgn_throw(SSHRAM_ERR_FGETS); 137 | return; 138 | } 139 | 140 | if (strlen(pass) < 16) 141 | { 142 | mem_clean(pass, 257); 143 | munlock(pass, 257); 144 | 145 | dgn_throw(SSHRAM_ERR_ENC_PASS_LEN); 146 | return; 147 | } 148 | 149 | // confirm password 150 | char confirm[257] = {0}; 151 | 152 | err_mlock = mlock(confirm, 257); 153 | 154 | if (err_mlock != 0) 155 | { 156 | mem_clean(pass, 257); 157 | munlock(pass, 257); 158 | 159 | dgn_throw(SSHRAM_ERR_MLOCK); 160 | return; 161 | } 162 | 163 | printf("Please confirm this password by typing it one more time: "); 164 | 165 | err_pass = getpassword(confirm, 257, stdin); 166 | 167 | if (err_pass != confirm) 168 | { 169 | mem_clean(pass, 257); 170 | mem_clean(confirm, 257); 171 | munlock(pass, 257); 172 | munlock(confirm, 257); 173 | 174 | dgn_throw(SSHRAM_ERR_FGETS); 175 | return; 176 | } 177 | 178 | if (strcmp(pass, confirm) != 0) 179 | { 180 | mem_clean(pass, 257); 181 | mem_clean(confirm, 257); 182 | munlock(pass, 257); 183 | munlock(confirm, 257); 184 | 185 | dgn_throw(SSHRAM_ERR_ENC_PASS_MATCH); 186 | return; 187 | } 188 | 189 | // generate salt 190 | uint8_t salt[16]; 191 | 192 | printf("Generating the random salt (blocking while gathering entropy)\n"); 193 | 194 | sshram_rng(salt, 16); 195 | 196 | if (dgn_catch()) 197 | { 198 | mem_clean(pass, 257); 199 | mem_clean(confirm, 257); 200 | munlock(pass, 257); 201 | munlock(confirm, 257); 202 | 203 | return; 204 | } 205 | 206 | // derive password 207 | uint8_t hash[32]; 208 | 209 | err_mlock = mlock(hash, 32); 210 | 211 | if (err_mlock != 0) 212 | { 213 | mem_clean(pass, 257); 214 | mem_clean(confirm, 257); 215 | munlock(pass, 257); 216 | munlock(confirm, 257); 217 | 218 | dgn_throw(SSHRAM_ERR_MLOCK); 219 | return; 220 | } 221 | 222 | printf("Deriving password with Argon2...\n"); 223 | 224 | int err_hash = argon2i_hash_raw( 225 | 100, 226 | (1 << 16), 227 | 1, 228 | pass, 229 | strlen(pass), 230 | salt, 231 | 16, 232 | hash, 233 | 32); 234 | 235 | if (err_hash != ARGON2_OK) 236 | { 237 | mem_clean(pass, 257); 238 | mem_clean(confirm, 257); 239 | mem_clean(hash, 32); 240 | munlock(pass, 257); 241 | munlock(confirm, 257); 242 | munlock(hash, 32); 243 | 244 | dgn_throw(SSHRAM_ERR_ARGON2); 245 | return; 246 | } 247 | 248 | if (config->verbose == true) 249 | { 250 | for (int i = 0; i < 32; ++i) 251 | { 252 | printf("%02x ", hash[i]); 253 | } 254 | 255 | printf("\n"); 256 | } 257 | 258 | mem_clean(pass, 257); 259 | mem_clean(confirm, 257); 260 | munlock(pass, 257); 261 | munlock(confirm, 257); 262 | 263 | // generate nonce 264 | uint8_t nonce[12]; 265 | 266 | printf("Generating the random nonce (blocking while gathering entropy)\n"); 267 | 268 | sshram_rng(nonce, 12); 269 | 270 | if (dgn_catch()) 271 | { 272 | mem_clean(hash, 32); 273 | munlock(hash, 32); 274 | 275 | return; 276 | } 277 | 278 | int err_file; 279 | 280 | // allocate buffers 281 | printf("Encoding private key with ChaCha20-Poly1305...\n"); 282 | 283 | err_file = fseek(config->file_decoded, 0, SEEK_END); 284 | 285 | if (err_file != 0) 286 | { 287 | mem_clean(hash, 32); 288 | munlock(hash, 32); 289 | 290 | dgn_throw(SSHRAM_ERR_FSEEK); 291 | return; 292 | } 293 | 294 | long buf_len = ftell(config->file_decoded); 295 | long header_len = 16 + 12 + 16; 296 | 297 | if (buf_len < 2) 298 | { 299 | mem_clean(hash, 32); 300 | munlock(hash, 32); 301 | 302 | dgn_throw(SSHRAM_ERR_FTELL); 303 | return; 304 | } 305 | 306 | uint8_t* buf_decoded = malloc(buf_len); 307 | 308 | if (buf_decoded == NULL) 309 | { 310 | mem_clean(hash, 32); 311 | munlock(hash, 32); 312 | 313 | dgn_throw(SSHRAM_ERR_MALLOC); 314 | return; 315 | } 316 | 317 | uint8_t* buf_encoded = malloc(buf_len + header_len); 318 | 319 | if (buf_encoded == NULL) 320 | { 321 | mem_clean(hash, 32); 322 | munlock(hash, 32); 323 | 324 | free(buf_decoded); 325 | 326 | dgn_throw(SSHRAM_ERR_MALLOC); 327 | return; 328 | } 329 | 330 | // lock memory 331 | err_mlock = mlock(buf_decoded, buf_len); 332 | 333 | if (err_mlock != 0) 334 | { 335 | mem_clean(hash, 32); 336 | munlock(hash, 32); 337 | 338 | free(buf_decoded); 339 | free(buf_encoded); 340 | 341 | dgn_throw(SSHRAM_ERR_MLOCK); 342 | return; 343 | } 344 | 345 | err_mlock = mlock(buf_encoded, buf_len + header_len); 346 | 347 | if (err_mlock != 0) 348 | { 349 | mem_clean(hash, 32); 350 | munlock(hash, 32); 351 | munlock(buf_decoded, buf_len); 352 | 353 | free(buf_decoded); 354 | free(buf_encoded); 355 | 356 | dgn_throw(SSHRAM_ERR_MLOCK); 357 | return; 358 | } 359 | 360 | // encode SSH private key 361 | err_file = fseek(config->file_decoded, 0, SEEK_SET); 362 | 363 | if (err_file != 0) 364 | { 365 | mem_clean(hash, 32); 366 | munlock(hash, 32); 367 | munlock(buf_decoded, buf_len); 368 | munlock(buf_encoded, buf_len + header_len); 369 | 370 | free(buf_decoded); 371 | free(buf_encoded); 372 | 373 | dgn_throw(SSHRAM_ERR_FSEEK); 374 | return; 375 | } 376 | 377 | err_file = fread(buf_decoded, 1, buf_len, config->file_decoded); 378 | 379 | if (err_file < 0) 380 | { 381 | mem_clean(hash, 32); 382 | mem_clean(buf_decoded, buf_len); 383 | munlock(hash, 32); 384 | munlock(buf_decoded, buf_len); 385 | munlock(buf_encoded, buf_len + header_len); 386 | 387 | free(buf_decoded); 388 | free(buf_encoded); 389 | 390 | dgn_throw(SSHRAM_ERR_FREAD); 391 | return; 392 | } 393 | 394 | uint8_t tag[16]; 395 | 396 | cf_chacha20poly1305_encrypt( 397 | hash, 398 | nonce, 399 | NULL, 400 | 0, 401 | buf_decoded, 402 | buf_len, 403 | buf_encoded, 404 | tag); 405 | 406 | err_file = fwrite(salt, 1, 16, config->file_encoded); 407 | err_file += fwrite(nonce, 1, 12, config->file_encoded); 408 | err_file += fwrite(tag, 1, 16, config->file_encoded); 409 | err_file += fwrite(buf_encoded, 1, buf_len, config->file_encoded); 410 | 411 | if (err_file != (buf_len + header_len)) 412 | { 413 | dgn_throw(SSHRAM_ERR_FWRITE); 414 | } 415 | 416 | // unlock remaining resources 417 | mem_clean(hash, 32); 418 | mem_clean(buf_decoded, buf_len); 419 | mem_clean(buf_encoded, buf_len); 420 | munlock(hash, 32); 421 | munlock(buf_decoded, buf_len); 422 | munlock(buf_encoded, buf_len + header_len); 423 | 424 | free(buf_decoded); 425 | free(buf_encoded); 426 | } 427 | 428 | void sshram_decode(struct config* config) 429 | { 430 | // set SIGINT handler 431 | const struct sigaction sig_struct = 432 | { 433 | .sa_handler = sigint_handler, 434 | .sa_flags = 0, // interrupt read 435 | }; 436 | 437 | int err_sig = sigaction(SIGINT, &sig_struct, NULL); 438 | 439 | if (err_sig == -1) 440 | { 441 | dgn_throw(SSHRAM_ERR_DEC_SIGACTION); 442 | return; 443 | } 444 | 445 | // get SSH private key length 446 | int err_file = fseek(config->file_encoded, 0, SEEK_END); 447 | 448 | if (err_file != 0) 449 | { 450 | dgn_throw(SSHRAM_ERR_FSEEK); 451 | return; 452 | } 453 | 454 | long header_len = 16 + 12 + 16; 455 | long buf_len = ftell(config->file_encoded) - header_len; 456 | 457 | if (buf_len < (header_len + 2)) 458 | { 459 | dgn_throw(SSHRAM_ERR_FTELL); 460 | return; 461 | } 462 | 463 | // read salt, nonce, tag 464 | err_file = fseek(config->file_encoded, 0, SEEK_SET); 465 | 466 | if (err_file != 0) 467 | { 468 | dgn_throw(SSHRAM_ERR_FSEEK); 469 | return; 470 | } 471 | 472 | uint8_t salt[16]; 473 | err_file = fread(salt, 1, 16, config->file_encoded); 474 | 475 | if (err_file < 0) 476 | { 477 | dgn_throw(SSHRAM_ERR_FREAD); 478 | return; 479 | } 480 | 481 | uint8_t nonce[12]; 482 | err_file = fread(nonce, 1, 12, config->file_encoded); 483 | 484 | if (err_file < 0) 485 | { 486 | dgn_throw(SSHRAM_ERR_FREAD); 487 | return; 488 | } 489 | 490 | uint8_t tag[16]; 491 | err_file = fread(tag, 1, 16, config->file_encoded); 492 | 493 | if (err_file < 0) 494 | { 495 | dgn_throw(SSHRAM_ERR_FREAD); 496 | return; 497 | } 498 | 499 | if (config->verbose == true) 500 | { 501 | printf("salt: "); 502 | for (int i = 0; i < 16; ++i) 503 | { 504 | printf("%02x ", salt[i]); 505 | } 506 | printf("\n"); 507 | 508 | printf("nonce: "); 509 | for (int i = 0; i < 12; ++i) 510 | { 511 | printf("%02x ", nonce[i]); 512 | } 513 | printf("\n"); 514 | 515 | printf("tag: "); 516 | for (int i = 0; i < 16; ++i) 517 | { 518 | printf("%02x ", tag[i]); 519 | } 520 | printf("\n"); 521 | } 522 | 523 | // get password 524 | char pass[257] = {0}; 525 | 526 | int err_mlock = mlock(pass, 257); 527 | 528 | if (err_mlock != 0) 529 | { 530 | dgn_throw(SSHRAM_ERR_MLOCK); 531 | return; 532 | } 533 | 534 | printf("Please enter your password: "); 535 | fflush(stdin); 536 | 537 | char* err_pass = getpassword(pass, 257, stdin); 538 | 539 | if (err_pass != pass) 540 | { 541 | mem_clean(pass, 257); 542 | munlock(pass, 257); 543 | 544 | dgn_throw(SSHRAM_ERR_FGETS); 545 | return; 546 | } 547 | 548 | // derive password 549 | uint8_t hash[32]; 550 | 551 | err_mlock = mlock(hash, 32); 552 | 553 | if (err_mlock != 0) 554 | { 555 | mem_clean(pass, 257); 556 | munlock(pass, 257); 557 | 558 | dgn_throw(SSHRAM_ERR_MLOCK); 559 | return; 560 | } 561 | 562 | printf("Deriving password with Argon2...\n"); 563 | 564 | int err_hash = argon2i_hash_raw( 565 | 100, 566 | (1 << 16), 567 | 1, 568 | pass, 569 | strlen(pass), 570 | salt, 571 | 16, 572 | hash, 573 | 32); 574 | 575 | if (err_hash != ARGON2_OK) 576 | { 577 | mem_clean(pass, 257); 578 | mem_clean(hash, 32); 579 | munlock(pass, 257); 580 | munlock(hash, 32); 581 | 582 | dgn_throw(SSHRAM_ERR_ARGON2); 583 | return; 584 | } 585 | 586 | if (config->verbose == true) 587 | { 588 | for (int i = 0; i < 32; ++i) 589 | { 590 | printf("%02x ", hash[i]); 591 | } 592 | 593 | printf("\n"); 594 | } 595 | 596 | mem_clean(pass, 257); 597 | munlock(pass, 257); 598 | 599 | // allocate buffers 600 | uint8_t* buf_decoded = malloc(buf_len + 1); 601 | 602 | if (buf_decoded == NULL) 603 | { 604 | mem_clean(hash, 32); 605 | munlock(hash, 32); 606 | 607 | dgn_throw(SSHRAM_ERR_MALLOC); 608 | return; 609 | } 610 | 611 | uint8_t* buf_encoded = malloc(buf_len); 612 | 613 | if (buf_encoded == NULL) 614 | { 615 | mem_clean(hash, 32); 616 | munlock(hash, 32); 617 | 618 | free(buf_decoded); 619 | 620 | dgn_throw(SSHRAM_ERR_MALLOC); 621 | return; 622 | } 623 | 624 | // lock memory 625 | err_mlock = mlock(buf_decoded, buf_len + 1); 626 | 627 | if (err_mlock != 0) 628 | { 629 | mem_clean(hash, 32); 630 | munlock(hash, 32); 631 | 632 | free(buf_decoded); 633 | free(buf_encoded); 634 | 635 | dgn_throw(SSHRAM_ERR_MLOCK); 636 | return; 637 | } 638 | 639 | err_mlock = mlock(buf_encoded, buf_len); 640 | 641 | if (err_mlock != 0) 642 | { 643 | mem_clean(hash, 32); 644 | munlock(hash, 32); 645 | munlock(buf_decoded, buf_len + 1); 646 | 647 | free(buf_decoded); 648 | free(buf_encoded); 649 | 650 | dgn_throw(SSHRAM_ERR_MLOCK); 651 | return; 652 | } 653 | 654 | // decode SSH private key 655 | err_file = fread(buf_encoded, 1, buf_len, config->file_encoded); 656 | 657 | if (err_file < 0) 658 | { 659 | mem_clean(hash, 32); 660 | mem_clean(buf_encoded, buf_len); 661 | munlock(hash, 32); 662 | munlock(buf_decoded, buf_len + 1); 663 | munlock(buf_encoded, buf_len); 664 | 665 | free(buf_decoded); 666 | free(buf_encoded); 667 | 668 | dgn_throw(SSHRAM_ERR_FREAD); 669 | return; 670 | } 671 | 672 | printf("Decoding private key with ChaCha20-Poly1305...\n"); 673 | 674 | int err_decode = cf_chacha20poly1305_decrypt( 675 | hash, 676 | nonce, 677 | NULL, 678 | 0, 679 | buf_encoded, 680 | buf_len, 681 | tag, 682 | buf_decoded); 683 | 684 | mem_clean(hash, 32); 685 | mem_clean(buf_encoded, buf_len); 686 | munlock(hash, 32); 687 | munlock(buf_encoded, buf_len); 688 | free(buf_encoded); 689 | 690 | if (err_decode != 0) 691 | { 692 | mem_clean(buf_decoded, buf_len + 1); 693 | munlock(buf_decoded, buf_len + 1); 694 | free(buf_decoded); 695 | 696 | dgn_throw(SSHRAM_ERR_DEC_CHACHAPOLY); 697 | return; 698 | } 699 | 700 | if (config->verbose == true) 701 | { 702 | buf_decoded[buf_len] = '\0'; 703 | printf("%s\n", buf_decoded); 704 | } 705 | 706 | // build key file path 707 | char* home = getenv("HOME"); 708 | 709 | if (home == NULL) 710 | { 711 | mem_clean(buf_decoded, buf_len + 1); 712 | munlock(buf_decoded, buf_len + 1); 713 | free(buf_decoded); 714 | 715 | dgn_throw(SSHRAM_ERR_ENV); 716 | return; 717 | } 718 | 719 | int path_len = strlen(home) + strlen("/.ssh/") + strlen(config->key_name); 720 | char* path = malloc(path_len + 1); 721 | 722 | if (path == NULL) 723 | { 724 | mem_clean(buf_decoded, buf_len + 1); 725 | munlock(buf_decoded, buf_len + 1); 726 | free(buf_decoded); 727 | 728 | dgn_throw(SSHRAM_ERR_MALLOC); 729 | return; 730 | } 731 | 732 | int err_path = snprintf(path, path_len + 1, "%s/.ssh/%s", home, config->key_name); 733 | 734 | if (err_path != path_len) 735 | { 736 | mem_clean(buf_decoded, buf_len + 1); 737 | munlock(buf_decoded, buf_len + 1); 738 | free(buf_decoded); 739 | free(path); 740 | 741 | dgn_throw(SSHRAM_ERR_DEC_PATH_LEN); 742 | return; 743 | } 744 | 745 | // check if the pipe already exists, create it if needed 746 | struct stat file_info = {0}; 747 | 748 | err_file = stat(path, &file_info); 749 | 750 | // file exists 751 | if (err_file != -1) 752 | { 753 | // can't continue because it's not a pipe 754 | if (!S_ISFIFO(file_info.st_mode)) 755 | { 756 | mem_clean(buf_decoded, buf_len + 1); 757 | munlock(buf_decoded, buf_len + 1); 758 | free(buf_decoded); 759 | free(path); 760 | 761 | dgn_throw(SSHRAM_ERR_DEC_PASUNEPIPE); 762 | return; 763 | } 764 | } 765 | // file does not exist 766 | else 767 | { 768 | // create named pipe 769 | int err_pipe = mkfifo(path, S_IRUSR | S_IWUSR); 770 | 771 | if (err_pipe != 0) 772 | { 773 | mem_clean(buf_decoded, buf_len + 1); 774 | munlock(buf_decoded, buf_len + 1); 775 | free(buf_decoded); 776 | free(path); 777 | 778 | dgn_throw(SSHRAM_ERR_DEC_MKFIFO); 779 | return; 780 | } 781 | } 782 | 783 | int inotify_fd = inotify_init(); 784 | 785 | if (inotify_fd == -1) 786 | { 787 | mem_clean(buf_decoded, buf_len + 1); 788 | munlock(buf_decoded, buf_len + 1); 789 | free(buf_decoded); 790 | free(path); 791 | 792 | dgn_throw(SSHRAM_ERR_DEC_INOTIFY_INIT); 793 | return; 794 | } 795 | 796 | int inotify_watch_fd = inotify_add_watch(inotify_fd, path, IN_ACCESS); 797 | 798 | if (inotify_watch_fd == -1) 799 | { 800 | close(inotify_fd); 801 | mem_clean(buf_decoded, buf_len + 1); 802 | munlock(buf_decoded, buf_len + 1); 803 | free(buf_decoded); 804 | free(path); 805 | 806 | dgn_throw(SSHRAM_ERR_DEC_INOTIFY_ADD_WATCH); 807 | return; 808 | } 809 | 810 | // allocate a large enough inotify event buffer 811 | size_t inotify_event_buf_size = (buf_len - 1) * (sizeof (struct inotify_event)); 812 | struct inotify_event* inotify_event_buf = malloc(inotify_event_buf_size); 813 | 814 | if (inotify_event_buf == NULL) 815 | { 816 | decode_run = 0; 817 | dgn_throw(SSHRAM_ERR_MALLOC); 818 | return; 819 | } 820 | 821 | // blocking, no-confirmation key transmission using inotify 822 | int pipe; 823 | ssize_t err_loop; 824 | 825 | if (decode_run == 1) 826 | { 827 | printf("Entering transmission loop\n"); 828 | } 829 | 830 | while (decode_run == 1) 831 | { 832 | // we *must* open in read-write mode to get a non-blocking descriptor 833 | // because unix pipes must be opened in read or read/write mode first 834 | // or we will not be able to open without non-blocking 835 | pipe = open(path, O_RDWR | O_NONBLOCK); 836 | 837 | if (pipe == -1) 838 | { 839 | dgn_throw(SSHRAM_ERR_DEC_PIPE_FOPEN); 840 | break; 841 | } 842 | 843 | // send the first character of the private key to be able to detect reads 844 | err_loop = write(pipe, buf_decoded, 1); 845 | 846 | if (err_loop != 1) 847 | { 848 | dgn_throw(SSHRAM_ERR_DEC_PIPE_FWRITE); 849 | break; 850 | } 851 | 852 | // wait for read 853 | err_loop = read(inotify_fd, inotify_event_buf, sizeof (struct inotify_event)); 854 | 855 | if (err_loop == -1) 856 | { 857 | if (errno == EINTR) 858 | { 859 | dgn_throw(SSHRAM_ERR_DEC_INOTIFY_READ_INT); 860 | } 861 | else 862 | { 863 | dgn_throw(SSHRAM_ERR_DEC_INOTIFY_READ); 864 | } 865 | 866 | break; 867 | } 868 | 869 | if (decode_run == 0) 870 | { 871 | break; 872 | } 873 | 874 | // write the rest of the private key 875 | err_loop = write(pipe, buf_decoded + 1, buf_len - 1); 876 | 877 | if (err_loop != ((ssize_t) buf_len - 1)) 878 | { 879 | dgn_throw(SSHRAM_ERR_DEC_PIPE_FWRITE); 880 | break; 881 | } 882 | 883 | // close pipe to simulate end-of-file 884 | err_file = close(pipe); 885 | 886 | if (err_file == -1) 887 | { 888 | dgn_throw(SSHRAM_ERR_DEC_PIPE_FCLOSE); 889 | break; 890 | } 891 | 892 | // wait for read 893 | err_loop = read(inotify_fd, inotify_event_buf, inotify_event_buf_size); 894 | 895 | if (err_loop == -1) 896 | { 897 | if (errno == EINTR) 898 | { 899 | dgn_throw(SSHRAM_ERR_DEC_INOTIFY_READ_INT); 900 | } 901 | else 902 | { 903 | dgn_throw(SSHRAM_ERR_DEC_INOTIFY_READ); 904 | } 905 | 906 | break; 907 | } 908 | 909 | // success! 910 | printf("Private key transmitted\n"); 911 | } 912 | 913 | if (config->keep_pipe == false) 914 | { 915 | err_file = unlink(path); 916 | 917 | if (err_file == -1) 918 | { 919 | dgn_throw(SSHRAM_ERR_DEC_PIPE_UNLINK); 920 | } 921 | } 922 | 923 | // cleanup 924 | inotify_rm_watch(inotify_fd, inotify_watch_fd); 925 | close(inotify_fd); 926 | mem_clean(buf_decoded, buf_len + 1); 927 | munlock(buf_decoded, buf_len + 1); 928 | free(inotify_event_buf); 929 | free(buf_decoded); 930 | free(path); 931 | 932 | printf("Exiting normally\n"); 933 | } 934 | -------------------------------------------------------------------------------- /src/sshram.h: -------------------------------------------------------------------------------- 1 | #ifndef H_SSHRAM 2 | #define H_SSHRAM 3 | 4 | #include 5 | #include 6 | 7 | // structs 8 | enum action 9 | { 10 | SSHRAM_ACTION_EXIT, 11 | SSHRAM_ACTION_DECODE, 12 | SSHRAM_ACTION_ENCODE, 13 | }; 14 | 15 | struct config 16 | { 17 | enum action action; 18 | FILE* file_encoded; 19 | FILE* file_decoded; 20 | char* key_name; 21 | bool keep_pipe; 22 | bool verbose; 23 | }; 24 | 25 | // functions 26 | void sshram_encode(struct config* config); 27 | void sshram_decode(struct config* config); 28 | 29 | #endif 30 | --------------------------------------------------------------------------------