LCOV - code coverage report
Current view: top level - lib/crypto - gnutls_aead_aes_256_cbc_hmac_sha512.c (source / functions) Hit Total Coverage
Test: coverage report for fix-15632 9995c5c2 Lines: 152 204 74.5 %
Date: 2024-04-13 12:30:31 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2021-2022 Andreas Schneider <asn@samba.org>
       3             :  *
       4             :  * This program is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "includes.h"
      19             : #include "lib/util/data_blob.h"
      20             : #include <gnutls/gnutls.h>
      21             : #include <gnutls/crypto.h>
      22             : #include "gnutls_helpers.h"
      23             : 
      24             : #define SAMR_AES_VERSION_BYTE 0x01
      25             : #define SAMR_AES_VERSION_BYTE_LEN 1
      26             : 
      27         340 : static NTSTATUS calculate_enc_key(const DATA_BLOB *cek,
      28             :                                   const DATA_BLOB *key_salt,
      29             :                                   uint8_t enc_key[32])
      30         340 : {
      31         340 :         gnutls_mac_algorithm_t hash_algo = GNUTLS_MAC_SHA512;
      32         340 :         size_t hmac_size = gnutls_hmac_get_len(hash_algo);
      33         340 :         uint8_t enc_key_data[hmac_size];
      34           4 :         int rc;
      35             : 
      36         344 :         rc = gnutls_hmac_fast(hash_algo,
      37         340 :                               cek->data,
      38         340 :                               cek->length,
      39         340 :                               key_salt->data,
      40         340 :                               key_salt->length,
      41             :                               enc_key_data);
      42         340 :         if (rc < 0) {
      43           0 :                 return gnutls_error_to_ntstatus(rc,
      44             :                                                 NT_STATUS_ENCRYPTION_FAILED);
      45             :         }
      46             : 
      47             :         /* The key gets truncated to 32 byte */
      48         340 :         memcpy(enc_key, enc_key_data, 32);
      49         340 :         BURN_DATA(enc_key_data);
      50             : 
      51         340 :         return NT_STATUS_OK;
      52             : }
      53             : 
      54         404 : static NTSTATUS calculate_mac_key(const DATA_BLOB *cek,
      55             :                                   const DATA_BLOB *mac_salt,
      56             :                                   uint8_t mac_key[64])
      57             : {
      58           4 :         int rc;
      59             : 
      60         408 :         rc = gnutls_hmac_fast(GNUTLS_MAC_SHA512,
      61         404 :                               cek->data,
      62         404 :                               cek->length,
      63         404 :                               mac_salt->data,
      64         404 :                               mac_salt->length,
      65             :                               mac_key);
      66         404 :         if (rc < 0) {
      67           0 :                 return gnutls_error_to_ntstatus(rc,
      68             :                                                 NT_STATUS_ENCRYPTION_FAILED);
      69             :         }
      70             : 
      71         404 :         return NT_STATUS_OK;
      72             : }
      73             : 
      74             : /* This is an implementation of [MS-SAMR] 3.2.2.4 AES Cipher Usage */
      75             : 
      76             : NTSTATUS
      77         204 : samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt(TALLOC_CTX *mem_ctx,
      78             :                                                   const DATA_BLOB *plaintext,
      79             :                                                   const DATA_BLOB *cek,
      80             :                                                   const DATA_BLOB *key_salt,
      81             :                                                   const DATA_BLOB *mac_salt,
      82             :                                                   const DATA_BLOB *iv,
      83             :                                                   DATA_BLOB *pciphertext,
      84             :                                                   uint8_t pauth_tag[64])
      85         204 : {
      86         204 :         gnutls_hmac_hd_t hmac_hnd = NULL;
      87         204 :         gnutls_mac_algorithm_t hmac_algo = GNUTLS_MAC_SHA512;
      88         204 :         size_t hmac_size = gnutls_hmac_get_len(hmac_algo);
      89         204 :         gnutls_cipher_hd_t cipher_hnd = NULL;
      90         204 :         gnutls_cipher_algorithm_t cipher_algo = GNUTLS_CIPHER_AES_256_CBC;
      91         204 :         uint32_t aes_block_size = gnutls_cipher_get_block_size(cipher_algo);
      92         204 :         gnutls_datum_t iv_datum = {
      93         204 :                 .data = iv->data,
      94         204 :                 .size = iv->length,
      95             :         };
      96         204 :         uint8_t enc_key_data[32] = {0};
      97         204 :         gnutls_datum_t enc_key = {
      98             :                 .data = enc_key_data,
      99             :                 .size = sizeof(enc_key_data),
     100             :         };
     101         204 :         uint8_t *cipher_text = NULL;
     102         204 :         size_t cipher_text_len = 0;
     103         204 :         uint8_t mac_key_data[64] = {0};
     104         204 :         gnutls_datum_t mac_key = {
     105             :                 .data = mac_key_data,
     106             :                 .size = sizeof(mac_key_data),
     107             :         };
     108         204 :         uint8_t version_byte = SAMR_AES_VERSION_BYTE;
     109         204 :         uint8_t version_byte_len = SAMR_AES_VERSION_BYTE_LEN;
     110         204 :         uint8_t auth_data[hmac_size];
     111           2 :         DATA_BLOB padded_plaintext;
     112           2 :         size_t padding;
     113           2 :         NTSTATUS status;
     114           2 :         int rc;
     115             : 
     116             :         /*
     117             :          * We don't want to overflow 'pauth_tag', which is 64 bytes in
     118             :          * size.
     119             :          */
     120         204 :         SMB_ASSERT(hmac_size == 64);
     121             : 
     122         204 :         if (plaintext->length == 0 || cek->length == 0 ||
     123         204 :             key_salt->length == 0 || mac_salt->length == 0 || iv->length == 0) {
     124           0 :                 return NT_STATUS_INVALID_PARAMETER;
     125             :         }
     126             : 
     127             :         /*
     128             :          * PKCS#7 padding
     129             :          *
     130             :          * TODO: Use gnutls_cipher_encrypt3()
     131             :          */
     132             : 
     133         204 :         if (plaintext->length + aes_block_size < plaintext->length) {
     134           0 :                 return NT_STATUS_INVALID_BUFFER_SIZE;
     135             :         }
     136             : 
     137         204 :         padded_plaintext.length =
     138         204 :                 aes_block_size * (plaintext->length / aes_block_size) +
     139             :                 aes_block_size;
     140             : 
     141         204 :         padding = padded_plaintext.length - plaintext->length;
     142             : 
     143           2 :         padded_plaintext =
     144         204 :                 data_blob_talloc(mem_ctx, NULL, padded_plaintext.length);
     145         204 :         if (padded_plaintext.data == NULL) {
     146           0 :                 return NT_STATUS_NO_MEMORY;
     147             :         }
     148             : 
     149             :         /* Allocate buffer for cipher text */
     150         204 :         cipher_text_len = padded_plaintext.length;
     151         204 :         cipher_text = talloc_size(mem_ctx, cipher_text_len);
     152         204 :         if (cipher_text == NULL) {
     153           0 :                 data_blob_free(&padded_plaintext);
     154           0 :                 return NT_STATUS_NO_MEMORY;
     155             :         }
     156             : 
     157         204 :         memcpy(padded_plaintext.data, plaintext->data, plaintext->length);
     158         204 :         memset(padded_plaintext.data + plaintext->length, padding, padding);
     159             : 
     160         204 :         status = calculate_enc_key(cek, key_salt, enc_key_data);
     161         204 :         if (!NT_STATUS_IS_OK(status)) {
     162           0 :                 data_blob_clear_free(&padded_plaintext);
     163           0 :                 return status;
     164             :         }
     165             : 
     166             :         /* Encrypt plaintext */
     167         204 :         rc = gnutls_cipher_init(&cipher_hnd, cipher_algo, &enc_key, &iv_datum);
     168         204 :         if (rc < 0) {
     169           0 :                 data_blob_clear_free(&padded_plaintext);
     170           0 :                 BURN_DATA(enc_key_data);
     171           0 :                 TALLOC_FREE(cipher_text);
     172           0 :                 return gnutls_error_to_ntstatus(rc,
     173             :                                                 NT_STATUS_ENCRYPTION_FAILED);
     174             :         }
     175             : 
     176         206 :         rc = gnutls_cipher_encrypt2(cipher_hnd,
     177         204 :                                     padded_plaintext.data,
     178             :                                     padded_plaintext.length,
     179             :                                     cipher_text,
     180             :                                     cipher_text_len);
     181         204 :         gnutls_cipher_deinit(cipher_hnd);
     182         204 :         data_blob_clear_free(&padded_plaintext);
     183         204 :         BURN_DATA(enc_key_data);
     184         204 :         if (rc < 0) {
     185           0 :                 TALLOC_FREE(cipher_text);
     186           0 :                 return gnutls_error_to_ntstatus(rc,
     187             :                                                 NT_STATUS_ENCRYPTION_FAILED);
     188             :         }
     189             : 
     190             :         /* Calculate mac key */
     191         204 :         status = calculate_mac_key(cek, mac_salt, mac_key_data);
     192         204 :         if (!NT_STATUS_IS_OK(status)) {
     193           0 :                 TALLOC_FREE(cipher_text);
     194           0 :                 return status;
     195             :         }
     196             : 
     197             :         /* Generate auth tag */
     198         204 :         rc = gnutls_hmac_init(&hmac_hnd, hmac_algo, mac_key.data, mac_key.size);
     199         204 :         BURN_DATA(mac_key_data);
     200         204 :         if (rc < 0) {
     201           0 :                 TALLOC_FREE(cipher_text);
     202           0 :                 return gnutls_error_to_ntstatus(rc,
     203             :                                                 NT_STATUS_ENCRYPTION_FAILED);
     204             :         }
     205             : 
     206         204 :         rc = gnutls_hmac(hmac_hnd, &version_byte, sizeof(uint8_t));
     207         204 :         if (rc < 0) {
     208           0 :                 TALLOC_FREE(cipher_text);
     209           0 :                 gnutls_hmac_deinit(hmac_hnd, NULL);
     210           0 :                 return gnutls_error_to_ntstatus(rc,
     211             :                                                 NT_STATUS_ENCRYPTION_FAILED);
     212             :         }
     213             : 
     214         204 :         rc = gnutls_hmac(hmac_hnd, iv->data, iv->length);
     215         204 :         if (rc < 0) {
     216           0 :                 TALLOC_FREE(cipher_text);
     217           0 :                 gnutls_hmac_deinit(hmac_hnd, NULL);
     218           0 :                 return gnutls_error_to_ntstatus(rc,
     219             :                                                 NT_STATUS_ENCRYPTION_FAILED);
     220             :         }
     221             : 
     222         204 :         rc = gnutls_hmac(hmac_hnd, cipher_text, cipher_text_len);
     223         204 :         if (rc < 0) {
     224           0 :                 TALLOC_FREE(cipher_text);
     225           0 :                 gnutls_hmac_deinit(hmac_hnd, NULL);
     226           0 :                 return gnutls_error_to_ntstatus(rc,
     227             :                                                 NT_STATUS_ENCRYPTION_FAILED);
     228             :         }
     229             : 
     230         204 :         rc = gnutls_hmac(hmac_hnd, &version_byte_len, sizeof(uint8_t));
     231         204 :         if (rc < 0) {
     232           0 :                 TALLOC_FREE(cipher_text);
     233           0 :                 gnutls_hmac_deinit(hmac_hnd, NULL);
     234           0 :                 return gnutls_error_to_ntstatus(rc,
     235             :                                                 NT_STATUS_ENCRYPTION_FAILED);
     236             :         }
     237         204 :         gnutls_hmac_deinit(hmac_hnd, auth_data);
     238             : 
     239         204 :         if (pciphertext != NULL) {
     240         204 :                 pciphertext->length = cipher_text_len;
     241         204 :                 pciphertext->data = cipher_text;
     242             :         }
     243         204 :         (void)memcpy(pauth_tag, auth_data, hmac_size);
     244             : 
     245         204 :         return NT_STATUS_OK;
     246             : }
     247             : 
     248             : NTSTATUS
     249         199 : samba_gnutls_aead_aes_256_cbc_hmac_sha512_decrypt(TALLOC_CTX *mem_ctx,
     250             :                                                   const DATA_BLOB *ciphertext,
     251             :                                                   const DATA_BLOB *cdk,
     252             :                                                   const DATA_BLOB *key_salt,
     253             :                                                   const DATA_BLOB *mac_salt,
     254             :                                                   const DATA_BLOB *iv,
     255             :                                                   const uint8_t auth_tag[64],
     256             :                                                   DATA_BLOB *pplaintext)
     257         199 : {
     258         199 :         gnutls_hmac_hd_t hmac_hnd = NULL;
     259         199 :         gnutls_mac_algorithm_t hash_algo = GNUTLS_MAC_SHA512;
     260         199 :         size_t hmac_size = gnutls_hmac_get_len(hash_algo);
     261           1 :         uint8_t dec_key_data[32];
     262           1 :         uint8_t mac_key_data[64];
     263         199 :         gnutls_datum_t mac_key = {
     264             :                 .data = mac_key_data,
     265             :                 .size = sizeof(mac_key_data),
     266             :         };
     267         199 :         gnutls_cipher_hd_t cipher_hnd = NULL;
     268         199 :         gnutls_cipher_algorithm_t cipher_algo = GNUTLS_CIPHER_AES_256_CBC;
     269         199 :         gnutls_datum_t dec_key = {
     270             :                 .data = dec_key_data,
     271             :                 .size = sizeof(dec_key_data),
     272             :         };
     273         199 :         gnutls_datum_t iv_datum = {
     274         199 :                 .data = iv->data,
     275         199 :                 .size = iv->length,
     276             :         };
     277         199 :         uint8_t version_byte = SAMR_AES_VERSION_BYTE;
     278         199 :         uint8_t version_byte_len = SAMR_AES_VERSION_BYTE_LEN;
     279         199 :         uint8_t auth_data[hmac_size];
     280           1 :         uint8_t padding;
     281           1 :         size_t i;
     282           1 :         NTSTATUS status;
     283           1 :         bool equal;
     284           1 :         int rc;
     285             : 
     286         199 :         if (cdk->length == 0 || ciphertext->length == 0 ||
     287         199 :             key_salt->length == 0 || mac_salt->length == 0 || iv->length == 0 ||
     288             :             pplaintext == NULL) {
     289           0 :                 return NT_STATUS_INVALID_PARAMETER;
     290             :         }
     291             : 
     292             :         /* Calculate mac key */
     293         199 :         status = calculate_mac_key(cdk, mac_salt, mac_key_data);
     294         199 :         if (!NT_STATUS_IS_OK(status)) {
     295           0 :                 return status;
     296             :         }
     297             : 
     298         199 :         rc = gnutls_hmac_init(&hmac_hnd, hash_algo, mac_key.data, mac_key.size);
     299         199 :         BURN_DATA(mac_key_data);
     300         199 :         if (rc < 0) {
     301           0 :                 return gnutls_error_to_ntstatus(rc,
     302             :                                                 NT_STATUS_DECRYPTION_FAILED);
     303             :         }
     304             : 
     305         199 :         rc = gnutls_hmac(hmac_hnd, &version_byte, sizeof(uint8_t));
     306         199 :         if (rc < 0) {
     307           0 :                 gnutls_hmac_deinit(hmac_hnd, NULL);
     308           0 :                 return gnutls_error_to_ntstatus(rc,
     309             :                                                 NT_STATUS_DECRYPTION_FAILED);
     310             :         }
     311             : 
     312         199 :         rc = gnutls_hmac(hmac_hnd, iv->data, iv->length);
     313         199 :         if (rc < 0) {
     314           0 :                 gnutls_hmac_deinit(hmac_hnd, NULL);
     315           0 :                 return gnutls_error_to_ntstatus(rc,
     316             :                                                 NT_STATUS_DECRYPTION_FAILED);
     317             :         }
     318             : 
     319         199 :         rc = gnutls_hmac(hmac_hnd, ciphertext->data, ciphertext->length);
     320         199 :         if (rc < 0) {
     321           0 :                 gnutls_hmac_deinit(hmac_hnd, NULL);
     322           0 :                 return gnutls_error_to_ntstatus(rc,
     323             :                                                 NT_STATUS_DECRYPTION_FAILED);
     324             :         }
     325             : 
     326         199 :         rc = gnutls_hmac(hmac_hnd, &version_byte_len, sizeof(uint8_t));
     327         199 :         if (rc < 0) {
     328           0 :                 gnutls_hmac_deinit(hmac_hnd, NULL);
     329           0 :                 return gnutls_error_to_ntstatus(rc,
     330             :                                                 NT_STATUS_DECRYPTION_FAILED);
     331             :         }
     332         199 :         gnutls_hmac_deinit(hmac_hnd, auth_data);
     333             : 
     334         199 :         equal = mem_equal_const_time(auth_data, auth_tag, sizeof(auth_data));
     335         199 :         if (!equal) {
     336          64 :                 return NT_STATUS_DECRYPTION_FAILED;
     337             :         }
     338             : 
     339         135 :         *pplaintext = data_blob_talloc_zero(mem_ctx, ciphertext->length);
     340         135 :         if (pplaintext->data == NULL) {
     341           0 :                 return NT_STATUS_NO_MEMORY;
     342             :         }
     343             : 
     344             :         /* Calculate decryption key */
     345         135 :         status = calculate_enc_key(cdk, key_salt, dec_key_data);
     346         135 :         if (!NT_STATUS_IS_OK(status)) {
     347           0 :                 return status;
     348             :         }
     349             : 
     350         135 :         rc = gnutls_cipher_init(&cipher_hnd, cipher_algo, &dec_key, &iv_datum);
     351         135 :         BURN_DATA(dec_key_data);
     352         135 :         if (rc < 0) {
     353           0 :                 data_blob_free(pplaintext);
     354           0 :                 return gnutls_error_to_ntstatus(rc,
     355             :                                                 NT_STATUS_DECRYPTION_FAILED);
     356             :         }
     357             : 
     358         136 :         rc = gnutls_cipher_decrypt2(cipher_hnd,
     359         135 :                                     ciphertext->data,
     360         135 :                                     ciphertext->length,
     361         135 :                                     pplaintext->data,
     362             :                                     pplaintext->length);
     363         135 :         gnutls_cipher_deinit(cipher_hnd);
     364         135 :         if (rc < 0) {
     365           0 :                 data_blob_clear_free(pplaintext);
     366           0 :                 return gnutls_error_to_ntstatus(rc,
     367             :                                                 NT_STATUS_DECRYPTION_FAILED);
     368             :         }
     369             : 
     370             :         /*
     371             :          * PKCS#7 padding
     372             :          *
     373             :          * TODO: Use gnutls_cipher_decrypt3()
     374             :          */
     375             : 
     376             :         /*
     377             :          * The plaintext is always padded.
     378             :          *
     379             :          * We already checked for ciphertext->length == 0 above and the
     380             :          * pplaintext->length is equal to ciphertext->length here. We need to
     381             :          * remove the padding from the plaintext size.
     382             :          */
     383         135 :         padding = pplaintext->data[pplaintext->length - 1];
     384         135 :         if (padding == 0 || padding > 16) {
     385           0 :                 data_blob_clear_free(pplaintext);
     386           0 :                 return NT_STATUS_DECRYPTION_FAILED;
     387             :         }
     388             : 
     389        2025 :         for (i = pplaintext->length - padding; i < pplaintext->length; i++) {
     390        1890 :                 if (pplaintext->data[i] != padding) {
     391           0 :                         data_blob_clear_free(pplaintext);
     392           0 :                         return NT_STATUS_DECRYPTION_FAILED;
     393             :                 }
     394             :         }
     395             : 
     396         135 :         pplaintext->length -= padding;
     397             : 
     398         135 :         return NT_STATUS_OK;
     399             : }

Generated by: LCOV version 1.14