apm

Minimalistic command line password manager and a rewrite of pass in C
git clone https://codeberg.org/night0721/apm
Log | Files | Refs | README | LICENSE

commit e762f76c0bcd6398065404e119171b7480697ed8
parent 66bb56ad19043744641960c416040b49f9def6c6
Author: night0721 <[email protected]>
Date:   Sun, 19 May 2024 16:52:18 +0100

change argon to apm

Diffstat:
M.gitignore | 2+-
MMakefile | 69+++++++++++++++++++++++++++++++++++++--------------------------------
MREADME.md | 20++++++++++++--------
Aapm.1 | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapm.c | 416+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dargon.1 | 53-----------------------------------------------------
Dargon.c | 416-------------------------------------------------------------------------------
7 files changed, 524 insertions(+), 510 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,3 @@ -argon +apm *.o *.tar.gz diff --git a/Makefile b/Makefile @@ -1,40 +1,45 @@ .POSIX: .SUFFIXES: -CC=cc +CC = cc +VERSION = 1.0 +TARGET = apm +MANPAGE = $(TARGET).1 +PREFIX ?= /usr/local +BINDIR = $(PREFIX)/bin +MANDIR = $(PREFIX)/share/man/man1 + +# Flags +LDFLAGS = $(shell pkg-config --libs libsodium) +CFLAGS = -O3 -march=native -mtune=native -pipe -s -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 + +SRC = apm.c + +$(TARGET): $(SRC) + $(CC) $(SRC) -o $@ $(CFLAGS) $(LDFLAGS) + +dist: + mkdir -p $(TARGET)-$(VERSION) + cp -R README.md $(MANPAGE) $(TARGET) $(TARGET)-$(VERSION) + tar -cf $(TARGET)-$(VERSION).tar $(TARGET)-$(VERSION) + gzip $(TARGET)-$(VERSION).tar + rm -rf $(TARGET)-$(VERSION) + +install: $(TARGET) + mkdir -p $(DESTDIR)$(BINDIR) + mkdir -p $(DESTDIR)$(MANDIR) + cp -p $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET) + chmod 755 $(DESTDIR)$(BINDIR)/$(TARGET) + cp -p $(MANPAGE) $(DESTDIR)$(MANDIR)/$(MANPAGE) + chmod 644 $(DESTDIR)$(MANDIR)/$(MANPAGE) -VERSION=1.0.0 -PREFIX = /usr/local -MANPREFIX = ${PREFIX}/share/man - -CFLAGS = -O3 -march=native -mtune=native -pipe -s -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lsodium - -SRC = argon.c - -argon: argon.c - ${CC} ${SRC} -o $@ ${CFLAGS} +uninstall: + $(RM) $(DESTDIR)$(BINDIR)/$(TARGET) + $(RM) $(DESTDIR)$(MANDIR)/$(MANPAGE) clean: - rm -rf argon - -dist: version argon - mkdir -p argon-${VERSION} - cp -R LICENSE README.md argon.1 argon argon-${VERSION} - tar -cf argon-${VERSION}.tar argon-${VERSION} - gzip argon-${VERSION}.tar - rm -rf argon-${VERSION} - -install: all - mkdir -p ${DESTDIR}${PREFIX}/bin - cp -f argon ${DESTDIR}${PREFIX}/bin - chmod 755 ${DESTDIR}${PREFIX}/bin/argon - mkdir -p ${DESTDIR}${MANPREFIX}/man1 - cp argon.1 ${DESTDIR}${MANPREFIX}/man1/argon.1 - chmod 644 ${DESTDIR}${MANPREFIX}/man1/argon.1 + $(RM) $(TARGET) -uninstall: - rm -f ${DESTDIR}${PREFIX}/bin/argon\ - ${DESTDIR}${MANPREFIX}/man1/argon.1 -all: argon +all: $(TARGET) -.PHONY: all clean dist install uninstall argon +.PHONY: all dist install uninstall clean diff --git a/README.md b/README.md @@ -1,22 +1,26 @@ -# argon +# apm -A minimalistic command line password manager and a rewrite of [pass](https://www.passwordstore.org/) in C. It uses a unique key to encrypt every password, it provides functionality to edit, add, generate, show, list, remove passwords. It uses argon2 to create hash of master password and uses XSalsa20 to encrypt the password. +apm, argon password manager; a minimalistic command line password manager and a rewrite of [pass](https://www.passwordstore.org/) in C. It uses a unique key to encrypt every password, it provides functionality to edit, add, generate, show, list, remove passwords. It uses argon2 to create hash of master password and uses XSalsa20 to encrypt the password. > The name "argon" is chosen as it uses argon2 algorithm and sodium(library) is stored with argon. -Before using argon, you must export 2 environment variables in order to make it work +Before using apm, you must export 2 environment variables in order to make it work ```sh -export ARGON_DIR=~/secret/argon -export ARGON_KEY=~/secret/argon_key +export APM_DIR=~/secret/apm +export APM_KEY=~/secret/apm_key ``` -`ARGON_DIR` is the directory where passwords are stored and `ARGON_KEY` is the path to the master key file. +`APM_DIR` is the directory where passwords are stored and `APM_KEY` is the path to the master key file. # Dependencies + - libsodium - gcc # Building + +You will need to run these with elevated privilages. + ```sh $ make # make install @@ -24,11 +28,11 @@ $ make # Usage ``` -Usage: argon [-vhL] [[-e | -R | -I | -Q] <password>] [-M <file>] [-G <password> <length>] +Usage: apm [-vhL] [[-e | -R | -I | -Q] <password>] [-M <file>] [-G <password> <length>] ``` # Contributions Contributions are welcomed, feel free to open a pull request. # License -This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/argon/blob/master/LICENSE) for more information. +This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/apm/blob/master/LICENSE) for more information. diff --git a/apm.1 b/apm.1 @@ -0,0 +1,58 @@ +.TH apm 1 apm\-1.0.0 +.SH NAME +apm \- Minimalistic password manager +.SH SYNOPSIS +.B apm +.RB [ \-v ] +.RB [ \-h ] +.RB [ \-e ] +.RB [ \-R ] +.RB [ \-I ] +.RB [ \-Q ] +.RB [ \-L ] +.RB [ \-M ] +.RB [ \-G ] + +.SH DESCRIPTION +apm is a minimalistic command line password manager and a rewrite of pass in C. It uses a unique key to encrypt every password, it provides functionality to edit, add, generate, show, list, remove passwords. It uses argon2 to create hash of master password and uses XSalsa20 to encrypt the password. + +.SH OPTIONS +.TP +.B \-v +Prints the version of the program. + +.TP +.B \-h +Shows the help message. + +.TP +.B \-e <filename> +Edits the password using EDITOR. + +.TP +.B \-R <filename> +Removes password from APM_DIR. + +.TP +.B \-I <filename> +Insert password to APM_DIR. + +.TP +.B \-Q <filename> +Show password from APM_DIR. + +.TP +.B \-L +Lists the passwords in APM_DIR in a tree format. + +.TP +.B \-M +Insert password from a file, in order to fix -I can't take multi line input. + +.TP +.B \-G <filename> <length> +Generate a password with specific length to add to APM_DIR. + +.SH AUTHOR +Made by Night Kaly +.B <[email protected]> diff --git a/apm.c b/apm.c @@ -0,0 +1,416 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <libgen.h> +#include <time.h> +#include <sys/stat.h> +#include <sys/resource.h> +#include <sodium.h> + +#include "arg.h" + +#define KEY_SIZE crypto_secretbox_KEYBYTES +#define NONCE_SIZE crypto_secretbox_NONCEBYTES +#define SALT_SIZE crypto_pwhash_SALTBYTES + +char *argv0; + +void die(char *str); + +void *memalloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr) { + perror("apm"); + exit(EXIT_FAILURE); + } + return ptr; +} + +char *get_master_key(); + +void usage() +{ + printf("Usage: %s [-vhL] [[-e | -R | -I | -Q] <password>] [-M <file>] [-G <password> <length>]\n", argv0); + exit(EXIT_SUCCESS); +} + +int compare(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + +void tree(const char *basepath, int depth) +{ + char path[PATH_MAX]; + struct dirent *dp; + DIR *dir = opendir(basepath); + + if (!dir) + return; + + /* max 1024 files */ + char *files[1024]; + int file_count = 0; + + while ((dp = readdir(dir)) != NULL) { + if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) { + files[file_count] = strdup(dp->d_name); + file_count++; + } + } + + qsort(files, file_count, sizeof(char *), compare); + + for (int i = 0; i < file_count; i++) { + for (int j = 0; j < depth - 1; j++) { + printf("│ "); + } + + if (depth > 0) { + printf("├── "); + } + + printf("%s\n", files[i]); + + strcpy(path, basepath); + strcat(path, "/"); + strcat(path, files[i]); + + tree(path, depth + 1); + + free(files[i]); + } + + closedir(dir); +} + +char *get_apm() +{ + char dirname[] = "apm"; + const char *apm_dir = getenv("APM_DIR"); + /* for / and null */ + size_t len = 2; + if (apm_dir == NULL) { + apm_dir = getenv("XDG_DATA_HOME"); + if (apm_dir == NULL) { + apm_dir = getenv("HOME"); + if (apm_dir == NULL) { + die("HOME not defined"); + } + } + len += strlen(dirname); + } else { + /* no / */ + len -= 1; + } + size_t dir_len = strlen(apm_dir); + len += dir_len; + char *dir = memalloc(len * sizeof(char)); + + /* check if it is apm_DIR or other */ + if (len > dir_len) { + snprintf(dir, len, "%s/%s", apm_dir, dirname); + } else { + strncpy(dir, apm_dir, len); + } + struct stat stats; + /* check defined path is directory */ + if (!((stat(dir, &stats) == 0) && S_ISDIR(stats.st_mode))) { + if (mkdir(dir, S_IRWXU)) { /* 700 */ + die("Cannot initialize directory"); + } + } + return dir; +} + +char *get_passfile(const char *key) +{ + char *dir = get_apm(); + /* concat file name */ + /* / and null */ + size_t len = strlen(dir) + strlen(key) + 2; + char *path = memalloc(len * sizeof(char)); + snprintf(path, len, "%s/%s", dir, key); + free(dir); + return path; +} + +char *get_password() +{ + size_t len; + char *password = NULL; + printf("Enter password to encrypt: \n"); + getline(&password, &len, stdin); + /* remove newline character */ + password[strcspn(password, "\n")] = '\0'; + return password; +} + +void encrypt_password(const char *name, char *password) +{ + char *m_key = get_master_key(); + unsigned char salt[SALT_SIZE]; + unsigned char key[KEY_SIZE]; + unsigned char nonce[NONCE_SIZE]; + + /* generate random bytes for salt and nonce(number used once) */ + randombytes_buf(salt, sizeof(salt)); + randombytes_buf(nonce, sizeof(nonce)); + /* hash master password to give us the key for encrypting the password */ + if (crypto_pwhash(key, sizeof(key), m_key, strlen(m_key), salt, + crypto_pwhash_OPSLIMIT_INTERACTIVE, + crypto_pwhash_MEMLIMIT_INTERACTIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + sodium_free(m_key); + perror("apm"); + die("Cannot create key"); + } + size_t pw_len = strlen(password); + /* Include space for authentication tag */ + size_t ciphered_len = crypto_secretbox_MACBYTES + pw_len; + char ciphered[ciphered_len]; + + /* encrypt password */ + if (crypto_secretbox_easy((unsigned char *) ciphered, + (unsigned char *) password, pw_len, + nonce, key) != 0) { + sodium_free(m_key); + die("Error encrypting password"); + } + + char *filepath = get_passfile(name); + FILE *file = fopen(filepath, "wb"); + if (file == NULL) { + sodium_free(m_key); + free(filepath); + die("Error opening pass file to write"); + } + + fwrite(salt, sizeof(salt), 1, file); + fwrite(nonce, sizeof(nonce), 1, file); + fwrite(ciphered, ciphered_len, 1, file); + + fclose(file); + free(filepath); + sodium_free(m_key); +} + +void decrypt_password(const char *name, int open) +{ + char *m_key = get_master_key(); + unsigned char salt[SALT_SIZE]; + unsigned char nonce[NONCE_SIZE]; + unsigned char key[KEY_SIZE]; + + char *filepath = get_passfile(name); + FILE *file = fopen(filepath, "rb"); + if (file == NULL) { + sodium_free(m_key); + free(filepath); + die("Error opening pass file to read"); + } + + /* get salt and nonce from file */ + fread(salt, sizeof(char), sizeof(salt), file); + fread(nonce, sizeof(char), sizeof(nonce), file); + + fseek(file, 0, SEEK_END); + size_t ciphered_len = ftell(file) - sizeof(salt) - sizeof(nonce); + fseek(file, sizeof(salt) + sizeof(nonce), SEEK_SET); + + char ciphered[ciphered_len]; + fread(ciphered, sizeof(char), ciphered_len, file); + if (crypto_pwhash(key, sizeof(key), m_key, strlen(m_key), salt, + crypto_pwhash_OPSLIMIT_INTERACTIVE, + crypto_pwhash_MEMLIMIT_INTERACTIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + sodium_free(m_key); + free(filepath); + perror("apm"); + die("Cannot create key"); + } + /* take authentication bytes away */ + size_t deciphered_len = ciphered_len - crypto_secretbox_MACBYTES; + char deciphered[deciphered_len]; + if (crypto_secretbox_open_easy((unsigned char *) deciphered, + (unsigned char *) ciphered, ciphered_len, nonce, key) != 0) { + sodium_free(m_key); + free(filepath); + fclose(file); + die("Error decrypting password"); + } + + deciphered[deciphered_len] = '\0'; + if (open) { + char *editor = getenv("EDITOR"); + if (editor == NULL) { + die("EDITOR not defined"); + } + char tmp_f[] = "/tmp/apm"; + FILE *tmp = fopen(tmp_f, "w+"); + fprintf(tmp, "%s\n", deciphered); + fclose(tmp); + char *cmd = memalloc((strlen(editor) + strlen(tmp_f) + 1) * sizeof(char)); + sprintf(cmd, "%s %s", editor, tmp_f); + system(cmd); + free(cmd); + tmp = fopen(tmp_f, "r"); + fseek(tmp, 0, SEEK_END); + long tmp_size = ftell(tmp); + fseek(tmp, 0, SEEK_SET); + char content[tmp_size + 1]; + fgets(content, tmp_size, tmp); + encrypt_password(name, content); + fclose(tmp); + } else { + printf("%s\n", deciphered); + } + + sodium_free(m_key); + fclose(file); +} + +char *get_master_key() +{ + char *key_path = getenv("APM_KEY"); + char *m_key = NULL; + if (key_path != NULL) { + FILE *key_file = fopen(key_path, "r"); + if (key_file == NULL) { + perror("apm"); + exit(EXIT_FAILURE); + } + struct stat st; + if ((fstat(fileno(key_file), &st) == 0) || (S_ISREG(st.st_mode))) { + size_t pass_size = st.st_size; + m_key = (char *) sodium_malloc(pass_size * sizeof(char)); + if (m_key == NULL) { + perror("apm"); + exit(EXIT_FAILURE); + } + if (fgets(m_key, pass_size, key_file) == NULL) { + perror("apm"); + exit(EXIT_FAILURE); + } + } + } else { + fprintf(stderr, "apm: You are required to set APM_KEY to pass file\n"); + exit(EXIT_FAILURE); + } + return m_key; +} + +void generate_password(char*name, int length) +{ + srand(time(NULL)); + const char *characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890123456789~`!@#$%^&*()-_+=[]{}|/,.<>;:'"; + size_t characters_len = strlen(characters); + char *random_string = memalloc(length + 1); + for (int i = 0; i < length; i++) { + random_string[i] = characters[rand() % (characters_len - 1)]; + } + random_string[length] = '\0'; + printf("The generated password for %s is: %s\n", name, random_string); + encrypt_password(name, random_string); + free(random_string); +} + +void die(char *str) +{ + fprintf(stderr, "apm: %s\n", str); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) +{ + if (sodium_init() == -1) { + die("Error initializing sodium"); + } + + /* disable core dump for security */ + setrlimit(RLIMIT_CORE, &(struct rlimit) {0, 0}); + + ARGBEGIN { + case 'h': + usage(); + break; + case 'v': + printf("apm 1.0.0\n"); + exit(EXIT_SUCCESS); + break; + case 'e': + decrypt_password(EARGF(usage()), 1); + exit(EXIT_SUCCESS); + break; + case 'R':; + char *pass_file = get_passfile(EARGF(usage())); + if (remove(pass_file)) { + perror("apm"); + } else { + printf("Removed %s\n", basename(pass_file)); + } + free(pass_file); + exit(EXIT_SUCCESS); + break; + case 'I':; + char *pw = get_password(); + encrypt_password(EARGF(usage()), pw); + free(pw); + exit(EXIT_SUCCESS); + break; + case 'Q': + decrypt_password(EARGF(usage()), 0); + exit(EXIT_SUCCESS); + break; + case 'L':; + char *apm = get_apm(); + tree(apm, 0); + free(apm); + exit(EXIT_SUCCESS); + break; + case 'M':; + char *filename = EARGF(usage()); + FILE *file = fopen(filename, "r"); + if (file == NULL) { + die("Cannot open file to read"); + } + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + fseek(file, 0, SEEK_SET); + char *content = memalloc(file_size * sizeof(char)); + fread(content, sizeof(char), file_size, file); + char *f_basename = basename(filename); + char *dot = strrchr(f_basename, '.'); + if (dot != NULL) { + *dot = '\0'; + } + encrypt_password(f_basename, content); + exit(EXIT_SUCCESS); + break; + case 'G':; + if (argc > 0) + --argc, ++argv; + goto run; + default: + usage(); + } ARGEND; + + run: + switch (argc) { + case 0: + usage(); + break; + case 1: + decrypt_password(argv[0], 0); + break; + case 2: + generate_password(argv[0], atoi(argv[1])); + break; + } + + return 0; + +} diff --git a/argon.1 b/argon.1 @@ -1,53 +0,0 @@ -.TH argon 1 argon\-1.0.0 -.SH NAME -argon \- Minimalistic password manager -.SH SYNOPSIS -.B argon -.RB [ \-v ] -.RB [ \-h ] -.RB [ \-e ] -.RB [ \-R ] -.RB [ \-I ] -.RB [ \-Q ] -.RB [ \-L ] -.RB [ \-G ] - -.SH DESCRIPTION -argon is a minimalistic command line password manager and a rewrite of pass in C. It uses a unique key to encrypt every password, it provides functionality to edit, add, generate, show, list, remove passwords. It uses argon2 to create hash of master password and uses XSalsa20 to encrypt the password. - -.SH OPTIONS -.TP -.B \-v -Prints the version of the program. - -.TP -.B \-h -Shows the help message. - -.TP -.B \-e <filename> -Edits the password using EDITOR. - -.TP -.B \-R <filename> -Removes password from ARGON_DIR. - -.TP -.B \-I <filename> -Insert password to ARGON_DIR. - -.TP -.B \-Q <filename> -Show password from ARGON_DIR. - -.TP -.B \-L -Lists the passwords in ARGON_DIR in a tree format. - -.TP -.B \-G <filename> <length> -Generate a password with specific length to add to ARGON_DIR. - -.SH AUTHOR -Made by Night Kaly -.B <[email protected]> diff --git a/argon.c b/argon.c @@ -1,416 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <dirent.h> -#include <libgen.h> -#include <time.h> -#include <sys/stat.h> -#include <sys/resource.h> -#include <sodium.h> - -#include "arg.h" - -#define KEY_SIZE crypto_secretbox_KEYBYTES -#define NONCE_SIZE crypto_secretbox_NONCEBYTES -#define SALT_SIZE crypto_pwhash_SALTBYTES - -char *argv0; - -void die(char *str); - -void *memalloc(size_t size) -{ - void *ptr = malloc(size); - if (!ptr) { - perror("argon"); - exit(EXIT_FAILURE); - } - return ptr; -} - -char *get_master_key(); - -void usage() -{ - printf("Usage: %s [-vhL] [[-e | -R | -I | -Q] <password>] [-M <file>] [-G <password> <length>]\n", argv0); - exit(EXIT_SUCCESS); -} - -int compare(const void *a, const void *b) -{ - return strcmp(*(const char **)a, *(const char **)b); -} - -void tree(const char *basepath, int depth) -{ - char path[PATH_MAX]; - struct dirent *dp; - DIR *dir = opendir(basepath); - - if (!dir) - return; - - /* max 1024 files */ - char *files[1024]; - int file_count = 0; - - while ((dp = readdir(dir)) != NULL) { - if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) { - files[file_count] = strdup(dp->d_name); - file_count++; - } - } - - qsort(files, file_count, sizeof(char *), compare); - - for (int i = 0; i < file_count; i++) { - for (int j = 0; j < depth - 1; j++) { - printf("│ "); - } - - if (depth > 0) { - printf("├── "); - } - - printf("%s\n", files[i]); - - strcpy(path, basepath); - strcat(path, "/"); - strcat(path, files[i]); - - tree(path, depth + 1); - - free(files[i]); - } - - closedir(dir); -} - -char *get_argon() -{ - char dirname[] = "argon"; - const char *argon_dir = getenv("ARGON_DIR"); - /* for / and null */ - size_t len = 2; - if (argon_dir == NULL) { - argon_dir = getenv("XDG_DATA_HOME"); - if (argon_dir == NULL) { - argon_dir = getenv("HOME"); - if (argon_dir == NULL) { - die("HOME not defined"); - } - } - len += strlen(dirname); - } else { - /* no / */ - len -= 1; - } - size_t dir_len = strlen(argon_dir); - len += dir_len; - char *dir = memalloc(len * sizeof(char)); - - /* check if it is ARGON_DIR or other */ - if (len > dir_len) { - snprintf(dir, len, "%s/%s", argon_dir, dirname); - } else { - strncpy(dir, argon_dir, len); - } - struct stat stats; - /* check defined path is directory */ - if (!((stat(dir, &stats) == 0) && S_ISDIR(stats.st_mode))) { - if (mkdir(dir, S_IRWXU)) { /* 700 */ - die("Cannot initialize directory"); - } - } - return dir; -} - -char *get_passfile(const char *key) -{ - char *dir = get_argon(); - /* concat file name */ - /* / and null */ - size_t len = strlen(dir) + strlen(key) + 2; - char *path = memalloc(len * sizeof(char)); - snprintf(path, len, "%s/%s", dir, key); - free(dir); - return path; -} - -char *get_password() -{ - size_t len; - char *password = NULL; - printf("Enter password to encrypt: \n"); - getline(&password, &len, stdin); - /* remove newline character */ - password[strcspn(password, "\n")] = '\0'; - return password; -} - -void encrypt_password(const char *name, char *password) -{ - char *m_key = get_master_key(); - unsigned char salt[SALT_SIZE]; - unsigned char key[KEY_SIZE]; - unsigned char nonce[NONCE_SIZE]; - - /* generate random bytes for salt and nonce(number used once) */ - randombytes_buf(salt, sizeof(salt)); - randombytes_buf(nonce, sizeof(nonce)); - /* hash master password to give us the key for encrypting the password */ - if (crypto_pwhash(key, sizeof(key), m_key, strlen(m_key), salt, - crypto_pwhash_OPSLIMIT_INTERACTIVE, - crypto_pwhash_MEMLIMIT_INTERACTIVE, - crypto_pwhash_ALG_DEFAULT) != 0) { - sodium_free(m_key); - perror("argon"); - die("Cannot create key"); - } - size_t pw_len = strlen(password); - /* Include space for authentication tag */ - size_t ciphered_len = crypto_secretbox_MACBYTES + pw_len; - char ciphered[ciphered_len]; - - /* encrypt password */ - if (crypto_secretbox_easy((unsigned char *) ciphered, - (unsigned char *) password, pw_len, - nonce, key) != 0) { - sodium_free(m_key); - die("Error encrypting password"); - } - - char *filepath = get_passfile(name); - FILE *file = fopen(filepath, "wb"); - if (file == NULL) { - sodium_free(m_key); - free(filepath); - die("Error opening pass file to write"); - } - - fwrite(salt, sizeof(salt), 1, file); - fwrite(nonce, sizeof(nonce), 1, file); - fwrite(ciphered, ciphered_len, 1, file); - - fclose(file); - free(filepath); - sodium_free(m_key); -} - -void decrypt_password(const char *name, int open) -{ - char *m_key = get_master_key(); - unsigned char salt[SALT_SIZE]; - unsigned char nonce[NONCE_SIZE]; - unsigned char key[KEY_SIZE]; - - char *filepath = get_passfile(name); - FILE *file = fopen(filepath, "rb"); - if (file == NULL) { - sodium_free(m_key); - free(filepath); - die("Error opening pass file to read"); - } - - /* get salt and nonce from file */ - fread(salt, sizeof(char), sizeof(salt), file); - fread(nonce, sizeof(char), sizeof(nonce), file); - - fseek(file, 0, SEEK_END); - size_t ciphered_len = ftell(file) - sizeof(salt) - sizeof(nonce); - fseek(file, sizeof(salt) + sizeof(nonce), SEEK_SET); - - char ciphered[ciphered_len]; - fread(ciphered, sizeof(char), ciphered_len, file); - if (crypto_pwhash(key, sizeof(key), m_key, strlen(m_key), salt, - crypto_pwhash_OPSLIMIT_INTERACTIVE, - crypto_pwhash_MEMLIMIT_INTERACTIVE, - crypto_pwhash_ALG_DEFAULT) != 0) { - sodium_free(m_key); - free(filepath); - perror("argon"); - die("Cannot create key"); - } - /* take authentication bytes away */ - size_t deciphered_len = ciphered_len - crypto_secretbox_MACBYTES; - char deciphered[deciphered_len]; - if (crypto_secretbox_open_easy((unsigned char *) deciphered, - (unsigned char *) ciphered, ciphered_len, nonce, key) != 0) { - sodium_free(m_key); - free(filepath); - fclose(file); - die("Error decrypting password"); - } - - deciphered[deciphered_len] = '\0'; - if (open) { - char *editor = getenv("EDITOR"); - if (editor == NULL) { - die("EDITOR not defined"); - } - char tmp_f[] = "/tmp/argon"; - FILE *tmp = fopen(tmp_f, "w+"); - fprintf(tmp, "%s\n", deciphered); - fclose(tmp); - char *cmd = memalloc((strlen(editor) + strlen(tmp_f) + 1) * sizeof(char)); - sprintf(cmd, "%s %s", editor, tmp_f); - system(cmd); - free(cmd); - tmp = fopen(tmp_f, "r"); - fseek(tmp, 0, SEEK_END); - long tmp_size = ftell(tmp); - fseek(tmp, 0, SEEK_SET); - char content[tmp_size + 1]; - fgets(content, tmp_size, tmp); - encrypt_password(name, content); - fclose(tmp); - } else { - printf("%s\n", deciphered); - } - - sodium_free(m_key); - fclose(file); -} - -char *get_master_key() -{ - char *key_path = getenv("ARGON_KEY"); - char *m_key = NULL; - if (key_path != NULL) { - FILE *key_file = fopen(key_path, "r"); - if (key_file == NULL) { - perror("argon"); - exit(EXIT_FAILURE); - } - struct stat st; - if ((fstat(fileno(key_file), &st) == 0) || (S_ISREG(st.st_mode))) { - size_t pass_size = st.st_size; - m_key = (char *) sodium_malloc(pass_size * sizeof(char)); - if (m_key == NULL) { - perror("argon"); - exit(EXIT_FAILURE); - } - if (fgets(m_key, pass_size, key_file) == NULL) { - perror("argon"); - exit(EXIT_FAILURE); - } - } - } else { - fprintf(stderr, "argon: You are required to set ARGON_KEY to pass file\n"); - exit(EXIT_FAILURE); - } - return m_key; -} - -void generate_password(char*name, int length) -{ - srand(time(NULL)); - const char *characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890123456789~`!@#$%^&*()-_+=[]{}|/,.<>;:'"; - size_t characters_len = strlen(characters); - char *random_string = memalloc(length + 1); - for (int i = 0; i < length; i++) { - random_string[i] = characters[rand() % (characters_len - 1)]; - } - random_string[length] = '\0'; - printf("The generated password for %s is: %s\n", name, random_string); - encrypt_password(name, random_string); - free(random_string); -} - -void die(char *str) -{ - fprintf(stderr, "argon: %s\n", str); - exit(EXIT_FAILURE); -} - -int main(int argc, char *argv[]) -{ - if (sodium_init() == -1) { - die("Error initializing sodium"); - } - - /* disable core dump for security */ - setrlimit(RLIMIT_CORE, &(struct rlimit) {0, 0}); - - ARGBEGIN { - case 'h': - usage(); - break; - case 'v': - printf("argon 1.0.0\n"); - exit(EXIT_SUCCESS); - break; - case 'e': - decrypt_password(EARGF(usage()), 1); - exit(EXIT_SUCCESS); - break; - case 'R':; - char *pass_file = get_passfile(EARGF(usage())); - if (remove(pass_file)) { - perror("argon"); - } else { - printf("Removed %s\n", basename(pass_file)); - } - free(pass_file); - exit(EXIT_SUCCESS); - break; - case 'I':; - char *pw = get_password(); - encrypt_password(EARGF(usage()), pw); - free(pw); - exit(EXIT_SUCCESS); - break; - case 'Q': - decrypt_password(EARGF(usage()), 0); - exit(EXIT_SUCCESS); - break; - case 'L':; - char *argon = get_argon(); - tree(argon, 0); - free(argon); - exit(EXIT_SUCCESS); - break; - case 'M':; - char *filename = EARGF(usage()); - FILE *file = fopen(filename, "r"); - if (file == NULL) { - die("Cannot open file to read"); - } - fseek(file, 0, SEEK_END); - long file_size = ftell(file); - fseek(file, 0, SEEK_SET); - char *content = memalloc(file_size * sizeof(char)); - fread(content, sizeof(char), file_size, file); - char *f_basename = basename(filename); - char *dot = strrchr(f_basename, '.'); - if (dot != NULL) { - *dot = '\0'; - } - encrypt_password(f_basename, content); - exit(EXIT_SUCCESS); - break; - case 'G':; - if (argc > 0) - --argc, ++argv; - goto run; - default: - usage(); - } ARGEND; - - run: - switch (argc) { - case 0: - usage(); - break; - case 1: - decrypt_password(argv[0], 0); - break; - case 2: - generate_password(argv[0], atoi(argv[1])); - break; - } - - return 0; - -}