Skip to content

Commit

Permalink
Merge pull request #50 from atarnvik/feature/gcm-decryption
Browse files Browse the repository at this point in the history
Add support for AES/GCM/NoPadding decryption of eurl parameter
  • Loading branch information
beetlebugorg authored Aug 13, 2024
2 parents 12ba4dc + 537620a commit 5e6df5d
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 8 deletions.
4 changes: 3 additions & 1 deletion autorun.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ done

"$AUTORECONF" -i || exit $?
"$AUTOMAKE" || exit $?
"$AUTOCONF" || exit $?01
"$AUTOCONF" || exit $?01

./configure $@
124 changes: 117 additions & 7 deletions src/mod_dims.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ dims_create_config(apr_pool_t *p, server_rec *s)
config->curl_queue_size = 10;
config->cache_dir = NULL;
config->secret_key = apr_pstrdup(p,"m0d1ms");
config->encryption_algorithm = "AES/ECB/PKCS5Padding";
config->max_expiry_period= 0; // never expire

return (void *) config;
Expand Down Expand Up @@ -253,6 +254,15 @@ dims_config_set_encoded_fetch(cmd_parms *cmd, void *dummy, const char *arg)
return NULL;
}

static const char *
dims_config_set_encryption_algorithm(cmd_parms *cmd, void *dummy, const char *arg)
{
dims_config_rec *config = (dims_config_rec *) ap_get_module_config(
cmd->server->module_config, &dims_module);
config->encryption_algorithm = (char *) arg;
return NULL;
}

static const char *
dims_config_set_default_output_format(cmd_parms *cmd, void *dummy, const char *arg)
{
Expand Down Expand Up @@ -1599,6 +1609,97 @@ aes_128_decrypt(request_rec *r, unsigned char *key, unsigned char *encrypted_tex
return plaintext;
}

static char *
aes_128_gcm_decrypt(request_rec *r, unsigned char *key, unsigned char *base64_encrypted_text) {
EVP_CIPHER_CTX *ctx;
int ret;
int plaintext_length = 0;
int out_length;
char *plaintext;

// Decode the Base64 input
int encrypted_length = apr_base64_decode_len((const char *)base64_encrypted_text);
unsigned char *encrypted_data = apr_palloc(r->pool, encrypted_length);
int decoded_length = apr_base64_decode((char *)encrypted_data, (const char *)base64_encrypted_text);

// Extract IV (12 bytes), ciphertext, and tag (16 bytes)
unsigned char *iv = encrypted_data;
unsigned char *encrypted_text = encrypted_data + 12; // 12-byte IV
int ciphertext_length = decoded_length - 12 - 16; // 16-byte tag at the end
unsigned char *tag = encrypted_text + ciphertext_length; // 16-byte tag

// Initialize the context
if (!(ctx = EVP_CIPHER_CTX_new())) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to create new EVP_CIPHER_CTX");
ERR_print_errors_cb(aes_errors, r);
return NULL;
}

// Initialize the decryption operation
if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptInit_ex failed (1)");
ERR_print_errors_cb(aes_errors, r);
EVP_CIPHER_CTX_free(ctx);
return NULL;
}

// Set the IV length, if necessary
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_CIPHER_CTX_ctrl failed to set IV length");
ERR_print_errors_cb(aes_errors, r);
EVP_CIPHER_CTX_free(ctx);
return NULL;
}

// Set the key and IV
if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptInit_ex failed (2)");
ERR_print_errors_cb(aes_errors, r);
EVP_CIPHER_CTX_free(ctx);
return NULL;
}

plaintext = apr_palloc(r->pool, ciphertext_length + 1); // +1 for null terminator
if (!plaintext) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Memory allocation failed");
EVP_CIPHER_CTX_free(ctx);
return NULL;
}

// Provide the message to be decrypted and obtain the plaintext output
if (!EVP_DecryptUpdate(ctx, (unsigned char *)plaintext, &out_length, encrypted_text, ciphertext_length)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptUpdate failed");
ERR_print_errors_cb(aes_errors, r);
EVP_CIPHER_CTX_free(ctx);
return NULL;
}

plaintext_length = out_length;

// Set expected tag value (must be done after EVP_DecryptUpdate)
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_CIPHER_CTX_ctrl failed to set tag");
ERR_print_errors_cb(aes_errors, r);
EVP_CIPHER_CTX_free(ctx);
return NULL;
}

// Finalize the decryption
ret = EVP_DecryptFinal_ex(ctx, (unsigned char *)plaintext + plaintext_length, &out_length);

EVP_CIPHER_CTX_free(ctx);

if (ret > 0) {
plaintext_length += out_length;
plaintext[plaintext_length] = '\0'; // Explicitly add the null terminator
return plaintext;
} else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "EVP_DecryptFinal_ex failed");
ERR_print_errors_cb(aes_errors, r);
return NULL;
}
}

/**
* The apache handler. Apache will call this method when a request
* for /dims/, /dims3/, /dims4/ or an image is recieved.
Expand Down Expand Up @@ -1786,9 +1887,6 @@ dims_handler(request_rec *r)
} else if (strncmp(token, "eurl=", 4) == 0) {
eurl = apr_pstrdup(r->pool, token + 5);

unsigned char *encrypted_text = apr_palloc(r->pool, apr_base64_decode_len(eurl));
int encrypted_length = apr_base64_decode((char *) encrypted_text, eurl);

// Hash secret via SHA-1.
unsigned char *secret = (unsigned char *) d->client_config->secret_key;
unsigned char hash[SHA_DIGEST_LENGTH];
Expand All @@ -1797,7 +1895,7 @@ dims_handler(request_rec *r)
// Convert to hex.
char hex[SHA_DIGEST_LENGTH * 2 + 1];
if (apr_escape_hex(hex, hash, SHA_DIGEST_LENGTH, 0, NULL) != APR_SUCCESS) {
return dims_cleanup(d, NULL, DIMS_BAD_ARGUMENTS);
return dims_cleanup(d, "URL Decryption Failed", DIMS_FAILURE);
}

// Use first 16 bytes.
Expand All @@ -1809,11 +1907,19 @@ dims_handler(request_rec *r)
unsigned char *s = key;
while (*s) { *s = toupper(*s); s++; }

fixed_url = aes_128_decrypt(r, key, encrypted_text, encrypted_length);
if (d->config->encryption_algorithm != NULL &&
strncmp((char *)d->config->encryption_algorithm, "AES/GCM/NoPadding", strlen("AES/GCM/NoPadding")) == 0) {

fixed_url = aes_128_gcm_decrypt(r, key, eurl);
} else {
//Default is AES/ECB/PKCS5Padding
unsigned char *encrypted_text = apr_palloc(r->pool, apr_base64_decode_len(eurl));
int encrypted_length = apr_base64_decode((char *) encrypted_text, eurl);
fixed_url = aes_128_decrypt(r, key, encrypted_text, encrypted_length);
}
if (fixed_url == NULL) {
return dims_cleanup(d, "URL Description Failed", DIMS_FAILURE);
return dims_cleanup(d, "URL Decryption Failed", DIMS_FAILURE);
}

ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, d->r, "Decrypted URL: %s", fixed_url);
break;

Expand Down Expand Up @@ -2173,6 +2279,10 @@ static const command_rec dims_commands[] =
dims_config_set_encoded_fetch, NULL, RSRC_CONF,
"Should DIMS encode image url before fetching it."
"The default is 0."),
AP_INIT_TAKE1("DimsEncryptionAlgorithm",
dims_config_set_encryption_algorithm, NULL, RSRC_CONF,
"What algorithm should DIMS user to decrypt the 'eurl' parameter."
"The default is AES/ECB/PKCS5Padding."),
AP_INIT_TAKE1("DimsDefaultOutputFormat",
dims_config_set_default_output_format, NULL, RSRC_CONF,
"Default output format if 'format' command is not present in the request."),
Expand Down
1 change: 1 addition & 0 deletions src/mod_dims.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ struct dims_config_rec {

int curl_queue_size;
char *secret_key;
char *encryption_algorithm;
long max_expiry_period;
char *cache_dir;
char *default_image_prefix;
Expand Down

0 comments on commit 5e6df5d

Please sign in to comment.