Coverage Report

Created: 2023-03-26 08:33

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