Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Wrapper for gnutls key derivation functions
4 :
5 : Copyright (C) Stefan Metzmacher 2009
6 : Copyright (C) Catalyst.Net Ltd 2023
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 :
21 : */
22 :
23 : #include "includes.h"
24 : #include <gnutls/gnutls.h>
25 : #include <gnutls/crypto.h>
26 : #include "gnutls_helpers.h"
27 :
28 169105 : static NTSTATUS samba_gnutls_sp800_108_derive_key_part(
29 : const gnutls_hmac_hd_t hmac_hnd,
30 : const uint8_t *FixedData,
31 : const size_t FixedData_len,
32 : const uint8_t *Label,
33 : const size_t Label_len,
34 : const uint8_t *Context,
35 : const size_t Context_len,
36 : const uint32_t L,
37 : const uint32_t i,
38 : uint8_t *digest)
39 : {
40 5829 : uint8_t buf[4];
41 5829 : static const uint8_t zero = 0;
42 5829 : int rc;
43 :
44 169105 : PUSH_BE_U32(buf, 0, i);
45 169105 : rc = gnutls_hmac(hmac_hnd, buf, sizeof(buf));
46 169105 : if (rc < 0) {
47 0 : return gnutls_error_to_ntstatus(rc,
48 : NT_STATUS_HMAC_NOT_SUPPORTED);
49 : }
50 169105 : if (FixedData != NULL) {
51 250 : rc = gnutls_hmac(hmac_hnd, FixedData, FixedData_len);
52 250 : if (rc < 0) {
53 0 : return gnutls_error_to_ntstatus(
54 : rc, NT_STATUS_HMAC_NOT_SUPPORTED);
55 : }
56 : } else {
57 168855 : rc = gnutls_hmac(hmac_hnd, Label, Label_len);
58 168855 : if (rc < 0) {
59 0 : return gnutls_error_to_ntstatus(
60 : rc, NT_STATUS_HMAC_NOT_SUPPORTED);
61 : }
62 168855 : rc = gnutls_hmac(hmac_hnd, &zero, 1);
63 168855 : if (rc < 0) {
64 0 : return gnutls_error_to_ntstatus(
65 : rc, NT_STATUS_HMAC_NOT_SUPPORTED);
66 : }
67 168855 : rc = gnutls_hmac(hmac_hnd, Context, Context_len);
68 168855 : if (rc < 0) {
69 0 : return gnutls_error_to_ntstatus(
70 : rc, NT_STATUS_HMAC_NOT_SUPPORTED);
71 : }
72 168855 : PUSH_BE_U32(buf, 0, L);
73 168855 : rc = gnutls_hmac(hmac_hnd, buf, sizeof(buf));
74 168855 : if (rc < 0) {
75 0 : return gnutls_error_to_ntstatus(
76 : rc, NT_STATUS_HMAC_NOT_SUPPORTED);
77 : }
78 : }
79 :
80 169105 : gnutls_hmac_output(hmac_hnd, digest);
81 :
82 169105 : return NT_STATUS_OK;
83 : }
84 :
85 162973 : static size_t ceiling_div(const size_t a, const size_t b)
86 : {
87 168424 : return a / b + (a % b != 0);
88 : }
89 :
90 : /**
91 : * @brief Derive a key using the NIST SP 800‐108 algorithm.
92 : *
93 : * The details of the algorithm can be found at
94 : * https://csrc.nist.gov/pubs/sp/800/108/r1/final.
95 : *
96 : * @param KI The key‐derivation key used as input.
97 : *
98 : * @param KI_len The length of the key‐derivation key.
99 : *
100 : * @param FixedData If non‐NULL, specifies fixed data to be used in place of
101 : * that constructed from the Label and Context parameters.
102 : *
103 : * @param FixedData_len The length of the fixed data, if it is present.
104 : *
105 : * @param Label A label that identifies the purpose for the derived key.
106 : * Ignored if FixedData is non‐NULL.
107 : *
108 : * @param Label_len The length of the label.
109 : *
110 : * @param Context Information related to the derived key. Ignored if
111 : * FixedData is non‐NULL.
112 : *
113 : * @param Context_len The length of the context data.
114 : *
115 : * @param algorithm The HMAC algorithm to use.
116 : *
117 : * @param KO A buffer to receive the derived key.
118 : *
119 : * @param KO_len The length of the key to be derived.
120 : *
121 : * @return NT_STATUS_OK on success, an NT status error code otherwise.
122 : */
123 168424 : NTSTATUS samba_gnutls_sp800_108_derive_key(
124 : const uint8_t *KI,
125 : size_t KI_len,
126 : const uint8_t *FixedData,
127 : size_t FixedData_len,
128 : const uint8_t *Label,
129 : size_t Label_len,
130 : const uint8_t *Context,
131 : size_t Context_len,
132 : const gnutls_mac_algorithm_t algorithm,
133 : uint8_t *KO,
134 : size_t KO_len)
135 : {
136 168424 : gnutls_hmac_hd_t hmac_hnd = NULL;
137 168424 : const size_t digest_len = gnutls_hmac_get_len(algorithm);
138 5451 : uint32_t i;
139 168424 : uint32_t L = KO_len * 8;
140 5451 : size_t KO_idx;
141 168424 : NTSTATUS status = NT_STATUS_OK;
142 5451 : int rc;
143 :
144 168424 : if (KO_len > UINT32_MAX / 8) {
145 : /* The calculation of L has overflowed. */
146 0 : return NT_STATUS_INTERNAL_ERROR;
147 : }
148 :
149 168424 : if (digest_len == 0) {
150 0 : return NT_STATUS_HMAC_NOT_SUPPORTED;
151 : }
152 :
153 : {
154 168424 : const size_t n_iterations = ceiling_div(KO_len, digest_len);
155 : /*
156 : * To ensure that the counter values are distinct, n shall not
157 : * be larger than 2ʳ−1, where r = 32. We have made sure that
158 : * |KO| × 8 < 2³², and we know that n ≤ |KO| from its
159 : * definition. Thus n ≤ |KO| ≤ |KO| × 8 < 2³², and so the
160 : * requirement n ≤ 2³² − 1 must always hold.
161 : */
162 168424 : SMB_ASSERT(n_iterations <= UINT32_MAX);
163 : }
164 :
165 : /*
166 : * a simplified version of
167 : * "NIST Special Publication 800-108" section 5.1.
168 : */
169 168424 : rc = gnutls_hmac_init(&hmac_hnd,
170 : algorithm,
171 : KI,
172 : KI_len);
173 168424 : if (rc < 0) {
174 0 : return gnutls_error_to_ntstatus(rc,
175 : NT_STATUS_HMAC_NOT_SUPPORTED);
176 : }
177 :
178 : /* (This loop would make an excellent candidate for parallelization.) */
179 :
180 172899 : for (KO_idx = 0, i = 1; KO_len - KO_idx >= digest_len;
181 4475 : KO_idx += digest_len, ++i)
182 : {
183 4475 : status = samba_gnutls_sp800_108_derive_key_part(hmac_hnd,
184 : FixedData,
185 : FixedData_len,
186 : Label,
187 : Label_len,
188 : Context,
189 : Context_len,
190 : L,
191 : i,
192 : KO + KO_idx);
193 4475 : if (!NT_STATUS_IS_OK(status)) {
194 0 : goto out;
195 : }
196 : }
197 :
198 168424 : if (KO_idx < KO_len) {
199 : /* Get the last little bit. */
200 164630 : uint8_t digest[digest_len];
201 164630 : status = samba_gnutls_sp800_108_derive_key_part(hmac_hnd,
202 : FixedData,
203 : FixedData_len,
204 : Label,
205 : Label_len,
206 : Context,
207 : Context_len,
208 : L,
209 : i,
210 : digest);
211 164630 : if (!NT_STATUS_IS_OK(status)) {
212 0 : goto out;
213 : }
214 :
215 164630 : memcpy(KO + KO_idx, digest, KO_len - KO_idx);
216 :
217 164630 : ZERO_ARRAY(digest);
218 : }
219 :
220 3794 : out:
221 168424 : if (hmac_hnd != NULL) {
222 168424 : gnutls_hmac_deinit(hmac_hnd, NULL);
223 : }
224 168424 : if (!NT_STATUS_IS_OK(status)) {
225 : /* Hide the evidence. */
226 0 : memset_s(KO, KO_len, 0, KO_idx);
227 : }
228 :
229 168424 : return status;
230 : }
|