diff options
Diffstat (limited to '')
-rw-r--r-- | lib/x509/mpi.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/lib/x509/mpi.c b/lib/x509/mpi.c new file mode 100644 index 0000000..c0751df --- /dev/null +++ b/lib/x509/mpi.c @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2015-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/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <global.h> +#include <libtasn1.h> +#include <datum.h> +#include "common.h" +#include "x509_int.h" +#include <num.h> +#include <limits.h> + +/* Reads an Integer from the DER encoded data + */ + +int _gnutls_x509_read_der_int(uint8_t * der, int dersize, bigint_t * out) +{ + int result; + asn1_node spk = NULL; + + /* == INTEGER */ + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + /* Read Y */ + + if ((result = _gnutls_x509_read_int(spk, "", out)) < 0) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + asn1_delete_structure(&spk); + + return 0; + +} + +int _gnutls_x509_read_der_uint(uint8_t * der, int dersize, unsigned int *out) +{ + int result; + asn1_node spk = NULL; + + /* == INTEGER */ + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = _asn1_strict_der_decode(&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + /* Read Y */ + + if ((result = _gnutls_x509_read_uint(spk, "", out)) < 0) { + gnutls_assert(); + asn1_delete_structure(&spk); + return _gnutls_asn2err(result); + } + + asn1_delete_structure(&spk); + + return 0; + +} + + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_get_asn_mpis(asn1_node asn, const char *root, + gnutls_pk_params_st * params) +{ + int result; + char name[256]; + gnutls_datum_t tmp = { NULL, 0 }; + gnutls_pk_algorithm_t pk_algorithm; + gnutls_ecc_curve_t curve; + + gnutls_pk_params_init(params); + + result = _gnutls_x509_get_pk_algorithm(asn, root, &curve, NULL); + if (result < 0) { + gnutls_assert(); + return result; + } + + pk_algorithm = result; + params->curve = curve; + params->algo = pk_algorithm; + + /* Read the algorithm's parameters + */ + _asnstr_append_name(name, sizeof(name), root, + ".algorithm.parameters"); + + if (pk_algorithm != GNUTLS_PK_RSA && + pk_algorithm != GNUTLS_PK_EDDSA_ED25519 && pk_algorithm != GNUTLS_PK_ECDH_X25519 && + pk_algorithm != GNUTLS_PK_EDDSA_ED448 && pk_algorithm != GNUTLS_PK_ECDH_X448) { + /* RSA and EdDSA do not use parameters */ + result = _gnutls_x509_read_value(asn, name, &tmp); + if (pk_algorithm == GNUTLS_PK_RSA_PSS && + (result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)) { + goto skip_params; + } + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = + _gnutls_x509_read_pubkey_params(pk_algorithm, + tmp.data, tmp.size, + params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + _gnutls_free_datum(&tmp); + } + + skip_params: + /* Now read the public key */ + _asnstr_append_name(name, sizeof(name), root, ".subjectPublicKey"); + + result = _gnutls_x509_read_value(asn, name, &tmp); + if (result < 0) { + gnutls_assert(); + goto error; + } + + if ((result = + _gnutls_x509_read_pubkey(pk_algorithm, tmp.data, tmp.size, + params)) < 0) { + gnutls_assert(); + goto error; + } + + result = _gnutls_x509_check_pubkey_params(params); + if (result < 0) { + gnutls_assert(); + goto error; + } + + result = 0; + + error: + if (result < 0) + gnutls_pk_params_release(params); + _gnutls_free_datum(&tmp); + return result; +} + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_x509_crt_get_mpis(gnutls_x509_crt_t cert, + gnutls_pk_params_st * params) +{ + /* Read the algorithm's OID + */ + return _gnutls_get_asn_mpis(cert->cert, + "tbsCertificate.subjectPublicKeyInfo", + params); +} + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_x509_crq_get_mpis(gnutls_x509_crq_t cert, + gnutls_pk_params_st * params) +{ + /* Read the algorithm's OID + */ + return _gnutls_get_asn_mpis(cert->crq, + "certificationRequestInfo.subjectPKInfo", + params); +} + +/* + * This function reads and decodes the parameters for DSS or RSA keys. + * This is the "signatureAlgorithm" fields. + */ +int +_gnutls_x509_read_pkalgo_params(asn1_node src, const char *src_name, + gnutls_x509_spki_st *spki, unsigned is_sig) +{ + int result; + char name[128]; + char oid[MAX_OID_SIZE]; + int oid_size; + + memset(spki, 0, sizeof(*spki)); + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".algorithm"); + + oid_size = sizeof(oid); + result = asn1_read_value(src, name, oid, &oid_size); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (strcmp (oid, PK_PKIX1_RSA_PSS_OID) == 0) { + gnutls_datum_t tmp = { NULL, 0 }; + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".parameters"); + + result = _gnutls_x509_read_value(src, name, &tmp); + if (result < 0) { + if (!is_sig) { + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || + result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + /* it is ok to not have parameters in SPKI, but + * not in signatures */ + return 0; + } + } + + return gnutls_assert_val(result); + } + + result = _gnutls_x509_read_rsa_pss_params(tmp.data, tmp.size, + spki); + _gnutls_free_datum(&tmp); + + if (result < 0) + gnutls_assert(); + + return result; + } + + return 0; +} + +static int write_oid_and_params(asn1_node dst, const char *dst_name, const char *oid, gnutls_x509_spki_st *params) +{ + int result; + char name[128]; + + if (params == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + _gnutls_str_cpy(name, sizeof(name), dst_name); + _gnutls_str_cat(name, sizeof(name), ".algorithm"); + + /* write the OID. + */ + result = asn1_write_value(dst, name, oid, 1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + _gnutls_str_cpy(name, sizeof(name), dst_name); + _gnutls_str_cat(name, sizeof(name), ".parameters"); + + if (params->pk == GNUTLS_PK_RSA) + result = + asn1_write_value(dst, name, ASN1_NULL, ASN1_NULL_SIZE); + else if (params->pk == GNUTLS_PK_RSA_PSS) { + gnutls_datum_t tmp = { NULL, 0 }; + + result = _gnutls_x509_write_rsa_pss_params(params, &tmp); + if (result < 0) + return gnutls_assert_val(result); + + result = asn1_write_value(dst, name, tmp.data, tmp.size); + _gnutls_free_datum(&tmp); + } else + result = asn1_write_value(dst, name, NULL, 0); + + if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) { + /* Here we ignore the element not found error, since this + * may have been disabled before. + */ + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +int +_gnutls_x509_write_spki_params(asn1_node dst, const char *dst_name, + gnutls_x509_spki_st *params) +{ + const char *oid; + + if (params->legacy && params->pk == GNUTLS_PK_RSA) + oid = PK_PKIX1_RSA_OID; + else if (params->pk == GNUTLS_PK_RSA_PSS) + oid = PK_PKIX1_RSA_PSS_OID; + else + oid = gnutls_pk_get_oid(params->pk); + + if (oid == NULL) { + gnutls_assert(); + _gnutls_debug_log + ("Cannot find OID for public key algorithm %s\n", + gnutls_pk_get_name(params->pk)); + return GNUTLS_E_INVALID_REQUEST; + } + + return write_oid_and_params(dst, dst_name, oid, params); +} + +int +_gnutls_x509_write_sign_params(asn1_node dst, const char *dst_name, + const gnutls_sign_entry_st *se, gnutls_x509_spki_st *params) +{ + const char *oid; + + if (params->legacy && params->pk == GNUTLS_PK_RSA) + oid = PK_PKIX1_RSA_OID; + else if (params->pk == GNUTLS_PK_RSA_PSS) + oid = PK_PKIX1_RSA_PSS_OID; + else + oid = se->oid; + + if (oid == NULL) { + gnutls_assert(); + _gnutls_debug_log + ("Cannot find OID for sign algorithm %s\n", + se->name); + return GNUTLS_E_INVALID_REQUEST; + } + + return write_oid_and_params(dst, dst_name, oid, params); +} + +/* this function reads a (small) unsigned integer + * from asn1 structs. Combines the read and the conversion + * steps. + */ +int +_gnutls_x509_read_uint(asn1_node node, const char *value, + unsigned int *ret) +{ + int len, result; + uint8_t *tmpstr; + + len = 0; + result = asn1_read_value(node, value, NULL, &len); + if (result != ASN1_MEM_ERROR) { + return _gnutls_asn2err(result); + } + + tmpstr = gnutls_malloc(len); + if (tmpstr == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_read_value(node, value, tmpstr, &len); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(tmpstr); + return _gnutls_asn2err(result); + } + + if (len == 1) + *ret = tmpstr[0]; + else if (len == 2) + *ret = _gnutls_read_uint16(tmpstr); + else if (len == 3) + *ret = _gnutls_read_uint24(tmpstr); + else if (len == 4) + *ret = _gnutls_read_uint32(tmpstr); + else { + gnutls_assert(); + gnutls_free(tmpstr); + return GNUTLS_E_INTERNAL_ERROR; + } + + gnutls_free(tmpstr); + + return 0; +} + +/* Writes the specified integer into the specified node. + */ +int +_gnutls_x509_write_uint32(asn1_node node, const char *value, uint32_t num) +{ + uint8_t tmpstr[5]; + int result; + + tmpstr[0] = 0; + _gnutls_write_uint32(num, tmpstr+1); + + if (tmpstr[1] > SCHAR_MAX) { + result = asn1_write_value(node, value, tmpstr, 5); + } else { + result = asn1_write_value(node, value, tmpstr+1, 4); + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} |