/* * Copyright (C) 2001-2014 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 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 * */ /* This file contains the functions needed for RSA/DSA public key * encryption and signatures. */ #include "gnutls_int.h" #include #include #include "errors.h" #include #include #include #include "debug.h" #include #include #include #include /* encodes the Dss-Sig-Value structure */ int _gnutls_encode_ber_rs_raw(gnutls_datum_t * sig_value, const gnutls_datum_t * r, const gnutls_datum_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_write_value(sig, "r", r->data, r->size); if (result != ASN1_SUCCESS) { gnutls_assert(); asn1_delete_structure(&sig); return _gnutls_asn2err(result); } result = asn1_write_value(sig, "s", s->data, s->size); if (result != ASN1_SUCCESS) { gnutls_assert(); asn1_delete_structure(&sig); return _gnutls_asn2err(result); } result = _gnutls_x509_der_encode(sig, "", sig_value, 0); asn1_delete_structure(&sig); if (result < 0) return gnutls_assert_val(result); return 0; } 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) return gnutls_assert_val(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; } dst->flags = src->flags; dst->algo = src->algo; for (i = 0; i < src->params_nr; i++) { dst->params[i] = _gnutls_mpi_copy(src->params[i]); if (dst->params[i] == NULL) { goto fail; } dst->params_nr++; } if (_gnutls_set_datum(&dst->raw_priv, src->raw_priv.data, src->raw_priv.size) < 0) { gnutls_assert(); goto fail; } if (_gnutls_set_datum(&dst->raw_pub, src->raw_pub.data, src->raw_pub.size) < 0) { gnutls_assert(); goto fail; } if (src->seed_size) { dst->seed_size = src->seed_size; memcpy(dst->seed, src->seed, src->seed_size); } return 0; fail: for (j = 0; j < i; j++) _gnutls_mpi_release(&dst->params[j]); return GNUTLS_E_MEMORY_ERROR; } 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]); } gnutls_free(p->raw_priv.data); gnutls_free(p->raw_pub.data); p->raw_priv.data = NULL; p->raw_pub.data = NULL; p->params_nr = 0; } void gnutls_pk_params_clear(gnutls_pk_params_st * p) { unsigned int i; for (i = 0; i < p->params_nr; i++) { if (p->params[i] != NULL) _gnutls_mpi_clear(p->params[i]); } gnutls_memset(p->seed, 0, p->seed_size); p->seed_size = 0; if (p->raw_priv.data != NULL) { gnutls_memset(p->raw_priv.data, 0, p->raw_priv.size); p->raw_priv.size = 0; } } /* 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(const mac_entry_st * e, 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(e); if (algo == NULL) { gnutls_assert(); _gnutls_debug_log("Hash algorithm: %d has no OID\n", e->id); 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; result = asn1_der_coding(dinfo, "", NULL, &tmp_output_size, NULL); if (result != ASN1_MEM_ERROR) { gnutls_assert(); asn1_delete_structure(&dinfo); return _gnutls_asn2err(result); } 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; } /** * gnutls_encode_ber_digest_info: * @info: an RSA BER encoded DigestInfo structure * @hash: the hash algorithm that was used to get the digest * @digest: must contain the digest data * @output: will contain the allocated DigestInfo BER encoded data * * This function will encode the provided digest data, and its * algorithm into an RSA PKCS#1 1.5 DigestInfo structure. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. * * Since: 3.5.0 * **/ int gnutls_encode_ber_digest_info(gnutls_digest_algorithm_t hash, const gnutls_datum_t * digest, gnutls_datum_t * output) { const mac_entry_st *e = hash_to_entry(hash); if (unlikely(e == NULL)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); return encode_ber_digest_info(e , digest, output); } /** * gnutls_decode_ber_digest_info: * @info: an RSA BER encoded DigestInfo structure * @hash: will contain the hash algorithm of the structure * @digest: will contain the hash output of the structure * @digest_size: will contain the hash size of the structure; initially must hold the maximum size of @digest * * This function will parse an RSA PKCS#1 1.5 DigestInfo structure * and report the hash algorithm used as well as the digest data. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. * * Since: 3.5.0 * **/ int gnutls_decode_ber_digest_info(const gnutls_datum_t * info, gnutls_digest_algorithm_t * hash, unsigned char * digest, unsigned int *digest_size) { ASN1_TYPE dinfo = ASN1_TYPE_EMPTY; int result; char str[MAX(MAX_OID_SIZE, MAX_HASH_SIZE)]; 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_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_HASH_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; } int _gnutls_params_get_rsa_raw(const gnutls_pk_params_st* params, gnutls_datum_t * m, gnutls_datum_t * e, gnutls_datum_t * d, gnutls_datum_t * p, gnutls_datum_t * q, gnutls_datum_t * u, gnutls_datum_t * e1, gnutls_datum_t * e2) { int ret; if (params == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if (params->algo != GNUTLS_PK_RSA) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if (m) { ret = _gnutls_mpi_dprint_lz(params->params[0], m); if (ret < 0) { gnutls_assert(); goto error; } } /* E */ if (e) { ret = _gnutls_mpi_dprint_lz(params->params[1], e); if (ret < 0) { gnutls_assert(); goto error; } } /* D */ if (d && params->params[2]) { ret = _gnutls_mpi_dprint_lz(params->params[2], d); if (ret < 0) { gnutls_assert(); goto error; } } else if (d) { d->data = NULL; d->size = 0; } /* P */ if (p && params->params[3]) { ret = _gnutls_mpi_dprint_lz(params->params[3], p); if (ret < 0) { gnutls_assert(); goto error; } } else if (p) { p->data = NULL; p->size = 0; } /* Q */ if (q && params->params[4]) { ret = _gnutls_mpi_dprint_lz(params->params[4], q); if (ret < 0) { gnutls_assert(); goto error; } } else if (q) { q->data = NULL; q->size = 0; } /* U */ if (u && params->params[5]) { ret = _gnutls_mpi_dprint_lz(params->params[5], u); if (ret < 0) { gnutls_assert(); goto error; } } else if (u) { u->data = NULL; u->size = 0; } /* E1 */ if (e1 && params->params[6]) { ret = _gnutls_mpi_dprint_lz(params->params[6], e1); if (ret < 0) { gnutls_assert(); goto error; } } else if (e1) { e1->data = NULL; e1->size = 0; } /* E2 */ if (e2 && params->params[7]) { ret = _gnutls_mpi_dprint_lz(params->params[7], e2); if (ret < 0) { gnutls_assert(); goto error; } } else if (e2) { e2->data = NULL; e2->size = 0; } return 0; error: _gnutls_free_datum(m); _gnutls_free_datum(d); _gnutls_free_datum(e); _gnutls_free_datum(e1); _gnutls_free_datum(e2); _gnutls_free_datum(p); _gnutls_free_datum(q); return ret; } int _gnutls_params_get_dsa_raw(const gnutls_pk_params_st* params, gnutls_datum_t * p, gnutls_datum_t * q, gnutls_datum_t * g, gnutls_datum_t * y, gnutls_datum_t * x) { int ret; if (params == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if (params->algo != GNUTLS_PK_DSA) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* P */ if (p) { ret = _gnutls_mpi_dprint_lz(params->params[0], p); if (ret < 0) { gnutls_assert(); return ret; } } /* Q */ if (q) { ret = _gnutls_mpi_dprint_lz(params->params[1], q); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(p); return ret; } } /* G */ if (g) { ret = _gnutls_mpi_dprint_lz(params->params[2], g); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(p); _gnutls_free_datum(q); return ret; } } /* Y */ if (y) { ret = _gnutls_mpi_dprint_lz(params->params[3], y); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(p); _gnutls_free_datum(g); _gnutls_free_datum(q); return ret; } } /* X */ if (x) { ret = _gnutls_mpi_dprint_lz(params->params[4], x); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(y); _gnutls_free_datum(p); _gnutls_free_datum(g); _gnutls_free_datum(q); return ret; } } return 0; } int _gnutls_params_get_ecc_raw(const gnutls_pk_params_st* params, gnutls_ecc_curve_t * curve, gnutls_datum_t * x, gnutls_datum_t * y, gnutls_datum_t * k) { int ret; if (params == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if (curve) *curve = params->flags; /* X */ if (x) { ret = _gnutls_mpi_dprint_lz(params->params[ECC_X], x); if (ret < 0) { gnutls_assert(); return ret; } } /* Y */ if (y) { ret = _gnutls_mpi_dprint_lz(params->params[ECC_Y], y); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(x); return ret; } } /* K */ if (k) { ret = _gnutls_mpi_dprint_lz(params->params[ECC_K], k); if (ret < 0) { gnutls_assert(); _gnutls_free_datum(x); _gnutls_free_datum(y); return ret; } } return 0; } int pk_hash_data(gnutls_pk_algorithm_t pk, const mac_entry_st * hash, gnutls_pk_params_st * params, const gnutls_datum_t * data, gnutls_datum_t * digest) { int ret; digest->size = _gnutls_hash_get_algo_len(hash); digest->data = gnutls_malloc(digest->size); if (digest->data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } ret = _gnutls_hash_fast((gnutls_digest_algorithm_t)hash->id, data->data, data->size, digest->data); if (ret < 0) { gnutls_assert(); goto cleanup; } return 0; cleanup: gnutls_free(digest->data); return ret; } /* * This function will do RSA PKCS #1 1.5 encoding * on the given digest. The given digest must be allocated * and will be freed if replacement is required. */ int pk_prepare_hash(gnutls_pk_algorithm_t pk, const mac_entry_st * hash, gnutls_datum_t * digest) { int ret; gnutls_datum_t old_digest = { digest->data, digest->size }; switch (pk) { case GNUTLS_PK_RSA: if (unlikely(hash == NULL)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); /* Encode the digest as a DigestInfo */ if ((ret = encode_ber_digest_info(hash, &old_digest, digest)) != 0) { gnutls_assert(); return ret; } _gnutls_free_datum(&old_digest); break; case GNUTLS_PK_DSA: case GNUTLS_PK_EC: break; default: gnutls_assert(); return GNUTLS_E_UNIMPLEMENTED_FEATURE; } return 0; }