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/crq.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/crq.c')
-rw-r--r-- | lib/x509/crq.c | 3099 |
1 files changed, 3099 insertions, 0 deletions
diff --git a/lib/x509/crq.c b/lib/x509/crq.c new file mode 100644 index 0000000..2603022 --- /dev/null +++ b/lib/x509/crq.c @@ -0,0 +1,3099 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * Copyright (C) 2012-2016 Nikos Mavrogiannopoulos + * 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 PKCS #10 certificate + requests, see RFC 2986. + */ + +#include "gnutls_int.h" + +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_b64.h> +#include <gnutls/x509-ext.h> +#include "x509_int.h" +#include <libtasn1.h> +#include <pk.h> +#include "attributes.h" + +/** + * gnutls_x509_crq_init: + * @crq: A pointer to the type to be initialized + * + * This function will initialize a PKCS#10 certificate request + * structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crq_init(gnutls_x509_crq_t * crq) +{ + int result; + + FAIL_IF_LIB_ERROR; + + *crq = gnutls_calloc(1, sizeof(gnutls_x509_crq_int)); + if (!*crq) + return GNUTLS_E_MEMORY_ERROR; + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.pkcs-10-CertificationRequest", + &((*crq)->crq)); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(*crq); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crq_deinit: + * @crq: the type to be deinitialized + * + * This function will deinitialize a PKCS#10 certificate request + * structure. + **/ +void gnutls_x509_crq_deinit(gnutls_x509_crq_t crq) +{ + if (!crq) + return; + + if (crq->crq) + asn1_delete_structure(&crq->crq); + + gnutls_free(crq); +} + +#define PEM_CRQ "NEW CERTIFICATE REQUEST" +#define PEM_CRQ2 "CERTIFICATE REQUEST" + +/** + * gnutls_x509_crq_import: + * @crq: The data to store the parsed certificate request. + * @data: The DER or PEM encoded certificate. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded certificate + * request to a #gnutls_x509_crq_t type. The output will be + * stored in @crq. + * + * If the Certificate is PEM encoded it should have a header of "NEW + * CERTIFICATE REQUEST". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_import(gnutls_x509_crq_t crq, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + _data.data = data->data; + _data.size = data->size; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + /* Try the first header */ + result = + _gnutls_fbase64_decode(PEM_CRQ, data->data, data->size, + &_data); + + if (result < 0) /* Go for the second header */ + result = + _gnutls_fbase64_decode(PEM_CRQ2, data->data, + data->size, &_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + need_free = 1; + } + + result = + _asn1_strict_der_decode(&crq->crq, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + if (need_free) + _gnutls_free_datum(&_data); + return result; +} + +/** + * gnutls_x509_crq_get_signature_algorithm: + * @crq: should contain a #gnutls_x509_cr_t type + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm that has been used to + * sign this certificate request. + * + * Since 3.6.0 this function never returns a negative error code. + * Error cases and unknown/unsupported signature algorithms are + * mapped to %GNUTLS_SIGN_UNKNOWN. + * + * Returns: a #gnutls_sign_algorithm_t value + * + * Since: 3.4.0 + **/ +int gnutls_x509_crq_get_signature_algorithm(gnutls_x509_crq_t crq) +{ + return map_errs_to_zero(_gnutls_x509_get_signature_algorithm(crq->crq, + "signatureAlgorithm")); +} + +/** + * gnutls_x509_crq_get_private_key_usage_period: + * @crq: should contain a #gnutls_x509_crq_t type + * @activation: The activation time + * @expiration: The expiration time + * @critical: the extension status + * + * This function will return the expiration and activation + * times of the private key of the certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the extension is not present, otherwise a negative error value. + **/ +int +gnutls_x509_crq_get_private_key_usage_period(gnutls_x509_crq_t crq, + time_t * activation, + time_t * expiration, + unsigned int *critical) +{ + int result, ret; + asn1_node c2 = NULL; + uint8_t buf[128]; + size_t buf_size = sizeof(buf); + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.16", 0, + buf, &buf_size, + critical); + if (ret < 0) + return gnutls_assert_val(ret); + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.PrivateKeyUsagePeriod", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, buf, buf_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + if (activation) + *activation = _gnutls_x509_get_time(c2, "notBefore", 1); + + if (expiration) + *expiration = _gnutls_x509_get_time(c2, "notAfter", 1); + + ret = 0; + + cleanup: + asn1_delete_structure(&c2); + + return ret; +} + + +/** + * gnutls_x509_crq_get_dn: + * @crq: should contain a #gnutls_x509_crq_t type + * @buf: a pointer to a structure to hold the name (may be %NULL) + * @buf_size: initially holds the size of @buf + * + * This function will copy the name of the Certificate request subject + * to the provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC 2253. The output string + * @buf will be ASCII or UTF-8 encoded, depending on the certificate + * data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crq_get_dn3(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *@buf_size will be updated with + * the required size. On success 0 is returned. + **/ +int +gnutls_x509_crq_get_dn(gnutls_x509_crq_t crq, char *buf, size_t * buf_size) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn(crq->crq, + "certificationRequestInfo.subject.rdnSequence", + buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crq_get_dn2: + * @crq: should contain a #gnutls_x509_crq_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * + * This function will allocate buffer and copy the name of the Certificate + * request. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crq_get_dn3(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. and a negative error code on error. + * + * Since: 3.1.10 + **/ +int gnutls_x509_crq_get_dn2(gnutls_x509_crq_t crq, gnutls_datum_t * dn) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(crq->crq, + "certificationRequestInfo.subject.rdnSequence", + dn, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crq_get_dn3: + * @crq: should contain a #gnutls_x509_crq_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will allocate buffer and copy the name of the Certificate + * request. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. and a negative error code on error. + * + * Since: 3.5.7 + **/ +int gnutls_x509_crq_get_dn3(gnutls_x509_crq_t crq, gnutls_datum_t * dn, unsigned flags) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(crq->crq, + "certificationRequestInfo.subject.rdnSequence", + dn, flags); +} + +/** + * gnutls_x509_crq_get_dn_by_oid: + * @crq: should contain a gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies + * which to get. Use (0) to get the first one. + * @raw_flag: If non-zero returns the raw DER data of the DN part. + * @buf: a pointer to a structure to hold the name (may be %NULL) + * @buf_size: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * request subject, specified by the given OID. The output will be + * encoded as described in RFC2253. The output string will be ASCII + * or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is (0), this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC2253 -- + * in hex format with a '\#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *@buf_size will be + * updated with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crq_get_dn_by_oid(gnutls_x509_crq_t crq, const char *oid, + unsigned indx, unsigned int raw_flag, + void *buf, size_t * buf_size) +{ + gnutls_datum_t td; + int ret; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_parse_dn_oid + (crq->crq, + "certificationRequestInfo.subject.rdnSequence", + oid, indx, raw_flag, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_crq_get_dn_oid: + * @crq: should contain a gnutls_x509_crq_t type + * @indx: Specifies which DN OID to get. Use (0) to get the first one. + * @oid: a pointer to a structure to hold the name (may be %NULL) + * @sizeof_oid: initially holds the size of @oid + * + * This function will extract the requested OID of the name of the + * certificate request subject, specified by the given index. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *@sizeof_oid will be + * updated with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crq_get_dn_oid(gnutls_x509_crq_t crq, + unsigned indx, void *oid, size_t * sizeof_oid) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid(crq->crq, + "certificationRequestInfo.subject.rdnSequence", + indx, oid, sizeof_oid); +} + +/** + * gnutls_x509_crq_get_challenge_password: + * @crq: should contain a #gnutls_x509_crq_t type + * @pass: will hold a (0)-terminated password string + * @pass_size: Initially holds the size of @pass. + * + * This function will return the challenge password in the request. + * The challenge password is intended to be used for requesting a + * revocation of the certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_get_challenge_password(gnutls_x509_crq_t crq, + char *pass, size_t * pass_size) +{ + gnutls_datum_t td; + int ret; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _x509_parse_attribute(crq->crq, + "certificationRequestInfo.attributes", + "1.2.840.113549.1.9.7", 0, 0, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, pass, pass_size); +} + +/** + * gnutls_x509_crq_set_attribute_by_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a null-terminated string + * @buf: a pointer to a structure that holds the attribute data + * @buf_size: holds the size of @buf + * + * This function will set the attribute in the certificate request + * specified by the given Object ID. The provided attribute must be be DER + * encoded. + * + * Attributes in a certificate request is an optional set of data + * appended to the request. Their interpretation depends on the CA policy. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_attribute_by_oid(gnutls_x509_crq_t crq, + const char *oid, void *buf, + size_t buf_size) +{ + gnutls_datum_t data; + + data.data = buf; + data.size = buf_size; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _x509_set_attribute(crq->crq, + "certificationRequestInfo.attributes", oid, + &data); +} + +/** + * gnutls_x509_crq_get_attribute_by_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in null-terminated string + * @indx: In case multiple same OIDs exist in the attribute list, this + * specifies which to get, use (0) to get the first one + * @buf: a pointer to a structure to hold the attribute data (may be %NULL) + * @buf_size: initially holds the size of @buf + * + * This function will return the attribute in the certificate request + * specified by the given Object ID. The attribute will be DER + * encoded. + * + * Attributes in a certificate request is an optional set of data + * appended to the request. Their interpretation depends on the CA policy. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_get_attribute_by_oid(gnutls_x509_crq_t crq, + const char *oid, unsigned indx, void *buf, + size_t * buf_size) +{ + int ret; + gnutls_datum_t td; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _x509_parse_attribute(crq->crq, + "certificationRequestInfo.attributes", oid, + indx, 1, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_crq_set_dn_by_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a (0)-terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @data: a pointer to the input data + * @sizeof_data: holds the size of @data + * + * This function will set the part of the name of the Certificate + * request 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_crq_set_dn_by_oid(gnutls_x509_crq_t crq, const char *oid, + unsigned int raw_flag, const void *data, + unsigned int sizeof_data) +{ + if (sizeof_data == 0 || data == NULL || crq == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_dn_oid(crq->crq, + "certificationRequestInfo.subject", + oid, raw_flag, data, sizeof_data); +} + +/** + * gnutls_x509_crq_set_version: + * @crq: should contain a #gnutls_x509_crq_t type + * @version: holds the version number, for v1 Requests must be 1 + * + * This function will set the version of the certificate request. For + * version 1 requests this must be one. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_version(gnutls_x509_crq_t crq, unsigned int version) +{ + int result; + unsigned char null = version; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (null > 0) + null--; + + result = + asn1_write_value(crq->crq, "certificationRequestInfo.version", + &null, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crq_get_version: + * @crq: should contain a #gnutls_x509_crq_t type + * + * This function will return the version of the specified Certificate + * request. + * + * Returns: version of certificate request, or a negative error code on + * error. + **/ +int gnutls_x509_crq_get_version(gnutls_x509_crq_t crq) +{ + uint8_t version[8]; + int len, result; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + len = sizeof(version); + if ((result = + asn1_read_value(crq->crq, "certificationRequestInfo.version", + version, &len)) != ASN1_SUCCESS) { + + if (result == ASN1_ELEMENT_NOT_FOUND) + return 1; /* the DEFAULT version */ + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return (int) version[0] + 1; +} + +/** + * gnutls_x509_crq_set_key: + * @crq: should contain a #gnutls_x509_crq_t type + * @key: holds a private key + * + * This function will set the public parameters from the given private + * key to the request. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_key(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key) +{ + int result; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_encode_and_copy_PKI_params + (crq->crq, + "certificationRequestInfo.subjectPKInfo", + &key->params); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_key_rsa_raw: + * @crq: Holds the certificate + * @m: will hold the modulus + * @e: will hold the public exponent + * + * This function will export the RSA public key's parameters found in + * the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_key_rsa_raw(gnutls_x509_crq_t crq, + gnutls_datum_t * m, gnutls_datum_t * e) +{ + int ret; + gnutls_pk_params_st params; + + gnutls_pk_params_init(¶ms); + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crq_get_pk_algorithm(crq, NULL); + if (ret != GNUTLS_PK_RSA) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_mpi_dprint(params.params[0], m); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_mpi_dprint(params.params[1], e); + if (ret < 0) { + gnutls_assert(); + _gnutls_free_datum(m); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_pk_params_release(¶ms); + return ret; +} + +/** + * gnutls_x509_crq_set_key_rsa_raw: + * @crq: should contain a #gnutls_x509_crq_t type + * @m: holds the modulus + * @e: holds the public exponent + * + * This function will set the public parameters from the given private + * key to the request. Only RSA keys are currently supported. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.6.0 + **/ +int +gnutls_x509_crq_set_key_rsa_raw(gnutls_x509_crq_t crq, + const gnutls_datum_t * m, + const gnutls_datum_t * e) +{ + int result, ret; + size_t siz = 0; + gnutls_pk_params_st temp_params; + + gnutls_pk_params_init(&temp_params); + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(&temp_params, 0, sizeof(temp_params)); + + siz = m->size; + if (_gnutls_mpi_init_scan_nz(&temp_params.params[0], m->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto error; + } + + siz = e->size; + if (_gnutls_mpi_init_scan_nz(&temp_params.params[1], e->data, siz)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto error; + } + + temp_params.params_nr = RSA_PUBLIC_PARAMS; + temp_params.algo = GNUTLS_PK_RSA; + + result = _gnutls_x509_encode_and_copy_PKI_params + (crq->crq, + "certificationRequestInfo.subjectPKInfo", + &temp_params); + + if (result < 0) { + gnutls_assert(); + ret = result; + goto error; + } + + ret = 0; + + error: + gnutls_pk_params_release(&temp_params); + return ret; +} + +/** + * gnutls_x509_crq_set_challenge_password: + * @crq: should contain a #gnutls_x509_crq_t type + * @pass: holds a (0)-terminated password + * + * This function will set a challenge password to be used when + * revoking the request. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_set_challenge_password(gnutls_x509_crq_t crq, + const char *pass) +{ + int result; + char *password = NULL; + + if (crq == NULL || pass == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Add the attribute. + */ + result = + asn1_write_value(crq->crq, + "certificationRequestInfo.attributes", "NEW", + 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (pass) { + gnutls_datum_t out; + result = _gnutls_utf8_password_normalize(pass, strlen(pass), &out, 0); + if (result < 0) + return gnutls_assert_val(result); + + password = (char*)out.data; + } + + assert(password != NULL); + + result = _gnutls_x509_encode_and_write_attribute + ("1.2.840.113549.1.9.7", crq->crq, + "certificationRequestInfo.attributes.?LAST", password, + strlen(password), 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + gnutls_free(password); + return result; +} + +/** + * gnutls_x509_crq_sign2: + * @crq: should contain a #gnutls_x509_crq_t type + * @key: holds a private key + * @dig: The message digest to use, i.e., %GNUTLS_DIG_SHA256 + * @flags: must be 0 + * + * This function will sign the certificate request with a private key. + * This must be the same key as the one used in + * gnutls_x509_crt_set_key() since a certificate request is self + * signed. + * + * This must be the last step in a certificate request generation + * since all the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed request 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: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * %GNUTLS_E_ASN1_VALUE_NOT_FOUND is returned if you didn't set all + * information in the certificate request (e.g., the version using + * gnutls_x509_crq_set_version()). + * + **/ +int +gnutls_x509_crq_sign2(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t dig, unsigned int flags) +{ + int result; + gnutls_privkey_t privkey; + + if (crq == 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, key, 0); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = gnutls_x509_crq_privkey_sign(crq, privkey, dig, flags); + if (result < 0) { + gnutls_assert(); + goto fail; + } + + result = 0; + + fail: + gnutls_privkey_deinit(privkey); + + return result; +} + +/** + * gnutls_x509_crq_sign: + * @crq: should contain a #gnutls_x509_crq_t type + * @key: holds a private key + * + * This function is the same a gnutls_x509_crq_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. + * + * A known limitation of this function is, that a newly-signed request will not + * be fully functional (e.g., for signature verification), until it + * is exported an re-imported. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + */ +int gnutls_x509_crq_sign(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key) +{ + return gnutls_x509_crq_sign2(crq, key, 0, 0); +} + +/** + * gnutls_x509_crq_export: + * @crq: should contain a #gnutls_x509_crq_t type + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a certificate request PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the certificate request to a PEM or DER + * encoded PKCS10 structure. + * + * If the buffer provided is not long enough to hold the output, then + * %GNUTLS_E_SHORT_MEMORY_BUFFER will be returned and + * *@output_data_size will be updated. + * + * If the structure is PEM encoded, it will have a header of "BEGIN + * NEW CERTIFICATE REQUEST". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_export(gnutls_x509_crq_t crq, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int(crq->crq, format, PEM_CRQ, + output_data, output_data_size); +} + +/** + * gnutls_x509_crq_export2: + * @crq: should contain a #gnutls_x509_crq_t type + * @format: the format of output params. One of PEM or DER. + * @out: will contain a certificate request PEM or DER encoded + * + * This function will export the certificate request to a PEM or DER + * encoded PKCS10 structure. + * + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header of "BEGIN + * NEW CERTIFICATE REQUEST". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since 3.1.3 + **/ +int +gnutls_x509_crq_export2(gnutls_x509_crq_t crq, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int2(crq->crq, format, PEM_CRQ, out); +} + +/** + * gnutls_x509_crq_get_pk_algorithm: + * @crq: should contain a #gnutls_x509_crq_t type + * @bits: if bits is non-%NULL it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of a PKCS#10 + * certificate request. + * + * If bits is non-%NULL, it should have enough size to hold the + * parameters size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + **/ +int +gnutls_x509_crq_get_pk_algorithm(gnutls_x509_crq_t crq, unsigned int *bits) +{ + int result; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_get_pk_algorithm + (crq->crq, "certificationRequestInfo.subjectPKInfo", NULL, bits); + if (result < 0) { + gnutls_assert(); + return result; + } + + return result; +} + +/** + * gnutls_x509_crq_get_spki; + * @crq: should contain a #gnutls_x509_crq_t type + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will return the public key information of a PKCS#10 + * certificate request. The provided @spki must be initialized. + * + * Returns: Zero on success, or a negative error code on error. + **/ +int +gnutls_x509_crq_get_spki(gnutls_x509_crq_t crq, + gnutls_x509_spki_t spki, + unsigned int flags) +{ + int result; + gnutls_x509_spki_st params; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(¶ms, 0, sizeof(params)); + + spki->pk = gnutls_x509_crq_get_pk_algorithm(crq, NULL); + + result = _gnutls_x509_crq_read_spki_params(crq, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (params.pk == GNUTLS_PK_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + spki->rsa_pss_dig = params.rsa_pss_dig; + spki->salt_size = params.salt_size; + + return 0; +} + +/** + * gnutls_x509_crq_get_signature_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the signature algorithm + * that has been used to sign this certificate request. This function + * is useful in the case gnutls_x509_crq_get_signature_algorithm() + * returned %GNUTLS_SIGN_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crq_get_signature_oid(gnutls_x509_crq_t crq, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(crq->crq, "signatureAlgorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_pk_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the public key algorithm + * on that certificate request. This function + * is useful in the case gnutls_x509_crq_get_pk_algorithm() + * returned %GNUTLS_PK_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crq_get_pk_oid(gnutls_x509_crq_t crq, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(crq->crq, "certificationRequestInfo.subjectPKInfo.algorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_attribute_info: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: Specifies which attribute number to get. Use (0) to get the first one. + * @oid: a pointer to a structure to hold the OID + * @sizeof_oid: initially holds the maximum size of @oid, on return + * holds actual size of @oid. + * + * This function will return the requested attribute OID in the + * certificate, and the critical flag for it. The attribute OID will + * be stored as a string in the provided buffer. Use + * gnutls_x509_crq_get_attribute_data() to extract the data. + * + * If the buffer provided is not long enough to hold the output, then + * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be + * returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_attribute_info(gnutls_x509_crq_t crq, unsigned indx, + void *oid, size_t * sizeof_oid) +{ + int result; + char name[MAX_NAME_SIZE]; + int len; + + if (!crq) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "certificationRequestInfo.attributes.?%u.type", indx + 1); + + len = *sizeof_oid; + result = asn1_read_value(crq->crq, name, oid, &len); + *sizeof_oid = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result < 0) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; + +} + +/** + * gnutls_x509_crq_get_attribute_data: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: Specifies which attribute number to get. Use (0) to get the first one. + * @data: a pointer to a structure to hold the data (may be null) + * @sizeof_data: initially holds the size of @oid + * + * This function will return the requested attribute data in the + * certificate request. The attribute data will be stored as a string in the + * provided buffer. + * + * Use gnutls_x509_crq_get_attribute_info() to extract the OID. + * Use gnutls_x509_crq_get_attribute_by_oid() instead, + * if you want to get data indexed by the attribute OID rather than + * sequence. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_attribute_data(gnutls_x509_crq_t crq, unsigned indx, + void *data, size_t * sizeof_data) +{ + int result, len; + char name[MAX_NAME_SIZE]; + + if (!crq) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "certificationRequestInfo.attributes.?%u.values.?1", + indx + 1); + + len = *sizeof_data; + result = asn1_read_value(crq->crq, name, data, &len); + *sizeof_data = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result < 0) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crq_get_extension_info: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: Specifies which extension number to get. Use (0) to get the first one. + * @oid: a pointer to store the OID + * @sizeof_oid: initially holds the maximum size of @oid, on return + * holds actual size of @oid. + * @critical: output variable with critical flag, may be NULL. + * + * This function will return the requested extension OID in the + * certificate, and the critical flag for it. The extension OID will + * be stored as a string in the provided buffer. Use + * gnutls_x509_crq_get_extension_data() to extract the data. + * + * If the buffer provided is not long enough to hold the output, then + * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be + * returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_extension_info(gnutls_x509_crq_t crq, unsigned indx, + void *oid, size_t * sizeof_oid, + unsigned int *critical) +{ + int result; + char str_critical[10]; + char name[MAX_NAME_SIZE]; + char *extensions = NULL; + size_t extensions_size = 0; + asn1_node c2; + int len; + + if (!crq) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* read extensionRequest */ + result = + gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, NULL, + &extensions_size); + if (result == GNUTLS_E_SHORT_MEMORY_BUFFER) { + extensions = gnutls_malloc(extensions_size); + if (extensions == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, + extensions, + &extensions_size); + } + if (result < 0) { + gnutls_assert(); + goto out; + } + + result = + asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions", + &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto out; + } + + result = _asn1_strict_der_decode(&c2, extensions, extensions_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + result = _gnutls_asn2err(result); + goto out; + } + + snprintf(name, sizeof(name), "?%u.extnID", indx + 1); + + len = *sizeof_oid; + result = asn1_read_value(c2, name, oid, &len); + *sizeof_oid = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) { + asn1_delete_structure(&c2); + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto out; + } else if (result < 0) { + gnutls_assert(); + asn1_delete_structure(&c2); + result = _gnutls_asn2err(result); + goto out; + } + + snprintf(name, sizeof(name), "?%u.critical", indx + 1); + len = sizeof(str_critical); + result = asn1_read_value(c2, name, str_critical, &len); + + asn1_delete_structure(&c2); + + if (result < 0) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto out; + } + + if (critical) { + if (str_critical[0] == 'T') + *critical = 1; + else + *critical = 0; + } + + result = 0; + + out: + gnutls_free(extensions); + return result; +} + +/** + * gnutls_x509_crq_get_extension_data: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: Specifies which extension number to get. Use (0) to get the first one. + * @data: a pointer to a structure to hold the data (may be null) + * @sizeof_data: initially holds the size of @oid + * + * This function will return the requested extension data in the + * certificate. The extension data will be stored as a string in the + * provided buffer. + * + * Use gnutls_x509_crq_get_extension_info() to extract the OID and + * critical flag. Use gnutls_x509_crq_get_extension_by_oid() instead, + * if you want to get data indexed by the extension OID rather than + * sequence. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If your have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_extension_data(gnutls_x509_crq_t crq, unsigned indx, + void *data, size_t * sizeof_data) +{ + int ret; + gnutls_datum_t raw; + + ret = gnutls_x509_crq_get_extension_data2(crq, indx, &raw); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_copy_data(&raw, data, sizeof_data); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && data == NULL) + ret = 0; + gnutls_free(raw.data); + return ret; +} + +/** + * gnutls_x509_crq_get_extension_data2: + * @crq: should contain a #gnutls_x509_crq_t type + * @extension_id: An X.509 extension OID. + * @indx: Specifies which extension OID to read. Use (0) to get the first one. + * @data: will contain the extension DER-encoded data + * + * This function will return the requested extension data in the + * certificate request. The extension data will be allocated using + * gnutls_malloc(). + * + * Use gnutls_x509_crq_get_extension_info() to extract the OID. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If you have reached the + * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + * Since: 3.3.0 + **/ +int +gnutls_x509_crq_get_extension_data2(gnutls_x509_crq_t crq, + unsigned indx, gnutls_datum_t * data) +{ + int ret, result; + char name[MAX_NAME_SIZE]; + unsigned char *extensions = NULL; + size_t extensions_size = 0; + asn1_node c2 = NULL; + + if (!crq) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* read extensionRequest */ + ret = + gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, NULL, + &extensions_size); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) { + gnutls_assert(); + if (ret == 0) + return GNUTLS_E_INTERNAL_ERROR; + return ret; + } + + extensions = gnutls_malloc(extensions_size); + if (extensions == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crq_get_attribute_by_oid(crq, + "1.2.840.113549.1.9.14", + 0, extensions, + &extensions_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + result = + asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions", + &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, extensions, extensions_size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + + snprintf(name, sizeof(name), "?%u.extnValue", indx + 1); + + ret = _gnutls_x509_read_value(c2, name, data); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } else if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + asn1_delete_structure(&c2); + gnutls_free(extensions); + return ret; +} + +/** + * gnutls_x509_crq_get_key_usage: + * @crq: should contain a #gnutls_x509_crq_t type + * @key_usage: where the key usage bits will be stored + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return certificate's key usage, by reading the + * keyUsage X.509 extension (2.5.29.15). The key usage value will + * ORed values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE, + * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT, + * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT, + * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN, + * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY. + * + * Returns: the certificate key usage, or a negative error code in case of + * parsing error. If the certificate does not contain the keyUsage + * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be + * returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_key_usage(gnutls_x509_crq_t crq, + unsigned int *key_usage, + unsigned int *critical) +{ + int result; + uint8_t buf[128]; + size_t buf_size = sizeof(buf); + gnutls_datum_t bd; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.15", 0, + buf, &buf_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + bd.data = buf; + bd.size = buf_size; + result = gnutls_x509_ext_import_key_usage(&bd, key_usage); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_basic_constraints: + * @crq: should contain a #gnutls_x509_crq_t type + * @critical: will be non-zero if the extension is marked as critical + * @ca: pointer to output integer indicating CA status, may be NULL, + * value is 1 if the certificate CA flag is set, 0 otherwise. + * @pathlen: pointer to output integer indicating path length (may be + * NULL), non-negative error codes indicate a present pathLenConstraint + * field and the actual value, -1 indicate that the field is absent. + * + * This function will read the certificate's basic constraints, and + * return the certificates CA status. It reads the basicConstraints + * X.509 extension (2.5.29.19). + * + * Returns: If the certificate is a CA a positive value will be + * returned, or (0) if the certificate does not have CA flag set. + * A negative error code may be returned in case of errors. If the + * certificate does not contain the basicConstraints extension + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_basic_constraints(gnutls_x509_crq_t crq, + unsigned int *critical, + unsigned int *ca, int *pathlen) +{ + int result; + unsigned int tmp_ca; + uint8_t buf[256]; + size_t buf_size = sizeof(buf); + gnutls_datum_t bd; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.19", 0, + buf, &buf_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + bd.data = buf; + bd.size = buf_size; + result = gnutls_x509_ext_import_basic_constraints(&bd, &tmp_ca, pathlen); + if (ca) + *ca = tmp_ca; + + if (result < 0) { + gnutls_assert(); + return result; + } + + return tmp_ca; +} + +static int +get_subject_alt_name(gnutls_x509_crq_t crq, + unsigned int seq, void *ret, + size_t * ret_size, unsigned int *ret_type, + unsigned int *critical, int othername_oid) +{ + int result; + asn1_node c2 = NULL; + gnutls_x509_subject_alt_name_t type; + gnutls_datum_t dnsname = { NULL, 0 }; + size_t dns_size = 0; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (ret) + memset(ret, 0, *ret_size); + else + *ret_size = 0; + + /* Extract extension. + */ + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0, + NULL, &dns_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + dnsname.size = dns_size; + dnsname.data = gnutls_malloc(dnsname.size); + if (dnsname.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0, + dnsname.data, + &dns_size, critical); + if (result < 0) { + gnutls_assert(); + gnutls_free(dnsname.data); + return result; + } + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.SubjectAltName", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(dnsname.data); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, dnsname.data, dnsname.size, NULL); + gnutls_free(dnsname.data); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + result = _gnutls_parse_general_name(c2, "", seq, ret, ret_size, + ret_type, othername_oid); + asn1_delete_structure(&c2); + if (result < 0) { + return result; + } + + type = result; + + return type; +} + +/** + * gnutls_x509_crq_get_subject_alt_name: + * @crq: should contain a #gnutls_x509_crq_t type + * @seq: specifies the sequence number of the alt name, 0 for the + * first one, 1 for the second etc. + * @ret: is the place where the alternative name will be copied to + * @ret_size: holds the size of ret. + * @ret_type: holds the #gnutls_x509_subject_alt_name_t name type + * @critical: will be non-zero if the extension is marked as critical + * (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. It is the same as + * gnutls_x509_crq_get_subject_alt_name() except for the fact that it + * will return the type of the alternative name in @ret_type even if + * the function fails for some reason (i.e. the buffer provided is + * not enough). + * + * Returns: the alternative subject name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough to + * hold the value. In that case @ret_size will be updated with the + * required size. If the certificate request does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_subject_alt_name(gnutls_x509_crq_t crq, + unsigned int seq, void *ret, + size_t * ret_size, + unsigned int *ret_type, + unsigned int *critical) +{ + return get_subject_alt_name(crq, seq, ret, ret_size, ret_type, + critical, 0); +} + +/** + * gnutls_x509_crq_get_subject_alt_othername_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the otherName OID will be copied to + * @ret_size: holds the size of ret. + * + * This function will extract the type OID of an otherName Subject + * Alternative Name, contained in the given certificate, and return + * the type as an enumerated element. + * + * This function is only useful if + * gnutls_x509_crq_get_subject_alt_name() returned + * %GNUTLS_SAN_OTHERNAME. + * + * Returns: the alternative subject name type on success, one of the + * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, + * it will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types, + * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for + * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if + * @ret_size is not large enough to hold the value. In that case + * @ret_size will be updated with the required size. If the + * certificate does not have an Alternative name with the specified + * sequence number and with the otherName type then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_subject_alt_othername_oid(gnutls_x509_crq_t crq, + unsigned int seq, + void *ret, size_t * ret_size) +{ + return get_subject_alt_name(crq, seq, ret, ret_size, NULL, NULL, + 1); +} + +/** + * gnutls_x509_crq_get_extension_by_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this + * specifies which to get. Use (0) to get the first one. + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in + * the certificate. The extensions will be returned as binary data + * DER encoded, in the provided buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If the certificate does not + * contain the specified extension + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_extension_by_oid(gnutls_x509_crq_t crq, + const char *oid, unsigned indx, + void *buf, size_t * buf_size, + unsigned int *critical) +{ + int result; + unsigned int i; + char _oid[MAX_OID_SIZE]; + size_t oid_size; + + for (i = 0;; i++) { + oid_size = sizeof(_oid); + result = + gnutls_x509_crq_get_extension_info(crq, i, _oid, + &oid_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (strcmp(oid, _oid) == 0) { /* found */ + if (indx == 0) + return + gnutls_x509_crq_get_extension_data(crq, + i, + buf, + buf_size); + else + indx--; + } + } + + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + +} + +/** + * gnutls_x509_crq_get_extension_by_oid2: + * @crq: should contain a #gnutls_x509_crq_t type + * @oid: holds an Object Identifier in a null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this + * specifies which to get. Use (0) to get the first one. + * @output: will hold the allocated extension data + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in + * the certificate. The extensions will be returned as binary data + * DER encoded, in the provided buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error code in case of an error. If the certificate does not + * contain the specified extension + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 3.3.8 + **/ +int +gnutls_x509_crq_get_extension_by_oid2(gnutls_x509_crq_t crq, + const char *oid, unsigned indx, + gnutls_datum_t *output, + unsigned int *critical) +{ + int result; + unsigned int i; + char _oid[MAX_OID_SIZE]; + size_t oid_size; + + for (i = 0;; i++) { + oid_size = sizeof(_oid); + result = + gnutls_x509_crq_get_extension_info(crq, i, _oid, + &oid_size, + critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (strcmp(oid, _oid) == 0) { /* found */ + if (indx == 0) + return + gnutls_x509_crq_get_extension_data2(crq, + i, + output); + else + indx--; + } + } + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + +} + +/** + * gnutls_x509_crq_set_subject_alt_name: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @nt: 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 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.8.0 + **/ +int +gnutls_x509_crq_set_subject_alt_name(gnutls_x509_crq_t crq, + gnutls_x509_subject_alt_name_t nt, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result = 0; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + unsigned int critical = 0; + size_t prev_data_size = 0; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + if (flags & GNUTLS_FSAN_APPEND) { + result = + gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", + 0, NULL, + &prev_data_size, + &critical); + prev_der_data.size = prev_data_size; + + switch (result) { + case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: + /* Replacing non-existing data means the same as set data. */ + break; + + case GNUTLS_E_SUCCESS: + prev_der_data.data = + gnutls_malloc(prev_der_data.size); + if (prev_der_data.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + gnutls_x509_crq_get_extension_by_oid(crq, + "2.5.29.17", + 0, + prev_der_data. + data, + &prev_data_size, + &critical); + if (result < 0) { + gnutls_assert(); + gnutls_free(prev_der_data.data); + return result; + } + break; + + default: + gnutls_assert(); + return result; + } + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_subject_alt_name(nt, NULL, data, data_size, + &prev_der_data, + &der_data); + gnutls_free(prev_der_data.data); + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = + _gnutls_x509_crq_set_extension(crq, "2.5.29.17", &der_data, + critical); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; + + finish: + return result; +} + +/** + * gnutls_x509_crq_set_subject_alt_othername: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @oid: is the othername 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 the subject alternative name certificate + * extension. It can set the following types: + * + * The values set must be binary values and must be properly DER encoded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.0 + **/ +int +gnutls_x509_crq_set_subject_alt_othername(gnutls_x509_crq_t crq, + const char *oid, + const void *data, + unsigned int data_size, + unsigned int flags) +{ + int result = 0; + gnutls_datum_t der_data = { NULL, 0 }; + gnutls_datum_t encoded_data = { NULL, 0 }; + gnutls_datum_t prev_der_data = { NULL, 0 }; + unsigned int critical = 0; + size_t prev_data_size = 0; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + if (flags & GNUTLS_FSAN_APPEND) { + result = + gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", + 0, NULL, + &prev_data_size, + &critical); + prev_der_data.size = prev_data_size; + + switch (result) { + case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: + /* Replacing non-existing data means the same as set data. */ + break; + + case GNUTLS_E_SUCCESS: + prev_der_data.data = + gnutls_malloc(prev_der_data.size); + if (prev_der_data.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + gnutls_x509_crq_get_extension_by_oid(crq, + "2.5.29.17", + 0, + prev_der_data. + data, + &prev_data_size, + &critical); + if (result < 0) { + gnutls_assert(); + goto finish; + } + break; + + default: + 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_crq_set_extension(crq, "2.5.29.17", &der_data, + critical); + + if (result < 0) { + gnutls_assert(); + goto finish; + } + + result = 0; + + finish: + _gnutls_free_datum(&prev_der_data); + _gnutls_free_datum(&der_data); + _gnutls_free_datum(&encoded_data); + return result; +} + +/** + * gnutls_x509_crq_set_basic_constraints: + * @crq: a certificate request of type #gnutls_x509_crq_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. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_set_basic_constraints(gnutls_x509_crq_t crq, + unsigned int ca, + int pathLenConstraint) +{ + int result; + gnutls_datum_t der_data; + + if (crq == 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_crq_set_extension(crq, "2.5.29.19", &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_set_key_usage: + * @crq: a certificate request of type #gnutls_x509_crq_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. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_set_key_usage(gnutls_x509_crq_t crq, unsigned int usage) +{ + int result; + gnutls_datum_t der_data; + + if (crq == 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_crq_set_extension(crq, "2.5.29.15", &der_data, 1); + + _gnutls_free_datum(&der_data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_key_purpose_oid: + * @crq: should contain a #gnutls_x509_crq_t type + * @indx: This specifies which OID to return, use (0) to get the first one + * @oid: a pointer to store the OID (may be %NULL) + * @sizeof_oid: initially holds the size of @oid + * @critical: output variable with critical flag, may be %NULL. + * + * This function will extract the key purpose OIDs of the Certificate + * specified by the given index. These are stored in the Extended Key + * Usage extension (2.5.29.37). See the GNUTLS_KP_* definitions for + * human readable names. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *@sizeof_oid will be + * updated with the required size. On success 0 is returned. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_key_purpose_oid(gnutls_x509_crq_t crq, + unsigned indx, void *oid, + size_t * sizeof_oid, + unsigned int *critical) +{ + char tmpstr[MAX_NAME_SIZE]; + int result, len; + gnutls_datum_t prev = { NULL, 0 }; + asn1_node c2 = NULL; + size_t prev_size = 0; + + if (oid) + memset(oid, 0, *sizeof_oid); + else + *sizeof_oid = 0; + + /* Extract extension. + */ + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, + NULL, &prev_size, + critical); + prev.size = prev_size; + + if (result < 0) { + gnutls_assert(); + return result; + } + + prev.data = gnutls_malloc(prev.size); + if (prev.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, + prev.data, + &prev_size, + critical); + if (result < 0) { + gnutls_assert(); + gnutls_free(prev.data); + return result; + } + + result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(prev.data); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&c2, prev.data, prev.size, NULL); + gnutls_free(prev.data); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + indx++; + /* create a string like "?1" + */ + snprintf(tmpstr, sizeof(tmpstr), "?%u", indx); + + len = *sizeof_oid; + result = asn1_read_value(c2, tmpstr, oid, &len); + + *sizeof_oid = len; + asn1_delete_structure(&c2); + + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (result != ASN1_SUCCESS) { + if (result != ASN1_MEM_ERROR) + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crq_set_key_purpose_oid: + * @crq: a certificate of type #gnutls_x509_crq_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 value. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_set_key_purpose_oid(gnutls_x509_crq_t crq, + const void *oid, unsigned int critical) +{ + int result; + gnutls_datum_t prev = { NULL, 0 }, der_data; + asn1_node c2 = NULL; + size_t prev_size = 0; + + /* Read existing extension, if there is one. + */ + result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0, + NULL, &prev_size, + &critical); + prev.size = prev_size; + + switch (result) { + case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE: + /* No existing extension, that's fine. */ + break; + + case GNUTLS_E_SUCCESS: + prev.data = gnutls_malloc(prev.size); + if (prev.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", + 0, prev.data, + &prev_size, + &critical); + if (result < 0) { + gnutls_assert(); + gnutls_free(prev.data); + return result; + } + break; + + default: + gnutls_assert(); + return result; + } + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.ExtKeyUsageSyntax", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(prev.data); + return _gnutls_asn2err(result); + } + + if (prev.data) { + /* decode it. + */ + result = + _asn1_strict_der_decode(&c2, prev.data, prev.size, NULL); + gnutls_free(prev.data); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + } + + /* generate the extension. + */ + /* 1. create a new element. + */ + result = asn1_write_value(c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + /* 2. Add the OID. + */ + result = asn1_write_value(c2, "?LAST", oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_der_encode(c2, "", &der_data, 0); + asn1_delete_structure(&c2); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_crq_set_extension(crq, "2.5.29.37", + &der_data, critical); + _gnutls_free_datum(&der_data); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_key_id: + * @crq: a certificate of type #gnutls_x509_crq_t + * @flags: should be one of the flags from %gnutls_keyid_flags_t + * @output_data: will contain the key ID + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will return a unique ID that depends on the public key + * parameters. This ID can be used in checking whether a certificate + * corresponds to the given private key. + * + * If the buffer provided is not long enough to hold the output, then + * *@output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. The output will normally be a SHA-1 hash output, + * which is 20 bytes. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + * + * Since: 2.8.0 + **/ +int +gnutls_x509_crq_get_key_id(gnutls_x509_crq_t crq, unsigned int flags, + unsigned char *output_data, + size_t * output_data_size) +{ + int ret = 0; + gnutls_pk_params_st params; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_get_key_id(¶ms, output_data, output_data_size, flags); + + gnutls_pk_params_release(¶ms); + + return ret; +} + +/** + * gnutls_x509_crq_privkey_sign: + * @crq: should contain a #gnutls_x509_crq_t type + * @key: holds a private key + * @dig: The message digest to use, i.e., %GNUTLS_DIG_SHA1 + * @flags: must be 0 + * + * This function will sign the certificate request with a private key. + * This must be the same key as the one used in + * gnutls_x509_crt_set_key() since a certificate request is self + * signed. + * + * This must be the last step in a certificate request generation + * since all the previously set parameters are now signed. + * + * A known limitation of this function is, that a newly-signed request 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: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * %GNUTLS_E_ASN1_VALUE_NOT_FOUND is returned if you didn't set all + * information in the certificate request (e.g., the version using + * gnutls_x509_crq_set_version()). + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key, + gnutls_digest_algorithm_t dig, + unsigned int flags) +{ + int result; + gnutls_datum_t signature; + gnutls_datum_t tbs; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; + const gnutls_sign_entry_st *se; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Make sure version field is set. */ + if (gnutls_x509_crq_get_version(crq) == + GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + result = gnutls_x509_crq_set_version(crq, 1); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + if (dig == 0) { + /* attempt to find a reasonable choice */ + gnutls_pubkey_t pubkey; + int ret; + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_privkey(pubkey, key, 0, 0); + if (ret < 0) { + gnutls_pubkey_deinit(pubkey); + return gnutls_assert_val(ret); + } + ret = gnutls_pubkey_get_preferred_hash_algorithm(pubkey, &dig, NULL); + gnutls_pubkey_deinit(pubkey); + + if (ret < 0) + return gnutls_assert_val(ret); + } + + result = _gnutls_privkey_get_spki_params(key, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + pk = gnutls_privkey_get_pk_algorithm(key, NULL); + result = _gnutls_privkey_update_spki_params(key, pk, dig, 0, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 1. Self sign the request. + */ + result = + _gnutls_x509_get_tbs(crq->crq, "certificationRequestInfo", + &tbs); + + if (result < 0) { + gnutls_assert(); + return result; + } + + se = _gnutls_pk_to_sign_entry(params.pk, dig); + if (se == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + FIX_SIGN_PARAMS(params, flags, dig); + + result = privkey_sign_and_hash_data(key, se, + &tbs, &signature, ¶ms); + gnutls_free(tbs.data); + + if (result < 0) { + gnutls_assert(); + return result; + } + + /* Step 2. write the signature (bits) + */ + result = + asn1_write_value(crq->crq, "signature", signature.data, + signature.size * 8); + + _gnutls_free_datum(&signature); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* Step 3. Write the signatureAlgorithm field. + */ + result = + _gnutls_x509_write_sign_params(crq->crq, "signatureAlgorithm", + se, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + + +/** + * gnutls_x509_crq_verify: + * @crq: is the crq to be verified + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * + * This function will verify self signature in the certificate + * request and return its status. + * + * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED + * is returned, and zero or positive code on success. + * + * Since 2.12.0 + **/ +int gnutls_x509_crq_verify(gnutls_x509_crq_t crq, unsigned int flags) +{ + gnutls_datum_t data = { NULL, 0 }; + gnutls_datum_t signature = { NULL, 0 }; + gnutls_pk_params_st params; + gnutls_x509_spki_st sign_params; + const gnutls_sign_entry_st *se; + int ret; + + gnutls_pk_params_init(¶ms); + + ret = + _gnutls_x509_get_signed_data(crq->crq, NULL, + "certificationRequestInfo", + &data); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_x509_get_signature_algorithm(crq->crq, + "signatureAlgorithm"); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + se = _gnutls_sign_to_entry(ret); + if (se == NULL) { + gnutls_assert(); + ret = GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM; + goto cleanup; + } + + ret = + _gnutls_x509_get_signature(crq->crq, "signature", &signature); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_read_sign_params(crq->crq, + "signatureAlgorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + pubkey_verify_data(se, hash_to_entry(se->hash), &data, &signature, + ¶ms, &sign_params, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + _gnutls_free_datum(&data); + _gnutls_free_datum(&signature); + gnutls_pk_params_release(¶ms); + + return ret; +} + +/** + * gnutls_x509_crq_set_private_key_usage_period: + * @crq: a certificate of type #gnutls_x509_crq_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_crq_set_private_key_usage_period(gnutls_x509_crq_t crq, + time_t activation, + time_t expiration) +{ + int result; + gnutls_datum_t der_data; + asn1_node c2 = NULL; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + asn1_create_element(_gnutls_get_pkix(), + "PKIX1.PrivateKeyUsagePeriod", &c2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _gnutls_x509_set_time(c2, "notBefore", activation, 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_set_time(c2, "notAfter", expiration, 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(c2, "", &der_data, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_crq_set_extension(crq, "2.5.29.16", + &der_data, 0); + + _gnutls_free_datum(&der_data); + + cleanup: + asn1_delete_structure(&c2); + + return result; +} + +/** + * gnutls_x509_crq_get_tlsfeatures: + * @crq: An X.509 certificate request + * @features: If the function succeeds, the + * features will be stored in this variable. + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND + * @critical: the extension status + * + * This function will get the X.509 TLS features + * extension structure from the certificate request. + * The returned structure needs to be freed using + * gnutls_x509_tlsfeatures_deinit(). + * + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, + * then if the @features structure is empty this function will behave + * identically as if the flag was not set. Otherwise if there are elements + * in the @features structure then they will be merged with. + * + * Note that @features must be initialized prior to calling this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_crq_get_tlsfeatures(gnutls_x509_crq_t crq, + gnutls_x509_tlsfeatures_t features, + unsigned int flags, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der = {NULL, 0}; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + gnutls_x509_crq_get_extension_by_oid2(crq, GNUTLS_X509EXT_OID_TLSFEATURES, 0, + &der, critical)) < 0) + { + return ret; + } + + if (der.size == 0 || der.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_ext_import_tlsfeatures(&der, features, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_x509_crq_set_tlsfeatures: + * @crq: An X.509 certificate request + * @features: If the function succeeds, the + * features will be added to the certificate + * request. + * + * This function will set the certificate request's + * X.509 TLS extension from the given structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_crq_set_tlsfeatures(gnutls_x509_crq_t crq, + gnutls_x509_tlsfeatures_t features) +{ + int ret; + gnutls_datum_t der; + + if (crq == NULL || features == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_ext_export_tlsfeatures(features, &der); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_crq_set_extension(crq, GNUTLS_X509EXT_OID_TLSFEATURES, &der, 0); + + _gnutls_free_datum(&der); + + if (ret < 0) { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_x509_crq_set_extension_by_oid: + * @crq: a certificate of type #gnutls_x509_crq_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 request. 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_crq_set_extension_by_oid(gnutls_x509_crq_t crq, + 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 (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_x509_crq_set_extension(crq, oid, &der_data, critical); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; + +} + +/** + * gnutls_x509_crq_set_spki: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will set the certificate request'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_crq_set_spki(gnutls_x509_crq_t crq, + const gnutls_x509_spki_t spki, + unsigned int flags) +{ + int ret; + gnutls_pk_algorithm_t crq_pk; + gnutls_x509_spki_st tpki; + gnutls_pk_params_st params; + unsigned bits; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crq_get_mpis(crq, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + bits = pubkey_to_bits(¶ms); + crq_pk = params.algo; + + if (!_gnutls_pk_are_compat(crq_pk, spki->pk)) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + if (spki->pk != GNUTLS_PK_RSA_PSS) { + if (crq_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 (crq_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 (crq_pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_crq_read_spki_params(crq, &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; + } + + ret = _gnutls_x509_write_spki_params(crq->crq, + "certificationRequestInfo." + "subjectPKInfo." + "algorithm", + &tpki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_pk_params_release(¶ms); + return ret; +} |