diff options
Diffstat (limited to 'lib/mpi.c')
-rw-r--r-- | lib/mpi.c | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/lib/mpi.c b/lib/mpi.c new file mode 100644 index 0000000..be6c29d --- /dev/null +++ b/lib/mpi.c @@ -0,0 +1,484 @@ +/* + * 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 <https://www.gnu.org/licenses/> + * + */ + +/* 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 <libtasn1.h> +#include "errors.h" +#include <num.h> +#include <mpi.h> +#include <random.h> +#include <x509/x509_int.h> + +/* 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_node 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_node 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_node 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_node 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_node 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_node 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_node 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_node node, const char *value, bigint_t mpi) +{ + return __gnutls_x509_write_int(node, value, mpi, + GNUTLS_X509_INT_OVERWRITE | + GNUTLS_X509_INT_LE); +} |