/* * 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 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 * */ /* Here lie everything that has to do with large numbers, libgcrypt and * other stuff that didn't fit anywhere else. */ #include "gnutls_int.h" #include #include "errors.h" #include #include #include #include /* Functions that refer to the mpi library. */ /* Returns a random number r, 0 < r < p */ bigint_t _gnutls_mpi_random_modp(bigint_t r, bigint_t p, gnutls_rnd_level_t level) { size_t size; int ret; bigint_t tmp; uint8_t tmpbuf[512]; uint8_t *buf; int buf_release = 0; size = ((_gnutls_mpi_get_nbits(p)+64)/8) + 1; if (size < sizeof(tmpbuf)) { buf = tmpbuf; } else { buf = gnutls_malloc(size); if (buf == NULL) { gnutls_assert(); goto cleanup; } buf_release = 1; } ret = gnutls_rnd(level, buf, size); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = _gnutls_mpi_init_scan(&tmp, buf, size); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = _gnutls_mpi_modm(tmp, tmp, p); if (ret < 0) { gnutls_assert(); goto cleanup; } if (_gnutls_mpi_cmp_ui(tmp, 0) == 0) { ret = _gnutls_mpi_add_ui(tmp, tmp, 1); if (ret < 0) { gnutls_assert(); goto cleanup; } } if (buf_release != 0) { gnutls_free(buf); } if (r != NULL) { ret = _gnutls_mpi_set(r, tmp); if (ret < 0) goto cleanup; _gnutls_mpi_release(&tmp); return r; } return tmp; cleanup: if (buf_release != 0) gnutls_free(buf); return NULL; } /* returns %GNUTLS_E_SUCCESS (0) on success */ int _gnutls_mpi_init_scan(bigint_t * ret_mpi, const void *buffer, size_t nbytes) { bigint_t r; int ret; ret = _gnutls_mpi_init(&r); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_mpi_scan(r, buffer, nbytes); if (ret < 0) { gnutls_assert(); _gnutls_mpi_release(&r); return ret; } *ret_mpi = r; return 0; } /* returns %GNUTLS_E_SUCCESS (0) on success. Fails if the number is zero. */ int _gnutls_mpi_init_scan_nz(bigint_t * ret_mpi, const void *buffer, size_t nbytes) { int ret; ret = _gnutls_mpi_init_scan(ret_mpi, buffer, nbytes); if (ret < 0) return ret; /* MPIs with 0 bits are illegal */ if (_gnutls_mpi_cmp_ui(*ret_mpi, 0) == 0) { _gnutls_mpi_release(ret_mpi); return GNUTLS_E_MPI_SCAN_FAILED; } return 0; } int _gnutls_mpi_init_scan_le(bigint_t * ret_mpi, const void *buffer, size_t nbytes) { bigint_t r; int ret; ret = _gnutls_mpi_init(&r); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_mpi_scan_le(r, buffer, nbytes); if (ret < 0) { gnutls_assert(); _gnutls_mpi_release(&r); return ret; } *ret_mpi = r; return 0; } int _gnutls_mpi_dprint_le(const bigint_t a, gnutls_datum_t * dest) { int ret; uint8_t *buf = NULL; size_t bytes = 0; if (dest == NULL || a == NULL) return GNUTLS_E_INVALID_REQUEST; _gnutls_mpi_print_le(a, NULL, &bytes); if (bytes != 0) buf = gnutls_malloc(bytes); if (buf == NULL) return GNUTLS_E_MEMORY_ERROR; ret = _gnutls_mpi_print_le(a, buf, &bytes); if (ret < 0) { gnutls_free(buf); return ret; } dest->data = buf; dest->size = bytes; return 0; } /* Always has the first bit zero */ int _gnutls_mpi_dprint_lz(const bigint_t a, gnutls_datum_t * dest) { int ret; uint8_t *buf = NULL; size_t bytes = 0; if (dest == NULL || a == NULL) return GNUTLS_E_INVALID_REQUEST; _gnutls_mpi_print_lz(a, NULL, &bytes); if (bytes != 0) buf = gnutls_malloc(bytes); if (buf == NULL) return GNUTLS_E_MEMORY_ERROR; ret = _gnutls_mpi_print_lz(a, buf, &bytes); if (ret < 0) { gnutls_free(buf); return ret; } dest->data = buf; dest->size = bytes; return 0; } int _gnutls_mpi_dprint(const bigint_t a, gnutls_datum_t * dest) { int ret; uint8_t *buf = NULL; size_t bytes = 0; if (dest == NULL || a == NULL) return GNUTLS_E_INVALID_REQUEST; _gnutls_mpi_print(a, NULL, &bytes); if (bytes != 0) buf = gnutls_malloc(bytes); if (buf == NULL) return GNUTLS_E_MEMORY_ERROR; ret = _gnutls_mpi_print(a, buf, &bytes); if (ret < 0) { gnutls_free(buf); return ret; } dest->data = buf; dest->size = bytes; return 0; } /* This function will copy the mpi data into a datum, * but will set minimum size to 'size'. That means that * the output value is left padded with zeros. */ int _gnutls_mpi_dprint_size(const bigint_t a, gnutls_datum_t * dest, size_t size) { int ret; uint8_t *buf = NULL; size_t bytes = 0; unsigned int i; if (dest == NULL || a == NULL) return GNUTLS_E_INVALID_REQUEST; _gnutls_mpi_print(a, NULL, &bytes); if (bytes != 0) buf = gnutls_malloc(MAX(size, bytes)); if (buf == NULL) return GNUTLS_E_MEMORY_ERROR; if (bytes <= size) { size_t diff = size - bytes; for (i = 0; i < diff; i++) buf[i] = 0; ret = _gnutls_mpi_print(a, &buf[diff], &bytes); } else { ret = _gnutls_mpi_print(a, buf, &bytes); } if (ret < 0) { gnutls_free(buf); return ret; } dest->data = buf; dest->size = MAX(size, bytes); return 0; } /* like _gnutls_mpi_dprint_size, but prints into preallocated byte buffer */ int _gnutls_mpi_bprint_size(const bigint_t a, uint8_t *buf, size_t size) { int result; size_t bytes = 0; result = _gnutls_mpi_print(a, NULL, &bytes); if (result != GNUTLS_E_SHORT_MEMORY_BUFFER) return gnutls_assert_val(result); if (bytes <= size) { unsigned i; size_t diff = size - bytes; for (i = 0; i < diff; i++) buf[i] = 0; result = _gnutls_mpi_print(a, &buf[diff], &bytes); } else { result = _gnutls_mpi_print(a, buf, &bytes); } return result; } /* Flags for __gnutls_x509_read_int() and __gnutls_x509_write_int */ #define GNUTLS_X509_INT_OVERWRITE (1 << 0) #define GNUTLS_X509_INT_LE (1 << 1) #define GNUTLS_X509_INT_LZ (1 << 2) /* write only */ /* this function reads an integer * from asn1 structs. Combines the read and mpi_scan * steps. */ static int __gnutls_x509_read_int(ASN1_TYPE node, const char *value, bigint_t * ret_mpi, unsigned int flags) { int result; uint8_t *tmpstr = NULL; int tmpstr_size; tmpstr_size = 0; result = asn1_read_value(node, value, NULL, &tmpstr_size); if (result != ASN1_MEM_ERROR) { gnutls_assert(); return _gnutls_asn2err(result); } tmpstr = gnutls_malloc(tmpstr_size); if (tmpstr == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } result = asn1_read_value(node, value, tmpstr, &tmpstr_size); if (result != ASN1_SUCCESS) { gnutls_assert(); gnutls_free(tmpstr); return _gnutls_asn2err(result); } if (flags & GNUTLS_X509_INT_LE) result = _gnutls_mpi_init_scan_le(ret_mpi, tmpstr, tmpstr_size); else result = _gnutls_mpi_init_scan(ret_mpi, tmpstr, tmpstr_size); if (flags & GNUTLS_X509_INT_OVERWRITE) zeroize_key(tmpstr, tmpstr_size); gnutls_free(tmpstr); if (result < 0) { gnutls_assert(); return result; } return 0; } int _gnutls_x509_read_int(ASN1_TYPE node, const char *value, bigint_t * ret_mpi) { return __gnutls_x509_read_int(node, value, ret_mpi, 0); } int _gnutls_x509_read_key_int(ASN1_TYPE node, const char *value, bigint_t * ret_mpi) { return __gnutls_x509_read_int(node, value, ret_mpi, GNUTLS_X509_INT_OVERWRITE); } int _gnutls_x509_read_key_int_le(ASN1_TYPE node, const char *value, bigint_t * ret_mpi) { return __gnutls_x509_read_int(node, value, ret_mpi, GNUTLS_X509_INT_OVERWRITE | GNUTLS_X509_INT_LE); } /* Writes the specified integer into the specified node. */ static int __gnutls_x509_write_int(ASN1_TYPE node, const char *value, bigint_t mpi, unsigned int flags) { uint8_t *tmpstr; size_t s_len; int result; s_len = 0; if (flags & GNUTLS_X509_INT_LZ) result = _gnutls_mpi_print_lz(mpi, NULL, &s_len); else if (GNUTLS_X509_INT_LE) result = _gnutls_mpi_print_le(mpi, NULL, &s_len); else result = _gnutls_mpi_print(mpi, NULL, &s_len); if (result != GNUTLS_E_SHORT_MEMORY_BUFFER) { gnutls_assert(); return result; } tmpstr = gnutls_malloc(s_len); if (tmpstr == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } if (flags & GNUTLS_X509_INT_LZ) result = _gnutls_mpi_print_lz(mpi, tmpstr, &s_len); else if (GNUTLS_X509_INT_LE) result = _gnutls_mpi_print_le(mpi, tmpstr, &s_len); else result = _gnutls_mpi_print(mpi, tmpstr, &s_len); if (result != 0) { gnutls_assert(); gnutls_free(tmpstr); return GNUTLS_E_MPI_PRINT_FAILED; } result = asn1_write_value(node, value, tmpstr, s_len); if (flags & GNUTLS_X509_INT_OVERWRITE) zeroize_key(tmpstr, s_len); gnutls_free(tmpstr); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } return 0; } int _gnutls_x509_write_int(ASN1_TYPE node, const char *value, bigint_t mpi, int lz) { return __gnutls_x509_write_int(node, value, mpi, lz ? GNUTLS_X509_INT_LZ : 0); } int _gnutls_x509_write_key_int(ASN1_TYPE node, const char *value, bigint_t mpi, int lz) { return __gnutls_x509_write_int(node, value, mpi, (lz ? GNUTLS_X509_INT_LZ : 0) | GNUTLS_X509_INT_OVERWRITE); } int _gnutls_x509_write_key_int_le(ASN1_TYPE node, const char *value, bigint_t mpi) { return __gnutls_x509_write_int(node, value, mpi, GNUTLS_X509_INT_OVERWRITE | GNUTLS_X509_INT_LE); }