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 173 : static NTSTATUS calculate_enc_key(const DATA_BLOB *cek,
28 : const DATA_BLOB *key_salt,
29 : uint8_t enc_key[32])
30 173 : {
31 173 : gnutls_mac_algorithm_t hash_algo = GNUTLS_MAC_SHA512;
32 173 : size_t hmac_size = gnutls_hmac_get_len(hash_algo);
33 173 : uint8_t enc_key_data[hmac_size];
34 : int rc;
35 :
36 365 : rc = gnutls_hmac_fast(hash_algo,
37 173 : cek->data,
38 77 : cek->length,
39 173 : key_salt->data,
40 77 : key_salt->length,
41 : enc_key_data);
42 173 : 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 173 : memcpy(enc_key, enc_key_data, 32);
49 173 : BURN_DATA(enc_key_data);
50 :
51 173 : return NT_STATUS_OK;
52 : }
53 :
54 221 : static NTSTATUS calculate_mac_key(const DATA_BLOB *cek,
55 : const DATA_BLOB *mac_salt,
56 : uint8_t mac_key[64])
57 : {
58 : int rc;
59 :
60 477 : rc = gnutls_hmac_fast(GNUTLS_MAC_SHA512,
61 221 : cek->data,
62 93 : cek->length,
63 221 : mac_salt->data,
64 93 : mac_salt->length,
65 : mac_key);
66 221 : if (rc < 0) {
67 0 : return gnutls_error_to_ntstatus(rc,
68 : NT_STATUS_ENCRYPTION_FAILED);
69 : }
70 :
71 221 : 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 107 : 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 107 : {
86 107 : gnutls_hmac_hd_t hmac_hnd = NULL;
87 107 : gnutls_mac_algorithm_t hmac_algo = GNUTLS_MAC_SHA512;
88 107 : size_t hmac_size = gnutls_hmac_get_len(hmac_algo);
89 107 : gnutls_cipher_hd_t cipher_hnd = NULL;
90 107 : gnutls_cipher_algorithm_t cipher_algo = GNUTLS_CIPHER_AES_256_CBC;
91 107 : uint32_t aes_block_size = gnutls_cipher_get_block_size(cipher_algo);
92 171 : gnutls_datum_t iv_datum = {
93 107 : .data = iv->data,
94 107 : .size = iv->length,
95 : };
96 107 : uint8_t enc_key_data[32] = {0};
97 107 : gnutls_datum_t enc_key = {
98 : .data = enc_key_data,
99 : .size = sizeof(enc_key_data),
100 : };
101 107 : uint8_t *cipher_text = NULL;
102 107 : size_t cipher_text_len = 0;
103 107 : uint8_t mac_key_data[64] = {0};
104 107 : gnutls_datum_t mac_key = {
105 : .data = mac_key_data,
106 : .size = sizeof(mac_key_data),
107 : };
108 107 : uint8_t version_byte = SAMR_AES_VERSION_BYTE;
109 107 : uint8_t version_byte_len = SAMR_AES_VERSION_BYTE_LEN;
110 107 : uint8_t auth_data[hmac_size];
111 : DATA_BLOB padded_plaintext;
112 : size_t padding;
113 : NTSTATUS status;
114 : int rc;
115 :
116 171 : if (plaintext->length == 0 || cek->length == 0 ||
117 171 : key_salt->length == 0 || mac_salt->length == 0 || iv->length == 0) {
118 0 : return NT_STATUS_INVALID_PARAMETER;
119 : }
120 :
121 : /*
122 : * PKCS#7 padding
123 : *
124 : * TODO: Use gnutls_cipher_encrypt3()
125 : */
126 :
127 107 : if (hmac_size > 64) {
128 : /*
129 : * We don't want to overflow 'pauth_tag', which is 64 bytes in
130 : * size.
131 : */
132 0 : return NT_STATUS_INVALID_BUFFER_SIZE;
133 : }
134 :
135 107 : if (plaintext->length + aes_block_size < plaintext->length) {
136 0 : return NT_STATUS_INVALID_BUFFER_SIZE;
137 : }
138 :
139 107 : padded_plaintext.length =
140 107 : aes_block_size * (plaintext->length / aes_block_size) +
141 : aes_block_size;
142 :
143 107 : padding = padded_plaintext.length - plaintext->length;
144 :
145 64 : padded_plaintext =
146 107 : data_blob_talloc(mem_ctx, NULL, padded_plaintext.length);
147 107 : if (padded_plaintext.data == NULL) {
148 0 : return NT_STATUS_NO_MEMORY;
149 : }
150 :
151 : /* Allocate buffer for cipher text */
152 107 : cipher_text_len = padded_plaintext.length;
153 107 : cipher_text = talloc_size(mem_ctx, cipher_text_len);
154 107 : if (cipher_text == NULL) {
155 0 : data_blob_free(&padded_plaintext);
156 0 : return NT_STATUS_NO_MEMORY;
157 : }
158 :
159 107 : memcpy(padded_plaintext.data, plaintext->data, plaintext->length);
160 107 : memset(padded_plaintext.data + plaintext->length, padding, padding);
161 :
162 107 : status = calculate_enc_key(cek, key_salt, enc_key_data);
163 107 : if (!NT_STATUS_IS_OK(status)) {
164 0 : data_blob_clear_free(&padded_plaintext);
165 0 : return status;
166 : }
167 :
168 : /* Encrypt plaintext */
169 107 : rc = gnutls_cipher_init(&cipher_hnd, cipher_algo, &enc_key, &iv_datum);
170 107 : if (rc < 0) {
171 0 : data_blob_clear_free(&padded_plaintext);
172 0 : BURN_DATA(enc_key_data);
173 0 : TALLOC_FREE(cipher_text);
174 0 : return gnutls_error_to_ntstatus(rc,
175 : NT_STATUS_ENCRYPTION_FAILED);
176 : }
177 :
178 171 : rc = gnutls_cipher_encrypt2(cipher_hnd,
179 107 : padded_plaintext.data,
180 : padded_plaintext.length,
181 : cipher_text,
182 : cipher_text_len);
183 107 : gnutls_cipher_deinit(cipher_hnd);
184 107 : data_blob_clear_free(&padded_plaintext);
185 107 : BURN_DATA(enc_key_data);
186 107 : if (rc < 0) {
187 0 : TALLOC_FREE(cipher_text);
188 0 : return gnutls_error_to_ntstatus(rc,
189 : NT_STATUS_ENCRYPTION_FAILED);
190 : }
191 :
192 : /* Calculate mac key */
193 107 : status = calculate_mac_key(cek, mac_salt, mac_key_data);
194 107 : if (!NT_STATUS_IS_OK(status)) {
195 0 : TALLOC_FREE(cipher_text);
196 0 : return status;
197 : }
198 :
199 : /* Generate auth tag */
200 107 : rc = gnutls_hmac_init(&hmac_hnd, hmac_algo, mac_key.data, mac_key.size);
201 107 : BURN_DATA(mac_key_data);
202 107 : if (rc < 0) {
203 0 : TALLOC_FREE(cipher_text);
204 0 : return gnutls_error_to_ntstatus(rc,
205 : NT_STATUS_ENCRYPTION_FAILED);
206 : }
207 :
208 107 : rc = gnutls_hmac(hmac_hnd, &version_byte, sizeof(uint8_t));
209 107 : if (rc < 0) {
210 0 : TALLOC_FREE(cipher_text);
211 0 : gnutls_hmac_deinit(hmac_hnd, NULL);
212 0 : return gnutls_error_to_ntstatus(rc,
213 : NT_STATUS_ENCRYPTION_FAILED);
214 : }
215 :
216 107 : rc = gnutls_hmac(hmac_hnd, iv->data, iv->length);
217 107 : if (rc < 0) {
218 0 : TALLOC_FREE(cipher_text);
219 0 : gnutls_hmac_deinit(hmac_hnd, NULL);
220 0 : return gnutls_error_to_ntstatus(rc,
221 : NT_STATUS_ENCRYPTION_FAILED);
222 : }
223 :
224 107 : rc = gnutls_hmac(hmac_hnd, cipher_text, cipher_text_len);
225 107 : if (rc < 0) {
226 0 : TALLOC_FREE(cipher_text);
227 0 : gnutls_hmac_deinit(hmac_hnd, NULL);
228 0 : return gnutls_error_to_ntstatus(rc,
229 : NT_STATUS_ENCRYPTION_FAILED);
230 : }
231 :
232 107 : rc = gnutls_hmac(hmac_hnd, &version_byte_len, sizeof(uint8_t));
233 107 : if (rc < 0) {
234 0 : TALLOC_FREE(cipher_text);
235 0 : gnutls_hmac_deinit(hmac_hnd, NULL);
236 0 : return gnutls_error_to_ntstatus(rc,
237 : NT_STATUS_ENCRYPTION_FAILED);
238 : }
239 107 : gnutls_hmac_deinit(hmac_hnd, auth_data);
240 :
241 107 : if (pciphertext != NULL) {
242 107 : pciphertext->length = cipher_text_len;
243 107 : pciphertext->data = cipher_text;
244 : }
245 107 : (void)memcpy(pauth_tag, auth_data, hmac_size);
246 :
247 107 : return NT_STATUS_OK;
248 : }
249 :
250 : NTSTATUS
251 114 : samba_gnutls_aead_aes_256_cbc_hmac_sha512_decrypt(TALLOC_CTX *mem_ctx,
252 : const DATA_BLOB *ciphertext,
253 : const DATA_BLOB *cdk,
254 : const DATA_BLOB *key_salt,
255 : const DATA_BLOB *mac_salt,
256 : const DATA_BLOB *iv,
257 : const uint8_t auth_tag[64],
258 : DATA_BLOB *pplaintext)
259 114 : {
260 114 : gnutls_hmac_hd_t hmac_hnd = NULL;
261 114 : gnutls_mac_algorithm_t hash_algo = GNUTLS_MAC_SHA512;
262 114 : size_t hmac_size = gnutls_hmac_get_len(hash_algo);
263 : uint8_t dec_key_data[32];
264 : uint8_t mac_key_data[64];
265 114 : gnutls_datum_t mac_key = {
266 : .data = mac_key_data,
267 : .size = sizeof(mac_key_data),
268 : };
269 114 : gnutls_cipher_hd_t cipher_hnd = NULL;
270 114 : gnutls_cipher_algorithm_t cipher_algo = GNUTLS_CIPHER_AES_256_CBC;
271 114 : gnutls_datum_t dec_key = {
272 : .data = dec_key_data,
273 : .size = sizeof(dec_key_data),
274 : };
275 178 : gnutls_datum_t iv_datum = {
276 114 : .data = iv->data,
277 114 : .size = iv->length,
278 : };
279 114 : uint8_t version_byte = SAMR_AES_VERSION_BYTE;
280 114 : uint8_t version_byte_len = SAMR_AES_VERSION_BYTE_LEN;
281 114 : uint8_t auth_data[hmac_size];
282 : uint8_t padding;
283 : size_t i;
284 : NTSTATUS status;
285 : bool equal;
286 : int rc;
287 :
288 178 : if (cdk->length == 0 || ciphertext->length == 0 ||
289 178 : key_salt->length == 0 || mac_salt->length == 0 || iv->length == 0 ||
290 : pplaintext == NULL) {
291 0 : return NT_STATUS_INVALID_PARAMETER;
292 : }
293 :
294 : /* Calculate mac key */
295 114 : status = calculate_mac_key(cdk, mac_salt, mac_key_data);
296 114 : if (!NT_STATUS_IS_OK(status)) {
297 0 : return status;
298 : }
299 :
300 114 : rc = gnutls_hmac_init(&hmac_hnd, hash_algo, mac_key.data, mac_key.size);
301 114 : BURN_DATA(mac_key_data);
302 114 : if (rc < 0) {
303 0 : return gnutls_error_to_ntstatus(rc,
304 : NT_STATUS_DECRYPTION_FAILED);
305 : }
306 :
307 114 : rc = gnutls_hmac(hmac_hnd, &version_byte, sizeof(uint8_t));
308 114 : if (rc < 0) {
309 0 : gnutls_hmac_deinit(hmac_hnd, NULL);
310 0 : return gnutls_error_to_ntstatus(rc,
311 : NT_STATUS_DECRYPTION_FAILED);
312 : }
313 :
314 114 : rc = gnutls_hmac(hmac_hnd, iv->data, iv->length);
315 114 : if (rc < 0) {
316 0 : gnutls_hmac_deinit(hmac_hnd, NULL);
317 0 : return gnutls_error_to_ntstatus(rc,
318 : NT_STATUS_DECRYPTION_FAILED);
319 : }
320 :
321 114 : rc = gnutls_hmac(hmac_hnd, ciphertext->data, ciphertext->length);
322 114 : if (rc < 0) {
323 0 : gnutls_hmac_deinit(hmac_hnd, NULL);
324 0 : return gnutls_error_to_ntstatus(rc,
325 : NT_STATUS_DECRYPTION_FAILED);
326 : }
327 :
328 114 : rc = gnutls_hmac(hmac_hnd, &version_byte_len, sizeof(uint8_t));
329 114 : if (rc < 0) {
330 0 : gnutls_hmac_deinit(hmac_hnd, NULL);
331 0 : return gnutls_error_to_ntstatus(rc,
332 : NT_STATUS_DECRYPTION_FAILED);
333 : }
334 114 : gnutls_hmac_deinit(hmac_hnd, auth_data);
335 :
336 114 : equal = mem_equal_const_time(auth_data, auth_tag, sizeof(auth_data));
337 114 : if (!equal) {
338 48 : return NT_STATUS_DECRYPTION_FAILED;
339 : }
340 :
341 66 : *pplaintext = data_blob_talloc_zero(mem_ctx, ciphertext->length);
342 66 : if (pplaintext->data == NULL) {
343 0 : return NT_STATUS_NO_MEMORY;
344 : }
345 :
346 : /* Calculate decryption key */
347 66 : status = calculate_enc_key(cdk, key_salt, dec_key_data);
348 66 : if (!NT_STATUS_IS_OK(status)) {
349 0 : return status;
350 : }
351 :
352 66 : rc = gnutls_cipher_init(&cipher_hnd, cipher_algo, &dec_key, &iv_datum);
353 66 : BURN_DATA(dec_key_data);
354 66 : if (rc < 0) {
355 0 : data_blob_free(pplaintext);
356 0 : return gnutls_error_to_ntstatus(rc,
357 : NT_STATUS_DECRYPTION_FAILED);
358 : }
359 :
360 130 : rc = gnutls_cipher_decrypt2(cipher_hnd,
361 66 : ciphertext->data,
362 34 : ciphertext->length,
363 66 : pplaintext->data,
364 : pplaintext->length);
365 66 : gnutls_cipher_deinit(cipher_hnd);
366 66 : if (rc < 0) {
367 0 : data_blob_clear_free(pplaintext);
368 0 : return gnutls_error_to_ntstatus(rc,
369 : NT_STATUS_DECRYPTION_FAILED);
370 : }
371 :
372 : /*
373 : * PKCS#7 padding
374 : *
375 : * TODO: Use gnutls_cipher_decrypt3()
376 : */
377 :
378 : /*
379 : * The plaintext is always padded.
380 : *
381 : * We already checked for ciphertext->length == 0 above and the
382 : * pplaintext->length is equal to ciphertext->length here. We need to
383 : * remove the padding from the plaintext size.
384 : */
385 66 : padding = pplaintext->data[pplaintext->length - 1];
386 66 : if (padding == 0 || padding > 16) {
387 0 : data_blob_clear_free(pplaintext);
388 0 : return NT_STATUS_DECRYPTION_FAILED;
389 : }
390 :
391 990 : for (i = pplaintext->length - padding; i < pplaintext->length; i++) {
392 924 : if (pplaintext->data[i] != padding) {
393 0 : data_blob_clear_free(pplaintext);
394 0 : return NT_STATUS_DECRYPTION_FAILED;
395 : }
396 : }
397 :
398 66 : pplaintext->length -= padding;
399 :
400 66 : return NT_STATUS_OK;
401 : }
|