Coverage Report

Created: 2025-03-18 06:55

/src/gnutls/lib/x509/privkey_openssl.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2012 Free Software Foundation, Inc.
3
 *
4
 * Author: David Woodhouse
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
25
#include "datum.h"
26
#include "global.h"
27
#include "errors.h"
28
#include "common.h"
29
#include "x509.h"
30
#include "x509_b64.h"
31
#include "x509_int.h"
32
#include "algorithms.h"
33
#include "num.h"
34
#include "random.h"
35
36
static int openssl_hash_password(const char *_password, gnutls_datum_t *key,
37
         gnutls_datum_t *salt)
38
0
{
39
0
  unsigned char md5[16];
40
0
  digest_hd_st hd;
41
0
  unsigned int count = 0;
42
0
  int ret;
43
0
  char *password = NULL;
44
45
0
  if (_password != NULL) {
46
0
    gnutls_datum_t pout;
47
0
    ret = _gnutls_utf8_password_normalize(
48
0
      _password, strlen(_password), &pout, 1);
49
0
    if (ret < 0)
50
0
      return gnutls_assert_val(ret);
51
52
0
    password = (char *)pout.data;
53
0
  }
54
55
0
  while (count < key->size) {
56
0
    ret = _gnutls_hash_init(&hd, mac_to_entry(GNUTLS_MAC_MD5));
57
0
    if (ret < 0) {
58
0
      gnutls_assert();
59
0
      goto cleanup;
60
0
    }
61
62
0
    if (count) {
63
0
      ret = _gnutls_hash(&hd, md5, sizeof(md5));
64
0
      if (ret < 0) {
65
0
      hash_err:
66
0
        _gnutls_hash_deinit(&hd, NULL);
67
0
        gnutls_assert();
68
0
        goto cleanup;
69
0
      }
70
0
    }
71
72
0
    if (password) {
73
0
      ret = _gnutls_hash(&hd, password, strlen(password));
74
0
      if (ret < 0) {
75
0
        gnutls_assert();
76
0
        goto hash_err;
77
0
      }
78
0
    }
79
0
    ret = _gnutls_hash(&hd, salt->data, 8);
80
0
    if (ret < 0) {
81
0
      gnutls_assert();
82
0
      goto hash_err;
83
0
    }
84
85
0
    _gnutls_hash_deinit(&hd, md5);
86
87
0
    if (key->size - count <= sizeof(md5)) {
88
0
      memcpy(&key->data[count], md5, key->size - count);
89
0
      break;
90
0
    }
91
92
0
    memcpy(&key->data[count], md5, sizeof(md5));
93
0
    count += sizeof(md5);
94
0
  }
95
0
  ret = 0;
96
97
0
cleanup:
98
0
  gnutls_free(password);
99
0
  return ret;
100
0
}
101
102
struct pem_cipher {
103
  const char *name;
104
  gnutls_cipher_algorithm_t cipher;
105
};
106
107
static const struct pem_cipher pem_ciphers[] = {
108
  { "DES-CBC", GNUTLS_CIPHER_DES_CBC },
109
  { "DES-EDE3-CBC", GNUTLS_CIPHER_3DES_CBC },
110
  { "AES-128-CBC", GNUTLS_CIPHER_AES_128_CBC },
111
  { "AES-192-CBC", GNUTLS_CIPHER_AES_192_CBC },
112
  { "AES-256-CBC", GNUTLS_CIPHER_AES_256_CBC },
113
  { "CAMELLIA-128-CBC", GNUTLS_CIPHER_CAMELLIA_128_CBC },
114
  { "CAMELLIA-192-CBC", GNUTLS_CIPHER_CAMELLIA_192_CBC },
115
  { "CAMELLIA-256-CBC", GNUTLS_CIPHER_CAMELLIA_256_CBC },
116
};
117
118
/**
119
 * gnutls_x509_privkey_import_openssl:
120
 * @key: The data to store the parsed key
121
 * @data: The DER or PEM encoded key.
122
 * @password: the password to decrypt the key (if it is encrypted).
123
 *
124
 * This function will convert the given PEM encrypted to 
125
 * the native gnutls_x509_privkey_t format. The
126
 * output will be stored in @key.  
127
 *
128
 * The @password should be in ASCII. If the password is not provided
129
 * or wrong then %GNUTLS_E_DECRYPTION_FAILED will be returned.
130
 *
131
 * If the Certificate is PEM encoded it should have a header of
132
 * "PRIVATE KEY" and the "DEK-Info" header. 
133
 *
134
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
135
 *   negative error value.
136
 **/
137
int gnutls_x509_privkey_import_openssl(gnutls_x509_privkey_t key,
138
               const gnutls_datum_t *data,
139
               const char *password)
140
0
{
141
0
  gnutls_cipher_hd_t handle;
142
0
  gnutls_cipher_algorithm_t cipher = GNUTLS_CIPHER_UNKNOWN;
143
0
  gnutls_datum_t b64_data;
144
0
  gnutls_datum_t salt, enc_key, hex_data;
145
0
  unsigned char *key_data;
146
0
  size_t key_data_size;
147
0
  const char *pem_header = (void *)data->data;
148
0
  const char *pem_header_start = (void *)data->data;
149
0
  ssize_t pem_header_size;
150
0
  int ret;
151
0
  unsigned int i, iv_size, l;
152
0
  size_t salt_size;
153
154
0
  pem_header_size = data->size;
155
156
0
  pem_header = memmem(pem_header, pem_header_size, "PRIVATE KEY---", 14);
157
0
  if (pem_header == NULL) {
158
0
    gnutls_assert();
159
0
    return GNUTLS_E_PARSING_ERROR;
160
0
  }
161
162
0
  pem_header_size -= (ptrdiff_t)(pem_header - pem_header_start);
163
164
0
  pem_header = memmem(pem_header, pem_header_size, "DEK-Info: ", 10);
165
0
  if (pem_header == NULL) {
166
0
    gnutls_assert();
167
0
    return GNUTLS_E_PARSING_ERROR;
168
0
  }
169
170
0
  pem_header_size =
171
0
    data->size - (ptrdiff_t)(pem_header - pem_header_start) - 10;
172
0
  pem_header += 10;
173
174
0
  for (i = 0; i < sizeof(pem_ciphers) / sizeof(pem_ciphers[0]); i++) {
175
0
    l = strlen(pem_ciphers[i].name);
176
0
    if (!strncmp(pem_header, pem_ciphers[i].name, l) &&
177
0
        pem_header[l] == ',') {
178
0
      pem_header += l + 1;
179
0
      cipher = pem_ciphers[i].cipher;
180
0
      break;
181
0
    }
182
0
  }
183
184
0
  if (cipher == GNUTLS_CIPHER_UNKNOWN) {
185
0
    _gnutls_debug_log("Unsupported PEM encryption type: %.10s\n",
186
0
          pem_header);
187
0
    gnutls_assert();
188
0
    return GNUTLS_E_INVALID_REQUEST;
189
0
  }
190
191
0
  iv_size = gnutls_cipher_get_iv_size(cipher);
192
0
  salt.size = iv_size;
193
0
  salt.data = gnutls_malloc(salt.size);
194
0
  if (!salt.data)
195
0
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
196
197
0
  hex_data.data = (unsigned char *)pem_header;
198
0
  hex_data.size = salt.size * 2;
199
0
  salt_size = salt.size;
200
201
0
  ret = gnutls_hex_decode(&hex_data, salt.data, &salt_size);
202
0
  if (ret < 0) {
203
0
    gnutls_assert();
204
0
    if (ret == GNUTLS_E_PARSING_ERROR) {
205
      /* Invalid salt in encrypted PEM file */
206
0
      ret = GNUTLS_E_INVALID_REQUEST;
207
0
    }
208
0
    goto out_salt;
209
0
  }
210
211
0
  pem_header += hex_data.size;
212
0
  if (*pem_header != '\r' && *pem_header != '\n') {
213
0
    gnutls_assert();
214
0
    ret = GNUTLS_E_INVALID_REQUEST;
215
0
    goto out_salt;
216
0
  }
217
0
  while (*pem_header == '\n' || *pem_header == '\r')
218
0
    pem_header++;
219
220
0
  ret = _gnutls_base64_decode((const void *)pem_header, pem_header_size,
221
0
            &b64_data);
222
0
  if (ret < 0) {
223
0
    gnutls_assert();
224
0
    goto out_salt;
225
0
  }
226
227
0
  if (b64_data.size < 16) {
228
    /* Just to be sure our parsing is OK */
229
0
    gnutls_assert();
230
0
    ret = GNUTLS_E_PARSING_ERROR;
231
0
    goto out_b64;
232
0
  }
233
234
0
  enc_key.size = gnutls_cipher_get_key_size(cipher);
235
0
  enc_key.data = gnutls_malloc(enc_key.size);
236
0
  if (!enc_key.data) {
237
0
    ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
238
0
    goto out_b64;
239
0
  }
240
241
0
  key_data_size = b64_data.size;
242
0
  key_data = gnutls_malloc(key_data_size);
243
0
  if (!key_data) {
244
0
    ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
245
0
    goto out_enc_key;
246
0
  }
247
248
0
  while (1) {
249
0
    memcpy(key_data, b64_data.data, key_data_size);
250
251
0
    ret = openssl_hash_password(password, &enc_key, &salt);
252
0
    if (ret < 0) {
253
0
      gnutls_assert();
254
0
      goto out;
255
0
    }
256
257
0
    ret = gnutls_cipher_init(&handle, cipher, &enc_key, &salt);
258
0
    if (ret < 0) {
259
0
      gnutls_assert();
260
0
      goto out;
261
0
    }
262
263
0
    ret = gnutls_cipher_decrypt(handle, key_data, key_data_size);
264
0
    gnutls_cipher_deinit(handle);
265
266
0
    if (ret < 0) {
267
0
      gnutls_assert();
268
0
      goto out;
269
0
    }
270
271
    /* We have to strip any padding to accept it.
272
       So a bit more ASN.1 parsing for us. */
273
0
    if (key_data[0] == 0x30) {
274
0
      gnutls_datum_t key_datum;
275
0
      unsigned int blocksize =
276
0
        gnutls_cipher_get_block_size(cipher);
277
0
      unsigned int keylen = key_data[1];
278
0
      unsigned int ofs = 2;
279
280
0
      if (keylen & 0x80) {
281
0
        int lenlen = keylen & 0x7f;
282
0
        keylen = 0;
283
284
0
        if (lenlen > 3) {
285
0
          gnutls_assert();
286
0
          goto fail;
287
0
        }
288
289
0
        while (lenlen) {
290
0
          keylen <<= 8;
291
0
          keylen |= key_data[ofs++];
292
0
          lenlen--;
293
0
        }
294
0
      }
295
0
      keylen += ofs;
296
297
      /* If there appears to be more or less padding than required, fail */
298
0
      if (key_data_size - keylen > blocksize ||
299
0
          key_data_size < keylen + 1) {
300
0
        gnutls_assert();
301
0
        goto fail;
302
0
      }
303
304
      /* If the padding bytes aren't all equal to the amount of padding, fail */
305
0
      ofs = keylen;
306
0
      while (ofs < key_data_size) {
307
0
        if (key_data[ofs] != key_data_size - keylen) {
308
0
          gnutls_assert();
309
0
          goto fail;
310
0
        }
311
0
        ofs++;
312
0
      }
313
314
0
      key_datum.data = key_data;
315
0
      key_datum.size = keylen;
316
0
      ret = gnutls_x509_privkey_import(key, &key_datum,
317
0
               GNUTLS_X509_FMT_DER);
318
0
      if (ret == 0)
319
0
        goto out;
320
0
    }
321
0
  fail:
322
0
    ret = GNUTLS_E_DECRYPTION_FAILED;
323
0
    goto out;
324
0
  }
325
0
out:
326
0
  zeroize_key(key_data, key_data_size);
327
0
  gnutls_free(key_data);
328
0
out_enc_key:
329
0
  _gnutls_free_key_datum(&enc_key);
330
0
out_b64:
331
0
  gnutls_free(b64_data.data);
332
0
out_salt:
333
0
  gnutls_free(salt.data);
334
0
  return ret;
335
0
}