mirror of
https://gitlab.gnome.org/GNOME/libsecret.git
synced 2024-12-22 20:58:52 +00:00
279 lines
6.5 KiB
C
279 lines
6.5 KiB
C
|
/*
|
||
|
* gnome-keyring
|
||
|
*
|
||
|
* Copyright (C) 2009 Stefan Walter
|
||
|
*
|
||
|
* This program 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 program 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 copies of the GNU General Public License and
|
||
|
* the GNU Lesser General Public License along with this program. If
|
||
|
* not, see http://www.gnu.org/licenses/.
|
||
|
*
|
||
|
* Author: Stef Walter <stefw@thewalter.net>
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include "egg-dh.h"
|
||
|
|
||
|
#include <gcrypt.h>
|
||
|
#include "egg-secure-memory.h"
|
||
|
|
||
|
/* Enabling this is a complete security compromise */
|
||
|
#define DEBUG_DH_SECRET 0
|
||
|
|
||
|
EGG_SECURE_DECLARE (dh);
|
||
|
|
||
|
struct egg_dh_params {
|
||
|
gcry_mpi_t prime;
|
||
|
gcry_mpi_t base;
|
||
|
};
|
||
|
|
||
|
struct egg_dh_pubkey {
|
||
|
gcry_mpi_t inner;
|
||
|
};
|
||
|
|
||
|
struct egg_dh_privkey {
|
||
|
gcry_mpi_t inner;
|
||
|
};
|
||
|
|
||
|
egg_dh_params *
|
||
|
egg_dh_default_params (const gchar *name)
|
||
|
{
|
||
|
const egg_dh_group *group;
|
||
|
gcry_error_t gcry;
|
||
|
gcry_mpi_t prime = NULL, base = NULL;
|
||
|
egg_dh_params *params = NULL;
|
||
|
|
||
|
g_return_val_if_fail (name, NULL);
|
||
|
|
||
|
for (group = egg_dh_groups; group->name; ++group)
|
||
|
if (g_str_equal (group->name, name))
|
||
|
break;
|
||
|
if (!group->name)
|
||
|
return NULL;
|
||
|
|
||
|
gcry = gcry_mpi_scan (&prime, GCRYMPI_FMT_USG,
|
||
|
group->prime, group->n_prime, NULL);
|
||
|
g_return_val_if_fail (gcry == 0, NULL);
|
||
|
g_return_val_if_fail (gcry_mpi_get_nbits (prime) == group->bits, NULL);
|
||
|
|
||
|
gcry = gcry_mpi_scan (&base, GCRYMPI_FMT_USG,
|
||
|
group->base, group->n_base, NULL);
|
||
|
g_return_val_if_fail (gcry == 0, NULL);
|
||
|
|
||
|
params = g_new (struct egg_dh_params, 1);
|
||
|
if (!params)
|
||
|
goto error;
|
||
|
|
||
|
params->prime = g_steal_pointer (&prime);
|
||
|
params->base = g_steal_pointer (&base);
|
||
|
|
||
|
error:
|
||
|
gcry_mpi_release (prime);
|
||
|
gcry_mpi_release (base);
|
||
|
return params;
|
||
|
}
|
||
|
|
||
|
gboolean
|
||
|
egg_dh_gen_pair (egg_dh_params *params, guint bits,
|
||
|
egg_dh_pubkey **pub, egg_dh_privkey **priv)
|
||
|
{
|
||
|
guint pbits;
|
||
|
gcry_mpi_t pub_inner = NULL, priv_inner = NULL;
|
||
|
|
||
|
g_return_val_if_fail (params, FALSE);
|
||
|
g_return_val_if_fail (pub, FALSE);
|
||
|
g_return_val_if_fail (priv, FALSE);
|
||
|
|
||
|
*pub = NULL;
|
||
|
*priv = NULL;
|
||
|
|
||
|
pbits = gcry_mpi_get_nbits (params->prime);
|
||
|
g_return_val_if_fail (pbits > 1, FALSE);
|
||
|
|
||
|
if (bits == 0) {
|
||
|
bits = pbits;
|
||
|
} else if (bits > pbits) {
|
||
|
g_return_val_if_reached (FALSE);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Generate a strong random number of bits, and not zero.
|
||
|
* gcry_mpi_randomize bumps up to the next byte. Since we
|
||
|
* need to have a value less than half of prime, we make sure
|
||
|
* we bump down.
|
||
|
*/
|
||
|
priv_inner = gcry_mpi_snew (bits);
|
||
|
g_return_val_if_fail (priv_inner, FALSE);
|
||
|
while (gcry_mpi_cmp_ui (priv_inner, 0) == 0)
|
||
|
gcry_mpi_randomize (priv_inner, bits, GCRY_STRONG_RANDOM);
|
||
|
|
||
|
/* Secret key value must be less than half of p */
|
||
|
if (gcry_mpi_get_nbits (priv_inner) > bits)
|
||
|
gcry_mpi_clear_highbit (priv_inner, bits);
|
||
|
if (gcry_mpi_get_nbits (priv_inner) > pbits - 1)
|
||
|
gcry_mpi_clear_highbit (priv_inner, pbits - 1);
|
||
|
g_assert (gcry_mpi_cmp (params->prime, priv_inner) > 0);
|
||
|
|
||
|
pub_inner = gcry_mpi_new (gcry_mpi_get_nbits (priv_inner));
|
||
|
if (!pub_inner)
|
||
|
goto error;
|
||
|
gcry_mpi_powm (pub_inner, params->base, priv_inner, params->prime);
|
||
|
|
||
|
*priv = g_new0 (struct egg_dh_privkey, 1);
|
||
|
if (!*priv)
|
||
|
goto error;
|
||
|
(*priv)->inner = g_steal_pointer (&priv_inner);
|
||
|
|
||
|
*pub = g_new0 (struct egg_dh_pubkey, 1);
|
||
|
if (!*pub)
|
||
|
goto error;
|
||
|
(*pub)->inner = g_steal_pointer (&pub_inner);
|
||
|
|
||
|
return TRUE;
|
||
|
error:
|
||
|
egg_dh_privkey_free (*priv);
|
||
|
egg_dh_pubkey_free (*pub);
|
||
|
|
||
|
gcry_mpi_release (priv_inner);
|
||
|
gcry_mpi_release (pub_inner);
|
||
|
|
||
|
g_return_val_if_reached (FALSE);
|
||
|
}
|
||
|
|
||
|
GBytes *
|
||
|
egg_dh_gen_secret (egg_dh_pubkey *peer, egg_dh_privkey *priv,
|
||
|
egg_dh_params *params)
|
||
|
{
|
||
|
gcry_error_t gcry;
|
||
|
guchar *value;
|
||
|
gsize n_prime;
|
||
|
gsize n_value;
|
||
|
gcry_mpi_t k;
|
||
|
gint bits;
|
||
|
|
||
|
g_return_val_if_fail (peer, NULL);
|
||
|
g_return_val_if_fail (priv, NULL);
|
||
|
g_return_val_if_fail (params, NULL);
|
||
|
|
||
|
bits = gcry_mpi_get_nbits (params->prime);
|
||
|
g_return_val_if_fail (bits >= 0, NULL);
|
||
|
|
||
|
k = gcry_mpi_snew (bits);
|
||
|
g_return_val_if_fail (k, NULL);
|
||
|
gcry_mpi_powm (k, peer->inner, priv->inner, params->prime);
|
||
|
|
||
|
/* Write out the secret */
|
||
|
gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_prime, params->prime);
|
||
|
g_return_val_if_fail (gcry == 0, NULL);
|
||
|
|
||
|
value = egg_secure_alloc (n_prime);
|
||
|
if (!value)
|
||
|
return NULL;
|
||
|
|
||
|
gcry = gcry_mpi_print (GCRYMPI_FMT_USG, value, n_prime, &n_value, k);
|
||
|
g_return_val_if_fail (gcry == 0, NULL);
|
||
|
|
||
|
/* Pad the secret with zero bytes to match length of prime in bytes. */
|
||
|
if (n_value < n_prime) {
|
||
|
memmove (value + (n_prime - n_value), value, n_value);
|
||
|
memset (value, 0, (n_prime - n_value));
|
||
|
}
|
||
|
|
||
|
#if DEBUG_DH_SECRET
|
||
|
g_printerr ("DH SECRET: ");
|
||
|
gcry_mpi_dump (k);
|
||
|
#endif
|
||
|
gcry_mpi_release (k);
|
||
|
|
||
|
#if DEBUG_DH_SECRET
|
||
|
gcry_mpi_scan (&k, GCRYMPI_FMT_USG, value, n_prime, NULL);
|
||
|
g_printerr ("RAW SECRET: ");
|
||
|
gcry_mpi_dump (k);
|
||
|
gcry_mpi_release (k);
|
||
|
#endif
|
||
|
|
||
|
return g_bytes_new_with_free_func (value, n_prime,
|
||
|
(GDestroyNotify)egg_secure_free,
|
||
|
value);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
egg_dh_params_free (egg_dh_params *params)
|
||
|
{
|
||
|
if (!params)
|
||
|
return;
|
||
|
gcry_mpi_release (params->prime);
|
||
|
gcry_mpi_release (params->base);
|
||
|
g_free (params);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
egg_dh_pubkey_free (egg_dh_pubkey *pubkey)
|
||
|
{
|
||
|
if (!pubkey)
|
||
|
return;
|
||
|
if (pubkey->inner)
|
||
|
gcry_mpi_release (pubkey->inner);
|
||
|
g_free (pubkey);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
egg_dh_privkey_free (egg_dh_privkey *privkey)
|
||
|
{
|
||
|
if (!privkey)
|
||
|
return;
|
||
|
if (privkey->inner)
|
||
|
gcry_mpi_release (privkey->inner);
|
||
|
g_free (privkey);
|
||
|
}
|
||
|
|
||
|
GBytes *
|
||
|
egg_dh_pubkey_export (const egg_dh_pubkey *pubkey)
|
||
|
{
|
||
|
gcry_error_t gcry;
|
||
|
unsigned char *buffer;
|
||
|
size_t n_buffer;
|
||
|
|
||
|
gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer,
|
||
|
pubkey->inner);
|
||
|
g_return_val_if_fail (gcry == 0, NULL);
|
||
|
|
||
|
return g_bytes_new_with_free_func (buffer, n_buffer,
|
||
|
gcry_free, buffer);
|
||
|
}
|
||
|
|
||
|
egg_dh_pubkey *
|
||
|
egg_dh_pubkey_new_from_bytes (const egg_dh_params *params,
|
||
|
GBytes *bytes)
|
||
|
{
|
||
|
gcry_error_t gcry;
|
||
|
gcry_mpi_t inner;
|
||
|
egg_dh_pubkey *pub;
|
||
|
|
||
|
gcry = gcry_mpi_scan (&inner, GCRYMPI_FMT_USG,
|
||
|
g_bytes_get_data (bytes, NULL),
|
||
|
g_bytes_get_size (bytes),
|
||
|
NULL);
|
||
|
if (gcry != 0)
|
||
|
return NULL;
|
||
|
|
||
|
pub = g_new (struct egg_dh_pubkey, 1);
|
||
|
if (!pub) {
|
||
|
gcry_mpi_release (inner);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pub->inner = inner;
|
||
|
return pub;
|
||
|
}
|