diff options
Diffstat (limited to '')
-rw-r--r-- | lib/x509/sign.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/lib/x509/sign.c b/lib/x509/sign.c new file mode 100644 index 0000000..303e9f2 --- /dev/null +++ b/lib/x509/sign.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 <https://www.gnu.org/licenses/> + * + */ + +/* All functions which relate to X.509 certificate signing stuff are + * included here + */ + +#include "gnutls_int.h" + +#include "errors.h" +#include <libtasn1.h> +#include <global.h> +#include <num.h> /* MAX */ +#include <tls-sig.h> +#include <str.h> +#include <datum.h> +#include <x509_int.h> +#include <common.h> +#include <gnutls/abstract.h> +#include <pk.h> + +/* This is the same as the _gnutls_x509_sign, but this one will decode + * the asn1_node given, and sign the DER data. Actually used to get the DER + * of the TBS and sign it on the fly. + */ +int +_gnutls_x509_get_tbs(asn1_node cert, const char *tbs_name, + gnutls_datum_t * tbs) +{ + return _gnutls_x509_der_encode(cert, tbs_name, tbs, 0); +} + +int +_gnutls_x509_crt_get_spki_params(gnutls_x509_crt_t crt, + const gnutls_x509_spki_st *key_params, + gnutls_x509_spki_st *params) +{ + int result; + gnutls_x509_spki_st crt_params; + + result = _gnutls_x509_crt_read_spki_params(crt, &crt_params); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (crt_params.pk == GNUTLS_PK_RSA_PSS) { + if (key_params->pk == GNUTLS_PK_RSA_PSS) { + if (crt_params.rsa_pss_dig != key_params->rsa_pss_dig) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (crt_params.salt_size < key_params->salt_size) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + } else if (key_params->pk != GNUTLS_PK_RSA && key_params->pk != GNUTLS_PK_UNKNOWN) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + memcpy(params, &crt_params, sizeof(gnutls_x509_spki_st)); + } else { + memcpy(params, key_params, sizeof(gnutls_x509_spki_st)); + } + + return 0; +} + +/*- + * _gnutls_x509_pkix_sign - This function will sign a CRL or a certificate with a key + * @src: should contain an asn1_node + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function will sign a CRL or a certificate with the issuer's private key, and + * will copy the issuer's information into the CRL or certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + -*/ +int +_gnutls_x509_pkix_sign(asn1_node src, const char *src_name, + gnutls_digest_algorithm_t dig, + unsigned int flags, + gnutls_x509_crt_t issuer, + gnutls_privkey_t issuer_key) +{ + int result; + gnutls_datum_t signature; + gnutls_datum_t tbs; + char name[128]; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st key_params, params; + const gnutls_sign_entry_st *se; + + pk = gnutls_x509_crt_get_pk_algorithm(issuer, NULL); + if (pk == GNUTLS_PK_UNKNOWN) + pk = gnutls_privkey_get_pk_algorithm(issuer_key, NULL); + + result = _gnutls_privkey_get_spki_params(issuer_key, &key_params); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_x509_crt_get_spki_params(issuer, &key_params, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_privkey_update_spki_params(issuer_key, pk, dig, flags, + ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 1. Copy the issuer's name into the certificate. + */ + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".issuer"); + + result = + asn1_copy_node(src, name, issuer->cert, + "tbsCertificate.subject"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Step 1.5. Write the signature stuff in the tbsCertificate. + */ + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".signature"); + + se = _gnutls_pk_to_sign_entry(params.pk, dig); + if (se == NULL) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + + _gnutls_debug_log("signing structure using %s\n", se->name); + + result = _gnutls_x509_write_sign_params(src, name, se, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 2. Sign the certificate. + */ + result = _gnutls_x509_get_tbs(src, src_name, &tbs); + + if (result < 0) { + gnutls_assert(); + return result; + } + + FIX_SIGN_PARAMS(params, flags, dig); + + if (_gnutls_pk_is_not_prehashed(params.pk)) { + result = privkey_sign_raw_data(issuer_key, se, &tbs, &signature, ¶ms); + } else { + result = privkey_sign_and_hash_data(issuer_key, se, + &tbs, &signature, ¶ms); + } + gnutls_free(tbs.data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + /* write the signature (bits) + */ + result = + asn1_write_value(src, "signature", signature.data, + signature.size * 8); + + _gnutls_free_datum(&signature); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Step 3. Move up and write the AlgorithmIdentifier, which is also + * the same. + */ + + result = _gnutls_x509_write_sign_params(src, "signatureAlgorithm", + se, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} |