Blame lib/cert-cred-rawpk.c

Packit aea12f
/*
Packit aea12f
 * Copyright (C) 2017 - 2018 ARPA2 project
Packit aea12f
 *
Packit aea12f
 * Author: Tom Vrancken (dev@tomvrancken.nl)
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
#include <gnutls/gnutls.h>
Packit aea12f
#include "datum.h"
Packit aea12f
#include "auth/cert.h"
Packit aea12f
#include "x509.h"
Packit aea12f
#include "cert-cred.h"
Packit aea12f
#include "read-file.h"
Packit aea12f
#include <stdint.h>
Packit aea12f
Packit aea12f
Packit aea12f
/**
Packit aea12f
 * gnutls_certificate_set_rawpk_key_mem:
Packit aea12f
 * @cred: is a #gnutls_certificate_credentials_t type.
Packit aea12f
 * @spki: contains a raw public key in
Packit aea12f
 *   PKIX.SubjectPublicKeyInfo format.
Packit aea12f
 * @pkey: contains a raw private key.
Packit aea12f
 * @format: encoding of the keys. DER or PEM.
Packit aea12f
 * @pass: an optional password to unlock the private key pkey.
Packit aea12f
 * @key_usage: An ORed sequence of %GNUTLS_KEY_* flags.
Packit aea12f
 * @names: is an array of DNS names belonging to the public-key (NULL if none).
Packit aea12f
 * @names_length: holds the length of the names list.
Packit aea12f
 * @flags: an ORed sequence of #gnutls_pkcs_encrypt_flags_t.
Packit aea12f
 *   These apply to the private key pkey.
Packit aea12f
 *
Packit aea12f
 * This function sets a public/private keypair in the
Packit aea12f
 * #gnutls_certificate_credentials_t type to be used for authentication
Packit aea12f
 * and/or encryption. @spki and @privkey should match otherwise set
Packit aea12f
 * signatures cannot be validated. In case of no match this function
Packit aea12f
 * returns %GNUTLS_E_CERTIFICATE_KEY_MISMATCH. This function should
Packit aea12f
 * be called once for the client because there is currently no mechanism
Packit aea12f
 * to determine which raw public-key to select for the peer when there
Packit aea12f
 * are multiple present. Multiple raw public keys for the server can be
Packit aea12f
 * distinghuished by setting the @names.
Packit aea12f
 *
Packit aea12f
 * Note here that @spki is a raw public-key as defined
Packit aea12f
 * in RFC7250. It means that there is no surrounding certificate that
Packit aea12f
 * holds the public key and that there is therefore no direct mechanism
Packit aea12f
 * to prove the authenticity of this key. The keypair can be used during
Packit aea12f
 * a TLS handshake but its authenticity should be established via a
Packit aea12f
 * different mechanism (e.g. TOFU or known fingerprint).
Packit aea12f
 *
Packit aea12f
 * The supported formats are basic unencrypted key, PKCS8, PKCS12,
Packit aea12f
 * and the openssl format and will be autodetected.
Packit aea12f
 *
Packit aea12f
 * If the raw public-key and the private key are given in PEM encoding
Packit aea12f
 * then the strings that hold their values must be null terminated.
Packit aea12f
 *
Packit aea12f
 * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly
Packit aea12f
 * set because there is no certificate structure around the key to define
Packit aea12f
 * this value. See for more info gnutls_x509_crt_get_key_usage().
Packit aea12f
 *
Packit aea12f
 * Note that, this function by default returns zero on success and a
Packit aea12f
 * negative value on error. Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2
Packit aea12f
 * is set using gnutls_certificate_set_flags() it returns an index
Packit aea12f
 * (greater or equal to zero). That index can be used in other functions
Packit aea12f
 * to refer to the added key-pair.
Packit aea12f
 *
Packit aea12f
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, in case the
Packit aea12f
 *   key pair does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH is returned,
Packit aea12f
 *   in other erroneous cases a different negative error code is returned.
Packit aea12f
 *
Packit aea12f
 * Since: 3.6.6
Packit aea12f
 **/
Packit aea12f
int gnutls_certificate_set_rawpk_key_mem(gnutls_certificate_credentials_t cred,
Packit aea12f
				    const gnutls_datum_t* spki,
Packit aea12f
				    const gnutls_datum_t* pkey,
Packit aea12f
				    gnutls_x509_crt_fmt_t format,
Packit aea12f
				    const char* pass,
Packit aea12f
				    unsigned int key_usage,
Packit aea12f
				    const char **names,
Packit aea12f
				    unsigned int names_length,
Packit aea12f
				    unsigned int flags)
Packit aea12f
{
Packit aea12f
	int ret;
Packit aea12f
	gnutls_privkey_t privkey;
Packit aea12f
	gnutls_pcert_st* pcert;
Packit aea12f
	gnutls_str_array_t str_names;
Packit aea12f
	unsigned int i;
Packit aea12f
Packit aea12f
	if (pkey == NULL || spki == NULL) {
Packit aea12f
		return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
Packit aea12f
	}
Packit aea12f
Packit aea12f
	/* Import our private key. This function does all the necessary
Packit aea12f
	 * inits, checks and imports. */
Packit aea12f
	ret = _gnutls_read_key_mem(cred, pkey->data, pkey->size,
Packit aea12f
				format, pass, flags, &privkey);
Packit aea12f
	if (ret < 0) {
Packit aea12f
		return gnutls_assert_val(ret);
Packit aea12f
	}
Packit aea12f
Packit aea12f
	/* We now convert our raw public key to a parsed certificate (pcert) structure */
Packit aea12f
	pcert = gnutls_calloc(1, sizeof(*pcert));
Packit aea12f
	if (pcert == NULL) {
Packit aea12f
		gnutls_privkey_deinit(privkey);
Packit aea12f
Packit aea12f
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
Packit aea12f
	}
Packit aea12f
	// Import our raw public key to the pcert structure
Packit aea12f
	ret = gnutls_pcert_import_rawpk_raw(pcert, spki,
Packit aea12f
					format, key_usage, 0);
Packit aea12f
	if (ret < 0) {
Packit aea12f
		gnutls_privkey_deinit(privkey);
Packit aea12f
Packit aea12f
		return gnutls_assert_val(ret);
Packit aea12f
	}
Packit aea12f
Packit aea12f
	/* Process the names, if any */
Packit aea12f
	_gnutls_str_array_init(&str_names);
Packit aea12f
Packit aea12f
	if (names != NULL && names_length > 0) {
Packit aea12f
		for (i = 0; i < names_length; i++) {
Packit aea12f
			ret =
Packit aea12f
			    _gnutls_str_array_append_idna(&str_names, names[i],
Packit aea12f
						     strlen(names[i]));
Packit aea12f
			if (ret < 0) {
Packit aea12f
				gnutls_privkey_deinit(privkey);
Packit aea12f
				_gnutls_str_array_clear(&str_names);
Packit aea12f
Packit aea12f
				return gnutls_assert_val(ret);
Packit aea12f
			}
Packit aea12f
		}
Packit aea12f
	}
Packit aea12f
Packit aea12f
	/* Now that we have converted the key material to our internal structures
Packit aea12f
	 * we can now add them to the credentials structure */
Packit aea12f
	ret = _gnutls_certificate_credential_append_keypair(cred, privkey, str_names, pcert, 1);
Packit aea12f
	// Check for errors
Packit aea12f
	if (ret < 0) {
Packit aea12f
		gnutls_privkey_deinit(privkey);
Packit aea12f
		gnutls_pcert_deinit(pcert);
Packit aea12f
		gnutls_free(pcert);
Packit aea12f
Packit aea12f
		return gnutls_assert_val(ret);
Packit aea12f
	}
Packit aea12f
	// Successfully added a certificate
Packit aea12f
	cred->ncerts++;
Packit aea12f
Packit aea12f
	/* Check whether the key pair matches.
Packit aea12f
	 * After this point we do not deinitialize anything on failure to avoid
Packit aea12f
	 * double freeing. We intentionally keep everything as the credentials state
Packit aea12f
	 * is documented to be in undefined state. */
Packit aea12f
	if ((ret = _gnutls_check_key_cert_match(cred)) < 0) {
Packit aea12f
		return gnutls_assert_val(ret);
Packit aea12f
	}
Packit aea12f
Packit aea12f
	CRED_RET_SUCCESS(cred);
Packit aea12f
}
Packit aea12f
Packit aea12f
Packit aea12f
/**
Packit aea12f
 * gnutls_certificate_set_rawpk_key_file:
Packit aea12f
 * @cred: is a #gnutls_certificate_credentials_t type.
Packit aea12f
 * @rawpkfile: contains a raw public key in
Packit aea12f
 *   PKIX.SubjectPublicKeyInfo format.
Packit aea12f
 * @privkeyfile: contains a file path to a private key.
Packit aea12f
 * @format: encoding of the keys. DER or PEM.
Packit aea12f
 * @pass: an optional password to unlock the private key privkeyfile.
Packit aea12f
 * @key_usage: an ORed sequence of %GNUTLS_KEY_* flags.
Packit aea12f
 * @names: is an array of DNS names belonging to the public-key (NULL if none).
Packit aea12f
 * @names_length: holds the length of the names list.
Packit aea12f
 * @privkey_flags: an ORed sequence of #gnutls_pkcs_encrypt_flags_t.
Packit aea12f
 *   These apply to the private key pkey.
Packit aea12f
 * @pkcs11_flags: one of gnutls_pkcs11_obj_flags. These apply to URLs.
Packit aea12f
 *
Packit aea12f
 * This function sets a public/private keypair read from file in the
Packit aea12f
 * #gnutls_certificate_credentials_t type to be used for authentication
Packit aea12f
 * and/or encryption. @spki and @privkey should match otherwise set
Packit aea12f
 * signatures cannot be validated. In case of no match this function
Packit aea12f
 * returns %GNUTLS_E_CERTIFICATE_KEY_MISMATCH. This function should
Packit aea12f
 * be called once for the client because there is currently no mechanism
Packit aea12f
 * to determine which raw public-key to select for the peer when there
Packit aea12f
 * are multiple present. Multiple raw public keys for the server can be
Packit aea12f
 * distinghuished by setting the @names.
Packit aea12f
 *
Packit aea12f
 * Note here that @spki is a raw public-key as defined
Packit aea12f
 * in RFC7250. It means that there is no surrounding certificate that
Packit aea12f
 * holds the public key and that there is therefore no direct mechanism
Packit aea12f
 * to prove the authenticity of this key. The keypair can be used during
Packit aea12f
 * a TLS handshake but its authenticity should be established via a
Packit aea12f
 * different mechanism (e.g. TOFU or known fingerprint).
Packit aea12f
 *
Packit aea12f
 * The supported formats are basic unencrypted key, PKCS8, PKCS12,
Packit aea12f
 * and the openssl format and will be autodetected.
Packit aea12f
 *
Packit aea12f
 * If the raw public-key and the private key are given in PEM encoding
Packit aea12f
 * then the strings that hold their values must be null terminated.
Packit aea12f
 *
Packit aea12f
 * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly
Packit aea12f
 * set because there is no certificate structure around the key to define
Packit aea12f
 * this value. See for more info gnutls_x509_crt_get_key_usage().
Packit aea12f
 *
Packit aea12f
 * Note that, this function by default returns zero on success and a
Packit aea12f
 * negative value on error. Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2
Packit aea12f
 * is set using gnutls_certificate_set_flags() it returns an index
Packit aea12f
 * (greater or equal to zero). That index can be used in other functions
Packit aea12f
 * to refer to the added key-pair.
Packit aea12f
 *
Packit aea12f
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, in case the
Packit aea12f
 *   key pair does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH is returned,
Packit aea12f
 *   in other erroneous cases a different negative error code is returned.
Packit aea12f
 *
Packit aea12f
 * Since: 3.6.6
Packit aea12f
 */
Packit aea12f
int gnutls_certificate_set_rawpk_key_file(gnutls_certificate_credentials_t cred,
Packit aea12f
				      const char* rawpkfile,
Packit aea12f
				      const char* privkeyfile,
Packit aea12f
				      gnutls_x509_crt_fmt_t format,
Packit aea12f
				      const char *pass,
Packit aea12f
				      unsigned int key_usage,
Packit aea12f
				      const char **names,
Packit aea12f
				      unsigned int names_length,
Packit aea12f
				      unsigned int privkey_flags,
Packit aea12f
				      unsigned int pkcs11_flags)
Packit aea12f
{
Packit aea12f
	int ret;
Packit aea12f
	gnutls_privkey_t privkey;
Packit aea12f
	gnutls_pubkey_t pubkey;
Packit aea12f
	gnutls_pcert_st* pcert;
Packit aea12f
	gnutls_str_array_t str_names;
Packit aea12f
	unsigned int i;
Packit aea12f
Packit aea12f
	if (rawpkfile == NULL || privkeyfile == NULL) {
Packit aea12f
		return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
Packit aea12f
	}
Packit aea12f
Packit aea12f
	/* Import our private key. This function does all the necessary
Packit aea12f
	 * inits, checks and imports. */
Packit aea12f
	ret = _gnutls_read_key_file(cred, privkeyfile, format, pass, privkey_flags, &privkey);
Packit aea12f
	if (ret < 0) {
Packit aea12f
		return gnutls_assert_val(ret);
Packit aea12f
	}
Packit aea12f
Packit aea12f
	pcert = gnutls_calloc(1, sizeof(*pcert));
Packit aea12f
	if (pcert == NULL) {
Packit aea12f
		gnutls_privkey_deinit(privkey);
Packit aea12f
Packit aea12f
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
Packit aea12f
	}
Packit aea12f
Packit aea12f
	/* Check whether we are importing our raw public-key from a URL
Packit aea12f
	 * or from a regular file.
Packit aea12f
	 */
Packit aea12f
	if (gnutls_url_is_supported(rawpkfile)) {
Packit aea12f
Packit aea12f
		ret = gnutls_pubkey_init(&pubkey);
Packit aea12f
		if (ret < 0) {
Packit aea12f
			gnutls_privkey_deinit(privkey);
Packit aea12f
Packit aea12f
			return gnutls_assert_val(ret);
Packit aea12f
		}
Packit aea12f
Packit aea12f
		ret = gnutls_pubkey_import_url(pubkey, rawpkfile, pkcs11_flags);
Packit aea12f
		if (ret < 0) {
Packit aea12f
			gnutls_privkey_deinit(privkey);
Packit aea12f
			gnutls_pubkey_deinit(pubkey);
Packit aea12f
Packit aea12f
			return gnutls_assert_val(ret);
Packit aea12f
		}
Packit aea12f
Packit aea12f
		ret = gnutls_pcert_import_rawpk(pcert, pubkey, 0);
Packit aea12f
		if (ret < 0) {
Packit aea12f
			gnutls_privkey_deinit(privkey);
Packit aea12f
			gnutls_pubkey_deinit(pubkey);
Packit aea12f
Packit aea12f
			return gnutls_assert_val(ret);
Packit aea12f
		}
Packit aea12f
Packit aea12f
	} else {
Packit Service 991b93
		gnutls_datum_t rawpubkey; // to hold rawpk data from file
Packit Service 991b93
		size_t key_size;
Packit Service 991b93
Packit aea12f
		/* Read our raw public-key into memory from file */
Packit Service 991b93
		rawpubkey.data = (void*) read_file(rawpkfile,
Packit Service 991b93
						   RF_BINARY | RF_SENSITIVE,
Packit Service 991b93
						   &key_size);
Packit aea12f
		if (rawpubkey.data == NULL) {
Packit aea12f
			gnutls_privkey_deinit(privkey);
Packit aea12f
Packit aea12f
			return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
Packit aea12f
		}
Packit aea12f
		rawpubkey.size = key_size; // Implicit type casting
Packit aea12f
Packit aea12f
		/* We now convert our raw public key that we've loaded into memory to
Packit aea12f
		 * a parsed certificate (pcert) structure. Note that rawpubkey will
Packit aea12f
		 * be copied into pcert. Therefore we can directly cleanup rawpubkey.
Packit aea12f
		 */
Packit aea12f
		ret = gnutls_pcert_import_rawpk_raw(pcert, &rawpubkey,
Packit aea12f
						format, key_usage, 0);
Packit aea12f
Packit Service 991b93
		zeroize_key(rawpubkey.data, rawpubkey.size);
Packit Service 991b93
		free(rawpubkey.data);
Packit Service 991b93
		rawpubkey.size = 0;
Packit aea12f
Packit aea12f
		if (ret < 0) {
Packit aea12f
			gnutls_privkey_deinit(privkey);
Packit aea12f
Packit aea12f
			return gnutls_assert_val(ret);
Packit aea12f
		}
Packit aea12f
Packit aea12f
	}
Packit aea12f
Packit aea12f
	/* Process the names, if any */
Packit aea12f
	_gnutls_str_array_init(&str_names);
Packit aea12f
Packit aea12f
	if (names != NULL && names_length > 0) {
Packit aea12f
		for (i = 0; i < names_length; i++) {
Packit aea12f
			ret =
Packit aea12f
			    _gnutls_str_array_append_idna(&str_names, names[i],
Packit aea12f
						     strlen(names[i]));
Packit aea12f
			if (ret < 0) {
Packit aea12f
				gnutls_privkey_deinit(privkey);
Packit aea12f
				_gnutls_str_array_clear(&str_names);
Packit aea12f
Packit aea12f
				return gnutls_assert_val(ret);
Packit aea12f
			}
Packit aea12f
		}
Packit aea12f
	}
Packit aea12f
Packit aea12f
	/* Now that we have converted the key material to our internal structures
Packit aea12f
	 * we can now add them to the credentials structure */
Packit aea12f
	ret = _gnutls_certificate_credential_append_keypair(cred, privkey, str_names, pcert, 1);
Packit aea12f
	if (ret < 0) {
Packit aea12f
		gnutls_privkey_deinit(privkey);
Packit aea12f
		gnutls_pcert_deinit(pcert);
Packit aea12f
		gnutls_free(pcert);
Packit aea12f
Packit aea12f
		return gnutls_assert_val(ret);
Packit aea12f
	}
Packit aea12f
	// Successfully added a certificate
Packit aea12f
	cred->ncerts++;
Packit aea12f
Packit aea12f
	/* Check whether the key pair matches.
Packit aea12f
	 * After this point we do not deinitialize anything on failure to avoid
Packit aea12f
	 * double freeing. We intentionally keep everything as the credentials state
Packit aea12f
	 * is documented to be in undefined state. */
Packit aea12f
	if ((ret = _gnutls_check_key_cert_match(cred)) < 0) {
Packit aea12f
		return gnutls_assert_val(ret);
Packit aea12f
	}
Packit aea12f
Packit aea12f
	CRED_RET_SUCCESS(cred);
Packit aea12f
}
Packit aea12f