/* ac.c - Alternative interface for asymmetric cryptography. Copyright (C) 2003, 2004, 2005, 2006 2007, 2008 Free Software Foundation, Inc. This file is part of Libgcrypt. Libgcrypt 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. Libgcrypt 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 . */ #include #include #include #include #include #include #include "g10lib.h" #include "cipher.h" #include "mpi.h" /* At the moment the ac interface is a wrapper around the pk interface, but this might change somewhen in the future, depending on how many people prefer the ac interface. */ /* Mapping of flag numbers to the according strings as it is expected for S-expressions. */ static struct number_string { int number; const char *string; } ac_flags[] = { { GCRY_AC_FLAG_NO_BLINDING, "no-blinding" }, }; /* The positions in this list correspond to the values contained in the gcry_ac_key_type_t enumeration list. */ static const char *ac_key_identifiers[] = { "private-key", "public-key" }; /* These specifications are needed for key-pair generation; the caller is allowed to pass additional, algorithm-specific `specs' to gcry_ac_key_pair_generate. This list is used for decoding the provided values according to the selected algorithm. */ struct gcry_ac_key_generate_spec { int algorithm; /* Algorithm for which this flag is relevant. */ const char *name; /* Name of this flag. */ size_t offset; /* Offset in the cipher-specific spec structure at which the MPI value associated with this flag is to be found. */ } ac_key_generate_specs[] = { { GCRY_AC_RSA, "rsa-use-e", offsetof (gcry_ac_key_spec_rsa_t, e) }, { 0 } }; /* Handle structure. */ struct gcry_ac_handle { int algorithm; /* Algorithm ID associated with this handle. */ const char *algorithm_name; /* Name of the algorithm. */ unsigned int flags; /* Flags, not used yet. */ gcry_module_t module; /* Reference to the algorithm module. */ }; /* A named MPI value. */ typedef struct gcry_ac_mpi { char *name; /* Self-maintained copy of name. */ gcry_mpi_t mpi; /* MPI value. */ unsigned int flags; /* Flags. */ } gcry_ac_mpi_t; /* A data set, that is simply a list of named MPI values. */ struct gcry_ac_data { gcry_ac_mpi_t *data; /* List of named values. */ unsigned int data_n; /* Number of values in DATA. */ }; /* A single key. */ struct gcry_ac_key { gcry_ac_data_t data; /* Data in native ac structure. */ gcry_ac_key_type_t type; /* Type of the key. */ }; /* A key pair. */ struct gcry_ac_key_pair { gcry_ac_key_t public; gcry_ac_key_t secret; }; /* * Functions for working with data sets. */ /* Creates a new, empty data set and store it in DATA. */ gcry_error_t _gcry_ac_data_new (gcry_ac_data_t *data) { gcry_ac_data_t data_new; gcry_error_t err; if (fips_mode ()) return gpg_error (GPG_ERR_NOT_SUPPORTED); data_new = gcry_malloc (sizeof (*data_new)); if (! data_new) { err = gcry_error_from_errno (errno); goto out; } data_new->data = NULL; data_new->data_n = 0; *data = data_new; err = 0; out: return err; } /* Destroys all the entries in DATA, but not DATA itself. */ static void ac_data_values_destroy (gcry_ac_data_t data) { unsigned int i; for (i = 0; i < data->data_n; i++) if (data->data[i].flags & GCRY_AC_FLAG_DEALLOC) { gcry_mpi_release (data->data[i].mpi); gcry_free (data->data[i].name); } } /* Destroys the data set DATA. */ void _gcry_ac_data_destroy (gcry_ac_data_t data) { if (data) { ac_data_values_destroy (data); gcry_free (data->data); gcry_free (data); } } /* This function creates a copy of the array of named MPIs DATA_MPIS, which is of length DATA_MPIS_N; the copy is stored in DATA_MPIS_CP. */ static gcry_error_t ac_data_mpi_copy (gcry_ac_mpi_t *data_mpis, unsigned int data_mpis_n, gcry_ac_mpi_t **data_mpis_cp) { gcry_ac_mpi_t *data_mpis_new; gcry_error_t err; unsigned int i; gcry_mpi_t mpi; char *label; data_mpis_new = gcry_malloc (sizeof (*data_mpis_new) * data_mpis_n); if (! data_mpis_new) { err = gcry_error_from_errno (errno); goto out; } memset (data_mpis_new, 0, sizeof (*data_mpis_new) * data_mpis_n); err = 0; for (i = 0; i < data_mpis_n; i++) { /* Copy values. */ label = gcry_strdup (data_mpis[i].name); mpi = gcry_mpi_copy (data_mpis[i].mpi); if (! (label && mpi)) { err = gcry_error_from_errno (errno); gcry_mpi_release (mpi); gcry_free (label); break; } data_mpis_new[i].flags = GCRY_AC_FLAG_DEALLOC; data_mpis_new[i].name = label; data_mpis_new[i].mpi = mpi; } if (err) goto out; *data_mpis_cp = data_mpis_new; err = 0; out: if (err) if (data_mpis_new) { for (i = 0; i < data_mpis_n; i++) { gcry_mpi_release (data_mpis_new[i].mpi); gcry_free (data_mpis_new[i].name); } gcry_free (data_mpis_new); } return err; } /* Create a copy of the data set DATA and store it in DATA_CP. */ gcry_error_t _gcry_ac_data_copy (gcry_ac_data_t *data_cp, gcry_ac_data_t data) { gcry_ac_mpi_t *data_mpis = NULL; gcry_ac_data_t data_new; gcry_error_t err; if (fips_mode ()) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Allocate data set. */ data_new = gcry_malloc (sizeof (*data_new)); if (! data_new) { err = gcry_error_from_errno (errno); goto out; } err = ac_data_mpi_copy (data->data, data->data_n, &data_mpis); if (err) goto out; data_new->data_n = data->data_n; data_new->data = data_mpis; *data_cp = data_new; out: if (err) gcry_free (data_new); return err; } /* Returns the number of named MPI values inside of the data set DATA. */ unsigned int _gcry_ac_data_length (gcry_ac_data_t data) { return data->data_n; } /* Add the value MPI to DATA with the label NAME. If FLAGS contains GCRY_AC_FLAG_COPY, the data set will contain copies of NAME and MPI. If FLAGS contains GCRY_AC_FLAG_DEALLOC or GCRY_AC_FLAG_COPY, the values contained in the data set will be deallocated when they are to be removed from the data set. */ gcry_error_t _gcry_ac_data_set (gcry_ac_data_t data, unsigned int flags, const char *name, gcry_mpi_t mpi) { gcry_error_t err; gcry_mpi_t mpi_cp; char *name_cp; unsigned int i; name_cp = NULL; mpi_cp = NULL; if (fips_mode ()) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (flags & ~(GCRY_AC_FLAG_DEALLOC | GCRY_AC_FLAG_COPY)) { err = gcry_error (GPG_ERR_INV_ARG); goto out; } if (flags & GCRY_AC_FLAG_COPY) { /* Create copies. */ flags |= GCRY_AC_FLAG_DEALLOC; name_cp = gcry_strdup (name); mpi_cp = gcry_mpi_copy (mpi); if (! (name_cp && mpi_cp)) { err = gcry_error_from_errno (errno); goto out; } } /* Search for existing entry. */ for (i = 0; i < data->data_n; i++) if (! strcmp (name, data->data[i].name)) break; if (i < data->data_n) { /* An entry for NAME does already exist. */ if (data->data[i].flags & GCRY_AC_FLAG_DEALLOC) { gcry_mpi_release (data->data[i].mpi); gcry_free (data->data[i].name); } } else { /* Create a new entry. */ gcry_ac_mpi_t *ac_mpis; ac_mpis = gcry_realloc (data->data, sizeof (*data->data) * (data->data_n + 1)); if (! ac_mpis) { err = gcry_error_from_errno (errno); goto out; } if (data->data != ac_mpis) data->data = ac_mpis; data->data_n++; } data->data[i].name = name_cp ? name_cp : ((char *) name); data->data[i].mpi = mpi_cp ? mpi_cp : mpi; data->data[i].flags = flags; err = 0; out: if (err) { gcry_mpi_release (mpi_cp); gcry_free (name_cp); } return err; } /* Stores the value labelled with NAME found in the data set DATA in MPI. The returned MPI value will be released in case gcry_ac_data_set is used to associate the label NAME with a different MPI value. */ gcry_error_t _gcry_ac_data_get_name (gcry_ac_data_t data, unsigned int flags, const char *name, gcry_mpi_t *mpi) { gcry_mpi_t mpi_return; gcry_error_t err; unsigned int i; if (fips_mode ()) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (flags & ~(GCRY_AC_FLAG_COPY)) { err = gcry_error (GPG_ERR_INV_ARG); goto out; } for (i = 0; i < data->data_n; i++) if (! strcmp (name, data->data[i].name)) break; if (i == data->data_n) { err = gcry_error (GPG_ERR_NOT_FOUND); goto out; } if (flags & GCRY_AC_FLAG_COPY) { mpi_return = gcry_mpi_copy (data->data[i].mpi); if (! mpi_return) { err = gcry_error_from_errno (errno); /* FIXME? */ goto out; } } else mpi_return = data->data[i].mpi; *mpi = mpi_return; err = 0; out: return err; } /* Stores in NAME and MPI the named MPI value contained in the data set DATA with the index IDX. NAME or MPI may be NULL. The returned MPI value will be released in case gcry_ac_data_set is used to associate the label NAME with a different MPI value. */ gcry_error_t _gcry_ac_data_get_index (gcry_ac_data_t data, unsigned int flags, unsigned int idx, const char **name, gcry_mpi_t *mpi) { gcry_error_t err; gcry_mpi_t mpi_cp; char *name_cp; name_cp = NULL; mpi_cp = NULL; if (fips_mode ()) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (flags & ~(GCRY_AC_FLAG_COPY)) { err = gcry_error (GPG_ERR_INV_ARG); goto out; } if (idx >= data->data_n) { err = gcry_error (GPG_ERR_INV_ARG); goto out; } if (flags & GCRY_AC_FLAG_COPY) { /* Return copies to the user. */ if (name) { name_cp = gcry_strdup (data->data[idx].name); if (! name_cp) { err = gcry_error_from_errno (errno); goto out; } } if (mpi) { mpi_cp = gcry_mpi_copy (data->data[idx].mpi); if (! mpi_cp) { err = gcry_error_from_errno (errno); goto out; } } } if (name) *name = name_cp ? name_cp : data->data[idx].name; if (mpi) *mpi = mpi_cp ? mpi_cp : data->data[idx].mpi; err = 0; out: if (err) { gcry_mpi_release (mpi_cp); gcry_free (name_cp); } return err; } /* Convert the data set DATA into a new S-Expression, which is to be stored in SEXP, according to the identifiers contained in IDENTIFIERS. */ gcry_error_t _gcry_ac_data_to_sexp (gcry_ac_data_t data, gcry_sexp_t *sexp, const char **identifiers) { gcry_sexp_t sexp_new; gcry_error_t err; char *sexp_buffer; size_t sexp_buffer_n; size_t identifiers_n; const char *label; gcry_mpi_t mpi; void **arg_list; size_t data_n; unsigned int i; sexp_buffer_n = 1; sexp_buffer = NULL; arg_list = NULL; err = 0; if (fips_mode ()) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Calculate size of S-expression representation. */ i = 0; if (identifiers) while (identifiers[i]) { /* For each identifier, we add "()". */ sexp_buffer_n += 1 + strlen (identifiers[i]) + 1; i++; } identifiers_n = i; if (! identifiers_n) /* If there are NO identifiers, we still add surrounding braces so that we have a list of named MPI value lists. Otherwise it wouldn't be too much fun to process these lists. */ sexp_buffer_n += 2; data_n = _gcry_ac_data_length (data); for (i = 0; i < data_n; i++) { err = gcry_ac_data_get_index (data, 0, i, &label, NULL); if (err) break; /* For each MPI we add "(