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/>.
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
,
33 const size_t Label_len
,
34 const uint8_t *Context
,
35 const size_t Context_len
,
41 static const uint8_t zero
= 0;
44 PUSH_BE_U32(buf
, 0, i
);
45 rc
= gnutls_hmac(hmac_hnd
, buf
, sizeof(buf
));
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
);
53 return gnutls_error_to_ntstatus(
54 rc
, NT_STATUS_HMAC_NOT_SUPPORTED
);
57 rc
= gnutls_hmac(hmac_hnd
, Label
, Label_len
);
59 return gnutls_error_to_ntstatus(
60 rc
, NT_STATUS_HMAC_NOT_SUPPORTED
);
62 rc
= gnutls_hmac(hmac_hnd
, &zero
, 1);
64 return gnutls_error_to_ntstatus(
65 rc
, NT_STATUS_HMAC_NOT_SUPPORTED
);
67 rc
= gnutls_hmac(hmac_hnd
, Context
, Context_len
);
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
));
75 return gnutls_error_to_ntstatus(
76 rc
, NT_STATUS_HMAC_NOT_SUPPORTED
);
80 gnutls_hmac_output(hmac_hnd
, digest
);
85 static size_t ceiling_div(const size_t a
, const size_t b
)
87 return a
/ b
+ (a
% b
!= 0);
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(
126 const uint8_t *FixedData
,
127 size_t FixedData_len
,
128 const uint8_t *Label
,
130 const uint8_t *Context
,
132 const gnutls_mac_algorithm_t algorithm
,
136 gnutls_hmac_hd_t hmac_hnd
= NULL
;
137 const size_t digest_len
= gnutls_hmac_get_len(algorithm
);
139 uint32_t L
= KO_len
* 8;
141 NTSTATUS status
= NT_STATUS_OK
;
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
,
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
,
193 if (!NT_STATUS_IS_OK(status
)) {
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
,
211 if (!NT_STATUS_IS_OK(status
)) {
215 memcpy(KO
+ KO_idx
, digest
, KO_len
- KO_idx
);
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
);