diff options
Diffstat (limited to 'lib/x509/crl_write.c')
-rw-r--r-- | lib/x509/crl_write.c | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/lib/x509/crl_write.c b/lib/x509/crl_write.c new file mode 100644 index 0000000..36ca452 --- /dev/null +++ b/lib/x509/crl_write.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2016 Red Hat, 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/> + * + */ + +/* This file contains functions to handle CRL generation. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <libtasn1.h> + +static void disable_optional_stuff(gnutls_x509_crl_t crl); + +/** + * gnutls_x509_crl_set_version: + * @crl: should contain a gnutls_x509_crl_t type + * @version: holds the version number. For CRLv1 crls must be 1. + * + * This function will set the version of the CRL. This + * must be one for CRL version 1, and so on. The CRLs generated + * by gnutls should have a version number of 2. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_set_version(gnutls_x509_crl_t crl, unsigned int version) +{ + int result; + uint8_t null = version & 0xFF; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (null > 0) + null -= 1; + + result = + asn1_write_value(crl->crl, "tbsCertList.version", &null, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crl_sign2: + * @crl: should contain a gnutls_x509_crl_t type + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * @dig: The message digest to use. GNUTLS_DIG_SHA256 is the safe choice unless you know what you're doing. + * @flags: must be 0 + * + * This function will sign the CRL with the issuer's private key, and + * will copy the issuer's information into the CRL. + * + * This must be the last step in a certificate CRL since all + * the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed CRL will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN, + * and in that case, a suitable but reasonable for the key algorithm will be selected. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + **/ +int +gnutls_x509_crl_sign2(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, unsigned int flags) +{ + int result; + gnutls_privkey_t privkey; + + if (crl == NULL || issuer == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_privkey_init(&privkey); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = gnutls_privkey_import_x509(privkey, issuer_key, 0); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = + gnutls_x509_crl_privkey_sign(crl, issuer, privkey, dig, flags); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = 0; + + fail: + gnutls_privkey_deinit(privkey); + + return result; +} + +/** + * gnutls_x509_crl_sign: + * @crl: should contain a gnutls_x509_crl_t type + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function is the same a gnutls_x509_crl_sign2() with no flags, + * and an appropriate hash algorithm. The hash algorithm used may + * vary between versions of GnuTLS, and it is tied to the security + * level of the issuer's public key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + */ +int +gnutls_x509_crl_sign(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key) +{ + return gnutls_x509_crl_sign2(crl, issuer, issuer_key, + 0, 0); +} + +/** + * gnutls_x509_crl_set_this_update: + * @crl: should contain a gnutls_x509_crl_t type + * @act_time: The actual time + * + * This function will set the time this CRL was issued. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crl_set_this_update(gnutls_x509_crl_t crl, time_t act_time) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_time(crl->crl, "tbsCertList.thisUpdate", + act_time, 0); +} + +/** + * gnutls_x509_crl_set_next_update: + * @crl: should contain a gnutls_x509_crl_t type + * @exp_time: The actual time + * + * This function will set the time this CRL will be updated. + * This is an optional value to be set on a CRL and this call + * can be omitted when generating a CRL. + * + * Prior to GnuTLS 3.5.7, setting a nextUpdate field was required + * in order to generate a CRL. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crl_set_next_update(gnutls_x509_crl_t crl, time_t exp_time) +{ + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + return _gnutls_x509_set_time(crl->crl, "tbsCertList.nextUpdate", + exp_time, 0); +} + +/** + * gnutls_x509_crl_set_crt_serial: + * @crl: should contain a gnutls_x509_crl_t type + * @serial: The revoked certificate's serial number + * @serial_size: Holds the size of the serial field. + * @revocation_time: The time this certificate was revoked + * + * This function will set a revoked certificate's serial number to the CRL. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_set_crt_serial(gnutls_x509_crl_t crl, + const void *serial, size_t serial_size, + time_t revocation_time) +{ + int ret; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + asn1_write_value(crl->crl, "tbsCertList.revokedCertificates", + "NEW", 1); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = + asn1_write_value(crl->crl, + "tbsCertList.revokedCertificates.?LAST.userCertificate", + serial, serial_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = + _gnutls_x509_set_time(crl->crl, + "tbsCertList.revokedCertificates.?LAST.revocationDate", + revocation_time, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + asn1_write_value(crl->crl, + "tbsCertList.revokedCertificates.?LAST.crlEntryExtensions", + NULL, 0); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/** + * gnutls_x509_crl_set_crt: + * @crl: should contain a gnutls_x509_crl_t type + * @crt: a certificate of type #gnutls_x509_crt_t with the revoked certificate + * @revocation_time: The time this certificate was revoked + * + * This function will set a revoked certificate's serial number to the CRL. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_set_crt(gnutls_x509_crl_t crl, gnutls_x509_crt_t crt, + time_t revocation_time) +{ + int ret; + uint8_t serial[128]; + size_t serial_size; + + if (crl == NULL || crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + serial_size = sizeof(serial); + ret = gnutls_x509_crt_get_serial(crt, serial, &serial_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + gnutls_x509_crl_set_crt_serial(crl, serial, serial_size, + revocation_time); + if (ret < 0) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + + +/* If OPTIONAL fields have not been initialized then + * disable them. + */ +static void disable_optional_stuff(gnutls_x509_crl_t crl) +{ + time_t t; + + t = _gnutls_x509_get_time(crl->crl, "tbsCertList.nextUpdate", 0); + if (t == (time_t)-1) { + (void)asn1_write_value(crl->crl, "tbsCertList.nextUpdate", NULL, 0); + } + + if (crl->use_extensions == 0) { + (void)asn1_write_value(crl->crl, "tbsCertList.crlExtensions", + NULL, 0); + } + + return; +} + +/** + * gnutls_x509_crl_set_authority_key_id: + * @crl: a CRL of type #gnutls_x509_crl_t + * @id: The key ID + * @id_size: Holds the size of the serial field. + * + * This function will set the CRL's authority key ID extension. Only + * the keyIdentifier field can be set with this function. This may + * be used by an authority that holds multiple private keys, to distinguish + * the used key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_set_authority_key_id(gnutls_x509_crl_t crl, + const void *id, size_t id_size) +{ + int result; + gnutls_datum_t old_id, der_data; + unsigned int critical; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crl_get_extension(crl, "2.5.29.35", 0, &old_id, + &critical); + + if (result >= 0) { + _gnutls_free_datum(&old_id); + } else if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_auth_key_id(id, id_size, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crl_set_extension(crl, "2.5.29.35", &der_data, 0); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + crl->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crl_set_number: + * @crl: a CRL of type #gnutls_x509_crl_t + * @nr: The CRL number + * @nr_size: Holds the size of the nr field. + * + * This function will set the CRL's number extension. This + * is to be used as a unique and monotonic number assigned to + * the CRL by the authority. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crl_set_number(gnutls_x509_crl_t crl, + const void *nr, size_t nr_size) +{ + int result; + gnutls_datum_t old_id, der_data; + unsigned int critical; + + if (crl == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crl_get_extension(crl, "2.5.29.20", 0, &old_id, + &critical); + + if (result >= 0) { + _gnutls_free_datum(&old_id); + } else if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_number(nr, nr_size, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crl_set_extension(crl, "2.5.29.20", &der_data, 0); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + crl->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crl_privkey_sign: + * @crl: should contain a gnutls_x509_crl_t type + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * @dig: The message digest to use. GNUTLS_DIG_SHA256 is the safe choice unless you know what you're doing. + * @flags: must be 0 + * + * This function will sign the CRL with the issuer's private key, and + * will copy the issuer's information into the CRL. + * + * This must be the last step in a certificate CRL since all + * the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed CRL will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN, + * and in that case, a suitable but reasonable for the key algorithm will be selected. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since 2.12.0 + **/ +int +gnutls_x509_crl_privkey_sign(gnutls_x509_crl_t crl, + gnutls_x509_crt_t issuer, + gnutls_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, + unsigned int flags) +{ + int result; + + if (crl == NULL || issuer == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (dig == 0) { + result = gnutls_x509_crt_get_preferred_hash_algorithm(issuer, &dig, NULL); + if (result < 0) + return gnutls_assert_val(result); + } + + /* disable all the unneeded OPTIONAL fields. + */ + disable_optional_stuff(crl); + + result = _gnutls_x509_pkix_sign(crl->crl, "tbsCertList", + dig, 0, issuer, issuer_key); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} |