/* * 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 #include #include #include #include #include #include /* Functions that refer to the mpi library. */ #define clearbit(v,n) ((unsigned char)(v) & ~( (unsigned char)(1) << (unsigned)(n))) bigint_t _gnutls_mpi_randomize (bigint_t r, unsigned int bits, gnutls_rnd_level_t level) { size_t size = 1 + (bits / 8); int ret; int rem, i; bigint_t tmp; uint8_t tmpbuf[512]; uint8_t *buf; int buf_release = 0; 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; } /* mask the bits that weren't requested */ rem = bits % 8; if (rem == 0) { buf[0] = 0; } else { for (i = 8; i >= rem; i--) buf[0] = clearbit (buf[0], i); } ret = _gnutls_mpi_scan (&tmp, buf, size); if (ret < 0) { gnutls_assert (); goto cleanup; } if (buf_release != 0) { gnutls_free (buf); buf = NULL; } if (r != NULL) { _gnutls_mpi_set (r, tmp); _gnutls_mpi_release (&tmp); return r; } return tmp; cleanup: if (buf_release != 0) gnutls_free (buf); return NULL; } void _gnutls_mpi_release (bigint_t * x) { if (*x == NULL) return; _gnutls_mpi_ops.bigint_release (*x); *x = NULL; } /* returns %GNUTLS_E_SUCCESS (0) on success */ int _gnutls_mpi_scan (bigint_t * ret_mpi, const void *buffer, size_t nbytes) { *ret_mpi = _gnutls_mpi_ops.bigint_scan (buffer, nbytes, GNUTLS_MPI_FORMAT_USG); if (*ret_mpi == NULL) { gnutls_assert (); return GNUTLS_E_MPI_SCAN_FAILED; } return 0; } /* returns %GNUTLS_E_SUCCESS (0) on success. Fails if the number is zero. */ int _gnutls_mpi_scan_nz (bigint_t * ret_mpi, const void *buffer, size_t nbytes) { int ret; ret = _gnutls_mpi_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_scan_pgp (bigint_t * ret_mpi, const void *buffer, size_t nbytes) { *ret_mpi = _gnutls_mpi_ops.bigint_scan (buffer, nbytes, GNUTLS_MPI_FORMAT_PGP); if (*ret_mpi == NULL) { gnutls_assert (); return GNUTLS_E_MPI_SCAN_FAILED; } 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; } /* this function reads an integer * from asn1 structs. Combines the read and mpi_scan * steps. */ int _gnutls_x509_read_int (ASN1_TYPE node, const char *value, bigint_t * ret_mpi) { 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); } result = _gnutls_mpi_scan (ret_mpi, tmpstr, tmpstr_size); gnutls_free (tmpstr); if (result < 0) { gnutls_assert (); return result; } return 0; } /* Writes the specified integer into the specified node. */ int _gnutls_x509_write_int (ASN1_TYPE node, const char *value, bigint_t mpi, int lz) { uint8_t *tmpstr; size_t s_len; int result; s_len = 0; if (lz) result = _gnutls_mpi_print_lz (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 (lz) result = _gnutls_mpi_print_lz (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); gnutls_free (tmpstr); if (result != ASN1_SUCCESS) { gnutls_assert (); return _gnutls_asn2err (result); } return 0; }