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 458 : static NTSTATUS calculate_enc_key(const DATA_BLOB *cek,
28 : const DATA_BLOB *key_salt,
29 : uint8_t enc_key[32])
30 458 : {
31 458 : gnutls_mac_algorithm_t hash_algo = GNUTLS_MAC_SHA512;
32 458 : size_t hmac_size = gnutls_hmac_get_len(hash_algo);
33 458 : uint8_t enc_key_data[hmac_size];
34 4 : int rc;
35 :
36 462 : rc = gnutls_hmac_fast(hash_algo,
37 458 : cek->data,
38 458 : cek->length,
39 458 : key_salt->data,
40 458 : key_salt->length,
41 : enc_key_data);
42 458 : 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 458 : memcpy(enc_key, enc_key_data, 32);
49 458 : BURN_DATA(enc_key_data);
50 :
51 458 : return NT_STATUS_OK;
52 : }
53 :
54 522 : 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 526 : rc = gnutls_hmac_fast(GNUTLS_MAC_SHA512,
61 522 : cek->data,
62 522 : cek->length,
63 522 : mac_salt->data,
64 522 : mac_salt->length,
65 : mac_key);
66 522 : if (rc < 0) {
67 0 : return gnutls_error_to_ntstatus(rc,
68 : NT_STATUS_ENCRYPTION_FAILED);
69 : }
70 :
71 522 : 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 263 : 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 263 : {
86 263 : gnutls_hmac_hd_t hmac_hnd = NULL;
87 263 : gnutls_mac_algorithm_t hmac_algo = GNUTLS_MAC_SHA512;
88 263 : size_t hmac_size = gnutls_hmac_get_len(hmac_algo);
89 263 : gnutls_cipher_hd_t cipher_hnd = NULL;
90 263 : gnutls_cipher_algorithm_t cipher_algo = GNUTLS_CIPHER_AES_256_CBC;
91 263 : uint32_t aes_block_size = gnutls_cipher_get_block_size(cipher_algo);
92 263 : gnutls_datum_t iv_datum = {
93 263 : .data = iv->data,
94 263 : .size = iv->length,
95 : };
96 263 : uint8_t enc_key_data[32] = {0};
97 263 : gnutls_datum_t enc_key = {
98 : .data = enc_key_data,
99 : .size = sizeof(enc_key_data),
100 : };
101 263 : uint8_t *cipher_text = NULL;
102 263 : size_t cipher_text_len = 0;
103 263 : uint8_t mac_key_data[64] = {0};
104 263 : gnutls_datum_t mac_key = {
105 : .data = mac_key_data,
106 : .size = sizeof(mac_key_data),
107 : };
108 263 : uint8_t version_byte = SAMR_AES_VERSION_BYTE;
109 263 : uint8_t version_byte_len = SAMR_AES_VERSION_BYTE_LEN;
110 263 : 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 263 : SMB_ASSERT(hmac_size == 64);
121 :
122 263 : if (plaintext->length == 0 || cek->length == 0 ||
123 263 : 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 263 : if (plaintext->length + aes_block_size < plaintext->length) {
134 0 : return NT_STATUS_INVALID_BUFFER_SIZE;
135 : }
136 :
137 263 : padded_plaintext.length =
138 263 : aes_block_size * (plaintext->length / aes_block_size) +
139 : aes_block_size;
140 :
141 263 : padding = padded_plaintext.length - plaintext->length;
142 :
143 2 : padded_plaintext =
144 263 : data_blob_talloc(mem_ctx, NULL, padded_plaintext.length);
145 263 : if (padded_plaintext.data == NULL) {
146 0 : return NT_STATUS_NO_MEMORY;
147 : }
148 :
149 : /* Allocate buffer for cipher text */
150 263 : cipher_text_len = padded_plaintext.length;
151 263 : cipher_text = talloc_size(mem_ctx, cipher_text_len);
152 263 : if (cipher_text == NULL) {
153 0 : data_blob_free(&padded_plaintext);
154 0 : return NT_STATUS_NO_MEMORY;
155 : }
156 :
157 263 : memcpy(padded_plaintext.data, plaintext->data, plaintext->length);
158 263 : memset(padded_plaintext.data + plaintext->length, padding, padding);
159 :
160 263 : status = calculate_enc_key(cek, key_salt, enc_key_data);
161 263 : if (!NT_STATUS_IS_OK(status)) {
162 0 : data_blob_clear_free(&padded_plaintext);
163 0 : return status;
164 : }
165 :
166 : /* Encrypt plaintext */
167 263 : rc = gnutls_cipher_init(&cipher_hnd, cipher_algo, &enc_key, &iv_datum);
168 263 : 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 265 : rc = gnutls_cipher_encrypt2(cipher_hnd,
177 263 : padded_plaintext.data,
178 : padded_plaintext.length,
179 : cipher_text,
180 : cipher_text_len);
181 263 : gnutls_cipher_deinit(cipher_hnd);
182 263 : data_blob_clear_free(&padded_plaintext);
183 263 : BURN_DATA(enc_key_data);
184 263 : 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 263 : status = calculate_mac_key(cek, mac_salt, mac_key_data);
192 263 : if (!NT_STATUS_IS_OK(status)) {
193 0 : TALLOC_FREE(cipher_text);
194 0 : return status;
195 : }
196 :
197 : /* Generate auth tag */
198 263 : rc = gnutls_hmac_init(&hmac_hnd, hmac_algo, mac_key.data, mac_key.size);
199 263 : BURN_DATA(mac_key_data);
200 263 : 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 263 : rc = gnutls_hmac(hmac_hnd, &version_byte, sizeof(uint8_t));
207 263 : 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 263 : rc = gnutls_hmac(hmac_hnd, iv->data, iv->length);
215 263 : 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 263 : rc = gnutls_hmac(hmac_hnd, cipher_text, cipher_text_len);
223 263 : 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 263 : rc = gnutls_hmac(hmac_hnd, &version_byte_len, sizeof(uint8_t));
231 263 : 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 263 : gnutls_hmac_deinit(hmac_hnd, auth_data);
238 :
239 263 : if (pciphertext != NULL) {
240 263 : pciphertext->length = cipher_text_len;
241 263 : pciphertext->data = cipher_text;
242 : }
243 263 : (void)memcpy(pauth_tag, auth_data, hmac_size);
244 :
245 263 : return NT_STATUS_OK;
246 : }
247 :
248 : NTSTATUS
249 258 : 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 258 : {
258 258 : gnutls_hmac_hd_t hmac_hnd = NULL;
259 258 : gnutls_mac_algorithm_t hash_algo = GNUTLS_MAC_SHA512;
260 258 : 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 258 : gnutls_datum_t mac_key = {
264 : .data = mac_key_data,
265 : .size = sizeof(mac_key_data),
266 : };
267 258 : gnutls_cipher_hd_t cipher_hnd = NULL;
268 258 : gnutls_cipher_algorithm_t cipher_algo = GNUTLS_CIPHER_AES_256_CBC;
269 258 : gnutls_datum_t dec_key = {
270 : .data = dec_key_data,
271 : .size = sizeof(dec_key_data),
272 : };
273 258 : gnutls_datum_t iv_datum = {
274 258 : .data = iv->data,
275 258 : .size = iv->length,
276 : };
277 258 : uint8_t version_byte = SAMR_AES_VERSION_BYTE;
278 258 : uint8_t version_byte_len = SAMR_AES_VERSION_BYTE_LEN;
279 258 : 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 258 : if (cdk->length == 0 || ciphertext->length == 0 ||
287 258 : 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 258 : status = calculate_mac_key(cdk, mac_salt, mac_key_data);
294 258 : if (!NT_STATUS_IS_OK(status)) {
295 0 : return status;
296 : }
297 :
298 258 : rc = gnutls_hmac_init(&hmac_hnd, hash_algo, mac_key.data, mac_key.size);
299 258 : BURN_DATA(mac_key_data);
300 258 : if (rc < 0) {
301 0 : return gnutls_error_to_ntstatus(rc,
302 : NT_STATUS_DECRYPTION_FAILED);
303 : }
304 :
305 258 : rc = gnutls_hmac(hmac_hnd, &version_byte, sizeof(uint8_t));
306 258 : 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 258 : rc = gnutls_hmac(hmac_hnd, iv->data, iv->length);
313 258 : 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 258 : rc = gnutls_hmac(hmac_hnd, ciphertext->data, ciphertext->length);
320 258 : 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 258 : rc = gnutls_hmac(hmac_hnd, &version_byte_len, sizeof(uint8_t));
327 258 : 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 258 : gnutls_hmac_deinit(hmac_hnd, auth_data);
333 :
334 258 : equal = mem_equal_const_time(auth_data, auth_tag, sizeof(auth_data));
335 258 : if (!equal) {
336 64 : return NT_STATUS_DECRYPTION_FAILED;
337 : }
338 :
339 194 : *pplaintext = data_blob_talloc_zero(mem_ctx, ciphertext->length);
340 194 : if (pplaintext->data == NULL) {
341 0 : return NT_STATUS_NO_MEMORY;
342 : }
343 :
344 : /* Calculate decryption key */
345 194 : status = calculate_enc_key(cdk, key_salt, dec_key_data);
346 194 : if (!NT_STATUS_IS_OK(status)) {
347 0 : return status;
348 : }
349 :
350 194 : rc = gnutls_cipher_init(&cipher_hnd, cipher_algo, &dec_key, &iv_datum);
351 194 : BURN_DATA(dec_key_data);
352 194 : 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 195 : rc = gnutls_cipher_decrypt2(cipher_hnd,
359 194 : ciphertext->data,
360 194 : ciphertext->length,
361 194 : pplaintext->data,
362 : pplaintext->length);
363 194 : gnutls_cipher_deinit(cipher_hnd);
364 194 : 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 194 : padding = pplaintext->data[pplaintext->length - 1];
384 194 : if (padding == 0 || padding > 16) {
385 0 : data_blob_clear_free(pplaintext);
386 0 : return NT_STATUS_DECRYPTION_FAILED;
387 : }
388 :
389 2596 : for (i = pplaintext->length - padding; i < pplaintext->length; i++) {
390 2402 : if (pplaintext->data[i] != padding) {
391 0 : data_blob_clear_free(pplaintext);
392 0 : return NT_STATUS_DECRYPTION_FAILED;
393 : }
394 : }
395 :
396 194 : pplaintext->length -= padding;
397 :
398 194 : return NT_STATUS_OK;
399 : }
|