//
// secrets.c
// secrets
//
// Created by Anders on 09/07/2020.
//
#if defined linux || defined __linux__
#include <bsd/stdlib.h>
#endif
#if defined linux || defined __linux__ || defined __APPLE__
#define UNIX 1
#include <unistd.h>
#include <limits.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util/stringu.h"
#include "util/require.h"
#include "secrets.h"
#include "core/crypt.h"
#include "core/secret.h"
#include "core/store.h"
#include "lib/monocypher.h"
#include "util/hexstring.h"
#include "util/inputu.h"
#define INPUT_BUFFER_SIZE 1024
#define MAX_INPUT_LINES 100
#define MAX_FILE_INPUT INPUT_BUFFER_SIZE * MAX_INPUT_LINES
static const char* store = NULL;
static void ask(char* result, const char* question_string) {
printf("%s\n", question_string);
char* state = input_fgets(result, INPUT_BUFFER_SIZE, stdin);
require(state != NULL, "no input receieved.\n");
stringu_trim(result);
}
static void ask_hidden(char* result, const char* question_string) {
printf("%s\n", question_string);
char* state = input_fgets_hidden(result, INPUT_BUFFER_SIZE, stdin);
require(state != NULL, "no input receieved.\n");
stringu_trim(result);
}
static void gen_password_128_bits(char* result) {
*result++ = 'K';
*result++ = '-';
uint8_t key[16];
arc4random_buf(key, 16);
hexstring_from_bin(result, key, 16);
result[16 * 2] = '\0';
}
static void printsecret(const Entry* entry) {
if (entry == NULL) {
printf("key not found.\n");
return;
}
const char* line = entry->value;
char password_buffer[INPUT_BUFFER_SIZE];
ask_hidden(password_buffer, "decrypt-password?");
printf("...\n");
Secret *secret = secret_from_line_alloc(line);
#ifdef __APPLE__
FILE *out = popen("pbcopy","w");
#else
FILE *out = stdout;
#endif
bool success = crypt_print(out, password_buffer, secret);
secret_free(secret);
#ifdef __APPLE__
pclose(out);
if (success) printf("saved to clipboard.\n");
#else
if (success) printf("\n");
#endif
else printf("decrypt failed.\n");
}
static void key_get(const char* key) {
const Entry* entry = store_get(key);
printsecret(entry);
}
static void index_get(uint32_t i) {
const Entry* entry = store_by_index(i);
printsecret(entry);
}
static void all_get(void) {
char password_buffer[INPUT_BUFFER_SIZE];
ask_hidden(password_buffer, "decrypt-password?");
uint32_t i = 0;
const Entry* next;
while ((next = store_by_index(i)) != NULL) {
printf("(%d) %s -> ", i, next->key);
size_t copy_buffer_size = strlen(password_buffer) + 1;
char password_copy[copy_buffer_size];
stringu_copy(password_copy, password_buffer, copy_buffer_size);
Secret *secret = secret_from_line_alloc(next->value);
bool success = crypt_print(stdout, password_copy, secret);
secret_free(secret);
if (success) printf("\n");
else printf("(not matching)\n");
i++;
}
crypt_wipe(password_buffer);
}
static void print_keys(void) {
uint32_t size = store_size();
if (size == 0) {
printf("nothing stored.\n");
return;
}
uint32_t max_rows = 25;
uint32_t columns = size / max_rows;
columns += ((size % max_rows) != 0);
char index_string[10 + 2 + 1];
for (uint32_t j = 0; j < max_rows; ++j) {
for (uint32_t i = 0; i < columns; ++i) {
const Entry* next;
if ((next = store_by_index(i * max_rows + j)) != NULL) {
sprintf(index_string,"(%d)", i * max_rows + j);
printf("%-4s %-25s ", index_string, next->key);
}
}
printf("\n");
}
}
static void print_detailed(void) {
char index_buffer[INPUT_BUFFER_SIZE];
ask(index_buffer, "index?");
uint32_t index = (uint32_t) stringu_to_long(index_buffer);;
const Entry* next = store_by_index(index);
if (next == NULL) {
printf("nothing stored.\n");
}
else {
printf("(%d) %s - %s\n", index, next->key, next->description);
}
}
static void create(const char* key, const char* description, char* message) {
require(!stringu_equals(key, message), "key and secret should not be the same\n");
char password_buffer[INPUT_BUFFER_SIZE];
ask_hidden(password_buffer, "decrypt-password?");
require(!stringu_equals(key, password_buffer), "key and decrypt-password should not be the same\n");
require(!stringu_equals(description, password_buffer), "description and decrypt-password should not be the same\n");
printf("...\n");
Secret *secret = crypt_create_alloc(password_buffer, message);
char* line = secret_to_line_alloc(secret);
secret_free(secret);
store_put(key, description, line);
free(line);
store_persist(store);
printf("ok\n");
}
static bool empty_input(char* input) {
return strlen(input) == 0 || *input == '\n';
}
static void create_from(const char* input_file) {
if (input_file == NULL) return;
char input_buffer[MAX_FILE_INPUT + 1];
FILE *file;
file = fopen(input_file,"rb");
size_t length = fread(input_buffer, sizeof(char), MAX_FILE_INPUT, file);
if ( ferror( file ) != 0 ) {
printf("Error reading file");
}
else {
require(length > 0, "no input receieved.\n");
input_buffer[length++] = '\0';
}
char key_buffer[INPUT_BUFFER_SIZE];
ask(key_buffer, "key?");
require(!empty_input(key_buffer), "key was empty\n");
char description_buffer[INPUT_BUFFER_SIZE];
ask(description_buffer, "description? (optional)");
if (empty_input(description_buffer)) {
*description_buffer = '0';
}
create(key_buffer, description_buffer, input_buffer);
}
void secrets_output(const char* store_file, const char* key) {
require(store_file != NULL, "no store");
store = store_file;
store_init(store);
printf("key = %s\n", key);
key_get(key);
}
void secrets_run(const char* store_file, const char* input_file) {
require(store_file != NULL, "no store");
store = store_file;
store_init(store);
create_from(input_file);
printf("available commands:\n");
printf("(k)ey get\n");
printf("(i)ndex get\n");
printf("(m)ulti get\n");
printf("(c)reate\n");
printf("(p)rint info\n");
printf("(d)etails\n");
printf("(q)uit\n");
fflush(stdout);
char buffer[INPUT_BUFFER_SIZE];
bool next = true;
while (next) {
char* state = fgets(buffer, INPUT_BUFFER_SIZE, stdin);
require(state != NULL, "no input receieved.");
stringu_trim(buffer);
if (stringu_equals(buffer, "k")) {
char key_buffer[INPUT_BUFFER_SIZE];
ask(key_buffer, "key?");
key_get(key_buffer);
}
else if (stringu_equals(buffer, "i")) {
char index_buffer[INPUT_BUFFER_SIZE];
ask(index_buffer, "index?");
uint32_t index = (uint32_t) stringu_to_long(index_buffer);
index_get(index);
}
else if (stringu_equals(buffer, "m")) {
all_get();
}
else if (stringu_equals(buffer, "c")) {
char key_buffer[INPUT_BUFFER_SIZE];
ask(key_buffer, "key?");
require(!empty_input(key_buffer), "key was empty\n");
char description_buffer[INPUT_BUFFER_SIZE];
ask(description_buffer, "description? (optional)");
if (empty_input(description_buffer)) {
*description_buffer = '0';
}
char message_buffer[INPUT_BUFFER_SIZE];
ask(message_buffer, "secret? (leave empty to generate)");
if (empty_input(message_buffer)) {
gen_password_128_bits(message_buffer);
printf("generated 128 bit secret:\n");
printf("%s\n\n", message_buffer);
}
create(key_buffer, description_buffer, message_buffer);
}
else if (stringu_equals(buffer, "p")) {
print_keys();
}
else if (stringu_equals(buffer, "d")) {
print_detailed();
}
else if (stringu_equals(buffer, "q")) next = false;
#ifdef UNIX
else if (stringu_equals(buffer, "pwd")) {
char cwd[PATH_MAX + 1];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("%s\n", cwd);
} else {
printf("???\n");
}
}
#endif
fflush(stdout);
}
store_persist(store);
store_destroy();
}