Skip to content

Commit 5150790

Browse files
committed
An IV should be generated for each encryption
We now have the ability to decide if the IV is communicated to the client in a non forgeable manner or we only keep it on the server side. Closes #2
1 parent 99662f8 commit 5150790

3 files changed

+210
-13
lines changed

src/ngx_http_encrypted_session_cipher.c

+19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "ngx_http_encrypted_session_cipher.h"
1414
#include <openssl/evp.h>
15+
#include <openssl/hmac.h>
1516
#include <openssl/md5.h>
1617
#include <stdint.h>
1718

@@ -291,3 +292,21 @@ ngx_http_encrypted_session_htonll(uint64_t n)
291292
+ htonl((unsigned long) (n >> 32));
292293
#endif
293294
}
295+
296+
unsigned char*
297+
ngx_http_encrypted_session_hmac(ngx_pool_t *pool,
298+
const u_char *key, size_t key_len,
299+
const u_char *data, size_t data_len, u_char **dst, size_t *dst_len)
300+
{
301+
u_char *result = NULL;
302+
u_char *input = ngx_pcalloc(pool, data_len + 1);
303+
memcpy(input, data, data_len);
304+
305+
unsigned int len;
306+
result = HMAC(EVP_sha256(), key, key_len, input, data_len, result, &len);
307+
*dst_len = len;
308+
*dst = (u_char*)ngx_pcalloc(pool, len + 1);
309+
memcpy(*dst, result, len);
310+
311+
return *dst;
312+
}

src/ngx_http_encrypted_session_cipher.h

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <ngx_core.h>
66
#include <ngx_http.h>
77
#include <openssl/evp.h>
8+
#include <openssl/hmac.h>
89

910

1011
typedef int (*cipher_ctx_reset_handle) (EVP_CIPHER_CTX *ctx);
@@ -34,6 +35,10 @@ ngx_int_t ngx_http_encrypted_session_aes_mac_decrypt(
3435
size_t key_len, const u_char *in, size_t in_len, u_char **dst,
3536
size_t *dst_len);
3637

38+
unsigned char* ngx_http_encrypted_session_hmac(
39+
ngx_pool_t *pool,
40+
const u_char *key, size_t key_len,
41+
const u_char *data, size_t data_len, u_char **dst, size_t *dst_len);
3742

3843
#endif /* NGX_HTTP_ENCRYPTED_SESSION_CIPHER_H */
3944

src/ngx_http_encrypted_session_module.c

+186-13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "ddebug.h"
1111

1212
#include <ndk.h>
13+
#include <string.h>
1314
#include "ngx_http_encrypted_session_cipher.h"
1415

1516
#define ngx_http_encrypted_session_default_iv (u_char *) "deadbeefdeadbeef"
@@ -19,11 +20,16 @@
1920

2021
typedef struct {
2122
u_char *key;
23+
size_t key_len;
2224
u_char *iv;
25+
size_t iv_len;
2326
time_t expires;
24-
27+
ngx_flag_t iv_in_content;
2528
} ngx_http_encrypted_session_conf_t;
2629

30+
const char *PARTS_DELIMITER = ":";
31+
const size_t PARTS_DELIMITER_LEN = 1;
32+
const size_t NUM_OF_DELIMITERS = 2;
2733

2834
static ngx_int_t ngx_http_set_encode_encrypted_session(ngx_http_request_t *r,
2935
ngx_str_t *res, ngx_http_variable_value_t *v);
@@ -42,6 +48,8 @@ static char *ngx_http_encrypted_session_iv(ngx_conf_t *cf, ngx_command_t *cmd,
4248
static char *ngx_http_encrypted_session_expires(ngx_conf_t *cf,
4349
ngx_command_t *cmd, void *conf);
4450

51+
static char *ngx_http_encrypted_iv_in_content(ngx_conf_t *cf,
52+
ngx_command_t *cmd, void *conf);
4553

4654
static ngx_int_t ngx_http_encrypted_session_init(ngx_conf_t *cf);
4755
static void *ngx_http_encrypted_session_create_main_conf(ngx_conf_t *cf);
@@ -53,7 +61,6 @@ static void *ngx_http_encrypted_session_create_conf(ngx_conf_t *cf);
5361
static char *ngx_http_encrypted_session_merge_conf(ngx_conf_t *cf, void *parent,
5462
void *child);
5563

56-
5764
static ndk_set_var_t ngx_http_set_encode_encrypted_session_filter = {
5865
NDK_SET_VAR_VALUE,
5966
(void *) ngx_http_set_encode_encrypted_session,
@@ -115,7 +122,14 @@ static ngx_command_t ngx_http_encrypted_session_commands[] = {
115122
0,
116123
&ngx_http_set_decode_encrypted_session_filter
117124
},
118-
125+
{ ngx_string("include_iv_in_encrypted_payload"),
126+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF
127+
|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
128+
ngx_http_encrypted_iv_in_content,
129+
NGX_HTTP_LOC_CONF_OFFSET,
130+
0,
131+
NULL
132+
},
119133
ngx_null_command
120134
};
121135

@@ -151,14 +165,14 @@ ngx_module_t ngx_http_encrypted_session_module = {
151165
};
152166

153167
static ngx_str_t ngx_http_get_variable_by_name(ngx_http_request_t *r,
154-
unsigned char *name, ngx_http_encrypted_session_conf_t *conf)
168+
unsigned char *name, size_t name_len, ngx_http_encrypted_session_conf_t *conf)
155169
{
156170
ngx_http_variable_value_t *v;
157171
ngx_str_t name_str;
158172
name_str.data = name;
159-
name_str.len = strlen((const char *)name);
173+
name_str.len = name_len;
160174

161-
ngx_uint_t key = ngx_hash_strlow(name, name, name_str.len);
175+
ngx_uint_t key = ngx_hash_strlow(name, name, name_len);
162176
v = ngx_http_get_variable(r, &name_str, key);
163177

164178
if (v->not_found) {
@@ -171,6 +185,37 @@ static ngx_str_t ngx_http_get_variable_by_name(ngx_http_request_t *r,
171185
return var_value;
172186
}
173187

188+
static char*
189+
ngx_http_encrypted_session_build_payload(ngx_http_request_t *r,
190+
char *input, size_t input_len, ngx_str_t *iv)
191+
{
192+
size_t new_len = input_len + iv->len + PARTS_DELIMITER_LEN;
193+
char *data = (char *)ngx_pcalloc(r->pool, new_len + 1);
194+
data = strncat(data, (char *)iv->data, iv->len);
195+
data = strcat(data, PARTS_DELIMITER);
196+
data = strncat(data, input, input_len);
197+
198+
return data;
199+
}
200+
201+
static ngx_str_t*
202+
ngx_http_session_encrypted_compute_hmac(ngx_http_request_t *r,
203+
ngx_str_t *key, ngx_str_t *content)
204+
{
205+
size_t signature_len;
206+
u_char* signature;
207+
208+
ngx_http_encrypted_session_hmac(r->pool, key->data, key->len,
209+
content->data, content->len,
210+
&signature, &signature_len);
211+
212+
ngx_str_t *result = (ngx_str_t*)ngx_pcalloc(r->pool, sizeof(ngx_str_t));
213+
result->len = signature_len;
214+
result->data = (u_char*)ngx_pcalloc(r->pool, signature_len + 1);
215+
result->data = signature;
216+
return result;
217+
}
218+
174219
static ngx_int_t
175220
ngx_http_set_encode_encrypted_session(ngx_http_request_t *r,
176221
ngx_str_t *res, ngx_http_variable_value_t *v)
@@ -196,12 +241,23 @@ ngx_http_set_encode_encrypted_session(ngx_http_request_t *r,
196241
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
197242
"encrypted_session: expires=%T", conf->expires);
198243

199-
ngx_str_t iv = ngx_http_get_variable_by_name(r, conf->iv, conf);
200-
ngx_str_t key = ngx_http_get_variable_by_name(r, conf->key, conf);
244+
ngx_str_t iv = ngx_http_get_variable_by_name(r, conf->iv, conf->iv_len,
245+
conf);
246+
ngx_str_t key = ngx_http_get_variable_by_name(r, conf->key, conf->key_len,
247+
conf);
248+
249+
char *data = (char *)v->data;
250+
if (conf->iv_in_content) {
251+
data = ngx_http_encrypted_session_build_payload(r, data, v->len, &iv);
252+
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
253+
"encrypted_session: content to encrypt=%s",
254+
data);
255+
v->len = strlen(data);
256+
}
201257

202258
rc = ngx_http_encrypted_session_aes_mac_encrypt(emcf, r->pool,
203259
r->connection->log, iv.data, iv.len, key.data, key.len,
204-
v->data, v->len, (ngx_uint_t) conf->expires, &dst, &len);
260+
(u_char*)data, v->len, (ngx_uint_t) conf->expires, &dst, &len);
205261

206262
if (rc != NGX_OK) {
207263
dst = NULL;
@@ -211,6 +267,33 @@ ngx_http_set_encode_encrypted_session(ngx_http_request_t *r,
211267
"encrypted_session: failed to encrypt");
212268
}
213269

270+
if (conf->iv_in_content) {
271+
size_t signature_content_len = iv.len + len;
272+
char* signature_content = (char*)ngx_pcalloc(r->pool, signature_content_len + 1);
273+
signature_content = strncat(signature_content, (char*)iv.data, iv.len);
274+
signature_content = strncat(signature_content, (char*)dst, len);
275+
276+
ngx_str_t signature_input;
277+
signature_input.len = signature_content_len;
278+
signature_input.data = (u_char*)signature_content;
279+
ngx_str_t *signature = ngx_http_session_encrypted_compute_hmac(r, &key,
280+
&signature_input);
281+
282+
size_t new_len = iv.len + len + signature->len + NUM_OF_DELIMITERS * PARTS_DELIMITER_LEN;
283+
char *new_content = (char*)ngx_pcalloc(r->pool, new_len + 1);
284+
new_content = strncat(new_content, (char*)iv.data, iv.len);
285+
new_content = strcat(new_content, PARTS_DELIMITER);
286+
new_content = strncat(new_content, (char*)dst, len);
287+
new_content = strcat(new_content, PARTS_DELIMITER);
288+
new_content = strncat(new_content, (char*)signature->data, signature->len);
289+
len = new_len;
290+
dst = (u_char*)new_content;
291+
292+
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
293+
"encrypted_session: full response=%s",
294+
dst);
295+
}
296+
214297
res->data = dst;
215298
res->len = len;
216299

@@ -240,18 +323,88 @@ ngx_http_set_decode_encrypted_session(ngx_http_request_t *r,
240323
return NGX_ERROR;
241324
}
242325

243-
ngx_str_t iv = ngx_http_get_variable_by_name(r, conf->iv, conf);
244-
ngx_str_t key = ngx_http_get_variable_by_name(r, conf->key, conf);
326+
ngx_str_t key = ngx_http_get_variable_by_name(r, conf->key, conf->key_len,
327+
conf);
328+
329+
ngx_str_t iv;
330+
char *data = (char*)v->data;
331+
size_t data_len = v->len;
332+
333+
334+
if (!conf->iv_in_content)
335+
{
336+
iv = ngx_http_get_variable_by_name(r, conf->iv, conf->iv_len, conf);
337+
}
338+
else
339+
{
340+
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
341+
"encrypted_session: input to decrypt=%s",
342+
data);
343+
iv.len = strcspn(data, PARTS_DELIMITER);
344+
iv.data = (u_char*)ngx_pcalloc(r->pool, iv.len + 1);
345+
strncpy((char*)iv.data, (char*)v->data, iv.len);
346+
347+
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
348+
"encrypted_session: iv=%s", iv.data);
349+
350+
size_t signature_start = iv.len + strcspn(&data[iv.len + 1], PARTS_DELIMITER) + NUM_OF_DELIMITERS * PARTS_DELIMITER_LEN;
351+
size_t signature_len = v->len - signature_start;
352+
353+
u_char* signature = (u_char*)ngx_pcalloc(r->pool, signature_len + 1);
354+
strncpy((char*)signature, &data[data_len - signature_len], signature_len);
355+
356+
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
357+
"encrypted_session: signature=%s", signature);
358+
359+
data_len = v->len - iv.len - signature_len - NUM_OF_DELIMITERS * PARTS_DELIMITER_LEN;
360+
data = (char*)ngx_pcalloc(r->pool, data_len + 1);
361+
strncpy(data, (char*)(&v->data[iv.len + 1]), data_len);
362+
363+
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
364+
"encrypted_session: data=%s", data);
365+
366+
ngx_str_t signature_input;
367+
signature_input.len = iv.len + data_len;
368+
signature_input.data = (u_char*)ngx_pcalloc(r->pool, signature_input.len + 1);
369+
strncat((char*)signature_input.data, (char*)iv.data, iv.len);
370+
strncat((char*)signature_input.data, (char*)data, data_len);
371+
372+
ngx_str_t *computed_signature = ngx_http_session_encrypted_compute_hmac(r,
373+
&key, &signature_input);
374+
if (signature_len != computed_signature->len ||
375+
strncmp((char*)computed_signature->data, (char*)signature, signature_len) != 0)
376+
{
377+
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
378+
"encrypted_session: signatures do not match");
379+
res->data = NULL;
380+
res->len = 0;
381+
382+
return NGX_OK;
383+
}
384+
}
245385

246386
rc = ngx_http_encrypted_session_aes_mac_decrypt(emcf, r->pool,
247387
r->connection->log, iv.data, iv.len, key.data, key.len,
248-
v->data, v->len, &dst, &len);
388+
(u_char*)data, data_len, &dst, &len);
249389

250390
if (rc != NGX_OK) {
391+
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
392+
"encrypted_session: failed to decrypt");
251393
dst = NULL;
252394
len = 0;
253395
}
254396

397+
if (conf->iv_in_content) {
398+
size_t payload_len = len - iv.len - 1;
399+
u_char *result = ngx_pcalloc(r->pool, payload_len + 1);
400+
memcpy(result, &dst[iv.len + 1], payload_len);
401+
dst = result;
402+
len = payload_len;
403+
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
404+
"encrypted_session: decrypted content=%s",
405+
dst);
406+
}
407+
255408
res->data = dst;
256409
res->len = len;
257410

@@ -273,7 +426,9 @@ ngx_http_encrypted_session_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
273426
value = cf->args->elts;
274427

275428
if (value[1].len > 1 && value[1].data[0] == '$') {
276-
llcf->key = &(value[1].data[1]);
429+
llcf->key_len = value[1].len - 1;
430+
llcf->key = (u_char*)ngx_pcalloc(cf->pool, llcf->key_len);
431+
strncpy((char*)llcf->key, (char*)&(value[1].data[1]), llcf->key_len);
277432
return NGX_CONF_OK;
278433
}
279434

@@ -287,6 +442,7 @@ ngx_http_encrypted_session_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
287442
}
288443

289444
llcf->key = value[1].data;
445+
llcf->key_len = value[1].len;
290446

291447
return NGX_CONF_OK;
292448
}
@@ -307,6 +463,7 @@ ngx_http_encrypted_session_iv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
307463

308464
if (value[1].len > 1 && value[1].data[0] == '$') {
309465
llcf->iv = &(value[1].data[1]);
466+
llcf->iv_len = value[1].len - 1;
310467
return NGX_CONF_OK;
311468
}
312469

@@ -320,6 +477,7 @@ ngx_http_encrypted_session_iv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
320477
}
321478

322479
llcf->iv = ngx_pcalloc(cf->pool, ngx_http_encrypted_session_iv_length);
480+
llcf->iv_len = ngx_http_encrypted_session_iv_length;
323481

324482
if (llcf->iv == NULL) {
325483
return NGX_CONF_ERROR;
@@ -360,6 +518,13 @@ ngx_http_encrypted_session_expires(ngx_conf_t *cf, ngx_command_t *cmd,
360518
return NGX_CONF_OK;
361519
}
362520

521+
static char *ngx_http_encrypted_iv_in_content(ngx_conf_t *cf,
522+
ngx_command_t *cmd, void *conf)
523+
{
524+
ngx_http_encrypted_session_conf_t *llcf = conf;
525+
llcf->iv_in_content = 1;
526+
return NGX_CONF_OK;
527+
}
363528

364529
static void
365530
ngx_http_encrypted_session_free_cipher_ctx(void *data)
@@ -435,8 +600,11 @@ ngx_http_encrypted_session_create_conf(ngx_conf_t *cf)
435600
}
436601

437602
conf->key = NGX_CONF_UNSET_PTR;
603+
conf->key_len = NGX_CONF_UNSET;
438604
conf->iv = NGX_CONF_UNSET_PTR;
605+
conf->iv_len = NGX_CONF_UNSET;
439606
conf->expires = NGX_CONF_UNSET;
607+
conf->iv_in_content = NGX_CONF_UNSET;
440608

441609
return conf;
442610
}
@@ -449,12 +617,17 @@ ngx_http_encrypted_session_merge_conf(ngx_conf_t *cf, void *parent, void *child)
449617
ngx_http_encrypted_session_conf_t *conf = child;
450618

451619
ngx_conf_merge_ptr_value(conf->key, prev->key, NULL);
620+
ngx_conf_merge_size_value(conf->key_len, prev->key_len,
621+
(size_t)ngx_http_encrypted_session_key_length);
452622

453623
ngx_conf_merge_ptr_value(conf->iv, prev->iv,
454624
ngx_http_encrypted_session_default_iv);
625+
ngx_conf_merge_size_value(conf->iv_len, prev->iv_len,
626+
(size_t)ngx_http_encrypted_session_iv_length);
455627

456628
ngx_conf_merge_value(conf->expires, prev->expires,
457629
ngx_http_encrypted_session_default_expires);
630+
ngx_conf_merge_value(conf->iv_in_content, prev->iv_in_content, 0);
458631

459632
return NGX_CONF_OK;
460633
}

0 commit comments

Comments
 (0)