Skip to content
Find file
280 lines (196 sloc) 6.21 KB
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_encrypted_session_cipher.h"
#include <openssl/evp.h>
#include <openssl/md5.h>
#include <stdint.h>
static uint64_t ngx_http_encrypted_session_ntohll(uint64_t n);
static uint64_t ngx_http_encrypted_session_htonll(uint64_t n);
ngx_int_t
ngx_http_encrypted_session_aes_mac_encrypt(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)
{
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher;
u_char *p, *data;
int ret;
size_t block_size, buf_size, data_size;
int len;
uint64_t expires_time;
time_t now;
if (key_len != ngx_http_encrypted_session_key_length) {
return NGX_ERROR;
}
EVP_CIPHER_CTX_init(&ctx);
cipher = EVP_aes_256_cbc();
block_size = EVP_CIPHER_block_size(cipher);
data_size = in_len + sizeof(expires_time);
buf_size = MD5_DIGEST_LENGTH /* for the digest */
+ (data_size + block_size - 1) /* for EVP_EncryptUpdate */
+ block_size; /* for EVP_EncryptFinal */
p = ngx_palloc(pool, buf_size + data_size);
if (p == NULL) {
return NGX_ERROR;
}
*dst = p;
data = p + buf_size;
ngx_memcpy(data, in, in_len);
if (expires == 0) {
expires_time = 0;
} else {
now = time(NULL);
if (now == -1) {
goto evp_error;
}
expires_time = (uint64_t) now + (uint64_t) expires;
}
dd("expires before encryption: %lld", (long long) expires_time);
expires_time = ngx_http_encrypted_session_htonll(expires_time);
ngx_memcpy(data + in_len, (u_char *) &expires_time, sizeof(expires_time));
MD5(data, data_size, p);
p += MD5_DIGEST_LENGTH;
ret = EVP_EncryptInit(&ctx, cipher, key, iv);
if (!ret) {
goto evp_error;
}
/* encrypt the raw input data */
ret = EVP_EncryptUpdate(&ctx, p, &len, data, data_size);
if (!ret) {
goto evp_error;
}
p += len;
ret = EVP_EncryptFinal(&ctx, p, &len);
if (!ret) {
return NGX_ERROR;
}
/* XXX we should still explicitly release the ctx
* or we'll leak memory here */
EVP_CIPHER_CTX_cleanup(&ctx);
p += len;
*dst_len = p - *dst;
if (*dst_len > buf_size) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"encrypted_session: aes_mac_encrypt: buffer error");
return NGX_ERROR;
}
return NGX_OK;
evp_error:
EVP_CIPHER_CTX_cleanup(&ctx);
return NGX_ERROR;
}
ngx_int_t
ngx_http_encrypted_session_aes_mac_decrypt(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)
{
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher;
int ret;
size_t block_size, buf_size;
int len;
u_char *p;
const u_char *digest;
uint64_t expires_time;
time_t now;
u_char new_digest[MD5_DIGEST_LENGTH];
if (key_len != ngx_http_encrypted_session_key_length
|| in_len < MD5_DIGEST_LENGTH)
{
return NGX_ERROR;
}
digest = in;
EVP_CIPHER_CTX_init(&ctx);
cipher = EVP_aes_256_cbc();
ret = EVP_DecryptInit(&ctx, cipher, key, iv);
if (!ret) {
goto evp_error;
}
block_size = EVP_CIPHER_block_size(cipher);
buf_size = in_len + block_size /* for EVP_DecryptUpdate */
+ block_size; /* for EVP_DecryptFinal */
p = ngx_palloc(pool, buf_size);
if (p == NULL) {
return NGX_ERROR;
}
*dst = p;
ret = EVP_DecryptUpdate(&ctx, p, &len, in + MD5_DIGEST_LENGTH,
in_len - MD5_DIGEST_LENGTH);
if (!ret) {
dd("decrypt update failed");
goto evp_error;
}
p += len;
ret = EVP_DecryptFinal(&ctx, p, &len);
/* XXX we should still explicitly release the ctx
* or we'll leak memory here */
EVP_CIPHER_CTX_cleanup(&ctx);
if (!ret) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0,
"failed to decrypt session: bad AES-256 digest");
return NGX_ERROR;
}
p += len;
*dst_len = p - *dst;
if (*dst_len > buf_size) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"encrypted_session: aes_mac_decrypt: buffer error");
return NGX_ERROR;
}
if (*dst_len < sizeof(expires_time)) {
return NGX_ERROR;
}
MD5(*dst, *dst_len, new_digest);
if (ngx_strncmp(digest, new_digest, MD5_DIGEST_LENGTH) != 0) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0,
"failed to decrypt session: MD5 checksum mismatch");
return NGX_ERROR;
}
*dst_len -= sizeof(expires_time);
dd("dst len: %d", (int) *dst_len);
dd("dst: %.*s", (int) *dst_len, *dst);
p -= sizeof(expires_time);
expires_time = ngx_http_encrypted_session_ntohll(*((uint64_t *) p));
now = time(NULL);
if (now == -1) {
return NGX_ERROR;
}
dd("expires after decryption: %lld", (long long) expires_time);
if (expires_time && expires_time <= (uint64_t) now) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"encrypted_session: session expired: %uL <= %T",
expires_time, now);
return NGX_ERROR;
}
dd("decrypted successfully");
return NGX_OK;
evp_error:
EVP_CIPHER_CTX_cleanup(&ctx);
return NGX_ERROR;
}
static uint64_t
ngx_http_encrypted_session_ntohll(uint64_t n)
{
#ifdef ntohll
return ntohll(n);
#else
return ((uint64_t) ntohl((unsigned long) n) << 32)
+ ntohl((unsigned long) (n >> 32));
#endif
}
static uint64_t
ngx_http_encrypted_session_htonll(uint64_t n)
{
#ifdef htonll
return htonll(n);
#else
return ((uint64_t) htonl((unsigned long) n) << 32)
+ htonl((unsigned long) (n >> 32));
#endif
}
Jump to Line
Something went wrong with that request. Please try again.