s4:kdc: also provide cross-realm keys via samba_kdc_seq()
[Samba.git] / lib / crypto / gnutls_sp800_108.c
blobfb0aa03921329499b8a69d9b45fbeb9b4afec698
1 /*
2 Unix SMB/CIFS implementation.
3 Wrapper for gnutls key derivation functions
5 Copyright (C) Stefan Metzmacher 2009
6 Copyright (C) Catalyst.Net Ltd 2023
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.
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.
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/>.
23 #include "includes.h"
24 #include <gnutls/gnutls.h>
25 #include <gnutls/crypto.h>
26 #include "gnutls_helpers.h"
28 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)
40 uint8_t buf[4];
41 static const uint8_t zero = 0;
42 int rc;
44 PUSH_BE_U32(buf, 0, i);
45 rc = gnutls_hmac(hmac_hnd, buf, sizeof(buf));
46 if (rc < 0) {
47 return gnutls_error_to_ntstatus(rc,
48 NT_STATUS_HMAC_NOT_SUPPORTED);
50 if (FixedData != NULL) {
51 rc = gnutls_hmac(hmac_hnd, FixedData, FixedData_len);
52 if (rc < 0) {
53 return gnutls_error_to_ntstatus(
54 rc, NT_STATUS_HMAC_NOT_SUPPORTED);
56 } else {
57 rc = gnutls_hmac(hmac_hnd, Label, Label_len);
58 if (rc < 0) {
59 return gnutls_error_to_ntstatus(
60 rc, NT_STATUS_HMAC_NOT_SUPPORTED);
62 rc = gnutls_hmac(hmac_hnd, &zero, 1);
63 if (rc < 0) {
64 return gnutls_error_to_ntstatus(
65 rc, NT_STATUS_HMAC_NOT_SUPPORTED);
67 rc = gnutls_hmac(hmac_hnd, Context, Context_len);
68 if (rc < 0) {
69 return gnutls_error_to_ntstatus(
70 rc, NT_STATUS_HMAC_NOT_SUPPORTED);
72 PUSH_BE_U32(buf, 0, L);
73 rc = gnutls_hmac(hmac_hnd, buf, sizeof(buf));
74 if (rc < 0) {
75 return gnutls_error_to_ntstatus(
76 rc, NT_STATUS_HMAC_NOT_SUPPORTED);
80 gnutls_hmac_output(hmac_hnd, digest);
82 return NT_STATUS_OK;
85 static size_t ceiling_div(const size_t a, const size_t b)
87 return a / b + (a % b != 0);
90 /**
91 * @brief Derive a key using the NIST SP 800‐108 algorithm.
93 * The details of the algorithm can be found at
94 * https://csrc.nist.gov/pubs/sp/800/108/r1/final.
96 * @param KI The key‐derivation key used as input.
98 * @param KI_len The length of the key‐derivation key.
100 * @param FixedData If non‐NULL, specifies fixed data to be used in place of
101 * that constructed from the Label and Context parameters.
103 * @param FixedData_len The length of the fixed data, if it is present.
105 * @param Label A label that identifies the purpose for the derived key.
106 * Ignored if FixedData is non‐NULL.
108 * @param Label_len The length of the label.
110 * @param Context Information related to the derived key. Ignored if
111 * FixedData is non‐NULL.
113 * @param Context_len The length of the context data.
115 * @param algorithm The HMAC algorithm to use.
117 * @param KO A buffer to receive the derived key.
119 * @param KO_len The length of the key to be derived.
121 * @return NT_STATUS_OK on success, an NT status error code otherwise.
123 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)
136 gnutls_hmac_hd_t hmac_hnd = NULL;
137 const size_t digest_len = gnutls_hmac_get_len(algorithm);
138 uint32_t i;
139 uint32_t L = KO_len * 8;
140 size_t KO_idx;
141 NTSTATUS status = NT_STATUS_OK;
142 int rc;
144 if (KO_len > UINT32_MAX / 8) {
145 /* The calculation of L has overflowed. */
146 return NT_STATUS_INTERNAL_ERROR;
149 if (digest_len == 0) {
150 return NT_STATUS_HMAC_NOT_SUPPORTED;
154 const size_t n_iterations = ceiling_div(KO_len, digest_len);
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.
162 SMB_ASSERT(n_iterations <= UINT32_MAX);
166 * a simplified version of
167 * "NIST Special Publication 800-108" section 5.1.
169 rc = gnutls_hmac_init(&hmac_hnd,
170 algorithm,
172 KI_len);
173 if (rc < 0) {
174 return gnutls_error_to_ntstatus(rc,
175 NT_STATUS_HMAC_NOT_SUPPORTED);
178 /* (This loop would make an excellent candidate for parallelization.) */
180 for (KO_idx = 0, i = 1; KO_len - KO_idx >= digest_len;
181 KO_idx += digest_len, ++i)
183 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,
192 KO + KO_idx);
193 if (!NT_STATUS_IS_OK(status)) {
194 goto out;
198 if (KO_idx < KO_len) {
199 /* Get the last little bit. */
200 uint8_t digest[digest_len];
201 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,
210 digest);
211 if (!NT_STATUS_IS_OK(status)) {
212 goto out;
215 memcpy(KO + KO_idx, digest, KO_len - KO_idx);
217 ZERO_ARRAY(digest);
220 out:
221 if (hmac_hnd != NULL) {
222 gnutls_hmac_deinit(hmac_hnd, NULL);
224 if (!NT_STATUS_IS_OK(status)) {
225 /* Hide the evidence. */
226 memset_s(KO, KO_len, 0, KO_idx);
229 return status;