/*
* Copyright (C) 2010-2012 Free Software Foundation, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
* This file is part of GNUTLS.
*
* The GNUTLS library 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, gmp.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TOMPZ(x) (*((mpz_t*)(x)))
static int
wrap_nettle_mpi_print (const bigint_t a, void *buffer, size_t * nbytes,
gnutls_bigint_format_t format)
{
unsigned int size;
mpz_t *p = (void *) a;
if (format == GNUTLS_MPI_FORMAT_USG)
{
size = nettle_mpz_sizeinbase_256_u (*p);
}
else if (format == GNUTLS_MPI_FORMAT_STD)
{
size = nettle_mpz_sizeinbase_256_s (*p);
}
else if (format == GNUTLS_MPI_FORMAT_PGP)
{
size = nettle_mpz_sizeinbase_256_u (*p) + 2;
}
else
{
gnutls_assert ();
return GNUTLS_E_INVALID_REQUEST;
}
if (buffer == NULL || size > *nbytes)
{
*nbytes = size;
return GNUTLS_E_SHORT_MEMORY_BUFFER;
}
if (format == GNUTLS_MPI_FORMAT_PGP)
{
uint8_t *buf = buffer;
unsigned int nbits = _gnutls_mpi_get_nbits (a);
buf[0] = (nbits >> 8) & 0xff;
buf[1] = (nbits) & 0xff;
nettle_mpz_get_str_256 (size - 2, buf + 2, *p);
}
else
{
nettle_mpz_get_str_256 (size, buffer, *p);
}
*nbytes = size;
return 0;
}
static bigint_t
wrap_nettle_mpi_new (int nbits)
{
mpz_t *p;
p = gnutls_malloc (sizeof (*p));
if (p == NULL)
{
gnutls_assert ();
return NULL;
}
if (nbits == 0)
mpz_init(*p);
else
mpz_init2 (*p, nbits);
return p;
}
static bigint_t
wrap_nettle_mpi_scan (const void *buffer, size_t nbytes,
gnutls_bigint_format_t format)
{
bigint_t r = wrap_nettle_mpi_new (nbytes * 8);
if (r == NULL)
{
gnutls_assert ();
return r;
}
if (format == GNUTLS_MPI_FORMAT_USG)
{
nettle_mpz_set_str_256_u (TOMPZ (r), nbytes, buffer);
}
else if (format == GNUTLS_MPI_FORMAT_STD)
{
nettle_mpz_set_str_256_s (TOMPZ (r), nbytes, buffer);
}
else if (format == GNUTLS_MPI_FORMAT_PGP)
{
const uint8_t *buf = buffer;
size_t size;
if (nbytes < 3)
{
gnutls_assert ();
goto fail;
}
size = (buf[0] << 8) | buf[1];
size = (size + 7) / 8;
if (size > nbytes - 2)
{
gnutls_assert ();
goto fail;
}
nettle_mpz_set_str_256_u (TOMPZ (r), size, buf + 2);
}
else
{
gnutls_assert ();
goto fail;
}
return r;
fail:
_gnutls_mpi_release (&r);
return NULL;
}
static int
wrap_nettle_mpi_cmp (const bigint_t u, const bigint_t v)
{
mpz_t *i1 = u, *i2 = v;
return mpz_cmp (*i1, *i2);
}
static int
wrap_nettle_mpi_cmp_ui (const bigint_t u, unsigned long v)
{
mpz_t *i1 = u;
return mpz_cmp_ui (*i1, v);
}
static bigint_t
wrap_nettle_mpi_set (bigint_t w, const bigint_t u)
{
mpz_t *i1, *i2 = u;
if (w == NULL)
w = _gnutls_mpi_alloc_like (u);
i1 = w;
mpz_set (*i1, *i2);
return i1;
}
static bigint_t
wrap_nettle_mpi_set_ui (bigint_t w, unsigned long u)
{
mpz_t *i1;
if (w == NULL)
w = wrap_nettle_mpi_new (32);
i1 = w;
mpz_set_ui (*i1, u);
return i1;
}
static unsigned int
wrap_nettle_mpi_get_nbits (bigint_t a)
{
return mpz_sizeinbase (TOMPZ( a), 2);
}
static void
wrap_nettle_mpi_release (bigint_t a)
{
mpz_clear (TOMPZ( a));
gnutls_free (a);
}
static void
wrap_nettle_mpi_clear (bigint_t a)
{
memset(TOMPZ(a)[0]._mp_d, 0, TOMPZ(a)[0]._mp_alloc*sizeof(mp_limb_t));
}
static bigint_t
wrap_nettle_mpi_mod (const bigint_t a, const bigint_t b)
{
bigint_t r = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (b));
if (r == NULL)
return NULL;
mpz_mod (TOMPZ( r), TOMPZ( a), TOMPZ( b));
return r;
}
static bigint_t
wrap_nettle_mpi_powm (bigint_t w, const bigint_t b, const bigint_t e,
const bigint_t m)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (m));
if (w == NULL)
return NULL;
mpz_powm (TOMPZ( w), TOMPZ( b), TOMPZ( e), TOMPZ( m));
return w;
}
static bigint_t
wrap_nettle_mpi_addm (bigint_t w, const bigint_t a, const bigint_t b,
const bigint_t m)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (a));
if (w == NULL)
return NULL;
mpz_add (TOMPZ( w), TOMPZ( b), TOMPZ( a));
mpz_fdiv_r (TOMPZ( w), TOMPZ( w), TOMPZ( m));
return w;
}
static bigint_t
wrap_nettle_mpi_subm (bigint_t w, const bigint_t a, const bigint_t b,
const bigint_t m)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (a));
if (w == NULL)
return NULL;
mpz_sub (TOMPZ( w), TOMPZ( a), TOMPZ( b));
mpz_fdiv_r (TOMPZ( w), TOMPZ( w), TOMPZ( m));
return w;
}
static bigint_t
wrap_nettle_mpi_mulm (bigint_t w, const bigint_t a, const bigint_t b,
const bigint_t m)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (m));
if (w == NULL)
return NULL;
mpz_mul (TOMPZ( w), TOMPZ( a), TOMPZ( b));
mpz_fdiv_r (TOMPZ( w), TOMPZ( w), TOMPZ( m));
return w;
}
static bigint_t
wrap_nettle_mpi_add (bigint_t w, const bigint_t a, const bigint_t b)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (b));
if (w == NULL)
return NULL;
mpz_add (TOMPZ( w), TOMPZ( a), TOMPZ( b));
return w;
}
static bigint_t
wrap_nettle_mpi_sub (bigint_t w, const bigint_t a, const bigint_t b)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (a));
if (w == NULL)
return NULL;
mpz_sub (TOMPZ( w), TOMPZ( a), TOMPZ( b));
return w;
}
static bigint_t
wrap_nettle_mpi_mul (bigint_t w, const bigint_t a, const bigint_t b)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (a));
if (w == NULL)
return NULL;
mpz_mul (TOMPZ( w), TOMPZ( a), TOMPZ( b));
return w;
}
/* q = a / b */
static bigint_t
wrap_nettle_mpi_div (bigint_t q, const bigint_t a, const bigint_t b)
{
if (q == NULL)
q = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (a));
if (q == NULL)
return NULL;
mpz_cdiv_q (TOMPZ( q), TOMPZ( a), TOMPZ( b));
return q;
}
static bigint_t
wrap_nettle_mpi_add_ui (bigint_t w, const bigint_t a, unsigned long b)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (a));
if (w == NULL)
return NULL;
mpz_add_ui (TOMPZ( w), TOMPZ( a), b);
return w;
}
static bigint_t
wrap_nettle_mpi_sub_ui (bigint_t w, const bigint_t a, unsigned long b)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (a));
if (w == NULL)
return NULL;
mpz_sub_ui (TOMPZ( w), TOMPZ( a), b);
return w;
}
static bigint_t
wrap_nettle_mpi_mul_ui (bigint_t w, const bigint_t a, unsigned long b)
{
if (w == NULL)
w = wrap_nettle_mpi_new (wrap_nettle_mpi_get_nbits (a));
if (w == NULL)
return NULL;
mpz_mul_ui (TOMPZ( w), TOMPZ( a), b);
return w;
}
static int
wrap_nettle_prime_check (bigint_t pp)
{
int ret;
ret = mpz_probab_prime_p (TOMPZ( pp), PRIME_CHECK_PARAM);
if (ret > 0)
{
return 0;
}
return GNUTLS_E_INTERNAL_ERROR; /* ignored */
}
/* generate a prime of the form p=2qw+1
* The algorithm is simple but probably it has to be modified to gcrypt's
* since it is slow. Nature did not want 2qw+1 to be prime.
* The generator will be the generator of a subgroup of order q-1.
*
* Algorithm based on the algorithm in "A Computational Introduction to Number
* Theory and Algebra" by V. Shoup, sec 11.1 Finding a generator for Z^{*}_p
*
*/
inline static int
gen_group (mpz_t * prime, mpz_t * generator, unsigned int nbits, unsigned int *q_bits)
{
mpz_t q, w, r;
unsigned int p_bytes = nbits / 8;
uint8_t *buffer = NULL;
unsigned int q_bytes, w_bytes, r_bytes, w_bits;
int ret;
/* security level enforcement.
* Values for q are selected according to ECRYPT II recommendations.
*/
q_bytes = _gnutls_pk_bits_to_subgroup_bits (nbits);
q_bytes /= 8;
if (q_bytes == 0 || q_bytes >= p_bytes)
{
gnutls_assert ();
return GNUTLS_E_INVALID_REQUEST;
}
if (nbits % 8 != 0)
p_bytes++;
w_bits = nbits - q_bytes * 8;
w_bytes = w_bits / 8;
if (w_bits % 8 != 0)
w_bytes++;
_gnutls_debug_log
("Generating group of prime of %u bits and format of 2wq+1. q_size=%u bits\n",
nbits, q_bytes * 8);
buffer = gnutls_malloc (p_bytes); /* p_bytes > q_bytes */
if (buffer == NULL)
{
gnutls_assert ();
return GNUTLS_E_MEMORY_ERROR;
}
mpz_init (q);
mpz_init (w);
mpz_init (r);
/* search for a prime. We are not that unlucky so search
* forever.
*/
for (;;)
{
ret = _gnutls_rnd (GNUTLS_RND_RANDOM, buffer, w_bytes);
if (ret < 0)
{
gnutls_assert ();
goto fail;
}
nettle_mpz_set_str_256_u (w, w_bytes, buffer);
/* always odd */
mpz_setbit (w, 0);
ret = mpz_probab_prime_p (w, PRIME_CHECK_PARAM);
if (ret > 0)
{
break;
}
}
/* now generate q of size p_bytes - w_bytes */
_gnutls_debug_log
("Found prime w of %u bits. Will look for q of %u bits...\n",
wrap_nettle_mpi_get_nbits (&w), q_bytes*8);
for (;;)
{
ret = _gnutls_rnd (GNUTLS_RND_RANDOM, buffer, q_bytes);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
nettle_mpz_set_str_256_u (q, q_bytes, buffer);
/* always odd */
mpz_setbit (q, 0);
ret = mpz_probab_prime_p (q, PRIME_CHECK_PARAM);
if (ret == 0)
{
continue;
}
/* check if 2wq+1 is prime */
mpz_mul_ui (*prime, w, 2);
mpz_mul (*prime, *prime, q);
mpz_add_ui (*prime, *prime, 1);
ret = mpz_probab_prime_p (*prime, PRIME_CHECK_PARAM);
if (ret > 0)
{
break;
}
}
*q_bits = wrap_nettle_mpi_get_nbits (&q);
_gnutls_debug_log ("Found prime q of %u bits. Looking for generator...\n",
*q_bits);
/* finally a prime! Let's calculate generator
*/
/* c = r^((p-1)/q), r == random
* c = r^(2w)
* if c!=1 c is the generator for the subgroup of order q-1
*
*/
r_bytes = p_bytes;
mpz_mul_ui (w, w, 2); /* w = w*2 */
mpz_fdiv_r (w, w, *prime);
for (;;)
{
ret = _gnutls_rnd (GNUTLS_RND_NONCE, buffer, r_bytes);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
nettle_mpz_set_str_256_u (r, r_bytes, buffer);
mpz_fdiv_r (r, r, *prime);
/* check if r^w mod n != 1 mod n */
mpz_powm (*generator, r, w, *prime);
if (mpz_cmp_ui (*generator, 1) == 0)
continue;
else
break;
}
_gnutls_debug_log ("Found generator g of %u bits\n",
wrap_nettle_mpi_get_nbits (generator));
_gnutls_debug_log ("Prime n is %u bits\n",
wrap_nettle_mpi_get_nbits (prime));
ret = 0;
goto exit;
fail:
mpz_clear (*prime);
mpz_clear (*generator);
exit:
mpz_clear (q);
mpz_clear (w);
mpz_clear (r);
gnutls_free (buffer);
return ret;
}
static int
wrap_nettle_generate_group (gnutls_group_st * group, unsigned int bits)
{
int ret;
bigint_t p = wrap_nettle_mpi_new (bits);
bigint_t g;
unsigned int q_bits;
if (p == NULL)
{
gnutls_assert ();
return GNUTLS_E_MEMORY_ERROR;
}
g = wrap_nettle_mpi_new (bits);
if (g == NULL)
{
gnutls_assert ();
_gnutls_mpi_release (&p);
return GNUTLS_E_MEMORY_ERROR;
}
ret = gen_group (p, g, bits, &q_bits);
if (ret < 0)
{
_gnutls_mpi_release (&g);
_gnutls_mpi_release (&p);
gnutls_assert ();
return ret;
}
group->p = p;
group->g = g;
group->q_bits = q_bits;
return 0;
}
int crypto_bigint_prio = INT_MAX;
gnutls_crypto_bigint_st _gnutls_mpi_ops = {
.bigint_new = wrap_nettle_mpi_new,
.bigint_cmp = wrap_nettle_mpi_cmp,
.bigint_cmp_ui = wrap_nettle_mpi_cmp_ui,
.bigint_mod = wrap_nettle_mpi_mod,
.bigint_set = wrap_nettle_mpi_set,
.bigint_set_ui = wrap_nettle_mpi_set_ui,
.bigint_get_nbits = wrap_nettle_mpi_get_nbits,
.bigint_powm = wrap_nettle_mpi_powm,
.bigint_addm = wrap_nettle_mpi_addm,
.bigint_subm = wrap_nettle_mpi_subm,
.bigint_add = wrap_nettle_mpi_add,
.bigint_sub = wrap_nettle_mpi_sub,
.bigint_add_ui = wrap_nettle_mpi_add_ui,
.bigint_sub_ui = wrap_nettle_mpi_sub_ui,
.bigint_mul = wrap_nettle_mpi_mul,
.bigint_mulm = wrap_nettle_mpi_mulm,
.bigint_mul_ui = wrap_nettle_mpi_mul_ui,
.bigint_div = wrap_nettle_mpi_div,
.bigint_prime_check = wrap_nettle_prime_check,
.bigint_release = wrap_nettle_mpi_release,
.bigint_clear = wrap_nettle_mpi_clear,
.bigint_print = wrap_nettle_mpi_print,
.bigint_scan = wrap_nettle_mpi_scan,
.bigint_generate_group = wrap_nettle_generate_group
};