Add new encryption scheme in CLI. Fix a bug : encrypt_domain doesn't add \0 on message returns leading to malformed server requests

This commit is contained in:
Grégory Soutadé 2017-04-17 20:39:53 +02:00
parent 36db5056a3
commit da72cb46eb

View File

@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2016 Grégory Soutadé
Copyright (C) 2013-2017 Grégory Soutadé
This file is part of gPass.
@ -23,6 +23,7 @@
#include <string.h>
#include <curl/curl.h>
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include "ini.h"
@ -33,6 +34,7 @@
#define DEFAULT_PBKDF2_LEVEL 1000
#define MASTER_KEY_LENGTH (256/8)
#define GLOBAL_IV_LENGTH 16
#define BLOCK_SIZE (128/8)
#define DEFAULT_SERVER_PORT 443
#define SERVER_PROTOCOL 4
@ -52,17 +54,49 @@ struct gpass_parameters {
char *ca_path;
unsigned verify_ssl_peer;
unsigned port_set;
unsigned crypto_v1_compatible;
unsigned char *global_iv;
} ;
#if OPENSSL_VERSION_NUMBER >= 0x10010000
// OpenSSL >= 1.1
static EVP_MD_CTX * s_md_ctx;
#else
static EVP_MD_CTX * s_md_ctx;
static EVP_MD_CTX ss_md_ctx;
#define EVP_MD_CTX_new(...) &ss_md_ctx
#define EVP_MD_CTX_free(...)
#endif
static const EVP_MD * s_md_256;
static EVP_CIPHER_CTX * s_cipher_ctx;
static int digest(unsigned char** out, unsigned char* in, unsigned size)
{
*out = NULL;
EVP_DigestInit(s_md_ctx, s_md_256);
EVP_DigestUpdate(s_md_ctx, in, size);
*out = malloc(32);
return EVP_DigestFinal(s_md_ctx, *out, NULL);
}
static void derive_master_key(struct gpass_parameters* params)
{
if (!params->derived_master_key)
params->derived_master_key = malloc(MASTER_KEY_LENGTH);
if (!params->global_iv)
params->global_iv = malloc(GLOBAL_IV_LENGTH);
PKCS5_PBKDF2_HMAC(params->orig_master_key, strlen(params->orig_master_key),
(unsigned char*)params->salt, strlen(params->salt),
params->pbkdf2_level, EVP_sha256(),
MASTER_KEY_LENGTH, params->derived_master_key);
PKCS5_PBKDF2_HMAC(params->salt, strlen(params->salt),
(unsigned char*)params->orig_master_key, strlen(params->orig_master_key),
params->pbkdf2_level, EVP_sha256(),
GLOBAL_IV_LENGTH, params->global_iv);
}
static void bin_to_hex(unsigned char* bin, unsigned char* hex, unsigned bin_size)
@ -112,10 +146,9 @@ static void hex_to_bin(unsigned char* bin, unsigned char* hex, long hex_size)
}
}
static void encrypt_domain(struct gpass_parameters* params, char* domain,
static void encrypt_domain_v1(struct gpass_parameters* params, char* domain,
unsigned char** res, unsigned* out_size)
{
EVP_CIPHER_CTX* evp_ctx;
unsigned size = 2+strlen(domain)+1+strlen(params->username);
unsigned char* buffer, *tmp;
@ -133,10 +166,8 @@ static void encrypt_domain(struct gpass_parameters* params, char* domain,
tmp = malloc(size);
*res = malloc(size*2);
evp_ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit(evp_ctx, EVP_aes_256_ecb(), params->derived_master_key, NULL);
EVP_CipherUpdate(evp_ctx, tmp, (int*)out_size, buffer, size);
EVP_CIPHER_CTX_free(evp_ctx);
EVP_EncryptInit(s_cipher_ctx, EVP_aes_256_ecb(), params->derived_master_key, NULL);
EVP_CipherUpdate(s_cipher_ctx, tmp, (int*)out_size, buffer, size);
bin_to_hex(tmp, *res, size);
@ -146,25 +177,66 @@ static void encrypt_domain(struct gpass_parameters* params, char* domain,
free(tmp);
}
static void append_to_request(char** request, char* new_req)
static void encrypt_domain(struct gpass_parameters* params, char* domain,
unsigned char** res, unsigned* out_size)
{
unsigned size = strlen(domain)+1+strlen(params->username);
unsigned padded_size;
unsigned char* buffer, *tmp;
if (params->verbose)
printf("%s: %s\n", __func__, domain);
if ((size % BLOCK_SIZE))
size = ((size/BLOCK_SIZE)+1)*BLOCK_SIZE;
padded_size = size;
size += 16; // For digest
buffer = malloc(size);
memset(buffer, 0, size);
snprintf((char*)buffer, size, "%s;%s", domain, params->username);
// Append digest
digest(&tmp, buffer, padded_size);
memcpy(&buffer[padded_size], &tmp[8], 16);
free(tmp);
tmp = malloc(size);
*res = malloc(size*2);
EVP_EncryptInit(s_cipher_ctx, EVP_aes_256_cbc(), params->derived_master_key, params->global_iv);
EVP_CipherUpdate(s_cipher_ctx, tmp, (int*)out_size, buffer, size);
bin_to_hex(tmp, *res, size);
*out_size *= 2;
free(buffer);
free(tmp);
}
static void append_to_request(char** request, char* new_req, unsigned new_req_size)
{
static int cur_req_idx = 0;
int size_added;
if (!cur_req_idx)
{
*request = malloc(3+strlen(new_req)+1);
sprintf(*request, "k0=%s", new_req);
*request = malloc(3+new_req_size+1);
snprintf(*request, 3+new_req_size+1, "k0=%s", new_req);
}
else
{
size_added = 4+strlen(new_req);
size_added = 4+new_req_size;
if (cur_req_idx >= 10)
size_added++;
*request = realloc(*request, strlen(*request)+1+size_added);
sprintf(&((*request)[strlen(*request)]), "&k%d=%s", cur_req_idx, new_req);
snprintf(&((*request)[strlen(*request)]), size_added+1, "&k%d=%s",
cur_req_idx, new_req);
}
cur_req_idx++;
@ -243,11 +315,10 @@ static int ask_server(struct gpass_parameters* params)
{
char* wc_domain, *saveptr, *token, *cur_ptr;
unsigned char* enc_domain;
unsigned enc_size;
unsigned enc_size, matched_key = 0, crypto_v1_index = 1;
char* request = NULL;
int ret = -1, res;
int ret = -1, res, len;
CURL *curl;
EVP_CIPHER_CTX* evp_ctx;
char response[RESPONSE_SIZE];
unsigned char password[256];
@ -255,17 +326,32 @@ static int ask_server(struct gpass_parameters* params)
printf("Username: %s\n", params->username);
encrypt_domain(params, params->domain, &enc_domain, &enc_size);
append_to_request(&request, (char*)enc_domain);
append_to_request(&request, (char*)enc_domain, enc_size);
free(enc_domain);
wc_domain = wildcard_domain(params->domain);
if (wc_domain)
{
crypto_v1_index++;
encrypt_domain(params, wc_domain, &enc_domain, &enc_size);
append_to_request(&request, (char*)enc_domain);
append_to_request(&request, (char*)enc_domain, enc_size);
free(enc_domain);
}
if (params->crypto_v1_compatible)
{
encrypt_domain_v1(params, params->domain, &enc_domain, &enc_size);
append_to_request(&request, (char*)enc_domain, enc_size);
free(enc_domain);
if (wc_domain)
{
encrypt_domain_v1(params, wc_domain, &enc_domain, &enc_size);
append_to_request(&request, (char*)enc_domain, enc_size);
free(enc_domain);
}
}
if (params->verbose)
printf("Request: %s\n", request);
@ -331,13 +417,23 @@ static int ask_server(struct gpass_parameters* params)
hex_to_bin(password, (unsigned char*)cur_ptr, strlen(cur_ptr));
evp_ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit(evp_ctx, EVP_aes_256_ecb(), params->derived_master_key, NULL);
EVP_CipherUpdate(evp_ctx, password, &res, password, strlen(cur_ptr)/2);
EVP_CIPHER_CTX_free(evp_ctx);
if (matched_key >= crypto_v1_index)
{
// Crypto v1
EVP_DecryptInit(s_cipher_ctx, EVP_aes_256_ecb(), params->derived_master_key, NULL);
EVP_CipherUpdate(s_cipher_ctx, password, &res, password, strlen(cur_ptr)/2);
// Remove salt
password[strlen((char*)password)-3] = 0;
}
else
{
EVP_DecryptInit(s_cipher_ctx, EVP_aes_256_cbc(), params->derived_master_key, params->global_iv);
EVP_CipherUpdate(s_cipher_ctx, password, &res, password, strlen(cur_ptr)/2);
// Remove salt
len = strlen((char*)password);
memmove(password, &password[3], len-3);
password[len-3] = 0;
}
printf("Password found: %s\n", password);
ret = 0;
goto end;
@ -353,6 +449,12 @@ static int ask_server(struct gpass_parameters* params)
break;
}
}
else if (!STRNCMP(token, "matched_key"))
{
cur_ptr += sizeof("matched_key"); // includes "="
matched_key = atoi(cur_ptr);
}
else
{
fprintf(stderr, "Error: Unknown server response %s\n", token);
@ -376,6 +478,7 @@ static void init_parameters(struct gpass_parameters* params)
params->pbkdf2_level = DEFAULT_PBKDF2_LEVEL;
params->server_port = DEFAULT_SERVER_PORT;
params->verify_ssl_peer = 1;
params->crypto_v1_compatible = 1; // For now, in the next version it must a command line parameter
}
static void release_parameters(struct gpass_parameters* params)
@ -387,6 +490,7 @@ static void release_parameters(struct gpass_parameters* params)
if (params->orig_master_key) free(params->orig_master_key);
if (params->derived_master_key) free(params->derived_master_key);
if( params->ca_path) free(params->ca_path);
if (params->global_iv) free(params->global_iv);
}
static int check_parameters(struct gpass_parameters* params)
@ -556,6 +660,12 @@ int main(int argc, char** argv)
}
}
s_md_ctx = EVP_MD_CTX_new();
s_md_256 = EVP_sha256();
EVP_DigestInit(s_md_ctx, s_md_256);
s_cipher_ctx = EVP_CIPHER_CTX_new();
// Let's go
tmp = getpass("Enter master key: ");
@ -577,5 +687,8 @@ int main(int argc, char** argv)
end:
release_parameters(&params);
if (s_md_ctx) EVP_MD_CTX_free(s_md_ctx);
if (s_cipher_ctx) EVP_CIPHER_CTX_free(s_cipher_ctx);
return ret;
}