From da72cb46ebe2b2ac8826822314fbeff9b0994cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Mon, 17 Apr 2017 20:39:53 +0200 Subject: [PATCH] Add new encryption scheme in CLI. Fix a bug : encrypt_domain doesn't add \0 on message returns leading to malformed server requests --- cli/main.c | 169 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 141 insertions(+), 28 deletions(-) diff --git a/cli/main.c b/cli/main.c index eaf03b6..50a6e55 100644 --- a/cli/main.c +++ b/cli/main.c @@ -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 #include +#include #include #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 @@ -40,7 +42,7 @@ #define MAX_SUBDOMAINS 10 struct gpass_parameters { - unsigned pbkdf2_level; + unsigned pbkdf2_level; char *server; char *salt; char *domain; @@ -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); - - // Remove salt - password[strlen((char*)password)-3] = 0; + 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: "); @@ -573,9 +683,12 @@ int main(int argc, char** argv) derive_master_key(¶ms); ask_server(¶ms); } - + end: release_parameters(¶ms); + if (s_md_ctx) EVP_MD_CTX_free(s_md_ctx); + if (s_cipher_ctx) EVP_CIPHER_CTX_free(s_cipher_ctx); + return ret; }