2 * Copyright (c) 2021-2022 Andreas Schneider <asn@samba.org>
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.
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.
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/>.
19 #include "lib/util/data_blob.h"
20 #include <gnutls/gnutls.h>
21 #include <gnutls/crypto.h>
22 #include "gnutls_helpers.h"
24 #define SAMR_AES_VERSION_BYTE 0x01
25 #define SAMR_AES_VERSION_BYTE_LEN 1
27 static NTSTATUS
calculate_enc_key(const DATA_BLOB
*cek
,
28 const DATA_BLOB
*key_salt
,
31 gnutls_mac_algorithm_t hash_algo
= GNUTLS_MAC_SHA512
;
32 size_t hmac_size
= gnutls_hmac_get_len(hash_algo
);
33 uint8_t enc_key_data
[hmac_size
];
36 rc
= gnutls_hmac_fast(hash_algo
,
43 return gnutls_error_to_ntstatus(rc
,
44 NT_STATUS_ENCRYPTION_FAILED
);
47 /* The key gets truncated to 32 byte */
48 memcpy(enc_key
, enc_key_data
, 32);
49 BURN_DATA(enc_key_data
);
54 static NTSTATUS
calculate_mac_key(const DATA_BLOB
*cek
,
55 const DATA_BLOB
*mac_salt
,
60 rc
= gnutls_hmac_fast(GNUTLS_MAC_SHA512
,
67 return gnutls_error_to_ntstatus(rc
,
68 NT_STATUS_ENCRYPTION_FAILED
);
74 /* This is an implementation of [MS-SAMR] 3.2.2.4 AES Cipher Usage */
77 samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt(TALLOC_CTX
*mem_ctx
,
78 const DATA_BLOB
*plaintext
,
80 const DATA_BLOB
*key_salt
,
81 const DATA_BLOB
*mac_salt
,
83 DATA_BLOB
*pciphertext
,
84 uint8_t pauth_tag
[64])
86 gnutls_hmac_hd_t hmac_hnd
= NULL
;
87 gnutls_mac_algorithm_t hmac_algo
= GNUTLS_MAC_SHA512
;
88 size_t hmac_size
= gnutls_hmac_get_len(hmac_algo
);
89 gnutls_cipher_hd_t cipher_hnd
= NULL
;
90 gnutls_cipher_algorithm_t cipher_algo
= GNUTLS_CIPHER_AES_256_CBC
;
91 uint32_t aes_block_size
= gnutls_cipher_get_block_size(cipher_algo
);
92 gnutls_datum_t iv_datum
= {
96 uint8_t enc_key_data
[32] = {0};
97 gnutls_datum_t enc_key
= {
99 .size
= sizeof(enc_key_data
),
101 uint8_t *cipher_text
= NULL
;
102 size_t cipher_text_len
= 0;
103 uint8_t mac_key_data
[64] = {0};
104 gnutls_datum_t mac_key
= {
105 .data
= mac_key_data
,
106 .size
= sizeof(mac_key_data
),
108 uint8_t version_byte
= SAMR_AES_VERSION_BYTE
;
109 uint8_t version_byte_len
= SAMR_AES_VERSION_BYTE_LEN
;
110 uint8_t auth_data
[hmac_size
];
111 DATA_BLOB padded_plaintext
;
117 * We don't want to overflow 'pauth_tag', which is 64 bytes in
120 SMB_ASSERT(hmac_size
== 64);
122 if (plaintext
->length
== 0 || cek
->length
== 0 ||
123 key_salt
->length
== 0 || mac_salt
->length
== 0 || iv
->length
== 0) {
124 return NT_STATUS_INVALID_PARAMETER
;
130 * TODO: Use gnutls_cipher_encrypt3()
133 if (plaintext
->length
+ aes_block_size
< plaintext
->length
) {
134 return NT_STATUS_INVALID_BUFFER_SIZE
;
137 padded_plaintext
.length
=
138 aes_block_size
* (plaintext
->length
/ aes_block_size
) +
141 padding
= padded_plaintext
.length
- plaintext
->length
;
144 data_blob_talloc(mem_ctx
, NULL
, padded_plaintext
.length
);
145 if (padded_plaintext
.data
== NULL
) {
146 return NT_STATUS_NO_MEMORY
;
149 /* Allocate buffer for cipher text */
150 cipher_text_len
= padded_plaintext
.length
;
151 cipher_text
= talloc_size(mem_ctx
, cipher_text_len
);
152 if (cipher_text
== NULL
) {
153 data_blob_free(&padded_plaintext
);
154 return NT_STATUS_NO_MEMORY
;
157 memcpy(padded_plaintext
.data
, plaintext
->data
, plaintext
->length
);
158 memset(padded_plaintext
.data
+ plaintext
->length
, padding
, padding
);
160 status
= calculate_enc_key(cek
, key_salt
, enc_key_data
);
161 if (!NT_STATUS_IS_OK(status
)) {
162 data_blob_clear_free(&padded_plaintext
);
166 /* Encrypt plaintext */
167 rc
= gnutls_cipher_init(&cipher_hnd
, cipher_algo
, &enc_key
, &iv_datum
);
169 data_blob_clear_free(&padded_plaintext
);
170 BURN_DATA(enc_key_data
);
171 TALLOC_FREE(cipher_text
);
172 return gnutls_error_to_ntstatus(rc
,
173 NT_STATUS_ENCRYPTION_FAILED
);
176 rc
= gnutls_cipher_encrypt2(cipher_hnd
,
177 padded_plaintext
.data
,
178 padded_plaintext
.length
,
181 gnutls_cipher_deinit(cipher_hnd
);
182 data_blob_clear_free(&padded_plaintext
);
183 BURN_DATA(enc_key_data
);
185 TALLOC_FREE(cipher_text
);
186 return gnutls_error_to_ntstatus(rc
,
187 NT_STATUS_ENCRYPTION_FAILED
);
190 /* Calculate mac key */
191 status
= calculate_mac_key(cek
, mac_salt
, mac_key_data
);
192 if (!NT_STATUS_IS_OK(status
)) {
193 TALLOC_FREE(cipher_text
);
197 /* Generate auth tag */
198 rc
= gnutls_hmac_init(&hmac_hnd
, hmac_algo
, mac_key
.data
, mac_key
.size
);
199 BURN_DATA(mac_key_data
);
201 TALLOC_FREE(cipher_text
);
202 return gnutls_error_to_ntstatus(rc
,
203 NT_STATUS_ENCRYPTION_FAILED
);
206 rc
= gnutls_hmac(hmac_hnd
, &version_byte
, sizeof(uint8_t));
208 TALLOC_FREE(cipher_text
);
209 gnutls_hmac_deinit(hmac_hnd
, NULL
);
210 return gnutls_error_to_ntstatus(rc
,
211 NT_STATUS_ENCRYPTION_FAILED
);
214 rc
= gnutls_hmac(hmac_hnd
, iv
->data
, iv
->length
);
216 TALLOC_FREE(cipher_text
);
217 gnutls_hmac_deinit(hmac_hnd
, NULL
);
218 return gnutls_error_to_ntstatus(rc
,
219 NT_STATUS_ENCRYPTION_FAILED
);
222 rc
= gnutls_hmac(hmac_hnd
, cipher_text
, cipher_text_len
);
224 TALLOC_FREE(cipher_text
);
225 gnutls_hmac_deinit(hmac_hnd
, NULL
);
226 return gnutls_error_to_ntstatus(rc
,
227 NT_STATUS_ENCRYPTION_FAILED
);
230 rc
= gnutls_hmac(hmac_hnd
, &version_byte_len
, sizeof(uint8_t));
232 TALLOC_FREE(cipher_text
);
233 gnutls_hmac_deinit(hmac_hnd
, NULL
);
234 return gnutls_error_to_ntstatus(rc
,
235 NT_STATUS_ENCRYPTION_FAILED
);
237 gnutls_hmac_deinit(hmac_hnd
, auth_data
);
239 if (pciphertext
!= NULL
) {
240 pciphertext
->length
= cipher_text_len
;
241 pciphertext
->data
= cipher_text
;
243 (void)memcpy(pauth_tag
, auth_data
, hmac_size
);
249 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
,
255 const uint8_t auth_tag
[64],
256 DATA_BLOB
*pplaintext
)
258 gnutls_hmac_hd_t hmac_hnd
= NULL
;
259 gnutls_mac_algorithm_t hash_algo
= GNUTLS_MAC_SHA512
;
260 size_t hmac_size
= gnutls_hmac_get_len(hash_algo
);
261 uint8_t dec_key_data
[32];
262 uint8_t mac_key_data
[64];
263 gnutls_datum_t mac_key
= {
264 .data
= mac_key_data
,
265 .size
= sizeof(mac_key_data
),
267 gnutls_cipher_hd_t cipher_hnd
= NULL
;
268 gnutls_cipher_algorithm_t cipher_algo
= GNUTLS_CIPHER_AES_256_CBC
;
269 gnutls_datum_t dec_key
= {
270 .data
= dec_key_data
,
271 .size
= sizeof(dec_key_data
),
273 gnutls_datum_t iv_datum
= {
277 uint8_t version_byte
= SAMR_AES_VERSION_BYTE
;
278 uint8_t version_byte_len
= SAMR_AES_VERSION_BYTE_LEN
;
279 uint8_t auth_data
[hmac_size
];
286 if (cdk
->length
== 0 || ciphertext
->length
== 0 ||
287 key_salt
->length
== 0 || mac_salt
->length
== 0 || iv
->length
== 0 ||
288 pplaintext
== NULL
) {
289 return NT_STATUS_INVALID_PARAMETER
;
292 /* Calculate mac key */
293 status
= calculate_mac_key(cdk
, mac_salt
, mac_key_data
);
294 if (!NT_STATUS_IS_OK(status
)) {
298 rc
= gnutls_hmac_init(&hmac_hnd
, hash_algo
, mac_key
.data
, mac_key
.size
);
299 BURN_DATA(mac_key_data
);
301 return gnutls_error_to_ntstatus(rc
,
302 NT_STATUS_DECRYPTION_FAILED
);
305 rc
= gnutls_hmac(hmac_hnd
, &version_byte
, sizeof(uint8_t));
307 gnutls_hmac_deinit(hmac_hnd
, NULL
);
308 return gnutls_error_to_ntstatus(rc
,
309 NT_STATUS_DECRYPTION_FAILED
);
312 rc
= gnutls_hmac(hmac_hnd
, iv
->data
, iv
->length
);
314 gnutls_hmac_deinit(hmac_hnd
, NULL
);
315 return gnutls_error_to_ntstatus(rc
,
316 NT_STATUS_DECRYPTION_FAILED
);
319 rc
= gnutls_hmac(hmac_hnd
, ciphertext
->data
, ciphertext
->length
);
321 gnutls_hmac_deinit(hmac_hnd
, NULL
);
322 return gnutls_error_to_ntstatus(rc
,
323 NT_STATUS_DECRYPTION_FAILED
);
326 rc
= gnutls_hmac(hmac_hnd
, &version_byte_len
, sizeof(uint8_t));
328 gnutls_hmac_deinit(hmac_hnd
, NULL
);
329 return gnutls_error_to_ntstatus(rc
,
330 NT_STATUS_DECRYPTION_FAILED
);
332 gnutls_hmac_deinit(hmac_hnd
, auth_data
);
334 equal
= mem_equal_const_time(auth_data
, auth_tag
, sizeof(auth_data
));
336 return NT_STATUS_DECRYPTION_FAILED
;
339 *pplaintext
= data_blob_talloc_zero(mem_ctx
, ciphertext
->length
);
340 if (pplaintext
->data
== NULL
) {
341 return NT_STATUS_NO_MEMORY
;
344 /* Calculate decryption key */
345 status
= calculate_enc_key(cdk
, key_salt
, dec_key_data
);
346 if (!NT_STATUS_IS_OK(status
)) {
350 rc
= gnutls_cipher_init(&cipher_hnd
, cipher_algo
, &dec_key
, &iv_datum
);
351 BURN_DATA(dec_key_data
);
353 data_blob_free(pplaintext
);
354 return gnutls_error_to_ntstatus(rc
,
355 NT_STATUS_DECRYPTION_FAILED
);
358 rc
= gnutls_cipher_decrypt2(cipher_hnd
,
363 gnutls_cipher_deinit(cipher_hnd
);
365 data_blob_clear_free(pplaintext
);
366 return gnutls_error_to_ntstatus(rc
,
367 NT_STATUS_DECRYPTION_FAILED
);
373 * TODO: Use gnutls_cipher_decrypt3()
377 * The plaintext is always padded.
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.
383 padding
= pplaintext
->data
[pplaintext
->length
- 1];
384 if (padding
== 0 || padding
> 16) {
385 data_blob_clear_free(pplaintext
);
386 return NT_STATUS_DECRYPTION_FAILED
;
389 for (i
= pplaintext
->length
- padding
; i
< pplaintext
->length
; i
++) {
390 if (pplaintext
->data
[i
] != padding
) {
391 data_blob_clear_free(pplaintext
);
392 return NT_STATUS_DECRYPTION_FAILED
;
396 pplaintext
->length
-= padding
;