/* * Copyright (C) 2001-2012 Free Software Foundation, Inc. * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 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 * */ /* This file contains the functions needed for RSA/DSA public key * encryption and signatures. */ #include #include #include #include #include #include #include #include "debug.h" #include #include #include /* encodes the Dss-Sig-Value structure */ int _gnutls_encode_ber_rs (gnutls_datum_t * sig_value, bigint_t r, bigint_t s) { ASN1_TYPE sig; int result; if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DSASignatureValue", &sig)) != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } result = _gnutls_x509_write_int (sig, "r", r, 1); if (result < 0) { gnutls_assert (); asn1_delete_structure (&sig); return result; } result = _gnutls_x509_write_int (sig, "s", s, 1); if (result < 0) { gnutls_assert (); asn1_delete_structure (&sig); return result; } result = _gnutls_x509_der_encode (sig, "", sig_value, 0); asn1_delete_structure (&sig); if (result < 0) { gnutls_assert (); return result; } return 0; } /* decodes the Dss-Sig-Value structure */ int _gnutls_decode_ber_rs (const gnutls_datum_t * sig_value, bigint_t * r, bigint_t * s) { ASN1_TYPE sig; int result; if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DSASignatureValue", &sig)) != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } result = asn1_der_decoding (&sig, sig_value->data, sig_value->size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&sig); return _gnutls_asn2err (result); } result = _gnutls_x509_read_int (sig, "r", r); if (result < 0) { gnutls_assert (); asn1_delete_structure (&sig); return result; } result = _gnutls_x509_read_int (sig, "s", s); if (result < 0) { gnutls_assert (); _gnutls_mpi_release (s); asn1_delete_structure (&sig); return result; } asn1_delete_structure (&sig); return 0; } /* some generic pk functions */ int _gnutls_pk_params_copy (gnutls_pk_params_st * dst, const gnutls_pk_params_st * src) { unsigned int i, j; dst->params_nr = 0; if (src == NULL || src->params_nr == 0) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } for (i = 0; i < src->params_nr; i++) { dst->params[i] = _gnutls_mpi_set (NULL, src->params[i]); if (dst->params[i] == NULL) { for (j = 0; j < i; j++) _gnutls_mpi_release (&dst->params[j]); return GNUTLS_E_MEMORY_ERROR; } dst->params_nr++; } return 0; } void gnutls_pk_params_init (gnutls_pk_params_st * p) { memset (p, 0, sizeof (gnutls_pk_params_st)); } void gnutls_pk_params_release (gnutls_pk_params_st * p) { unsigned int i; for (i = 0; i < p->params_nr; i++) { _gnutls_mpi_release (&p->params[i]); } p->params_nr = 0; } int _gnutls_pk_get_hash_algorithm (gnutls_pk_algorithm_t pk, gnutls_pk_params_st* params, gnutls_digest_algorithm_t * dig, unsigned int *mand) { if (mand) { if (pk == GNUTLS_PK_DSA) *mand = 1; else *mand = 0; } return _gnutls_x509_verify_algorithm (dig, NULL, pk, params); } /* Writes the digest information and the digest in a DER encoded * structure. The digest info is allocated and stored into the info structure. */ int encode_ber_digest_info (gnutls_digest_algorithm_t hash, const gnutls_datum_t * digest, gnutls_datum_t * output) { ASN1_TYPE dinfo = ASN1_TYPE_EMPTY; int result; const char *algo; uint8_t *tmp_output; int tmp_output_size; algo = _gnutls_x509_mac_to_oid ((gnutls_mac_algorithm_t) hash); if (algo == NULL) { gnutls_assert (); _gnutls_debug_log ("Hash algorithm: %d has no OID\n", hash); return GNUTLS_E_UNKNOWN_PK_ALGORITHM; } if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DigestInfo", &dinfo)) != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } result = asn1_write_value (dinfo, "digestAlgorithm.algorithm", algo, 1); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&dinfo); return _gnutls_asn2err (result); } /* Write an ASN.1 NULL in the parameters field. This matches RFC 3279 and RFC 4055, although is arguable incorrect from a historic perspective (see those documents for more information). Regardless of what is correct, this appears to be what most implementations do. */ result = asn1_write_value (dinfo, "digestAlgorithm.parameters", ASN1_NULL, ASN1_NULL_SIZE); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&dinfo); return _gnutls_asn2err (result); } result = asn1_write_value (dinfo, "digest", digest->data, digest->size); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&dinfo); return _gnutls_asn2err (result); } tmp_output_size = 0; asn1_der_coding (dinfo, "", NULL, &tmp_output_size, NULL); tmp_output = gnutls_malloc (tmp_output_size); if (tmp_output == NULL) { gnutls_assert (); asn1_delete_structure (&dinfo); return GNUTLS_E_MEMORY_ERROR; } result = asn1_der_coding (dinfo, "", tmp_output, &tmp_output_size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&dinfo); return _gnutls_asn2err (result); } asn1_delete_structure (&dinfo); output->size = tmp_output_size; output->data = tmp_output; return 0; } /* Reads the digest information. * we use DER here, although we should use BER. It works fine * anyway. */ int decode_ber_digest_info (const gnutls_datum_t * info, gnutls_digest_algorithm_t * hash, uint8_t * digest, unsigned int *digest_size) { ASN1_TYPE dinfo = ASN1_TYPE_EMPTY; int result; char str[1024]; int len; if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DigestInfo", &dinfo)) != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } result = asn1_der_decoding (&dinfo, info->data, info->size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&dinfo); return _gnutls_asn2err (result); } len = sizeof (str) - 1; result = asn1_read_value (dinfo, "digestAlgorithm.algorithm", str, &len); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&dinfo); return _gnutls_asn2err (result); } *hash = _gnutls_x509_oid_to_digest (str); if (*hash == GNUTLS_DIG_UNKNOWN) { _gnutls_debug_log ("verify.c: HASH OID: %s\n", str); gnutls_assert (); asn1_delete_structure (&dinfo); return GNUTLS_E_UNKNOWN_ALGORITHM; } len = sizeof (str) - 1; result = asn1_read_value (dinfo, "digestAlgorithm.parameters", str, &len); /* To avoid permitting garbage in the parameters field, either the parameters field is not present, or it contains 0x05 0x00. */ if (!(result == ASN1_ELEMENT_NOT_FOUND || (result == ASN1_SUCCESS && len == ASN1_NULL_SIZE && memcmp (str, ASN1_NULL, ASN1_NULL_SIZE) == 0))) { gnutls_assert (); asn1_delete_structure (&dinfo); return GNUTLS_E_ASN1_GENERIC_ERROR; } len = *digest_size; result = asn1_read_value (dinfo, "digest", digest, &len); if (result != ASN1_SUCCESS) { gnutls_assert (); *digest_size = len; asn1_delete_structure (&dinfo); return _gnutls_asn2err (result); } *digest_size = len; asn1_delete_structure (&dinfo); return 0; }