diff --git a/README.md b/README.md index 890222f..3b54a96 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,24 @@ Synopsis ======== ```nginx +set $key "abcdefghijklmnopqrstuvwxyz123456"; +set $iv "1234567812345678"; +set $ses_expires "3600"; + # key must be of 32 bytes long +encrypted_session_key $key; + encrypted_session_key "abcdefghijklmnopqrstuvwxyz123456"; # iv must not be longer than 16 bytes # default: "deadbeefdeadbeef" (w/o quotes) -encrypted_session_iv "1234567812345678"; +encrypted_session_iv $iv; + +encrypted_session_iv_in_content; # enable this only if you rotate IV for each encryption +encrypted_session_mode gcm; # cbc is the default mode. # default: 1d (1 day) -encrypted_session_expires 3600; # in sec +encrypted_session_expires $ses_expires; # in sec location /encrypt { set $raw 'text to encrypted'; # from the ngx_rewrite module diff --git a/src/ngx_http_encrypted_session_cipher.c b/src/ngx_http_encrypted_session_cipher.c index fe968e7..269ea84 100644 --- a/src/ngx_http_encrypted_session_cipher.c +++ b/src/ngx_http_encrypted_session_cipher.c @@ -12,6 +12,7 @@ #include "ngx_http_encrypted_session_cipher.h" #include +#include #include #include @@ -19,13 +20,28 @@ static uint64_t ngx_http_encrypted_session_ntohll(uint64_t n); static uint64_t ngx_http_encrypted_session_htonll(uint64_t n); +const EVP_CIPHER* +ngx_http_encrypted_session_get_cipher(enum ngx_http_encrypted_session_mode mode) +{ + if (mode == ngx_http_encrypted_session_mode_cbc) + { + return EVP_aes_256_cbc(); + } + else if (mode == ngx_http_encrypted_session_mode_gcm) + { + return EVP_aes_256_gcm(); + } + + return NULL; +} ngx_int_t ngx_http_encrypted_session_aes_mac_encrypt( ngx_http_encrypted_session_main_conf_t *emcf, ngx_pool_t *pool, ngx_log_t *log, const u_char *iv, size_t iv_len, const u_char *key, size_t key_len, const u_char *in, size_t in_len, ngx_uint_t expires, - u_char **dst, size_t *dst_len) + enum ngx_http_encrypted_session_mode mode, + u_char **dst, size_t *dst_len, u_char **tag) { const EVP_CIPHER *cipher; u_char *p, *data; @@ -49,7 +65,10 @@ ngx_http_encrypted_session_aes_mac_encrypt( } } - cipher = EVP_aes_256_cbc(); + cipher = ngx_http_encrypted_session_get_cipher(mode); + if (!cipher) { + goto evp_error; + } block_size = EVP_CIPHER_block_size(cipher); @@ -106,6 +125,15 @@ ngx_http_encrypted_session_aes_mac_encrypt( p += len; ret = EVP_EncryptFinal(emcf->session_ctx, p, &len); + if (!ret) { + goto evp_error; + } + + if (mode == ngx_http_encrypted_session_mode_gcm) { + *tag = (u_char*)ngx_pcalloc(pool, ngx_http_encrypted_session_aes_tag_size); + ret = EVP_CIPHER_CTX_ctrl(emcf->session_ctx, EVP_CTRL_GCM_GET_TAG, + ngx_http_encrypted_session_aes_tag_size, *tag); + } emcf->reset_cipher_ctx(emcf->session_ctx); @@ -138,8 +166,10 @@ ngx_int_t ngx_http_encrypted_session_aes_mac_decrypt( ngx_http_encrypted_session_main_conf_t *emcf, ngx_pool_t *pool, ngx_log_t *log, const u_char *iv, size_t iv_len, const u_char *key, - size_t key_len, const u_char *in, size_t in_len, u_char **dst, - size_t *dst_len) + size_t key_len, const u_char *in, size_t in_len, + enum ngx_http_encrypted_session_mode mode, + u_char *tag, + u_char **dst, size_t *dst_len) { const EVP_CIPHER *cipher; int ret; @@ -170,7 +200,10 @@ ngx_http_encrypted_session_aes_mac_decrypt( } } - cipher = EVP_aes_256_cbc(); + cipher = ngx_http_encrypted_session_get_cipher(mode); + if (!cipher) { + goto evp_error; + } ret = EVP_DecryptInit(emcf->session_ctx, cipher, key, iv); if (!ret) { @@ -199,6 +232,14 @@ ngx_http_encrypted_session_aes_mac_decrypt( p += len; + if (mode == ngx_http_encrypted_session_mode_gcm) { + ret = EVP_CIPHER_CTX_ctrl(emcf->session_ctx, EVP_CTRL_GCM_SET_TAG, + ngx_http_encrypted_session_aes_tag_size, tag); + if (!ret) { + goto evp_error; + } + } + ret = EVP_DecryptFinal(emcf->session_ctx, p, &len); emcf->reset_cipher_ctx(emcf->session_ctx); @@ -291,3 +332,21 @@ ngx_http_encrypted_session_htonll(uint64_t n) + htonl((unsigned long) (n >> 32)); #endif } + +unsigned char* +ngx_http_encrypted_session_hmac(ngx_pool_t *pool, + const u_char *key, size_t key_len, + const u_char *data, size_t data_len, u_char **dst, size_t *dst_len) +{ + u_char *result = NULL; + u_char *input = ngx_pcalloc(pool, data_len + 1); + ngx_memcpy(input, data, data_len); + + unsigned int len; + result = HMAC(EVP_sha256(), key, key_len, input, data_len, result, &len); + *dst_len = len; + *dst = (u_char*)ngx_pcalloc(pool, len + 1); + ngx_memcpy(*dst, result, len); + + return *dst; +} \ No newline at end of file diff --git a/src/ngx_http_encrypted_session_cipher.h b/src/ngx_http_encrypted_session_cipher.h index 2ac4718..4f9f176 100644 --- a/src/ngx_http_encrypted_session_cipher.h +++ b/src/ngx_http_encrypted_session_cipher.h @@ -5,6 +5,7 @@ #include #include #include +#include typedef int (*cipher_ctx_reset_handle) (EVP_CIPHER_CTX *ctx); @@ -18,22 +19,35 @@ typedef struct { enum { ngx_http_encrypted_session_key_length = 256 / 8, - ngx_http_encrypted_session_iv_length = EVP_MAX_IV_LENGTH + ngx_http_encrypted_session_iv_length = EVP_MAX_IV_LENGTH, + ngx_http_encrypted_session_aes_tag_size = 16 }; +enum ngx_http_encrypted_session_mode { + ngx_http_encrypted_session_mode_unknown = 0, // unknown / unset value. + ngx_http_encrypted_session_mode_cbc = 1, // equivalent of setting cbc string in config or nothing at all. + ngx_http_encrypted_session_mode_gcm = 2 // equivalent of explicitly setting gcm in nginx config. +}; ngx_int_t ngx_http_encrypted_session_aes_mac_encrypt( ngx_http_encrypted_session_main_conf_t *emcf, ngx_pool_t *pool, ngx_log_t *log, const u_char *iv, size_t iv_len, const u_char *key, size_t key_len, const u_char *in, size_t in_len, - ngx_uint_t expires, u_char **dst, size_t *dst_len); + ngx_uint_t expires, enum ngx_http_encrypted_session_mode mode, + u_char **dst, size_t *dst_len, u_char **tag); ngx_int_t ngx_http_encrypted_session_aes_mac_decrypt( ngx_http_encrypted_session_main_conf_t *emcf, ngx_pool_t *pool, ngx_log_t *log, const u_char *iv, size_t iv_len, const u_char *key, - size_t key_len, const u_char *in, size_t in_len, u_char **dst, - size_t *dst_len); - + size_t key_len, const u_char *in, size_t in_len, + enum ngx_http_encrypted_session_mode mode, + u_char *tag, + u_char **dst, size_t *dst_len); + +unsigned char* ngx_http_encrypted_session_hmac( + ngx_pool_t *pool, + const u_char *key, size_t key_len, + const u_char *data, size_t data_len, u_char **dst, size_t *dst_len); #endif /* NGX_HTTP_ENCRYPTED_SESSION_CIPHER_H */ diff --git a/src/ngx_http_encrypted_session_module.c b/src/ngx_http_encrypted_session_module.c index a2206be..348306b 100644 --- a/src/ngx_http_encrypted_session_module.c +++ b/src/ngx_http_encrypted_session_module.c @@ -10,20 +10,31 @@ #include "ddebug.h" #include +#include #include "ngx_http_encrypted_session_cipher.h" #define ngx_http_encrypted_session_default_iv (u_char *) "deadbeefdeadbeef" #define ngx_http_encrypted_session_default_expires 86400 +const size_t IV_LENGTH = 16; +const size_t SIGNATURE_LENGTH = 32; typedef struct { - u_char *key; - u_char *iv; - time_t expires; - + u_char *key; + size_t key_len; + u_char *iv; + size_t iv_len; + time_t expires; + u_char *expires_var; + size_t expires_var_len; + ngx_flag_t iv_in_content; + enum ngx_http_encrypted_session_mode encryption_mode; } ngx_http_encrypted_session_conf_t; +static time_t ngx_http_encrypted_session_get_expires_from_conf( + ngx_http_request_t *r, + ngx_http_encrypted_session_conf_t *conf); static ngx_int_t ngx_http_set_encode_encrypted_session(ngx_http_request_t *r, ngx_str_t *res, ngx_http_variable_value_t *v); @@ -39,9 +50,14 @@ static char *ngx_http_encrypted_session_key(ngx_conf_t *cf, ngx_command_t *cmd, static char *ngx_http_encrypted_session_iv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_encrypted_session_mode_set(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + static char *ngx_http_encrypted_session_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_encrypted_iv_in_content(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_encrypted_session_init(ngx_conf_t *cf); static void *ngx_http_encrypted_session_create_main_conf(ngx_conf_t *cf); @@ -53,7 +69,6 @@ static void *ngx_http_encrypted_session_create_conf(ngx_conf_t *cf); static char *ngx_http_encrypted_session_merge_conf(ngx_conf_t *cf, void *parent, void *child); - static ndk_set_var_t ngx_http_set_encode_encrypted_session_filter = { NDK_SET_VAR_VALUE, (void *) ngx_http_set_encode_encrypted_session, @@ -88,6 +103,15 @@ static ngx_command_t ngx_http_encrypted_session_commands[] = { 0, NULL }, + { + ngx_string("encrypted_session_mode"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_encrypted_session_mode_set, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL + }, { ngx_string("encrypted_session_expires"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF @@ -115,7 +139,14 @@ static ngx_command_t ngx_http_encrypted_session_commands[] = { 0, &ngx_http_set_decode_encrypted_session_filter }, - + { ngx_string("encrypted_session_iv_in_content"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, + ngx_http_encrypted_iv_in_content, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL + }, ngx_null_command }; @@ -150,6 +181,164 @@ ngx_module_t ngx_http_encrypted_session_module = { NGX_MODULE_V1_PADDING }; +static ngx_str_t ngx_http_get_variable_by_name(ngx_http_request_t *r, + unsigned char *name, size_t name_len, ngx_http_encrypted_session_conf_t *conf) +{ + ngx_http_variable_value_t *v; + ngx_str_t name_str; + name_str.data = name; + name_str.len = name_len; + + ngx_uint_t key = ngx_hash_strlow(name, name, name_len); + v = ngx_http_get_variable(r, &name_str, key); + + if (v->not_found) { + return name_str; + } + + ngx_str_t var_value; + var_value.len = v->len; + var_value.data = v->data; + return var_value; +} + +static time_t ngx_http_encrypted_session_parse_expires(ngx_str_t* value) +{ + return ngx_parse_time(value, 1); +} + +static time_t ngx_http_encrypted_session_get_expires_from_conf( + ngx_http_request_t *r, + ngx_http_encrypted_session_conf_t *conf) +{ + if (!conf->expires_var) { + return conf->expires; + } + + ngx_str_t expires = ngx_http_get_variable_by_name( + r, conf->expires_var, conf->expires_var_len, conf); + time_t expires_val = ngx_http_encrypted_session_parse_expires(&expires); + if (expires_val == NGX_ERROR) { + dd("expires %s has an invalid value.", conf->expires_var); + } + + return expires_val; +} + +static u_char* +ngx_http_encrypted_session_build_payload(ngx_http_request_t *r, + ngx_str_t *content, ngx_str_t *iv, size_t *len) +{ + size_t new_len = iv->len + content->len; + u_char *data = (u_char *)ngx_pcalloc(r->pool, new_len + 1); + ngx_memcpy(data, iv->data, iv->len); + ngx_memcpy(data + iv->len, content->data, content->len); + *len = new_len; + + return data; +} + +static ngx_str_t* +ngx_http_session_encrypted_compute_hmac(ngx_http_request_t *r, + ngx_str_t *key, ngx_str_t *content) +{ + size_t signature_len; + u_char* signature; + + ngx_http_encrypted_session_hmac(r->pool, key->data, key->len, + content->data, content->len, + &signature, &signature_len); + + ngx_str_t *result = (ngx_str_t*)ngx_pcalloc(r->pool, sizeof(ngx_str_t)); + result->len = signature_len; + result->data = (u_char*)ngx_pcalloc(r->pool, signature_len + 1); + result->data = signature; + return result; +} + +static ngx_str_t* +ngx_http_session_generate_signature(ngx_http_request_t *r, + ngx_str_t *iv, ngx_str_t *key, ngx_str_t *content, + ngx_str_t *tag, enum ngx_http_encrypted_session_mode mode) +{ + size_t signature_content_len = iv->len + content->len; + if (mode == ngx_http_encrypted_session_mode_gcm) + { + signature_content_len += tag->len; + } + + u_char* signature_content = (u_char*)ngx_pcalloc(r->pool, signature_content_len + 1); + ngx_memcpy(signature_content, iv->data, iv->len); + + if (mode == ngx_http_encrypted_session_mode_gcm) + { + ngx_memcpy(signature_content + iv->len, tag->data, tag->len); + ngx_memcpy(signature_content + iv->len + tag->len, + content->data, content->len); + } + else + { + ngx_memcpy(signature_content + iv->len, content->data, content->len); + } + + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: signature content len=%d", + signature_content_len); + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: signature content=%s", + signature_content); + + ngx_str_t signature_input; + signature_input.len = signature_content_len; + signature_input.data = (u_char*)signature_content; + ngx_str_t *signature = ngx_http_session_encrypted_compute_hmac(r, key, + &signature_input); + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: signature=%s", signature->data); + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: signature len=%d", signature->len); + + return signature; +} + +static ngx_str_t* +ngx_http_session_generate_response_with_iv(ngx_http_request_t *r, + ngx_str_t *iv, ngx_str_t *key, ngx_str_t *content, + ngx_str_t *tag, enum ngx_http_encrypted_session_mode mode) +{ + ngx_str_t *signature = ngx_http_session_generate_signature(r, iv, key, + content, tag, mode); + + size_t new_len = iv->len + signature->len + content->len; + + if (mode == ngx_http_encrypted_session_mode_gcm) + { + new_len += tag->len; + } + + u_char *new_content = (u_char*)ngx_pcalloc(r->pool, new_len + 1); + ngx_memcpy(new_content, iv->data, iv->len); + ngx_memcpy(new_content + iv->len, signature->data, signature->len); + + if (mode == ngx_http_encrypted_session_mode_gcm) + { + ngx_memcpy(new_content + iv->len + signature->len, tag->data, tag->len); + ngx_memcpy(new_content + iv->len + signature->len + tag->len, content->data, content->len); + } + else + { + ngx_memcpy(new_content + iv->len + signature->len, content->data, content->len); + } + + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: encrypted data len=%d", content->len); + + ngx_str_t *payload = (ngx_str_t*)ngx_palloc(r->pool, sizeof(ngx_str_t)); + payload->len = new_len; + payload->data = (u_char*)new_content; + + return payload; +} static ngx_int_t ngx_http_set_encode_encrypted_session(ngx_http_request_t *r, @@ -173,24 +362,80 @@ ngx_http_set_encode_encrypted_session(ngx_http_request_t *r, return NGX_ERROR; } + time_t expires_val = ngx_http_encrypted_session_get_expires_from_conf(r, + conf); + if (expires_val == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "encrypted_session: invalid session expires numeric value " + "defined by the encrypted_session_expires directive"); + return NGX_ERROR; + } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "encrypted_session: expires=%T", conf->expires); + "encrypted_session: expires=%T", expires_val); + + ngx_str_t iv = ngx_http_get_variable_by_name(r, conf->iv, conf->iv_len, + conf); + ngx_str_t key = ngx_http_get_variable_by_name(r, conf->key, conf->key_len, + conf); + + ngx_str_t content; + content.data = (u_char*)ngx_pcalloc(r->pool, v->len + 1); + ngx_memcpy(content.data, v->data, v->len); + content.len = v->len; + + if (conf->iv_in_content) { + size_t new_len; + content.data = ngx_http_encrypted_session_build_payload(r, + &content, &iv, &new_len); + content.len = new_len; + + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: content to encrypt len=%d", + content.len); + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: content to encrypt=%s", + content.data); + } + u_char *tag; rc = ngx_http_encrypted_session_aes_mac_encrypt(emcf, r->pool, - r->connection->log, conf->iv, ngx_http_encrypted_session_iv_length, - conf->key, ngx_http_encrypted_session_key_length, - v->data, v->len, (ngx_uint_t) conf->expires, &dst, &len); + r->connection->log, iv.data, iv.len, key.data, key.len, + content.data, content.len, + (ngx_uint_t) expires_val, + conf->encryption_mode, &dst, &len, &tag); if (rc != NGX_OK) { - dst = NULL; - len = 0; + res->data = NULL; + res->len = 0; ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "encrypted_session: failed to encrypt"); + return NGX_OK; } - res->data = dst; - res->len = len; + if (conf->iv_in_content) { + ngx_str_t encrypted_content; + encrypted_content.len = len; + encrypted_content.data = dst; + + ngx_str_t tag_content; + tag_content.len = ngx_http_encrypted_session_aes_tag_size; + tag_content.data = tag; + + ngx_str_t *result = ngx_http_session_generate_response_with_iv(r, &iv, + &key, &encrypted_content, &tag_content, conf->encryption_mode); + res->data = result->data; + res->len = result->len; + + } else { + res->data = dst; + res->len = len; + } + + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: full response len=%d", + res->len); return NGX_OK; } @@ -218,14 +463,107 @@ ngx_http_set_decode_encrypted_session(ngx_http_request_t *r, return NGX_ERROR; } + ngx_str_t key = ngx_http_get_variable_by_name(r, conf->key, conf->key_len, + conf); + + ngx_str_t iv; + ngx_str_t content; + ngx_str_t tag; + + content.data = v->data; + content.len = v->len; + + if (!conf->iv_in_content) + { + iv = ngx_http_get_variable_by_name(r, conf->iv, conf->iv_len, conf); + } + else + { + if (content.len < IV_LENGTH + SIGNATURE_LENGTH + 1) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "encrypted_session: input to decrypt is too short."); + res->data = NULL; + res->len = 0; + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: input to decrypt len=%d", + content.len); + iv.len = IV_LENGTH; + iv.data = (u_char*)ngx_pcalloc(r->pool, iv.len + 1); + ngx_memcpy(iv.data, content.data, iv.len); + + u_char* signature = (u_char*)ngx_pcalloc(r->pool, SIGNATURE_LENGTH + 1); + ngx_memcpy(signature, content.data + iv.len, SIGNATURE_LENGTH); + + if (conf->encryption_mode == ngx_http_encrypted_session_mode_gcm) + { + tag.len = ngx_http_encrypted_session_aes_tag_size; + tag.data = (u_char*)ngx_pcalloc(r->pool, tag.len); + ngx_memcpy(tag.data, content.data + iv.len + SIGNATURE_LENGTH, tag.len); + } + + ngx_str_t encrypted_content; + if (conf->encryption_mode == ngx_http_encrypted_session_mode_gcm) + { + encrypted_content.len = content.len - iv.len - SIGNATURE_LENGTH - tag.len; + encrypted_content.data = (u_char*)ngx_pcalloc(r->pool, encrypted_content.len + 1); + ngx_memcpy(encrypted_content.data, + v->data + iv.len + SIGNATURE_LENGTH + tag.len, + encrypted_content.len); + } + else + { + encrypted_content.len = content.len - iv.len - SIGNATURE_LENGTH; + encrypted_content.data = (u_char*)ngx_pcalloc(r->pool, encrypted_content.len + 1); + ngx_memcpy(encrypted_content.data, v->data + iv.len + SIGNATURE_LENGTH, encrypted_content.len); + } + + content.data = encrypted_content.data; + content.len = encrypted_content.len; + + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: data len=%d", content.len); + + ngx_str_t *computed_signature = ngx_http_session_generate_signature(r, + &iv, &key, &encrypted_content, &tag, conf->encryption_mode); + if (SIGNATURE_LENGTH != computed_signature->len || + ngx_memcmp(computed_signature->data, signature, SIGNATURE_LENGTH) != 0) + { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "encrypted_session: signatures do not match"); + res->data = NULL; + res->len = 0; + + return NGX_OK; + } + } + rc = ngx_http_encrypted_session_aes_mac_decrypt(emcf, r->pool, - r->connection->log, conf->iv, ngx_http_encrypted_session_iv_length, - conf->key, ngx_http_encrypted_session_key_length, - v->data, v->len, &dst, &len); + r->connection->log, iv.data, iv.len, key.data, key.len, + content.data, content.len, conf->encryption_mode, tag.data, + &dst, &len); if (rc != NGX_OK) { - dst = NULL; - len = 0; + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "encrypted_session: failed to decrypt"); + res->data = NULL; + res->len = 0; + + return NGX_OK; + } + + if (conf->iv_in_content) { + size_t payload_len = len - iv.len; + u_char *result = ngx_pcalloc(r->pool, payload_len + 1); + ngx_memcpy(result, dst + iv.len, payload_len); + dst = result; + len = payload_len; + ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, + "encrypted_session: decrypted content=%s", + dst); } res->data = dst; @@ -248,6 +586,13 @@ ngx_http_encrypted_session_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; + if (value[1].len > 1 && value[1].data[0] == '$') { + llcf->key_len = value[1].len - 1; + llcf->key = (u_char*)ngx_pcalloc(cf->pool, llcf->key_len); + ngx_memcpy(llcf->key, value[1].data + 1, llcf->key_len); + return NGX_CONF_OK; + } + if (value[1].len != ngx_http_encrypted_session_key_length) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "encrypted_session_key: the key must be of %d " @@ -258,6 +603,7 @@ ngx_http_encrypted_session_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } llcf->key = value[1].data; + llcf->key_len = value[1].len; return NGX_CONF_OK; } @@ -276,6 +622,12 @@ ngx_http_encrypted_session_iv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) value = cf->args->elts; + if (value[1].len > 1 && value[1].data[0] == '$') { + llcf->iv = &(value[1].data[1]); + llcf->iv_len = value[1].len - 1; + return NGX_CONF_OK; + } + if (value[1].len > ngx_http_encrypted_session_iv_length) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "encrypted_session_iv: the init vector must NOT " @@ -286,6 +638,7 @@ ngx_http_encrypted_session_iv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } llcf->iv = ngx_pcalloc(cf->pool, ngx_http_encrypted_session_iv_length); + llcf->iv_len = ngx_http_encrypted_session_iv_length; if (llcf->iv == NULL) { return NGX_CONF_ERROR; @@ -301,6 +654,23 @@ ngx_http_encrypted_session_iv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } +static char * +ngx_http_encrypted_session_mode_set(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + ngx_str_t *value; + ngx_http_encrypted_session_conf_t *llcf = conf; + + value = cf->args->elts; + if (value[1].len == 3 && strncmp("cbc", (char*)value[1].data, 3) == 0) { + llcf->encryption_mode = ngx_http_encrypted_session_mode_cbc; + } + else if (value[1].len == 3 && strncmp("gcm", (char*)value[1].data, 3) == 0) { + llcf->encryption_mode = ngx_http_encrypted_session_mode_gcm; + } + + return NGX_CONF_OK; +} static char * ngx_http_encrypted_session_expires(ngx_conf_t *cf, ngx_command_t *cmd, @@ -315,7 +685,13 @@ ngx_http_encrypted_session_expires(ngx_conf_t *cf, ngx_command_t *cmd, value = cf->args->elts; - llcf->expires = ngx_parse_time(&value[1], 1); + if (value[1].len > 1 && value[1].data[0] == '$') { + llcf->expires_var = &(value[1].data[1]); + llcf->expires_var_len = value[1].len - 1; + return NGX_CONF_OK; + } + + llcf->expires = ngx_http_encrypted_session_parse_expires(&value[1]); if (llcf->expires == NGX_ERROR) { return "invalid value"; @@ -326,6 +702,13 @@ ngx_http_encrypted_session_expires(ngx_conf_t *cf, ngx_command_t *cmd, return NGX_CONF_OK; } +static char *ngx_http_encrypted_iv_in_content(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + ngx_http_encrypted_session_conf_t *llcf = conf; + llcf->iv_in_content = 1; + return NGX_CONF_OK; +} static void ngx_http_encrypted_session_free_cipher_ctx(void *data) @@ -401,8 +784,14 @@ ngx_http_encrypted_session_create_conf(ngx_conf_t *cf) } conf->key = NGX_CONF_UNSET_PTR; + conf->key_len = NGX_CONF_UNSET; conf->iv = NGX_CONF_UNSET_PTR; + conf->iv_len = NGX_CONF_UNSET; conf->expires = NGX_CONF_UNSET; + conf->expires_var = NGX_CONF_UNSET_PTR; + conf->expires_var_len = NGX_CONF_UNSET; + conf->iv_in_content = NGX_CONF_UNSET; + conf->encryption_mode = ngx_http_encrypted_session_mode_unknown; return conf; } @@ -415,12 +804,29 @@ ngx_http_encrypted_session_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_encrypted_session_conf_t *conf = child; ngx_conf_merge_ptr_value(conf->key, prev->key, NULL); + ngx_conf_merge_size_value(conf->key_len, prev->key_len, + (size_t)ngx_http_encrypted_session_key_length); ngx_conf_merge_ptr_value(conf->iv, prev->iv, ngx_http_encrypted_session_default_iv); + ngx_conf_merge_size_value(conf->iv_len, prev->iv_len, + (size_t)ngx_http_encrypted_session_iv_length); ngx_conf_merge_value(conf->expires, prev->expires, ngx_http_encrypted_session_default_expires); + ngx_conf_merge_size_value(conf->expires_var_len, prev->expires_var_len, + (size_t)0); + ngx_conf_merge_ptr_value(conf->expires_var, prev->expires_var, + NULL); + ngx_conf_merge_value(conf->iv_in_content, prev->iv_in_content, 0); + + if (conf->encryption_mode == ngx_http_encrypted_session_mode_unknown) { + conf->encryption_mode = prev->encryption_mode; + } + + if (conf->encryption_mode == ngx_http_encrypted_session_mode_unknown) { + conf->encryption_mode = ngx_http_encrypted_session_mode_cbc; + } return NGX_CONF_OK; }