home / secrets / src / secrets.c

secrets.c



//
//  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();
}