Coverage Report

Created: 2026-06-07 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/crypto/gnutls_sp800_108.c
Line
Count
Source
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
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
0
{
40
0
  uint8_t buf[4];
41
0
  static const uint8_t zero = 0;
42
0
  int rc;
43
44
0
  PUSH_BE_U32(buf, 0, i);
45
0
  rc = gnutls_hmac(hmac_hnd, buf, sizeof(buf));
46
0
  if (rc < 0) {
47
0
    return gnutls_error_to_ntstatus(rc,
48
0
            NT_STATUS_HMAC_NOT_SUPPORTED);
49
0
  }
50
0
  if (FixedData != NULL) {
51
0
    rc = gnutls_hmac(hmac_hnd, FixedData, FixedData_len);
52
0
    if (rc < 0) {
53
0
      return gnutls_error_to_ntstatus(
54
0
        rc, NT_STATUS_HMAC_NOT_SUPPORTED);
55
0
    }
56
0
  } else {
57
0
    rc = gnutls_hmac(hmac_hnd, Label, Label_len);
58
0
    if (rc < 0) {
59
0
      return gnutls_error_to_ntstatus(
60
0
        rc, NT_STATUS_HMAC_NOT_SUPPORTED);
61
0
    }
62
0
    rc = gnutls_hmac(hmac_hnd, &zero, 1);
63
0
    if (rc < 0) {
64
0
      return gnutls_error_to_ntstatus(
65
0
        rc, NT_STATUS_HMAC_NOT_SUPPORTED);
66
0
    }
67
0
    rc = gnutls_hmac(hmac_hnd, Context, Context_len);
68
0
    if (rc < 0) {
69
0
      return gnutls_error_to_ntstatus(
70
0
        rc, NT_STATUS_HMAC_NOT_SUPPORTED);
71
0
    }
72
0
    PUSH_BE_U32(buf, 0, L);
73
0
    rc = gnutls_hmac(hmac_hnd, buf, sizeof(buf));
74
0
    if (rc < 0) {
75
0
      return gnutls_error_to_ntstatus(
76
0
        rc, NT_STATUS_HMAC_NOT_SUPPORTED);
77
0
    }
78
0
  }
79
80
0
  gnutls_hmac_output(hmac_hnd, digest);
81
82
0
  return NT_STATUS_OK;
83
0
}
84
85
static size_t ceiling_div(const size_t a, const size_t b)
86
0
{
87
0
  return a / b + (a % b != 0);
88
0
}
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
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
0
{
136
0
  gnutls_hmac_hd_t hmac_hnd = NULL;
137
0
  const size_t digest_len = gnutls_hmac_get_len(algorithm);
138
0
  uint32_t i;
139
0
  uint32_t L = KO_len * 8;
140
0
  size_t KO_idx;
141
0
  NTSTATUS status = NT_STATUS_OK;
142
0
  int rc;
143
144
0
  if (KO_len > UINT32_MAX / 8) {
145
    /* The calculation of L has overflowed. */
146
0
    return NT_STATUS_INTERNAL_ERROR;
147
0
  }
148
149
0
  if (digest_len == 0) {
150
0
    return NT_STATUS_HMAC_NOT_SUPPORTED;
151
0
  }
152
153
0
  {
154
0
    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
0
    SMB_ASSERT(n_iterations <= UINT32_MAX);
163
0
  }
164
165
  /*
166
   * a simplified version of
167
   * "NIST Special Publication 800-108" section 5.1.
168
   */
169
0
  rc = gnutls_hmac_init(&hmac_hnd,
170
0
            algorithm,
171
0
            KI,
172
0
            KI_len);
173
0
  if (rc < 0) {
174
0
    return gnutls_error_to_ntstatus(rc,
175
0
            NT_STATUS_HMAC_NOT_SUPPORTED);
176
0
  }
177
178
  /* (This loop would make an excellent candidate for parallelization.) */
179
180
0
  for (KO_idx = 0, i = 1; KO_len - KO_idx >= digest_len;
181
0
       KO_idx += digest_len, ++i)
182
0
  {
183
0
    status = samba_gnutls_sp800_108_derive_key_part(hmac_hnd,
184
0
                FixedData,
185
0
                FixedData_len,
186
0
                Label,
187
0
                Label_len,
188
0
                Context,
189
0
                Context_len,
190
0
                L,
191
0
                i,
192
0
                KO + KO_idx);
193
0
    if (!NT_STATUS_IS_OK(status)) {
194
0
      goto out;
195
0
    }
196
0
  }
197
198
0
  if (KO_idx < KO_len) {
199
    /* Get the last little bit. */
200
0
    uint8_t digest[digest_len];
201
0
    status = samba_gnutls_sp800_108_derive_key_part(hmac_hnd,
202
0
                FixedData,
203
0
                FixedData_len,
204
0
                Label,
205
0
                Label_len,
206
0
                Context,
207
0
                Context_len,
208
0
                L,
209
0
                i,
210
0
                digest);
211
0
    if (!NT_STATUS_IS_OK(status)) {
212
0
      goto out;
213
0
    }
214
215
0
    memcpy(KO + KO_idx, digest, KO_len - KO_idx);
216
217
0
    ZERO_ARRAY(digest);
218
0
  }
219
220
0
out:
221
0
  if (hmac_hnd != NULL) {
222
0
    gnutls_hmac_deinit(hmac_hnd, NULL);
223
0
  }
224
0
  if (!NT_STATUS_IS_OK(status)) {
225
    /* Hide the evidence. */
226
0
    ZERO_ARRAY_LEN(KO, KO_idx);
227
0
  }
228
229
0
  return status;
230
0
}