diff src/event/ngx_event_openssl.c @ 6854:75e7d55214bd

SSL: support AES256 encryption of tickets. This implies ticket key size of 80 bytes instead of previously used 48, as both HMAC and AES keys are 32 bytes now. When an old 48-byte ticket key is provided, we fall back to using backward-compatible AES128 encryption. OpenSSL switched to using AES256 in 1.1.0, and we are providing equivalent security. While here, order of HMAC and AES keys was reverted to make the implementation compatible with keys used by OpenSSL with SSL_CTX_set_tlsext_ticket_keys(). Prodded by Christian Klinger.
author Maxim Dounin <mdounin@mdounin.ru>
date Fri, 23 Dec 2016 17:28:20 +0300
parents 25d0d6dabe00
children 5cb85b0ee00b
line wrap: on
line diff
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -2856,7 +2856,8 @@ ngx_ssl_session_rbtree_insert_value(ngx_
 ngx_int_t
 ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
 {
-    u_char                         buf[48];
+    u_char                         buf[80];
+    size_t                         size;
     ssize_t                        n;
     ngx_str_t                     *path;
     ngx_file_t                     file;
@@ -2899,13 +2900,15 @@ ngx_ssl_session_ticket_keys(ngx_conf_t *
             goto failed;
         }
 
-        if (ngx_file_size(&fi) != 48) {
+        size = ngx_file_size(&fi);
+
+        if (size != 48 && size != 80) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "\"%V\" must be 48 bytes", &file.name);
+                               "\"%V\" must be 48 or 80 bytes", &file.name);
             goto failed;
         }
 
-        n = ngx_read_file(&file, buf, 48, 0);
+        n = ngx_read_file(&file, buf, size, 0);
 
         if (n == NGX_ERROR) {
             ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
@@ -2913,10 +2916,10 @@ ngx_ssl_session_ticket_keys(ngx_conf_t *
             goto failed;
         }
 
-        if (n != 48) {
+        if ((size_t) n != size) {
             ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
                                ngx_read_file_n " \"%V\" returned only "
-                               "%z bytes instead of 48", &file.name, n);
+                               "%z bytes instead of %uz", &file.name, n, size);
             goto failed;
         }
 
@@ -2925,9 +2928,18 @@ ngx_ssl_session_ticket_keys(ngx_conf_t *
             goto failed;
         }
 
-        ngx_memcpy(key->name, buf, 16);
-        ngx_memcpy(key->aes_key, buf + 16, 16);
-        ngx_memcpy(key->hmac_key, buf + 32, 16);
+        if (size == 48) {
+            key->size = 48;
+            ngx_memcpy(key->name, buf, 16);
+            ngx_memcpy(key->aes_key, buf + 16, 16);
+            ngx_memcpy(key->hmac_key, buf + 32, 16);
+
+        } else {
+            key->size = 80;
+            ngx_memcpy(key->name, buf, 16);
+            ngx_memcpy(key->hmac_key, buf + 16, 32);
+            ngx_memcpy(key->aes_key, buf + 48, 32);
+        }
 
         if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
@@ -2972,6 +2984,7 @@ ngx_ssl_session_ticket_key_callback(ngx_
     unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
     HMAC_CTX *hctx, int enc)
 {
+    size_t                         size;
     SSL_CTX                       *ssl_ctx;
     ngx_uint_t                     i;
     ngx_array_t                   *keys;
@@ -2986,7 +2999,6 @@ ngx_ssl_session_ticket_key_callback(ngx_
     c = ngx_ssl_get_connection(ssl_conn);
     ssl_ctx = c->ssl->session_ctx;
 
-    cipher = EVP_aes_128_cbc();
 #ifdef OPENSSL_NO_SHA256
     digest = EVP_sha1();
 #else
@@ -3008,6 +3020,15 @@ ngx_ssl_session_ticket_key_callback(ngx_
                        ngx_hex_dump(buf, key[0].name, 16) - buf, buf,
                        SSL_session_reused(ssl_conn) ? "reused" : "new");
 
+        if (key[0].size == 48) {
+            cipher = EVP_aes_128_cbc();
+            size = 16;
+
+        } else {
+            cipher = EVP_aes_256_cbc();
+            size = 32;
+        }
+
         if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {
             ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "RAND_bytes() failed");
             return -1;
@@ -3020,12 +3041,12 @@ ngx_ssl_session_ticket_key_callback(ngx_
         }
 
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
-        if (HMAC_Init_ex(hctx, key[0].hmac_key, 16, digest, NULL) != 1) {
+        if (HMAC_Init_ex(hctx, key[0].hmac_key, size, digest, NULL) != 1) {
             ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "HMAC_Init_ex() failed");
             return -1;
         }
 #else
-        HMAC_Init_ex(hctx, key[0].hmac_key, 16, digest, NULL);
+        HMAC_Init_ex(hctx, key[0].hmac_key, size, digest, NULL);
 #endif
 
         ngx_memcpy(name, key[0].name, 16);
@@ -3054,13 +3075,22 @@ ngx_ssl_session_ticket_key_callback(ngx_
                        ngx_hex_dump(buf, key[i].name, 16) - buf, buf,
                        (i == 0) ? " (default)" : "");
 
+        if (key[i].size == 48) {
+            cipher = EVP_aes_128_cbc();
+            size = 16;
+
+        } else {
+            cipher = EVP_aes_256_cbc();
+            size = 32;
+        }
+
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
-        if (HMAC_Init_ex(hctx, key[i].hmac_key, 16, digest, NULL) != 1) {
+        if (HMAC_Init_ex(hctx, key[i].hmac_key, size, digest, NULL) != 1) {
             ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "HMAC_Init_ex() failed");
             return -1;
         }
 #else
-        HMAC_Init_ex(hctx, key[i].hmac_key, 16, digest, NULL);
+        HMAC_Init_ex(hctx, key[i].hmac_key, size, digest, NULL);
 #endif
 
         if (EVP_DecryptInit_ex(ectx, cipher, NULL, key[i].aes_key, iv) != 1) {