diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
commit | 36082a2fe36ecd800d784ae44c14f1f18c66a7e9 (patch) | |
tree | 6c68e0c0097987aff85a01dabddd34b862309a7c /lib/x509/x509_write.c | |
parent | Initial commit. (diff) | |
download | gnutls28-upstream.tar.xz gnutls28-upstream.zip |
Adding upstream version 3.7.9.upstream/3.7.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/x509/x509_write.c')
-rw-r--r-- | lib/x509/x509_write.c | 2125 |
1 files changed, 2125 insertions, 0 deletions
diff --git a/lib/x509/x509_write.c b/lib/x509/x509_write.c new file mode 100644 index 0000000..e9a7087 --- /dev/null +++ b/lib/x509/x509_write.c @@ -0,0 +1,2125 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2016-2017 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 X.509 certificate generation. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <gnutls/x509-ext.h> +#include <x509_b64.h> +#include "x509_int.h" +#include <libtasn1.h> +#include <pk.h> + +static void disable_optional_stuff(gnutls_x509_crt_t cert); + +/** + * gnutls_x509_crt_set_dn_by_oid: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: holds an Object Identifier in a null terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @name: a pointer to the name + * @sizeof_name: holds the size of @name + * + * This function will set the part of the name of the Certificate + * subject, specified by the given OID. The input string should be + * ASCII or UTF-8 encoded. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * With this function you can only set the known OIDs. You can test + * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are + * not known (by gnutls) you should properly DER encode your data, + * and call this function with @raw_flag set. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_dn_by_oid(gnutls_x509_crt_t crt, const char *oid, + unsigned int raw_flag, const void *name, + unsigned int sizeof_name) +{ + if (sizeof_name == 0 || name == NULL || crt == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + return _gnutls_x509_set_dn_oid(crt->cert, "tbsCertificate.subject", + oid, raw_flag, name, sizeof_name); +} + +/** + * gnutls_x509_crt_set_issuer_dn_by_oid: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: holds an Object Identifier in a null terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @name: a pointer to the name + * @sizeof_name: holds the size of @name + * + * This function will set the part of the name of the Certificate + * issuer, specified by the given OID. The input string should be + * ASCII or UTF-8 encoded. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * With this function you can only set the known OIDs. You can test + * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are + * not known (by gnutls) you should properly DER encode your data, + * and call this function with @raw_flag set. + * + * Normally you do not need to call this function, since the signing + * operation will copy the signer's name as the issuer of the + * certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_issuer_dn_by_oid(gnutls_x509_crt_t crt, + const char *oid, + unsigned int raw_flag, + const void *name, + unsigned int sizeof_name) +{ + if (sizeof_name == 0 || name == NULL || crt == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + return _gnutls_x509_set_dn_oid(crt->cert, "tbsCertificate.issuer", + oid, raw_flag, name, sizeof_name); +} + +/** + * gnutls_x509_crt_set_proxy_dn: + * @crt: a gnutls_x509_crt_t type with the new proxy cert + * @eecrt: the end entity certificate that will be issuing the proxy + * @raw_flag: must be 0, or 1 if the CN is DER encoded + * @name: a pointer to the CN name, may be NULL (but MUST then be added later) + * @sizeof_name: holds the size of @name + * + * This function will set the subject in @crt to the end entity's + * @eecrt subject name, and add a single Common Name component @name + * of size @sizeof_name. This corresponds to the required proxy + * certificate naming style. Note that if @name is %NULL, you MUST + * set it later by using gnutls_x509_crt_set_dn_by_oid() or similar. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_proxy_dn(gnutls_x509_crt_t crt, + gnutls_x509_crt_t eecrt, + unsigned int raw_flag, const void *name, + unsigned int sizeof_name) +{ + int result; + + if (crt == NULL || eecrt == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + result = asn1_copy_node(crt->cert, "tbsCertificate.subject", + eecrt->cert, "tbsCertificate.subject"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (name && sizeof_name) { + return _gnutls_x509_set_dn_oid(crt->cert, + "tbsCertificate.subject", + GNUTLS_OID_X520_COMMON_NAME, + raw_flag, name, + sizeof_name); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_version: + * @crt: a certificate of type #gnutls_x509_crt_t + * @version: holds the version number. For X.509v1 certificates must be 1. + * + * This function will set the version of the certificate. This must + * be one for X.509 version 1, and so on. Plain certificates without + * extensions must have version set to one. + * + * To create well-formed certificates, you must specify version 3 if + * you use any certificate extensions. Extensions are created by + * functions such as gnutls_x509_crt_set_subject_alt_name() + * or gnutls_x509_crt_set_key_usage(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_version(gnutls_x509_crt_t crt, unsigned int version) +{ + int result; + unsigned char null = version; + + if (crt == NULL || version == 0 || version >= 0x80) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + if (null > 0) + null--; + + result = + asn1_write_value(crt->cert, "tbsCertificate.version", &null, + 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_key: + * @crt: a certificate of type #gnutls_x509_crt_t + * @key: holds a private key + * + * This function will set the public parameters from the given + * private key to the certificate. + * + * To export the public key (i.e., the SubjectPublicKeyInfo part), check + * gnutls_pubkey_import_x509(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + **/ +int +gnutls_x509_crt_set_key(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key) +{ + int result; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + result = _gnutls_x509_encode_and_copy_PKI_params(crt->cert, + "tbsCertificate.subjectPublicKeyInfo", + &key->params); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_crq: + * @crt: a certificate of type #gnutls_x509_crt_t + * @crq: holds a certificate request + * + * This function will set the name and public parameters as well as + * the extensions from the given certificate request to the certificate. + * Only RSA keys are currently supported. + * + * Note that this function will only set the @crq if it is self + * signed and the signature is correct. See gnutls_x509_crq_sign2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crt_set_crq(gnutls_x509_crt_t crt, gnutls_x509_crq_t crq) +{ + int result; + + if (crt == NULL || crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + result = gnutls_x509_crq_verify(crq, 0); + if (result < 0) + return gnutls_assert_val(result); + + result = asn1_copy_node(crt->cert, "tbsCertificate.subject", + crq->crq, + "certificationRequestInfo.subject"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = + asn1_copy_node(crt->cert, + "tbsCertificate.subjectPublicKeyInfo", crq->crq, + "certificationRequestInfo.subjectPKInfo"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_crq_extensions: + * @crt: a certificate of type #gnutls_x509_crt_t + * @crq: holds a certificate request + * + * This function will set the extensions from the given request to the + * certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crt_set_crq_extensions(gnutls_x509_crt_t crt, + gnutls_x509_crq_t crq) +{ + return gnutls_x509_crt_set_crq_extension_by_oid(crt, crq, NULL, 0); +} + +/** + * gnutls_x509_crt_set_crq_extension_by_oid: + * @crt: a certificate of type #gnutls_x509_crt_t + * @crq: holds a certificate request + * @oid: the object identifier of the OID to copy + * @flags: should be zero + * + * This function will set the extension specify by @oid from the given request to the + * certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.1 + **/ +int +gnutls_x509_crt_set_crq_extension_by_oid(gnutls_x509_crt_t crt, + gnutls_x509_crq_t crq, const char *oid, + unsigned flags) +{ + size_t i; + + if (crt == NULL || crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + for (i = 0;; i++) { + int result; + char local_oid[MAX_OID_SIZE]; + size_t local_oid_size; + uint8_t *extensions; + size_t extensions_size; + unsigned int critical; + gnutls_datum_t ext; + + local_oid_size = sizeof(local_oid); + result = gnutls_x509_crq_get_extension_info(crq, i, local_oid, + &local_oid_size, + &critical); + if (result < 0) { + if (result == + GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + + gnutls_assert(); + return result; + } + + if (oid && strcmp(local_oid, oid) != 0) + continue; + + extensions_size = 0; + result = gnutls_x509_crq_get_extension_data(crq, i, NULL, + &extensions_size); + if (result < 0) { + gnutls_assert(); + return result; + } + + extensions = gnutls_malloc(extensions_size); + if (extensions == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + gnutls_x509_crq_get_extension_data(crq, i, extensions, + &extensions_size); + if (result < 0) { + gnutls_assert(); + gnutls_free(extensions); + return result; + } + + ext.data = extensions; + ext.size = extensions_size; + + result = + _gnutls_x509_crt_set_extension(crt, local_oid, &ext, + critical); + gnutls_free(extensions); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + return 0; +} + +/** + * gnutls_x509_crt_set_extension_by_oid: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: holds an Object Identifier in null terminated string + * @buf: a pointer to a DER encoded data + * @sizeof_buf: holds the size of @buf + * @critical: should be non-zero if the extension is to be marked as critical + * + * This function will set an the extension, by the specified OID, in + * the certificate. The extension data should be binary data DER + * encoded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_extension_by_oid(gnutls_x509_crt_t crt, + const char *oid, const void *buf, + size_t sizeof_buf, + unsigned int critical) +{ + int result; + gnutls_datum_t der_data; + + der_data.data = (void *) buf; + der_data.size = sizeof_buf; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_x509_crt_set_extension(crt, oid, &der_data, critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; + +} + +/** + * gnutls_x509_crt_set_basic_constraints: + * @crt: a certificate of type #gnutls_x509_crt_t + * @ca: true(1) or false(0). Depending on the Certificate authority status. + * @pathLenConstraint: non-negative error codes indicate maximum length of path, + * and negative error codes indicate that the pathLenConstraints field should + * not be present. + * + * This function will set the basicConstraints certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_basic_constraints(gnutls_x509_crt_t crt, + unsigned int ca, + int pathLenConstraint) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = gnutls_x509_ext_export_basic_constraints(ca, pathLenConstraint, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.19", &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_ca_status: + * @crt: a certificate of type #gnutls_x509_crt_t + * @ca: true(1) or false(0). Depending on the Certificate authority status. + * + * This function will set the basicConstraints certificate extension. + * Use gnutls_x509_crt_set_basic_constraints() if you want to control + * the pathLenConstraint field too. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crt_set_ca_status(gnutls_x509_crt_t crt, unsigned int ca) +{ + return gnutls_x509_crt_set_basic_constraints(crt, ca, -1); +} + +/** + * gnutls_x509_crt_set_key_usage: + * @crt: a certificate of type #gnutls_x509_crt_t + * @usage: an ORed sequence of the GNUTLS_KEY_* elements. + * + * This function will set the keyUsage certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_key_usage(gnutls_x509_crt_t crt, unsigned int usage) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = + gnutls_x509_ext_export_key_usage(usage, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.15", &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_inhibit_anypolicy: + * @crt: a certificate of type #gnutls_x509_crt_t + * @skipcerts: number of certificates after which anypolicy is no longer acceptable. + * + * This function will set the Inhibit anyPolicy certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_inhibit_anypolicy(gnutls_x509_crt_t crt, unsigned int skipcerts) +{ + int ret; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + ret = + gnutls_x509_ext_export_inhibit_anypolicy(skipcerts, &der_data); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_x509_crt_set_extension(crt, "2.5.29.54", &der_data, 1); + _gnutls_free_datum(&der_data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_subject_alternative_name: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data_string: The data to be set, a (0) terminated string + * + * This function will set the subject alternative name certificate + * extension. This function assumes that data can be expressed as a null + * terminated string. + * + * The name of the function is unfortunate since it is inconsistent with + * gnutls_x509_crt_get_subject_alt_name(). + * + * See gnutls_x509_crt_set_subject_alt_name() for more information. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_subject_alternative_name(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t + type, const char *data_string) +{ + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* only handle text extensions */ + if (type != GNUTLS_SAN_DNSNAME && type != GNUTLS_SAN_RFC822NAME && + type != GNUTLS_SAN_URI) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return gnutls_x509_crt_set_subject_alt_name(crt, type, data_string, + strlen(data_string), + GNUTLS_FSAN_SET); +} + +/** + * gnutls_x509_crt_set_subject_alt_name: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append. + * + * This function will set the subject alternative name certificate + * extension. It can set the following types: %GNUTLS_SAN_DNSNAME as a text string, + * %GNUTLS_SAN_RFC822NAME as a text string, %GNUTLS_SAN_URI as a text string, + * %GNUTLS_SAN_IPADDRESS as a binary IP address (4 or 16 bytes), + * %GNUTLS_SAN_OTHERNAME_XMPP as a UTF8 string (since 3.5.0). + * + * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and + * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.6.0 + **/ +int +gnutls_x509_crt_set_subject_alt_name(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t type, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + unsigned int critical = 0; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + + if (flags & GNUTLS_FSAN_APPEND) { + result = + _gnutls_x509_crt_get_extension(crt, "2.5.29.17", 0, + &prev_der_data, + &critical); + if (result < 0 + && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return result; + } + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name(type, NULL, data, data_size, + &prev_der_data, + &der_data); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.17", &der_data, + critical); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + result = 0; + + finish: + _gnutls_free_datum(&prev_der_data); + return result; +} + +/** + * gnutls_x509_crt_set_issuer_alt_name: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append. + * + * This function will set the issuer alternative name certificate + * extension. It can set the same types as gnutls_x509_crt_set_subject_alt_name(). + * + * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and + * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.0 + **/ +int +gnutls_x509_crt_set_issuer_alt_name(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t type, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + unsigned int critical = 0; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + + if (flags & GNUTLS_FSAN_APPEND) { + result = + _gnutls_x509_crt_get_extension(crt, "2.5.29.18", 0, + &prev_der_data, + &critical); + if (result < 0 + && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return result; + } + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name(type, NULL, data, data_size, + &prev_der_data, + &der_data); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.18", &der_data, + critical); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + result = 0; + + finish: + _gnutls_free_datum(&prev_der_data); + return result; +} + +int _gnutls_encode_othername_data(unsigned flags, const void *data, unsigned data_size, gnutls_datum_t *output) +{ + int ret; + if (flags & GNUTLS_FSAN_ENCODE_OCTET_STRING) { + ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, + data, data_size, output); + } else if (flags & GNUTLS_FSAN_ENCODE_UTF8_STRING) { + ret = _gnutls_x509_encode_string(ASN1_ETYPE_UTF8_STRING, + data, data_size, output); + } else { + ret = _gnutls_set_datum(output, data, data_size); + } + return ret; +} + +/** + * gnutls_x509_crt_set_subject_alt_othername: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: The other name OID + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append. + * + * This function will set an "othername" to the subject alternative name certificate + * extension. + * + * The values set are set as binary values and are expected to have the proper DER encoding. + * For convenience the flags %GNUTLS_FSAN_ENCODE_OCTET_STRING and %GNUTLS_FSAN_ENCODE_UTF8_STRING + * can be used to encode the provided data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.0 + **/ +int +gnutls_x509_crt_set_subject_alt_othername(gnutls_x509_crt_t crt, + const char *oid, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + gnutls_datum_t encoded_data = { NULL, 0 }; + unsigned int critical = 0; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + + if (flags & GNUTLS_FSAN_APPEND) { + result = + _gnutls_x509_crt_get_extension(crt, "2.5.29.17", 0, + &prev_der_data, + &critical); + if (result < 0 + && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return result; + } + } + + result = _gnutls_encode_othername_data(flags, data, data_size, &encoded_data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name(GNUTLS_SAN_OTHERNAME, oid, + encoded_data.data, encoded_data.size, + &prev_der_data, &der_data); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.17", &der_data, + critical); + + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = 0; + + finish: + _gnutls_free_datum(&der_data); + _gnutls_free_datum(&prev_der_data); + _gnutls_free_datum(&encoded_data); + return result; +} + +/** + * gnutls_x509_crt_set_issuer_alt_othername: + * @crt: a certificate of type #gnutls_x509_crt_t + * @oid: The other name OID + * @data: The data to be set + * @data_size: The size of data to be set + * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append. + * + * This function will set an "othername" to the issuer alternative name certificate + * extension. + * + * The values set are set as binary values and are expected to have the proper DER encoding. + * For convenience the flags %GNUTLS_FSAN_ENCODE_OCTET_STRING and %GNUTLS_FSAN_ENCODE_UTF8_STRING + * can be used to encode the provided data. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.0 + **/ +int +gnutls_x509_crt_set_issuer_alt_othername(gnutls_x509_crt_t crt, + const char *oid, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + gnutls_datum_t encoded_data = {NULL, 0}; + unsigned int critical = 0; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + + if (flags & GNUTLS_FSAN_APPEND) { + result = + _gnutls_x509_crt_get_extension(crt, "2.5.29.18", 0, + &prev_der_data, + &critical); + if (result < 0 + && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return result; + } + } + + result = _gnutls_encode_othername_data(flags, data, data_size, &encoded_data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name(GNUTLS_SAN_OTHERNAME, oid, + encoded_data.data, encoded_data.size, + &prev_der_data, &der_data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crt_set_extension(crt, "2.5.29.18", &der_data, + critical); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = 0; + + finish: + _gnutls_free_datum(&der_data); + _gnutls_free_datum(&prev_der_data); + _gnutls_free_datum(&encoded_data); + return result; +} + +/** + * gnutls_x509_crt_set_proxy: + * @crt: a certificate of type #gnutls_x509_crt_t + * @pathLenConstraint: non-negative error codes indicate maximum length of path, + * and negative error codes indicate that the pathLenConstraints field should + * not be present. + * @policyLanguage: OID describing the language of @policy. + * @policy: uint8_t byte array with policy language, can be %NULL + * @sizeof_policy: size of @policy. + * + * This function will set the proxyCertInfo extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_proxy(gnutls_x509_crt_t crt, + int pathLenConstraint, + const char *policyLanguage, + const char *policy, size_t sizeof_policy) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = gnutls_x509_ext_export_proxy(pathLenConstraint, + policyLanguage, + policy, sizeof_policy, + &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_x509_crt_set_extension(crt, "1.3.6.1.5.5.7.1.14", + &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_private_key_usage_period: + * @crt: a certificate of type #gnutls_x509_crt_t + * @activation: The activation time + * @expiration: The expiration time + * + * This function will set the private key usage period extension (2.5.29.16). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_private_key_usage_period(gnutls_x509_crt_t crt, + time_t activation, + time_t expiration) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_ext_export_private_key_usage_period(activation, + expiration, &der_data); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_crt_set_extension(crt, "2.5.29.16", + &der_data, 0); + + _gnutls_free_datum(&der_data); + + cleanup: + return result; +} + +/** + * gnutls_x509_crt_sign2: + * @crt: a certificate of type #gnutls_x509_crt_t + * @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 a safe choice + * @flags: must be 0 + * + * This function will sign the certificate with the issuer's private key, and + * will copy the issuer's information into the certificate. + * + * This must be the last step in a certificate generation since all + * the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed certificate 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_crt_sign2(gnutls_x509_crt_t crt, 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 (crt == NULL || issuer == NULL || issuer_key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(crt); + + 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_crt_privkey_sign(crt, issuer, privkey, dig, flags); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = 0; + + fail: + gnutls_privkey_deinit(privkey); + + return result; +} + +/** + * gnutls_x509_crt_sign: + * @crt: a certificate of type #gnutls_x509_crt_t + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function is the same a gnutls_x509_crt_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_crt_sign(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key) +{ + return gnutls_x509_crt_sign2(crt, issuer, issuer_key, + 0, 0); +} + +/** + * gnutls_x509_crt_set_activation_time: + * @cert: a certificate of type #gnutls_x509_crt_t + * @act_time: The actual time + * + * This function will set the time this certificate was or will be + * activated. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_activation_time(gnutls_x509_crt_t cert, + time_t act_time) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + return _gnutls_x509_set_time(cert->cert, + "tbsCertificate.validity.notBefore", + act_time, 0); +} + +/** + * gnutls_x509_crt_set_expiration_time: + * @cert: a certificate of type #gnutls_x509_crt_t + * @exp_time: The actual time + * + * This function will set the time this Certificate will expire. + * Setting an expiration time to (time_t)-1 will set + * to the no well-defined expiration date value. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_expiration_time(gnutls_x509_crt_t cert, + time_t exp_time) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + return _gnutls_x509_set_time(cert->cert, + "tbsCertificate.validity.notAfter", + exp_time, 0); +} + +/** + * gnutls_x509_crt_set_serial: + * @cert: a certificate of type #gnutls_x509_crt_t + * @serial: The serial number + * @serial_size: Holds the size of the serial field. + * + * This function will set the X.509 certificate's serial number. + * While the serial number is an integer, it is often handled + * as an opaque field by several CAs. For this reason this function + * accepts any kind of data as a serial number. To be consistent + * with the X.509/PKIX specifications the provided @serial should be + * a big-endian positive number (i.e. its leftmost bit should be zero). + * + * The size of the serial is restricted to 20 bytes maximum by RFC5280. + * This function allows writing more than 20 bytes but the generated + * certificates in that case may be rejected by other implementations. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_serial(gnutls_x509_crt_t cert, const void *serial, + size_t serial_size) +{ + int ret; + unsigned all_zero, i; + const unsigned char *pserial = serial; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* check for non-zero serial */ + all_zero = 1; + for (i=0;i<serial_size;i++) { + if (pserial[i] != 0) { + all_zero = 0; + break; + } + } + + if (all_zero) { + _gnutls_debug_log("error: certificate serial is zero\n"); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + ret = + asn1_write_value(cert->cert, "tbsCertificate.serialNumber", + serial, serial_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; + +} + +/** + * gnutls_x509_crt_set_issuer_unique_id: + * @cert: a certificate of type #gnutls_x509_crt_t + * @id: The unique ID + * @id_size: Holds the size of the unique ID. + * + * This function will set the X.509 certificate's issuer unique ID field. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.7 + **/ +int +gnutls_x509_crt_set_issuer_unique_id(gnutls_x509_crt_t cert, const void *id, + size_t id_size) +{ + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + ret = + asn1_write_value(cert->cert, "tbsCertificate.issuerUniqueID", + id, id_size*8); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_subject_unique_id: + * @cert: a certificate of type #gnutls_x509_crt_t + * @id: The unique ID + * @id_size: Holds the size of the unique ID. + * + * This function will set the X.509 certificate's subject unique ID field. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.7 + **/ +int +gnutls_x509_crt_set_subject_unique_id(gnutls_x509_crt_t cert, const void *id, + size_t id_size) +{ + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + MODIFIED(cert); + + ret = + asn1_write_value(cert->cert, "tbsCertificate.subjectUniqueID", + id, id_size*8); + if (ret != ASN1_SUCCESS) { + 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_crt_t cert) +{ + asn1_data_node_st n; + asn1_node node; + unsigned remove_subject_unique_id = 1; + unsigned remove_issuer_unique_id = 1; + + node = asn1_find_node(cert->cert, "tbsCertificate.issuerUniqueID"); + if (node) { + if (asn1_read_node_value(node, &n) == ASN1_SUCCESS && n.value_len != 0) + remove_issuer_unique_id = 0; + } + + node = asn1_find_node(cert->cert, "tbsCertificate.subjectUniqueID"); + if (node) { + if (asn1_read_node_value(node, &n) == ASN1_SUCCESS && n.value_len != 0) + remove_subject_unique_id = 0; + } + + if (remove_issuer_unique_id) + (void)asn1_write_value(cert->cert, "tbsCertificate.issuerUniqueID", NULL, + 0); + + if (remove_subject_unique_id) + (void)asn1_write_value(cert->cert, "tbsCertificate.subjectUniqueID", + NULL, 0); + + if (cert->use_extensions == 0) { + _gnutls_debug_log("Disabling X.509 extensions.\n"); + (void)asn1_write_value(cert->cert, "tbsCertificate.extensions", + NULL, 0); + } + + return; +} + +/** + * gnutls_x509_crt_set_crl_dist_points: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data_string: The data to be set + * @reason_flags: revocation reasons + * + * This function will set the CRL distribution points certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_crl_dist_points(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t type, + const void *data_string, + unsigned int reason_flags) +{ + return gnutls_x509_crt_set_crl_dist_points2(crt, type, data_string, + strlen(data_string), + reason_flags); +} + +/** + * gnutls_x509_crt_set_crl_dist_points2: + * @crt: a certificate of type #gnutls_x509_crt_t + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data: The data to be set + * @data_size: The data size + * @reason_flags: revocation reasons + * + * This function will set the CRL distribution points certificate extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.6.0 + **/ +int +gnutls_x509_crt_set_crl_dist_points2(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t type, + const void *data, + unsigned int data_size, + unsigned int reason_flags) +{ + int ret; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t old_der = { NULL, 0 }; + unsigned int critical; + gnutls_x509_crl_dist_points_t cdp = NULL; + gnutls_datum_t san; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crl_dist_points_init(&cdp); + if (ret < 0) + return gnutls_assert_val(ret); + + /* Check if the extension already exists. + */ + ret = + _gnutls_x509_crt_get_extension(crt, "2.5.29.31", 0, &old_der, + &critical); + + if (ret >= 0 && old_der.data != NULL) { + ret = gnutls_x509_ext_import_crl_dist_points(&old_der, cdp, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + san.data = (void*)data; + san.size = data_size; + ret = gnutls_x509_crl_dist_points_set(cdp, type, &san, reason_flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* generate the extension. + */ + ret = + gnutls_x509_ext_export_crl_dist_points(cdp, &der_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_x509_crt_set_extension(crt, "2.5.29.31", &der_data, 0); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_free_datum(&der_data); + _gnutls_free_datum(&old_der); + if (cdp != NULL) + gnutls_x509_crl_dist_points_deinit(cdp); + + return ret; + +} + +/** + * gnutls_x509_crt_cpy_crl_dist_points: + * @dst: a certificate of type #gnutls_x509_crt_t + * @src: the certificate where the dist points will be copied from + * + * This function will copy the CRL distribution points certificate + * extension, from the source to the destination certificate. + * This may be useful to copy from a CA certificate to issued ones. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_cpy_crl_dist_points(gnutls_x509_crt_t dst, + gnutls_x509_crt_t src) +{ + int result; + gnutls_datum_t der_data; + unsigned int critical; + + if (dst == NULL || src == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension(src, "2.5.29.31", 0, &der_data, + &critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(dst, "2.5.29.31", &der_data, + critical); + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_subject_key_id: + * @cert: a certificate of type #gnutls_x509_crt_t + * @id: The key ID + * @id_size: Holds the size of the subject key ID field. + * + * This function will set the X.509 certificate's subject key ID + * extension. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_subject_key_id(gnutls_x509_crt_t cert, + const void *id, size_t id_size) +{ + int result; + gnutls_datum_t old_id, der_data; + gnutls_datum_t d_id; + unsigned int critical; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.14", 0, &old_id, + &critical); + + if (result >= 0) + _gnutls_free_datum(&old_id); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + d_id.data = (void*)id; + d_id.size = id_size; + + result = gnutls_x509_ext_export_subject_key_id(&d_id, &der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = + _gnutls_x509_crt_set_extension(cert, "2.5.29.14", &der_data, + 0); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_authority_key_id: + * @cert: a certificate of type #gnutls_x509_crt_t + * @id: The key ID + * @id_size: Holds the size of the key ID field. + * + * This function will set the X.509 certificate's authority key ID extension. + * Only the keyIdentifier field can be set with this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_set_authority_key_id(gnutls_x509_crt_t cert, + const void *id, size_t id_size) +{ + int result; + gnutls_datum_t old_id, der_data; + unsigned int critical; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &old_id, + &critical); + + if (result >= 0) + _gnutls_free_datum(&old_id); + 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_crt_set_extension(cert, "2.5.29.35", &der_data, + 0); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_key_purpose_oid: + * @cert: a certificate of type #gnutls_x509_crt_t + * @oid: a pointer to a null terminated string that holds the OID + * @critical: Whether this extension will be critical or not + * + * This function will set the key purpose OIDs of the Certificate. + * These are stored in the Extended Key Usage extension (2.5.29.37) + * See the GNUTLS_KP_* definitions for human readable names. + * + * Subsequent calls to this function will append OIDs to the OID list. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int +gnutls_x509_crt_set_key_purpose_oid(gnutls_x509_crt_t cert, + const void *oid, unsigned int critical) +{ + int ret; + gnutls_datum_t old_id = {NULL,0}; + gnutls_datum_t der = {NULL,0}; + gnutls_x509_key_purposes_t p = NULL; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_key_purpose_init(&p); + if (ret < 0) + return gnutls_assert_val(ret); + + /* Check if the extension already exists. + */ + ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.37", 0, &old_id, + NULL); + + if (ret >= 0) { + ret = gnutls_x509_ext_import_key_purposes(&old_id, p, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = gnutls_x509_key_purpose_set(p, oid); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_export_key_purposes(p, &der); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_set_extension(cert, "2.5.29.37", + &der, critical); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_free_datum(&der); + _gnutls_free_datum(&old_id); + if (p != NULL) + gnutls_x509_key_purpose_deinit(p); + + return ret; + +} + +/** + * gnutls_x509_crt_privkey_sign: + * @crt: a certificate of type #gnutls_x509_crt_t + * @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 a safe choice + * @flags: must be 0 + * + * This function will sign the certificate with the issuer's private key, and + * will copy the issuer's information into the certificate. + * + * This must be the last step in a certificate generation since all + * the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed certificate 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_crt_privkey_sign(gnutls_x509_crt_t crt, + gnutls_x509_crt_t issuer, + gnutls_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, + unsigned int flags) +{ + int result; + + if (crt == NULL || issuer == NULL || issuer_key == 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); + } + + MODIFIED(crt); + + /* disable all the unneeded OPTIONAL fields. + */ + disable_optional_stuff(crt); + + result = _gnutls_check_cert_sanity(crt); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_x509_pkix_sign(crt->cert, "tbsCertificate", + dig, flags, issuer, issuer_key); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_authority_info_access: + * @crt: Holds the certificate + * @what: what data to get, a #gnutls_info_access_what_t type. + * @data: output data to be freed with gnutls_free(). + * + * This function sets the Authority Information Access (AIA) + * extension, see RFC 5280 section 4.2.2.1 for more information. + * + * The type of data stored in @data is specified via @what which + * should be #gnutls_info_access_what_t values. + * + * If @what is %GNUTLS_IA_OCSP_URI, @data will hold the OCSP URI. + * If @what is %GNUTLS_IA_CAISSUERS_URI, @data will hold the caIssuers + * URI. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int +gnutls_x509_crt_set_authority_info_access(gnutls_x509_crt_t crt, + int what, gnutls_datum_t * data) +{ + int ret; + gnutls_datum_t der = { NULL, 0 }; + gnutls_datum_t new_der = { NULL, 0 }; + gnutls_x509_aia_t aia_ctx = NULL; + const char *oid; + unsigned int c; + + if (crt == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = gnutls_x509_aia_init(&aia_ctx); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_crt_get_extension(crt, GNUTLS_OID_AIA, 0, &der, + &c); + if (ret >= 0) { /* decode it */ + ret = gnutls_x509_ext_import_aia(&der, aia_ctx, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + if (what == GNUTLS_IA_OCSP_URI) + oid = GNUTLS_OID_AD_OCSP; + else if (what == GNUTLS_IA_CAISSUERS_URI) + oid = GNUTLS_OID_AD_CAISSUERS; + else + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + ret = gnutls_x509_aia_set(aia_ctx, oid, GNUTLS_SAN_URI, data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_export_aia(aia_ctx, &new_der); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_set_extension(crt, GNUTLS_OID_AIA, + &new_der, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + if (aia_ctx != NULL) + gnutls_x509_aia_deinit(aia_ctx); + _gnutls_free_datum(&new_der); + _gnutls_free_datum(&der); + + return ret; +} + + +/** + * gnutls_x509_crt_set_policy: + * @crt: should contain a #gnutls_x509_crt_t type + * @policy: A pointer to a policy + * @critical: use non-zero if the extension is marked as critical + * + * This function will set the certificate policy extension (2.5.29.32). + * Multiple calls to this function append a new policy. + * + * Note the maximum text size for the qualifier %GNUTLS_X509_QUALIFIER_NOTICE + * is 200 characters. This function will fail with %GNUTLS_E_INVALID_REQUEST + * if this is exceeded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.5 + **/ +int +gnutls_x509_crt_set_policy(gnutls_x509_crt_t crt, + const struct gnutls_x509_policy_st *policy, + unsigned int critical) +{ + int ret; + gnutls_datum_t der_data = {NULL, 0}, prev_der_data = { NULL, 0 }; + gnutls_x509_policies_t policies = NULL; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_policies_init(&policies); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_crt_get_extension(crt, "2.5.29.32", 0, + &prev_der_data, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_assert(); + goto cleanup; + } + + + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + ret = gnutls_x509_ext_import_policies(&prev_der_data, + policies, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = gnutls_x509_policies_set(policies, policy); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_export_policies(policies, &der_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_set_extension(crt, "2.5.29.32", + &der_data, 0); + + cleanup: + if (policies != NULL) + gnutls_x509_policies_deinit(policies); + _gnutls_free_datum(&prev_der_data); + _gnutls_free_datum(&der_data); + + return ret; +} + +/** + * gnutls_x509_crt_set_spki: + * @crt: a certificate of type #gnutls_x509_crt_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will set the certificate's subject public key + * information explicitly. This is intended to be used in the cases + * where a single public key (e.g., RSA) can be used for multiple + * signature algorithms (RSA PKCS1-1.5, and RSA-PSS). + * + * To export the public key (i.e., the SubjectPublicKeyInfo part), check + * gnutls_pubkey_import_x509(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crt_set_spki(gnutls_x509_crt_t crt, + const gnutls_x509_spki_t spki, + unsigned int flags) +{ + int ret; + gnutls_pk_algorithm_t crt_pk; + gnutls_x509_spki_st tpki; + gnutls_pk_params_st params; + unsigned bits; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crt_get_mpis(crt, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + bits = pubkey_to_bits(¶ms); + crt_pk = params.algo; + + if (!_gnutls_pk_are_compat(crt_pk, spki->pk)) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + if (spki->pk != GNUTLS_PK_RSA_PSS) { + if (crt_pk == spki->pk) { + ret = 0; + goto cleanup; + } + + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + memset(&tpki, 0, sizeof(gnutls_x509_spki_st)); + + if (crt_pk == GNUTLS_PK_RSA) { + const mac_entry_st *me; + + me = hash_to_entry(spki->rsa_pss_dig); + if (unlikely(me == NULL)) { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + tpki.pk = spki->pk; + tpki.rsa_pss_dig = spki->rsa_pss_dig; + + /* If salt size is zero, find the optimal salt size. */ + if (spki->salt_size == 0) { + ret = _gnutls_find_rsa_pss_salt_size(bits, me, + spki->salt_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + tpki.salt_size = ret; + } else + tpki.salt_size = spki->salt_size; + } else if (crt_pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_crt_read_spki_params(crt, &tpki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + tpki.salt_size = spki->salt_size; + tpki.rsa_pss_dig = spki->rsa_pss_dig; + } + + memcpy(¶ms.spki, &tpki, sizeof(tpki)); + ret = _gnutls_x509_check_pubkey_params(¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + MODIFIED(crt); + + ret = _gnutls_x509_write_spki_params(crt->cert, + "tbsCertificate." + "subjectPublicKeyInfo.algorithm", + &tpki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_pk_params_release(¶ms); + return ret; +} |