📄 hostkey_gcrypt.c
字号:
/* This file is part of GNUnet. (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet 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 General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//** * @file util/hostkey_gcrypt.c * @brief public key cryptography (RSA) with libgcrypt * @author Christian Grothoff * * Note that the code locks often needlessly on the gcrypt-locking api. * One would think that simple MPI operations should not require locking * (since only global operations on the random pool must be locked, * strictly speaking). But libgcrypt does sometimes require locking in * unexpected places, so the safe solution is to always lock even if it * is not required. The performance impact is minimal anyway. */#include "platform.h"#include <gcrypt.h>#include "gnunet_util.h"#include "gnunet_util_crypto.h"#include "locking_gcrypt.h"/** * Use LOG_ERROR for anything that should never happen * (indicates a bug). */#define LOG_ERROR (GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_DEVELOPER | GNUNET_GE_BULK)/** * The private information of an RSA key pair. */struct GNUNET_RSA_PrivateKey{ gcry_sexp_t sexp;};#define HOSTKEY_LEN 2048#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS/** * Log an error message at log-level 'level' that indicates * a failure of the command 'cmd' with the message given * by gcry_strerror(rc). */#define LOG_GCRY(ectx, level, cmd, rc) do { GNUNET_GE_LOG(ectx, level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0);/** * Die with an error message that indicates * a failure of the command 'cmd' with the message given * by gcry_strerror(rc). */#define DIE_GCRY(ectx, cmd, rc) do { GNUNET_GE_LOG(ectx, GNUNET_GE_FATAL | GNUNET_GE_USER | GNUNET_GE_DEVELOPER | GNUNET_GE_IMMEDIATE, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); abort(); } while(0);/** * If target != size, move target bytes to the * end of the size-sized buffer and zero out the * first target-size bytes. */static voidadjust (unsigned char *buf, size_t size, size_t target){ if (size < target) { memmove (&buf[target - size], buf, size); memset (buf, 0, target - size); }}/** * This HostKey implementation uses RSA. */struct GNUNET_RSA_PrivateKey *GNUNET_RSA_create_key (){ struct GNUNET_RSA_PrivateKey *ret; gcry_sexp_t s_key; gcry_sexp_t s_keyparam; int rc; GNUNET_lock_gcrypt_ (); rc = gcry_sexp_build (&s_keyparam, NULL, "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))", HOSTKEY_LEN); if (rc) { LOG_GCRY (NULL, LOG_ERROR, "gcry_sexp_build", rc); GNUNET_unlock_gcrypt_ (); return NULL; } rc = gcry_pk_genkey (&s_key, s_keyparam); gcry_sexp_release (s_keyparam); if (rc) { LOG_GCRY (NULL, LOG_ERROR, "gcry_pk_genkey", rc); GNUNET_unlock_gcrypt_ (); return NULL; }#if EXTRA_CHECKS if ((rc = gcry_pk_testkey (s_key))) { LOG_GCRY (NULL, LOG_ERROR, "gcry_pk_testkey", rc); GNUNET_unlock_gcrypt_ (); return NULL; }#endif GNUNET_unlock_gcrypt_ (); ret = GNUNET_malloc (sizeof (struct GNUNET_RSA_PrivateKey)); ret->sexp = s_key; return ret;}/** * Free memory occupied by hostkey */voidGNUNET_RSA_free_key (struct GNUNET_RSA_PrivateKey *hostkey){ GNUNET_lock_gcrypt_ (); gcry_sexp_release (hostkey->sexp); GNUNET_unlock_gcrypt_ (); GNUNET_free (hostkey);}static intkey_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, const char *elems){ gcry_sexp_t list, l2; const char *s; int i, idx; GNUNET_lock_gcrypt_ (); list = gcry_sexp_find_token (sexp, topname, 0); if (!list) { GNUNET_unlock_gcrypt_ (); return 1; } l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) { GNUNET_unlock_gcrypt_ (); return 2; } idx = 0; for (s = elems; *s; s++, idx++) { l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { for (i = 0; i < idx; i++) { gcry_free (array[i]); array[i] = NULL; } gcry_sexp_release (list); GNUNET_unlock_gcrypt_ (); return 3; /* required parameter not found */ } array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[idx]) { for (i = 0; i < idx; i++) { gcry_free (array[i]); array[i] = NULL; } gcry_sexp_release (list); GNUNET_unlock_gcrypt_ (); return 4; /* required parameter is invalid */ } } gcry_sexp_release (list); GNUNET_unlock_gcrypt_ (); return 0;}/** * Extract the public key of the host. * @param hostkey the hostkey to extract into the result. * @param result where to write the result. */voidGNUNET_RSA_get_public_key (const struct GNUNET_RSA_PrivateKey *hostkey, GNUNET_RSA_PublicKey * result){ gcry_mpi_t skey[2]; size_t size; int rc; GNUNET_lock_gcrypt_ (); rc = key_from_sexp (skey, hostkey->sexp, "public-key", "ne"); if (rc) rc = key_from_sexp (skey, hostkey->sexp, "private-key", "ne"); if (rc) rc = key_from_sexp (skey, hostkey->sexp, "rsa", "ne"); if (rc) DIE_GCRY (NULL, "key_from_sexp", rc); result->len = htons (sizeof (GNUNET_RSA_PublicKey) - sizeof (result->padding)); result->sizen = htons (GNUNET_RSA_DATA_ENCODING_LEN); result->padding = 0; size = GNUNET_RSA_DATA_ENCODING_LEN; rc = gcry_mpi_print (GCRYMPI_FMT_USG, &result->key[0], size, &size, skey[0]); if (rc) DIE_GCRY (NULL, "gcry_mpi_print", rc); adjust (&result->key[0], size, GNUNET_RSA_DATA_ENCODING_LEN); size = GNUNET_RSA_KEY_LEN - GNUNET_RSA_DATA_ENCODING_LEN; rc = gcry_mpi_print (GCRYMPI_FMT_USG, &result->key[GNUNET_RSA_DATA_ENCODING_LEN], size, &size, skey[1]); if (rc) DIE_GCRY (NULL, "gcry_mpi_print", rc); adjust (&result->key[GNUNET_RSA_DATA_ENCODING_LEN], size, GNUNET_RSA_KEY_LEN - GNUNET_RSA_DATA_ENCODING_LEN); gcry_mpi_release (skey[0]); gcry_mpi_release (skey[1]); GNUNET_unlock_gcrypt_ ();}/** * Internal: publicKey => RSA-Key. * * Note that the return type is not actually a private * key but rather an sexpression for the public key! */static struct GNUNET_RSA_PrivateKey *public2PrivateKey (const GNUNET_RSA_PublicKey * publicKey){ struct GNUNET_RSA_PrivateKey *ret; gcry_sexp_t result; gcry_mpi_t n; gcry_mpi_t e; size_t size; size_t erroff; int rc; if ((ntohs (publicKey->sizen) != GNUNET_RSA_DATA_ENCODING_LEN) || (ntohs (publicKey->len) != sizeof (GNUNET_RSA_PublicKey) - sizeof (publicKey->padding))) { GNUNET_GE_BREAK (NULL, 0); return NULL; } size = GNUNET_RSA_DATA_ENCODING_LEN; GNUNET_lock_gcrypt_ (); rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, &publicKey->key[0], size, &size); if (rc) { LOG_GCRY (NULL, LOG_ERROR, "gcry_mpi_scan", rc); GNUNET_unlock_gcrypt_ (); return NULL; } size = GNUNET_RSA_KEY_LEN - GNUNET_RSA_DATA_ENCODING_LEN; rc = gcry_mpi_scan (&e, GCRYMPI_FMT_USG, &publicKey->key[GNUNET_RSA_DATA_ENCODING_LEN], size, &size); if (rc) { LOG_GCRY (NULL, LOG_ERROR, "gcry_mpi_scan", rc); gcry_mpi_release (n); GNUNET_unlock_gcrypt_ (); return NULL; } rc = gcry_sexp_build (&result, &erroff, "(public-key(rsa(n %m)(e %m)))", n, e); gcry_mpi_release (n); gcry_mpi_release (e); if (rc) { LOG_GCRY (NULL, LOG_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */ GNUNET_unlock_gcrypt_ (); return NULL; } GNUNET_unlock_gcrypt_ (); ret = GNUNET_malloc (sizeof (struct GNUNET_RSA_PrivateKey)); ret->sexp = result; return ret;}/** * Encode the private key in a format suitable for * storing it into a file. * @returns encoding of the private key. * The first 4 bytes give the size of the array, as usual. */GNUNET_RSA_PrivateKeyEncoded *GNUNET_RSA_encode_key (const struct GNUNET_RSA_PrivateKey * hostkey){ GNUNET_RSA_PrivateKeyEncoded *retval; gcry_mpi_t pkv[6]; void *pbu[6]; size_t sizes[6]; int rc; int i; int size; GNUNET_lock_gcrypt_ ();#if EXTRA_CHECKS if (gcry_pk_testkey (hostkey->sexp)) { GNUNET_GE_BREAK (NULL, 0); GNUNET_unlock_gcrypt_ (); return NULL; }#endif memset (pkv, 0, sizeof (gcry_mpi_t) * 6); rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpqu"); if (rc) rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpqu"); if (rc) rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpq"); if (rc) rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpq"); if (rc) rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "ned"); if (rc) rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "ned"); if (rc) { LOG_GCRY (NULL, LOG_ERROR, "key_from_sexp", rc); GNUNET_unlock_gcrypt_ (); return NULL; } size = sizeof (GNUNET_RSA_PrivateKeyEncoded); for (i = 0; i < 6; i++) { if (pkv[i] != NULL) { rc = gcry_mpi_aprint (GCRYMPI_FMT_USG, (unsigned char **) &pbu[i], &sizes[i], pkv[i]); size += sizes[i]; if (rc) { LOG_GCRY (NULL, LOG_ERROR, "gcry_mpi_aprint", rc); while (i > 0) if (pbu[i] != NULL) free (pbu[--i]); for (i = 0; i < 6; i++) if (pkv[i] != NULL) gcry_mpi_release (pkv[i]); GNUNET_unlock_gcrypt_ (); return NULL; } } else { pbu[i] = NULL; sizes[i] = 0; } } GNUNET_GE_ASSERT (NULL, size < 65536); retval = GNUNET_malloc (size); retval->len = htons (size); i = 0; retval->sizen = htons (sizes[0]); memcpy (&((char *) (&retval[1]))[i], pbu[0], sizes[0]); i += sizes[0]; retval->sizee = htons (sizes[1]); memcpy (&((char *) (&retval[1]))[i], pbu[1], sizes[1]); i += sizes[1]; retval->sized = htons (sizes[2]); memcpy (&((char *) (&retval[1]))[i], pbu[2], sizes[2]); i += sizes[2]; /* swap p and q! */ retval->sizep = htons (sizes[4]); memcpy (&((char *) (&retval[1]))[i], pbu[4], sizes[4]); i += sizes[4]; retval->sizeq = htons (sizes[3]); memcpy (&((char *) (&retval[1]))[i], pbu[3], sizes[3]); i += sizes[3]; retval->sizedmp1 = htons (0); retval->sizedmq1 = htons (0); memcpy (&((char *) (&retval[1]))[i], pbu[5], sizes[5]); for (i = 0; i < 6; i++) { if (pkv[i] != NULL) gcry_mpi_release (pkv[i]); if (pbu[i] != NULL) free (pbu[i]); } GNUNET_unlock_gcrypt_ (); return retval;}/** * Decode the private key from the file-format back * to the "normal", internal format. */struct GNUNET_RSA_PrivateKey *GNUNET_RSA_decode_key (const GNUNET_RSA_PrivateKeyEncoded * encoding){ struct GNUNET_RSA_PrivateKey *ret; gcry_sexp_t res; gcry_mpi_t n, e, d, p, q, u; int rc; size_t size; int pos; pos = 0; size = ntohs (encoding->sizen); GNUNET_lock_gcrypt_ (); rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, &((const unsigned char *) (&encoding[1]))[pos], size, &size); pos += ntohs (encoding->sizen); if (rc) { LOG_GCRY (NULL, LOG_ERROR, "gcry_mpi_scan", rc); GNUNET_unlock_gcrypt_ (); return NULL; } size = ntohs (encoding->sizee); rc = gcry_mpi_scan (&e, GCRYMPI_FMT_USG, &((const unsigned char *) (&encoding[1]))[pos], size, &size); pos += ntohs (encoding->sizee); if (rc) { LOG_GCRY (NULL, LOG_ERROR, "gcry_mpi_scan", rc); gcry_mpi_release (n); GNUNET_unlock_gcrypt_ (); return NULL; } size = ntohs (encoding->sized); rc = gcry_mpi_scan (&d,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -