|
Packit |
aea12f |
/*
|
|
Packit |
aea12f |
* Copyright (C) 2012 Free Software Foundation, Inc.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Author: David Woodhouse
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This file is part of GnuTLS.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* The GnuTLS is free software; you can redistribute it and/or
|
|
Packit |
aea12f |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
aea12f |
* the License, or (at your option) any later version.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
aea12f |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
aea12f |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
aea12f |
* Lesser General Public License for more details.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#include "gnutls_int.h"
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#include <datum.h>
|
|
Packit |
aea12f |
#include <global.h>
|
|
Packit |
aea12f |
#include "errors.h"
|
|
Packit |
aea12f |
#include <common.h>
|
|
Packit |
aea12f |
#include <x509.h>
|
|
Packit |
aea12f |
#include <x509_b64.h>
|
|
Packit |
aea12f |
#include "x509_int.h"
|
|
Packit |
aea12f |
#include <algorithms.h>
|
|
Packit |
aea12f |
#include <num.h>
|
|
Packit |
aea12f |
#include <random.h>
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static int
|
|
Packit |
aea12f |
openssl_hash_password(const char *_password, gnutls_datum_t * key,
|
|
Packit |
aea12f |
gnutls_datum_t * salt)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
unsigned char md5[16];
|
|
Packit |
aea12f |
digest_hd_st hd;
|
|
Packit |
aea12f |
unsigned int count = 0;
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
char *password = NULL;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (_password != NULL) {
|
|
Packit |
aea12f |
gnutls_datum_t pout;
|
|
Packit |
aea12f |
ret = _gnutls_utf8_password_normalize(_password, strlen(_password), &pout, 1);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
password = (char*)pout.data;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
while (count < key->size) {
|
|
Packit |
aea12f |
ret = _gnutls_hash_init(&hd, mac_to_entry(GNUTLS_MAC_MD5));
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (count) {
|
|
Packit |
aea12f |
ret = _gnutls_hash(&hd, md5, sizeof(md5));
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
hash_err:
|
|
Packit |
aea12f |
_gnutls_hash_deinit(&hd, NULL);
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (password) {
|
|
Packit |
aea12f |
ret = _gnutls_hash(&hd, password, strlen(password));
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto hash_err;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
ret = _gnutls_hash(&hd, salt->data, 8);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto hash_err;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
_gnutls_hash_deinit(&hd, md5);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (key->size - count <= sizeof(md5)) {
|
|
Packit |
aea12f |
memcpy(&key->data[count], md5, key->size - count);
|
|
Packit |
aea12f |
break;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
memcpy(&key->data[count], md5, sizeof(md5));
|
|
Packit |
aea12f |
count += sizeof(md5);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
ret = 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
cleanup:
|
|
Packit |
aea12f |
gnutls_free(password);
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
struct pem_cipher {
|
|
Packit |
aea12f |
const char *name;
|
|
Packit |
aea12f |
gnutls_cipher_algorithm_t cipher;
|
|
Packit |
aea12f |
};
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static const struct pem_cipher pem_ciphers[] = {
|
|
Packit |
aea12f |
{"DES-CBC", GNUTLS_CIPHER_DES_CBC},
|
|
Packit |
aea12f |
{"DES-EDE3-CBC", GNUTLS_CIPHER_3DES_CBC},
|
|
Packit |
aea12f |
{"AES-128-CBC", GNUTLS_CIPHER_AES_128_CBC},
|
|
Packit |
aea12f |
{"AES-192-CBC", GNUTLS_CIPHER_AES_192_CBC},
|
|
Packit |
aea12f |
{"AES-256-CBC", GNUTLS_CIPHER_AES_256_CBC},
|
|
Packit |
aea12f |
{"CAMELLIA-128-CBC", GNUTLS_CIPHER_CAMELLIA_128_CBC},
|
|
Packit |
aea12f |
{"CAMELLIA-192-CBC", GNUTLS_CIPHER_CAMELLIA_192_CBC},
|
|
Packit |
aea12f |
{"CAMELLIA-256-CBC", GNUTLS_CIPHER_CAMELLIA_256_CBC},
|
|
Packit |
aea12f |
};
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/**
|
|
Packit |
aea12f |
* gnutls_x509_privkey_import_openssl:
|
|
Packit |
aea12f |
* @key: The data to store the parsed key
|
|
Packit |
aea12f |
* @data: The DER or PEM encoded key.
|
|
Packit |
aea12f |
* @password: the password to decrypt the key (if it is encrypted).
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This function will convert the given PEM encrypted to
|
|
Packit |
aea12f |
* the native gnutls_x509_privkey_t format. The
|
|
Packit |
aea12f |
* output will be stored in @key.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* The @password should be in ASCII. If the password is not provided
|
|
Packit |
aea12f |
* or wrong then %GNUTLS_E_DECRYPTION_FAILED will be returned.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* If the Certificate is PEM encoded it should have a header of
|
|
Packit |
aea12f |
* "PRIVATE KEY" and the "DEK-Info" header.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
|
|
Packit |
aea12f |
* negative error value.
|
|
Packit |
aea12f |
**/
|
|
Packit |
aea12f |
int
|
|
Packit |
aea12f |
gnutls_x509_privkey_import_openssl(gnutls_x509_privkey_t key,
|
|
Packit |
aea12f |
const gnutls_datum_t * data,
|
|
Packit |
aea12f |
const char *password)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
gnutls_cipher_hd_t handle;
|
|
Packit |
aea12f |
gnutls_cipher_algorithm_t cipher = GNUTLS_CIPHER_UNKNOWN;
|
|
Packit |
aea12f |
gnutls_datum_t b64_data;
|
|
Packit |
aea12f |
gnutls_datum_t salt, enc_key;
|
|
Packit |
aea12f |
unsigned char *key_data;
|
|
Packit |
aea12f |
size_t key_data_size;
|
|
Packit |
aea12f |
const char *pem_header = (void *) data->data;
|
|
Packit |
aea12f |
const char *pem_header_start = (void *) data->data;
|
|
Packit |
aea12f |
ssize_t pem_header_size;
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
unsigned int i, iv_size, l;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
pem_header_size = data->size;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
pem_header =
|
|
Packit |
aea12f |
memmem(pem_header, pem_header_size, "PRIVATE KEY---", 14);
|
|
Packit |
aea12f |
if (pem_header == NULL) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
return GNUTLS_E_PARSING_ERROR;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
pem_header_size -= (ptrdiff_t) (pem_header - pem_header_start);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
pem_header = memmem(pem_header, pem_header_size, "DEK-Info: ", 10);
|
|
Packit |
aea12f |
if (pem_header == NULL) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
return GNUTLS_E_PARSING_ERROR;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
pem_header_size =
|
|
Packit |
aea12f |
data->size - (ptrdiff_t) (pem_header - pem_header_start) - 10;
|
|
Packit |
aea12f |
pem_header += 10;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
for (i = 0; i < sizeof(pem_ciphers) / sizeof(pem_ciphers[0]); i++) {
|
|
Packit |
aea12f |
l = strlen(pem_ciphers[i].name);
|
|
Packit |
aea12f |
if (!strncmp(pem_header, pem_ciphers[i].name, l) &&
|
|
Packit |
aea12f |
pem_header[l] == ',') {
|
|
Packit |
aea12f |
pem_header += l + 1;
|
|
Packit |
aea12f |
cipher = pem_ciphers[i].cipher;
|
|
Packit |
aea12f |
break;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (cipher == GNUTLS_CIPHER_UNKNOWN) {
|
|
Packit |
aea12f |
_gnutls_debug_log
|
|
Packit |
aea12f |
("Unsupported PEM encryption type: %.10s\n",
|
|
Packit |
aea12f |
pem_header);
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
return GNUTLS_E_INVALID_REQUEST;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
iv_size = gnutls_cipher_get_iv_size(cipher);
|
|
Packit |
aea12f |
salt.size = iv_size;
|
|
Packit |
aea12f |
salt.data = gnutls_malloc(salt.size);
|
|
Packit |
aea12f |
if (!salt.data)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
for (i = 0; i < salt.size * 2; i++) {
|
|
Packit |
aea12f |
unsigned char x;
|
|
Packit |
aea12f |
const char *c = &pem_header[i];
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (*c >= '0' && *c <= '9')
|
|
Packit |
aea12f |
x = (*c) - '0';
|
|
Packit |
aea12f |
else if (*c >= 'A' && *c <= 'F')
|
|
Packit |
aea12f |
x = (*c) - 'A' + 10;
|
|
Packit |
aea12f |
else {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
/* Invalid salt in encrypted PEM file */
|
|
Packit |
aea12f |
ret = GNUTLS_E_INVALID_REQUEST;
|
|
Packit |
aea12f |
goto out_salt;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
if (i & 1)
|
|
Packit |
aea12f |
salt.data[i / 2] |= x;
|
|
Packit |
aea12f |
else
|
|
Packit |
aea12f |
salt.data[i / 2] = x << 4;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
pem_header += salt.size * 2;
|
|
Packit |
aea12f |
if (*pem_header != '\r' && *pem_header != '\n') {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
ret = GNUTLS_E_INVALID_REQUEST;
|
|
Packit |
aea12f |
goto out_salt;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
while (*pem_header == '\n' || *pem_header == '\r')
|
|
Packit |
aea12f |
pem_header++;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret =
|
|
Packit |
aea12f |
_gnutls_base64_decode((const void *) pem_header,
|
|
Packit |
aea12f |
pem_header_size, &b64_data);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto out_salt;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (b64_data.size < 16) {
|
|
Packit |
aea12f |
/* Just to be sure our parsing is OK */
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
ret = GNUTLS_E_PARSING_ERROR;
|
|
Packit |
aea12f |
goto out_b64;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
enc_key.size = gnutls_cipher_get_key_size(cipher);
|
|
Packit |
aea12f |
enc_key.data = gnutls_malloc(enc_key.size);
|
|
Packit |
aea12f |
if (!enc_key.data) {
|
|
Packit |
aea12f |
ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
aea12f |
goto out_b64;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
key_data_size = b64_data.size;
|
|
Packit |
aea12f |
key_data = gnutls_malloc(key_data_size);
|
|
Packit |
aea12f |
if (!key_data) {
|
|
Packit |
aea12f |
ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
aea12f |
goto out_enc_key;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
while (1) {
|
|
Packit |
aea12f |
memcpy(key_data, b64_data.data, key_data_size);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = openssl_hash_password(password, &enc_key, &salt);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto out;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = gnutls_cipher_init(&handle, cipher, &enc_key, &salt);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
gnutls_cipher_deinit(handle);
|
|
Packit |
aea12f |
goto out;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret =
|
|
Packit |
aea12f |
gnutls_cipher_decrypt(handle, key_data, key_data_size);
|
|
Packit |
aea12f |
gnutls_cipher_deinit(handle);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto out;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* We have to strip any padding to accept it.
|
|
Packit |
aea12f |
So a bit more ASN.1 parsing for us. */
|
|
Packit |
aea12f |
if (key_data[0] == 0x30) {
|
|
Packit |
aea12f |
gnutls_datum_t key_datum;
|
|
Packit |
aea12f |
unsigned int blocksize =
|
|
Packit |
aea12f |
gnutls_cipher_get_block_size(cipher);
|
|
Packit |
aea12f |
unsigned int keylen = key_data[1];
|
|
Packit |
aea12f |
unsigned int ofs = 2;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (keylen & 0x80) {
|
|
Packit |
aea12f |
int lenlen = keylen & 0x7f;
|
|
Packit |
aea12f |
keylen = 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (lenlen > 3) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto fail;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
while (lenlen) {
|
|
Packit |
aea12f |
keylen <<= 8;
|
|
Packit |
aea12f |
keylen |= key_data[ofs++];
|
|
Packit |
aea12f |
lenlen--;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
keylen += ofs;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* If there appears to be more or less padding than required, fail */
|
|
Packit |
aea12f |
if (key_data_size - keylen > blocksize || key_data_size < keylen+1) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto fail;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* If the padding bytes aren't all equal to the amount of padding, fail */
|
|
Packit |
aea12f |
ofs = keylen;
|
|
Packit |
aea12f |
while (ofs < key_data_size) {
|
|
Packit |
aea12f |
if (key_data[ofs] !=
|
|
Packit |
aea12f |
key_data_size - keylen) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto fail;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
ofs++;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
key_datum.data = key_data;
|
|
Packit |
aea12f |
key_datum.size = keylen;
|
|
Packit |
aea12f |
ret =
|
|
Packit |
aea12f |
gnutls_x509_privkey_import(key, &key_datum,
|
|
Packit |
aea12f |
GNUTLS_X509_FMT_DER);
|
|
Packit |
aea12f |
if (ret == 0)
|
|
Packit |
aea12f |
goto out;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
fail:
|
|
Packit |
aea12f |
ret = GNUTLS_E_DECRYPTION_FAILED;
|
|
Packit |
aea12f |
goto out;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
out:
|
|
Packit |
aea12f |
zeroize_key(key_data, key_data_size);
|
|
Packit |
aea12f |
gnutls_free(key_data);
|
|
Packit |
aea12f |
out_enc_key:
|
|
Packit |
aea12f |
_gnutls_free_key_datum(&enc_key);
|
|
Packit |
aea12f |
out_b64:
|
|
Packit |
aea12f |
gnutls_free(b64_data.data);
|
|
Packit |
aea12f |
out_salt:
|
|
Packit |
aea12f |
gnutls_free(salt.data);
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|