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:
		
							
								
								
									
										169
									
								
								cli/main.c
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								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 <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 | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user