Coverage Report

Created: 2026-05-16 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/cert-cred-rawpk.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2017 - 2018 ARPA2 project
3
 *
4
 * Author: Tom Vrancken (dev@tomvrancken.nl)
5
 *
6
 * This file is part of GnuTLS.
7
 *
8
 * The GnuTLS is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public License
10
 * as published by the Free Software Foundation; either version 2.1 of
11
 * the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
20
 *
21
 */
22
23
#include "gnutls_int.h"
24
#include <gnutls/gnutls.h>
25
#include "datum.h"
26
#include "auth/cert.h"
27
#include "x509.h"
28
#include "cert-cred.h"
29
#include "read-file.h"
30
#include <stdint.h>
31
32
/**
33
 * gnutls_certificate_set_rawpk_key_mem:
34
 * @cred: is a #gnutls_certificate_credentials_t type.
35
 * @spki: contains a raw public key in
36
 *   PKIX.SubjectPublicKeyInfo format.
37
 * @pkey: contains a raw private key.
38
 * @format: encoding of the keys. DER or PEM.
39
 * @pass: an optional password to unlock the private key pkey.
40
 * @key_usage: An ORed sequence of %GNUTLS_KEY_* flags.
41
 * @names: is an array of DNS names belonging to the public-key (NULL if none).
42
 * @names_length: holds the length of the names list.
43
 * @flags: an ORed sequence of #gnutls_pkcs_encrypt_flags_t.
44
 *   These apply to the private key pkey.
45
 *
46
 * This function sets a public/private keypair in the
47
 * #gnutls_certificate_credentials_t type to be used for authentication
48
 * and/or encryption. @spki and @privkey should match otherwise set
49
 * signatures cannot be validated. In case of no match this function
50
 * returns %GNUTLS_E_CERTIFICATE_KEY_MISMATCH. This function should
51
 * be called once for the client because there is currently no mechanism
52
 * to determine which raw public-key to select for the peer when there
53
 * are multiple present. Multiple raw public keys for the server can be
54
 * distinghuished by setting the @names.
55
 *
56
 * Note here that @spki is a raw public-key as defined
57
 * in RFC7250. It means that there is no surrounding certificate that
58
 * holds the public key and that there is therefore no direct mechanism
59
 * to prove the authenticity of this key. The keypair can be used during
60
 * a TLS handshake but its authenticity should be established via a
61
 * different mechanism (e.g. TOFU or known fingerprint).
62
 *
63
 * The supported formats are basic unencrypted key, PKCS8, PKCS12,
64
 * and the openssl format and will be autodetected.
65
 *
66
 * If the raw public-key and the private key are given in PEM encoding
67
 * then the strings that hold their values must be null terminated.
68
 *
69
 * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly
70
 * set because there is no certificate structure around the key to define
71
 * this value. See for more info gnutls_x509_crt_get_key_usage().
72
 *
73
 * Note that, this function by default returns zero on success and a
74
 * negative value on error. Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2
75
 * is set using gnutls_certificate_set_flags() it returns an index
76
 * (greater or equal to zero). That index can be used in other functions
77
 * to refer to the added key-pair.
78
 *
79
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, in case the
80
 *   key pair does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH is returned,
81
 *   in other erroneous cases a different negative error code is returned.
82
 *
83
 * Since: 3.6.6
84
 **/
85
int gnutls_certificate_set_rawpk_key_mem(
86
  gnutls_certificate_credentials_t cred, const gnutls_datum_t *spki,
87
  const gnutls_datum_t *pkey, gnutls_x509_crt_fmt_t format,
88
  const char *pass, unsigned int key_usage, const char **names,
89
  unsigned int names_length, unsigned int flags)
90
9.18k
{
91
9.18k
  int ret;
92
9.18k
  gnutls_privkey_t privkey;
93
9.18k
  gnutls_pcert_st *pcert;
94
9.18k
  gnutls_str_array_t str_names;
95
9.18k
  unsigned int i;
96
97
9.18k
  if (pkey == NULL || spki == NULL) {
98
0
    return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
99
0
  }
100
101
  /* Import our private key. This function does all the necessary
102
   * inits, checks and imports. */
103
9.18k
  ret = _gnutls_read_key_mem(cred, pkey->data, pkey->size, format, pass,
104
9.18k
           flags, &privkey);
105
9.18k
  if (ret < 0) {
106
0
    return gnutls_assert_val(ret);
107
0
  }
108
109
  /* We now convert our raw public key to a parsed certificate (pcert) structure */
110
9.18k
  pcert = gnutls_calloc(1, sizeof(*pcert));
111
9.18k
  if (pcert == NULL) {
112
0
    gnutls_privkey_deinit(privkey);
113
114
0
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
115
0
  }
116
  // Import our raw public key to the pcert structure
117
9.18k
  ret = gnutls_pcert_import_rawpk_raw(pcert, spki, format, key_usage, 0);
118
9.18k
  if (ret < 0) {
119
0
    gnutls_privkey_deinit(privkey);
120
0
    gnutls_pcert_deinit(pcert);
121
0
    gnutls_free(pcert);
122
123
0
    return gnutls_assert_val(ret);
124
0
  }
125
126
  /* Process the names, if any */
127
9.18k
  _gnutls_str_array_init(&str_names);
128
129
9.18k
  if (names != NULL && names_length > 0) {
130
0
    for (i = 0; i < names_length; i++) {
131
0
      ret = _gnutls_str_array_append_idna(
132
0
        &str_names, names[i], strlen(names[i]));
133
0
      if (ret < 0) {
134
0
        gnutls_privkey_deinit(privkey);
135
0
        _gnutls_str_array_clear(&str_names);
136
0
        gnutls_pcert_deinit(pcert);
137
0
        gnutls_free(pcert);
138
139
0
        return gnutls_assert_val(ret);
140
0
      }
141
0
    }
142
0
  }
143
144
  /* Now that we have converted the key material to our internal structures
145
   * we can now add them to the credentials structure */
146
9.18k
  ret = _gnutls_certificate_credential_append_keypair(
147
9.18k
    cred, privkey, str_names, pcert, 1);
148
  // Check for errors
149
9.18k
  if (ret < 0) {
150
0
    gnutls_privkey_deinit(privkey);
151
0
    gnutls_pcert_deinit(pcert);
152
0
    gnutls_free(pcert);
153
154
0
    return gnutls_assert_val(ret);
155
0
  }
156
  // Successfully added a certificate
157
9.18k
  cred->ncerts++;
158
159
  /* Check whether the key pair matches.
160
   * After this point we do not deinitialize anything on failure to avoid
161
   * double freeing. We intentionally keep everything as the credentials state
162
   * is documented to be in undefined state. */
163
9.18k
  if ((ret = _gnutls_check_key_cert_match(cred)) < 0) {
164
0
    return gnutls_assert_val(ret);
165
0
  }
166
167
9.18k
  CRED_RET_SUCCESS(cred);
168
0
}
169
170
/**
171
 * gnutls_certificate_set_rawpk_key_file:
172
 * @cred: is a #gnutls_certificate_credentials_t type.
173
 * @rawpkfile: contains a raw public key in
174
 *   PKIX.SubjectPublicKeyInfo format.
175
 * @privkeyfile: contains a file path to a private key.
176
 * @format: encoding of the keys. DER or PEM.
177
 * @pass: an optional password to unlock the private key privkeyfile.
178
 * @key_usage: an ORed sequence of %GNUTLS_KEY_* flags.
179
 * @names: is an array of DNS names belonging to the public-key (NULL if none).
180
 * @names_length: holds the length of the names list.
181
 * @privkey_flags: an ORed sequence of #gnutls_pkcs_encrypt_flags_t.
182
 *   These apply to the private key pkey.
183
 * @pkcs11_flags: one of gnutls_pkcs11_obj_flags. These apply to URLs.
184
 *
185
 * This function sets a public/private keypair read from file in the
186
 * #gnutls_certificate_credentials_t type to be used for authentication
187
 * and/or encryption. @spki and @privkey should match otherwise set
188
 * signatures cannot be validated. In case of no match this function
189
 * returns %GNUTLS_E_CERTIFICATE_KEY_MISMATCH. This function should
190
 * be called once for the client because there is currently no mechanism
191
 * to determine which raw public-key to select for the peer when there
192
 * are multiple present. Multiple raw public keys for the server can be
193
 * distinghuished by setting the @names.
194
 *
195
 * Note here that @spki is a raw public-key as defined
196
 * in RFC7250. It means that there is no surrounding certificate that
197
 * holds the public key and that there is therefore no direct mechanism
198
 * to prove the authenticity of this key. The keypair can be used during
199
 * a TLS handshake but its authenticity should be established via a
200
 * different mechanism (e.g. TOFU or known fingerprint).
201
 *
202
 * The supported formats are basic unencrypted key, PKCS8, PKCS12,
203
 * and the openssl format and will be autodetected.
204
 *
205
 * If the raw public-key and the private key are given in PEM encoding
206
 * then the strings that hold their values must be null terminated.
207
 *
208
 * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly
209
 * set because there is no certificate structure around the key to define
210
 * this value. See for more info gnutls_x509_crt_get_key_usage().
211
 *
212
 * Note that, this function by default returns zero on success and a
213
 * negative value on error. Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2
214
 * is set using gnutls_certificate_set_flags() it returns an index
215
 * (greater or equal to zero). That index can be used in other functions
216
 * to refer to the added key-pair.
217
 *
218
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, in case the
219
 *   key pair does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH is returned,
220
 *   in other erroneous cases a different negative error code is returned.
221
 *
222
 * Since: 3.6.6
223
 */
224
int gnutls_certificate_set_rawpk_key_file(
225
  gnutls_certificate_credentials_t cred, const char *rawpkfile,
226
  const char *privkeyfile, gnutls_x509_crt_fmt_t format, const char *pass,
227
  unsigned int key_usage, const char **names, unsigned int names_length,
228
  unsigned int privkey_flags, unsigned int pkcs11_flags)
229
0
{
230
0
  int ret;
231
0
  gnutls_privkey_t privkey;
232
0
  gnutls_pubkey_t pubkey;
233
0
  gnutls_pcert_st *pcert;
234
0
  gnutls_str_array_t str_names;
235
0
  unsigned int i;
236
237
0
  if (rawpkfile == NULL || privkeyfile == NULL) {
238
0
    return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
239
0
  }
240
241
  /* Import our private key. This function does all the necessary
242
   * inits, checks and imports. */
243
0
  ret = _gnutls_read_key_file(cred, privkeyfile, format, pass,
244
0
            privkey_flags, &privkey);
245
0
  if (ret < 0) {
246
0
    return gnutls_assert_val(ret);
247
0
  }
248
249
0
  pcert = gnutls_calloc(1, sizeof(*pcert));
250
0
  if (pcert == NULL) {
251
0
    gnutls_privkey_deinit(privkey);
252
253
0
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
254
0
  }
255
256
  /* Check whether we are importing our raw public-key from a URL
257
   * or from a regular file.
258
   */
259
0
  if (gnutls_url_is_supported(rawpkfile)) {
260
0
    ret = gnutls_pubkey_init(&pubkey);
261
0
    if (ret < 0) {
262
0
      gnutls_privkey_deinit(privkey);
263
0
      gnutls_free(pcert);
264
265
0
      return gnutls_assert_val(ret);
266
0
    }
267
268
0
    ret = gnutls_pubkey_import_url(pubkey, rawpkfile, pkcs11_flags);
269
0
    if (ret < 0) {
270
0
      gnutls_privkey_deinit(privkey);
271
0
      gnutls_pubkey_deinit(pubkey);
272
0
      gnutls_free(pcert);
273
274
0
      return gnutls_assert_val(ret);
275
0
    }
276
277
0
    ret = gnutls_pcert_import_rawpk(pcert, pubkey, 0);
278
0
    if (ret < 0) {
279
0
      gnutls_privkey_deinit(privkey);
280
0
      gnutls_pubkey_deinit(pubkey);
281
0
      gnutls_free(pcert);
282
283
0
      return gnutls_assert_val(ret);
284
0
    }
285
286
0
  } else {
287
0
    gnutls_datum_t rawpubkey; // to hold rawpk data from file
288
0
    size_t key_size;
289
290
    /* Read our raw public-key into memory from file */
291
0
    rawpubkey.data = (void *)read_file(
292
0
      rawpkfile, RF_BINARY | RF_SENSITIVE, &key_size);
293
0
    if (rawpubkey.data == NULL) {
294
0
      gnutls_privkey_deinit(privkey);
295
0
      gnutls_free(pcert);
296
297
0
      return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
298
0
    }
299
0
    rawpubkey.size = key_size; // Implicit type casting
300
301
    /* We now convert our raw public key that we've loaded into memory to
302
     * a parsed certificate (pcert) structure. Note that rawpubkey will
303
     * be copied into pcert. Therefore we can directly cleanup rawpubkey.
304
     */
305
0
    ret = gnutls_pcert_import_rawpk_raw(pcert, &rawpubkey, format,
306
0
                key_usage, 0);
307
308
0
    zeroize_key(rawpubkey.data, rawpubkey.size);
309
0
    free(rawpubkey.data);
310
0
    rawpubkey.size = 0;
311
312
0
    if (ret < 0) {
313
0
      gnutls_privkey_deinit(privkey);
314
0
      gnutls_free(pcert);
315
316
0
      return gnutls_assert_val(ret);
317
0
    }
318
0
  }
319
320
  /* Process the names, if any */
321
0
  _gnutls_str_array_init(&str_names);
322
323
0
  if (names != NULL && names_length > 0) {
324
0
    for (i = 0; i < names_length; i++) {
325
0
      ret = _gnutls_str_array_append_idna(
326
0
        &str_names, names[i], strlen(names[i]));
327
0
      if (ret < 0) {
328
0
        gnutls_privkey_deinit(privkey);
329
0
        _gnutls_str_array_clear(&str_names);
330
0
        gnutls_free(pcert);
331
332
0
        return gnutls_assert_val(ret);
333
0
      }
334
0
    }
335
0
  }
336
337
  /* Now that we have converted the key material to our internal structures
338
   * we can now add them to the credentials structure */
339
0
  ret = _gnutls_certificate_credential_append_keypair(
340
0
    cred, privkey, str_names, pcert, 1);
341
0
  if (ret < 0) {
342
0
    gnutls_privkey_deinit(privkey);
343
0
    gnutls_pcert_deinit(pcert);
344
0
    gnutls_free(pcert);
345
346
0
    return gnutls_assert_val(ret);
347
0
  }
348
  // Successfully added a certificate
349
0
  cred->ncerts++;
350
351
  /* Check whether the key pair matches.
352
   * After this point we do not deinitialize anything on failure to avoid
353
   * double freeing. We intentionally keep everything as the credentials state
354
   * is documented to be in undefined state. */
355
0
  if ((ret = _gnutls_check_key_cert_match(cred)) < 0) {
356
0
    return gnutls_assert_val(ret);
357
0
  }
358
359
0
  CRED_RET_SUCCESS(cred);
360
0
}