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