275 lines
8.9 KiB
C
275 lines
8.9 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <getopt.h>
|
||
|
#include <openssl/rand.h>
|
||
|
#include <openssl/evp.h>
|
||
|
#include <openssl/sha.h>
|
||
|
#include <openssl/aes.h>
|
||
|
#include <openssl/err.h>
|
||
|
#include <termios.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#define SALT_SIZE 16
|
||
|
#define IV_SIZE 16
|
||
|
#define BLOCK_SIZE 16
|
||
|
#define KEY_SIZE 32
|
||
|
#define MAX_CIPHERTEXT_LENGTH 256
|
||
|
|
||
|
const char *defaultCharSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()-_=+[]{};:,.<>?/\\|";
|
||
|
const char *noSymbolsCharSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||
|
|
||
|
int passwordLength = 16;
|
||
|
int useSymbols = 1;
|
||
|
char masterPassword[256]; // Master password for encryption
|
||
|
|
||
|
void getMasterPassword(char *password) {
|
||
|
struct termios oldt, newt;
|
||
|
printf("Enter master password: ");
|
||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||
|
newt = oldt;
|
||
|
newt.c_lflag &= ~(ECHO);
|
||
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||
|
fgets(password, sizeof(masterPassword), stdin);
|
||
|
password[strcspn(password, "\n")] = '\0';
|
||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
void printHelp() {
|
||
|
printf("Password Manager Usage:\n");
|
||
|
printf(" --add SERVICE --username USERNAME [--password PASSWORD] Add a new service with optional password (randomly generated if not provided)\n");
|
||
|
printf(" --get SERVICE Retrieve the username and password for a given service\n");
|
||
|
printf(" --list List all services stored in the vault\n");
|
||
|
printf(" --length LENGTH Specify the length of generated passwords (default: 16)\n");
|
||
|
printf(" --no-symbols Generate passwords without symbols\n");
|
||
|
printf(" --help Display this help message\n");
|
||
|
}
|
||
|
|
||
|
void parseArgs(int argc, char **argv, char **operation, char **service, char **username, char **password) {
|
||
|
int c;
|
||
|
while (1) {
|
||
|
static struct option long_options[] = {
|
||
|
{"length", required_argument, 0, 'l'},
|
||
|
{"no-symbols", no_argument, 0, 'n'},
|
||
|
{"add", required_argument, 0, 'a'},
|
||
|
{"get", required_argument, 0, 'g'},
|
||
|
{"list", no_argument, 0, 'L'},
|
||
|
{"username", required_argument, 0, 'u'},
|
||
|
{"password", required_argument, 0, 'p'},
|
||
|
{"help", no_argument, 0, 'h'},
|
||
|
{0, 0, 0, 0}
|
||
|
};
|
||
|
|
||
|
c = getopt_long(argc, argv, "l:na:g:L:u:p:h", long_options, NULL);
|
||
|
if (c == -1) break;
|
||
|
|
||
|
switch (c) {
|
||
|
case 'l':
|
||
|
passwordLength = atoi(optarg);
|
||
|
break;
|
||
|
case 'n':
|
||
|
useSymbols = 0;
|
||
|
break;
|
||
|
case 'a':
|
||
|
*operation = "add";
|
||
|
*service = optarg;
|
||
|
break;
|
||
|
case 'g':
|
||
|
*operation = "get";
|
||
|
*service = optarg;
|
||
|
break;
|
||
|
case 'L':
|
||
|
*operation = "list";
|
||
|
break;
|
||
|
case 'u':
|
||
|
*username = optarg;
|
||
|
break;
|
||
|
case 'p':
|
||
|
*password = optarg;
|
||
|
break;
|
||
|
case 'h':
|
||
|
printHelp();
|
||
|
exit(0);
|
||
|
default:
|
||
|
printf("Invalid option %c\n", c);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void generatePassword(char *password) {
|
||
|
const char *charSet = useSymbols ? defaultCharSet : noSymbolsCharSet;
|
||
|
for (int i = 0; i < passwordLength; ++i) {
|
||
|
unsigned char randomByte;
|
||
|
RAND_bytes(&randomByte, sizeof(randomByte));
|
||
|
int randomIndex = randomByte % (int)strlen(charSet);
|
||
|
password[i] = charSet[randomIndex];
|
||
|
}
|
||
|
password[passwordLength] = '\0';
|
||
|
}
|
||
|
|
||
|
void handleErrors() {
|
||
|
ERR_print_errors_fp(stderr);
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
void deriveKey(unsigned char *key, unsigned char *salt) {
|
||
|
if (!PKCS5_PBKDF2_HMAC(masterPassword, strlen(masterPassword), salt, SALT_SIZE, 10000, EVP_sha256(), KEY_SIZE, key)) {
|
||
|
handleErrors();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void encryptPassword(const char *plaintext, unsigned char *ciphertext, unsigned char *salt, int *ciphertext_len) {
|
||
|
unsigned char key[KEY_SIZE], iv[IV_SIZE];
|
||
|
deriveKey(key, salt);
|
||
|
|
||
|
// Generate random IV
|
||
|
RAND_bytes(iv, sizeof(iv));
|
||
|
|
||
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||
|
int len;
|
||
|
|
||
|
// Prepend salt and IV to the ciphertext
|
||
|
memcpy(ciphertext, salt, SALT_SIZE);
|
||
|
memcpy(ciphertext + SALT_SIZE, iv, IV_SIZE);
|
||
|
|
||
|
if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) handleErrors();
|
||
|
|
||
|
if (!EVP_EncryptUpdate(ctx, ciphertext + SALT_SIZE + IV_SIZE, &len, (unsigned char *)plaintext, strlen(plaintext))) handleErrors();
|
||
|
*ciphertext_len = len;
|
||
|
|
||
|
if (!EVP_EncryptFinal_ex(ctx, ciphertext + SALT_SIZE + IV_SIZE + len, &len)) handleErrors();
|
||
|
*ciphertext_len += len;
|
||
|
|
||
|
EVP_CIPHER_CTX_free(ctx);
|
||
|
}
|
||
|
|
||
|
void decryptPassword(const unsigned char *ciphertext, char *plaintext, int ciphertext_len) {
|
||
|
unsigned char key[KEY_SIZE], iv[IV_SIZE], salt[SALT_SIZE];
|
||
|
memcpy(salt, ciphertext, SALT_SIZE);
|
||
|
memcpy(iv, ciphertext + SALT_SIZE, IV_SIZE);
|
||
|
|
||
|
deriveKey(key, salt);
|
||
|
|
||
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||
|
int len, plaintext_len;
|
||
|
|
||
|
if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) handleErrors();
|
||
|
|
||
|
if (!EVP_DecryptUpdate(ctx, (unsigned char *)plaintext, &len, ciphertext + SALT_SIZE + IV_SIZE, ciphertext_len - SALT_SIZE - IV_SIZE)) handleErrors();
|
||
|
plaintext_len = len;
|
||
|
|
||
|
if (!EVP_DecryptFinal_ex(ctx, (unsigned char *)plaintext + len, &len)) handleErrors();
|
||
|
plaintext_len += len;
|
||
|
plaintext[plaintext_len] = '\0';
|
||
|
|
||
|
EVP_CIPHER_CTX_free(ctx);
|
||
|
}
|
||
|
|
||
|
void savePassword(const char *service, const char *username, const char *password) {
|
||
|
FILE *vault = fopen("vault.csv", "a");
|
||
|
if (!vault) {
|
||
|
printf("Error opening vault file.\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
unsigned char ciphertext[MAX_CIPHERTEXT_LENGTH];
|
||
|
int ciphertext_len;
|
||
|
unsigned char salt[SALT_SIZE];
|
||
|
RAND_bytes(salt, sizeof(salt));
|
||
|
|
||
|
encryptPassword(password, ciphertext, salt, &ciphertext_len);
|
||
|
|
||
|
// Convert to hex string for storage
|
||
|
char hexCiphertext[MAX_CIPHERTEXT_LENGTH * 2 + 1]; // Enough space for hex representation
|
||
|
for (int i = 0; i < ciphertext_len + SALT_SIZE + IV_SIZE; i++) {
|
||
|
sprintf(hexCiphertext + i * 2, "%02x", ciphertext[i]);
|
||
|
}
|
||
|
|
||
|
fprintf(vault, "%s,%s,%s\n", service, username, hexCiphertext);
|
||
|
fclose(vault);
|
||
|
}
|
||
|
|
||
|
void getPassword(const char *service) {
|
||
|
FILE *vault = fopen("vault.csv", "r");
|
||
|
if (!vault) {
|
||
|
printf("Error opening vault file.\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
char line[512];
|
||
|
while (fgets(line, sizeof(line), vault)) {
|
||
|
char *storedService = strtok(line, ",");
|
||
|
char *storedUsername = strtok(NULL, ",");
|
||
|
char *encryptedPassword = strtok(NULL, "\n");
|
||
|
|
||
|
if (strcmp(service, storedService) == 0) {
|
||
|
unsigned char ciphertext[MAX_CIPHERTEXT_LENGTH];
|
||
|
int ciphertext_len = strlen(encryptedPassword) / 2; // Each byte is represented by 2 hex characters
|
||
|
for (int i = 0; i < ciphertext_len; i++) {
|
||
|
sscanf(encryptedPassword + 2 * i, "%2hhx", &ciphertext[i]);
|
||
|
}
|
||
|
|
||
|
char decryptedPassword[MAX_CIPHERTEXT_LENGTH];
|
||
|
decryptPassword(ciphertext, decryptedPassword, ciphertext_len);
|
||
|
printf("Service: %s\nUsername: %s\nPassword: %s\n", storedService, storedUsername, decryptedPassword);
|
||
|
fclose(vault);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
printf("Service not found.\n");
|
||
|
fclose(vault);
|
||
|
}
|
||
|
|
||
|
void listServices() {
|
||
|
FILE *vault = fopen("vault.csv", "r");
|
||
|
if (!vault) {
|
||
|
printf("Error opening vault file.\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
printf("Services in the vault:\n");
|
||
|
char line[512];
|
||
|
while (fgets(line, sizeof(line), vault)) {
|
||
|
char *storedService = strtok(line, ",");
|
||
|
// Check if storedService exists
|
||
|
if (storedService != NULL) {
|
||
|
printf("%s\n", storedService);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fclose(vault);
|
||
|
}
|
||
|
|
||
|
int main(int argc, char **argv) {
|
||
|
char *operation = NULL;
|
||
|
char *service = NULL;
|
||
|
char *username = NULL;
|
||
|
char *password = NULL;
|
||
|
|
||
|
parseArgs(argc, argv, &operation, &service, &username, &password);
|
||
|
getMasterPassword(masterPassword); // Get the master password
|
||
|
|
||
|
if (operation != NULL) {
|
||
|
if (strcmp(operation, "add") == 0) {
|
||
|
char generatedPassword[256];
|
||
|
if (password == NULL) {
|
||
|
generatePassword(generatedPassword);
|
||
|
savePassword(service, username, generatedPassword);
|
||
|
} else {
|
||
|
savePassword(service, username, password);
|
||
|
}
|
||
|
} else if (strcmp(operation, "get") == 0) {
|
||
|
getPassword(service);
|
||
|
} else if (strcmp(operation, "list") == 0) {
|
||
|
listServices();
|
||
|
}
|
||
|
} else {
|
||
|
printf("No valid operation specified.\n");
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|