/* ac.c - Alternative interface for asymmetric cryptography. Copyright (C) 2003, 2004 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include "g10lib.h" #include "cipher.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 much people prefer the ac interface. */ /* Mapping of flag numbers to the according strings as it is expected for S-expressions. */ struct number_string { int number; const char *string; } gcry_ac_flags[] = { { GCRY_AC_FLAG_NO_BLINDING, "no-blinding" }, { 0, NULL }, }; /* 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. */ } gcry_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 { const char *name; /* Name of MPI value. */ 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. */ }; /* The key in `native' ac form and as an S-expression. */ struct gcry_ac_key { gcry_ac_data_t data; /* Data in native ac structure. */ gcry_sexp_t data_sexp; /* Data as an S-expression. */ gcry_ac_key_type_t type; /* Type of the key. */ }; /* Two keys. */ struct gcry_ac_key_pair { gcry_ac_key_t public; gcry_ac_key_t secret; }; /* * Primitive functions for the manipulation of `data sets'. */ /* Create a copy of the data set DATA and store it in DATA_CP. */ static gcry_err_code_t gcry_ac_data_copy_internal (gcry_ac_data_t *data_cp, gcry_ac_data_t data) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_ac_data_t data_new; void *p = NULL; int i = 0; /* Allocate data set. */ err = _gcry_malloc (sizeof (struct gcry_ac_data), 0, &p); data_new = p; if (! err) data_new->data_n = data->data_n; if (! err) /* Allocate space for named MPIs. */ err = _gcry_malloc (sizeof (gcry_ac_mpi_t) * data->data_n, 0, (void **) &data_new->data); if (! err) { /* Copy named MPIs. */ for (i = 0; i < data_new->data_n && (! err); i++) { data_new->data[i].name = NULL; data_new->data[i].mpi = NULL; /* Name. */ data_new->data[i].name = strdup (data->data[i].name); if (! data_new->data[i].name) err = gpg_err_code_from_errno (errno); if (! err) { /* MPI. */ data_new->data[i].mpi = gcry_mpi_copy (data->data[i].mpi); if (! data_new->data[i].mpi) err = gpg_err_code_from_errno (errno); } } } if (! err) { /* Copy out. */ *data_cp = data_new; } else { /* Deallocate resources. */ if (data_new) { if (data_new->data) { for (; i >= 0; i--) { if (data_new->data[i].name) free ((void *) data_new->data[i].name); if (data_new->data[i].mpi) gcry_mpi_release (data_new->data[i].mpi); } gcry_free (data_new->data); } gcry_free (data_new); } } return err; } /* * Functions for converting data between the native ac and the * S-expression structure. */ /* Extract the S-Expression DATA_SEXP into DATA under the control of TYPE and NAME. This function assumes that S-Expressions are of the following structure: (IDENTIFIER (ALGORITHM )) IDENTIFIER is one of `private-key', `public-key', `enc-val', `sig-val'; ALGORITHM is the name of the algorithm used. */ static gcry_err_code_t gcry_ac_data_extract (const char *identifier, const char *algorithm, gcry_sexp_t data_sexp, gcry_ac_data_t *data) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_sexp_t data_element_sexp = NULL; gcry_sexp_t inner_data_sexp = NULL; size_t inner_data_n; const char *name; size_t name_n; gcry_mpi_t data_elem_mpi = NULL; char *data_elem_name = NULL; gcry_ac_data_t data_new = NULL; int i = 0; /* Verify that the S-expression contains the correct identifier. */ name = gcry_sexp_nth_data (data_sexp, 0, &name_n); if (! name) err = GPG_ERR_INTERNAL; else if (strncmp (identifier, name, name_n)) err = GPG_ERR_INTERNAL; if (! err) { /* Extract inner S-expression. */ inner_data_sexp = gcry_sexp_find_token (data_sexp, algorithm, 0); if (! inner_data_sexp) err = GPG_ERR_INTERNAL; else /* Count data elements, this includes the name of the algorithm. */ inner_data_n = gcry_sexp_length (inner_data_sexp); } if (! err) { /* Allocate new data set. */ data_new = gcry_malloc (sizeof (struct gcry_ac_data)); if (! data_new) err = gpg_err_code_from_errno (errno); else { data_new->data = gcry_malloc (sizeof (gcry_ac_mpi_t) * (inner_data_n - 1)); if (! data_new->data) err = gpg_err_code_from_errno (errno); } } if (! err) { /* Iterate through list of data elements and add them to the data set. */ for (i = 1; i < inner_data_n; i++) { data_new->data[i - 1].name = NULL; data_new->data[i - 1].mpi = NULL; /* Get the S-expression of the named MPI, that contains the name and the MPI value. */ data_element_sexp = gcry_sexp_nth (inner_data_sexp, i); if (! data_element_sexp) err = GPG_ERR_INTERNAL; if (! err) { /* Extract the name. */ name = gcry_sexp_nth_data (data_element_sexp, 0, &name_n); if (! name) err = GPG_ERR_INTERNAL; } if (! err) { /* Extract the MPI value. */ data_elem_mpi = gcry_sexp_nth_mpi (data_element_sexp, 1, GCRYMPI_FMT_USG); if (! data_elem_mpi) err = GPG_ERR_INTERNAL; } if (! err) { /* Duplicate the name. */ data_elem_name = gcry_malloc (name_n + 1); if (! data_elem_name) err = gpg_err_code_from_errno (errno); else { strncpy (data_elem_name, name, name_n); data_elem_name[name_n] = 0; } } /* Done. */ if (data_element_sexp) gcry_sexp_release (data_element_sexp); if (! err) { data_new->data[i - 1].name = data_elem_name; data_new->data[i - 1].mpi = data_elem_mpi; } else break; } } if (! err) { /* Copy out. */ data_new->data_n = inner_data_n - 1; *data = data_new; } else { /* Deallocate resources. */ if (data_new) { if (data_new->data) { int j; for (j = 0; j < i - 1; j++) { if (data_new->data[j].name) gcry_free ((void *) data_new->data[j].name); if (data_new->data[j].mpi) gcry_mpi_release (data_new->data[j].mpi); } gcry_free (data_new->data); } gcry_free (data_new); } } return err; } /* Construct an S-expression from the DATA and store it in DATA_SEXP. The S-expression will be of the following structure: (IDENTIFIER [(flags [...])] (ALGORITHM )) */ static gcry_err_code_t gcry_ac_data_construct (const char *identifier, int include_flags, unsigned int flags, const char *algorithm, gcry_ac_data_t data, gcry_sexp_t *data_sexp) { gcry_err_code_t err = GPG_ERR_NO_ERROR; void **arg_list = NULL; gcry_sexp_t data_sexp_new = NULL; size_t data_format_n = 0; char *data_format = NULL; int i; /* We build a list of arguments to pass to gcry_sexp_build_array(). */ arg_list = gcry_malloc (sizeof (void *) * data->data_n); if (! arg_list) err = gpg_err_code_from_errno (errno); else /* Fill list with MPIs. */ for (i = 0; i < data->data_n; i++) arg_list[i] = (void *) &data->data[i].mpi; if (! err) { /* Calculate size of format string. */ data_format_n = (5 + (include_flags ? 7 : 0) + strlen (identifier) + strlen (algorithm)); for (i = 0; i < data->data_n; i++) { /* Per-element sizes. */ data_format_n += 4 + strlen (data->data[i].name); } if (include_flags) { /* Add flags. */ for (i = 0; gcry_ac_flags[i].number; i++) if (flags & gcry_ac_flags[i].number) data_format_n += strlen (gcry_ac_flags[i].string) + 1; } /* Done. */ data_format = gcry_malloc (data_format_n); if (! data_format) err = gpg_err_code_from_errno (errno); } if (! err) { /* Construct the format string. */ *data_format = 0; strcat (data_format, "("); strcat (data_format, identifier); if (include_flags) { strcat (data_format, "(flags"); for (i = 0; gcry_ac_flags[i].number; i++) if (flags & gcry_ac_flags[i].number) { strcat (data_format, " "); strcat (data_format, gcry_ac_flags[i].string); } strcat (data_format, ")"); } strcat (data_format, "("); strcat (data_format, algorithm); for (i = 0; i < data->data_n; i++) { strcat (data_format, "("); strcat (data_format, data->data[i].name); strcat (data_format, "%m)"); } strcat (data_format, "))"); /* Create final S-expression. */ err = gcry_sexp_build_array (&data_sexp_new, NULL, data_format, arg_list); } if (err) { /* Deallocate resources. */ if (arg_list) gcry_free (arg_list); if (data_format) gcry_free (data_format); if (data_sexp_new) gcry_sexp_release (data_sexp_new); } else /* Copy-out. */ *data_sexp = data_sexp_new; return err; } /* * Functions for working with data sets. */ /* Creates a new, empty data set and stores it in DATA. */ gcry_error_t gcry_ac_data_new (gcry_ac_data_t *data) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_ac_data_t data_new = NULL; data_new = gcry_malloc (sizeof (struct gcry_ac_data)); if (! data_new) err = gpg_err_code_from_errno (errno); if (! err) { data_new->data = NULL; data_new->data_n = 0; *data = data_new; } return gcry_error (err); } /* Destroys the data set DATA. */ void gcry_ac_data_destroy (gcry_ac_data_t data) { int i; for (i = 0; i < data->data_n; i++) { gcry_free ((void *) data->data[i].name); gcry_mpi_release (data->data[i].mpi); } gcry_free (data->data); gcry_free (data); } /* Add the value MPI to DATA with the label NAME. If FLAGS contains GCRY_AC_FLAG_DATA_COPY, the data set will contain copies of NAME and MPI. If FLAGS contains GCRY_AC_FLAG_DATA_DEALLOC or GCRY_AC_FLAG_DATA_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_err_code_t err = GPG_ERR_NO_ERROR; gcry_ac_mpi_t *ac_mpi = NULL; gcry_mpi_t mpi_add = NULL; char *name_add = NULL; unsigned int i = 0; if (flags & ~(GCRY_AC_FLAG_DEALLOC | GCRY_AC_FLAG_COPY)) err = GPG_ERR_INV_ARG; else { if (flags & GCRY_AC_FLAG_COPY) { /* Create copies. */ name_add = strdup (name); if (! name_add) err = GPG_ERR_ENOMEM; if (! err) { mpi_add = gcry_mpi_copy (mpi); if (! mpi_add) err = GPG_ERR_ENOMEM; } } else { name_add = (char *) name; mpi_add = mpi; } /* Search for existing entry. */ for (i = 0; (i < data->data_n) && (! ac_mpi); i++) if (! strcmp (name, data->data[i].name)) ac_mpi = data->data + i; if (ac_mpi) { /* An entry for NAME does already exist. */ if (ac_mpi->flags & GCRY_AC_FLAG_DEALLOC) { /* Deallocate old values. */ gcry_free ((char *) ac_mpi->name); gcry_mpi_release (ac_mpi->mpi); } } else { /* Create a new entry. */ gcry_ac_mpi_t *ac_mpis = NULL; ac_mpis = realloc (data->data, sizeof (*data->data) * (data->data_n + 1)); if (! ac_mpis) err = gpg_err_code_from_errno (errno); if (data->data != ac_mpis) data->data = ac_mpis; ac_mpi = data->data + data->data_n; data->data_n++; } ac_mpi->flags = flags; ac_mpi->name = name_add; ac_mpi->mpi = mpi_add; } return gcry_error (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_err_code_t err = GPG_ERR_NO_ERROR; err = gcry_ac_data_copy_internal (data_cp, data); return gcry_error (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; } /* Store the value labelled with NAME found in DATA in MPI. If FLAGS contains GCRY_AC_FLAG_COPY, store a copy of the MPI value contained in the data set. MPI may be NULL. */ gcry_error_t gcry_ac_data_get_name (gcry_ac_data_t data, unsigned int flags, const char *name, gcry_mpi_t *mpi) { gcry_err_code_t err = GPG_ERR_NO_DATA; gcry_mpi_t mpi_found = NULL; unsigned int i = 0; if (flags & ~(GCRY_AC_FLAG_COPY)) err = GPG_ERR_INV_ARG; else { for (i = 0; i < data->data_n && (! mpi_found); i++) if (! strcmp (data->data[i].name, name)) { if (flags & GCRY_AC_FLAG_COPY) { mpi_found = gcry_mpi_copy (data->data[i].mpi); if (! mpi_found) err = GPG_ERR_ENOMEM; } else mpi_found = data->data[i].mpi; if (mpi_found) err = GPG_ERR_NO_ERROR; } } if (! err) if (mpi) *mpi = mpi_found; return gcry_error (err); } /* Stores in NAME and MPI the named MPI value contained in the data set DATA with the index IDX. If FLAGS contains GCRY_AC_FLAG_COPY, store copies of the values contained in the data set. NAME or MPI may be NULL. */ 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_err_code_t err = GPG_ERR_NO_ERROR; gcry_mpi_t mpi_return = NULL; char *name_return = NULL; if (flags & ~(GCRY_AC_FLAG_COPY)) err = GPG_ERR_INV_ARG; else { if (idx < data->data_n) { if (flags & GCRY_AC_FLAG_COPY) { /* Return copies to the user. */ if (name) name_return = strdup (data->data[idx].name); if (mpi) mpi_return = gcry_mpi_copy (data->data[idx].mpi); if (! (name_return && mpi_return)) { if (name_return) free (name_return); if (mpi_return) gcry_mpi_release (mpi_return); err = GPG_ERR_ENOMEM; } } else { name_return = (char *) data->data[idx].name; mpi_return = data->data[idx].mpi; } } else err = GPG_ERR_NO_DATA; } if (! err) { if (name) *name = name_return; if (mpi) *mpi = mpi_return; } return gcry_error (err); } /* Destroys any values contained in the data set DATA. */ void gcry_ac_data_clear (gcry_ac_data_t data) { gcry_free (data->data); data->data = NULL; data->data_n = 0; } /* * Handle management. */ /* Creates a new handle for the algorithm ALGORITHM and store it in HANDLE. FLAGS is not used yet. */ gcry_error_t gcry_ac_open (gcry_ac_handle_t *handle, gcry_ac_id_t algorithm, unsigned int flags) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_module_t module = NULL; gcry_ac_handle_t handle_new; const char *algorithm_name; *handle = NULL; /* Get name. */ algorithm_name = _gcry_pk_aliased_algo_name (algorithm); if (! *algorithm_name) err = GPG_ERR_PUBKEY_ALGO; if (! err) /* Acquire reference to the pubkey module. */ err = _gcry_pk_module_lookup (algorithm, &module); if (! err) { /* Allocate. */ handle_new = gcry_malloc (sizeof (struct gcry_ac_handle)); if (! handle_new) err = gpg_err_code_from_errno (errno); } if (! err) { /* Done. */ handle_new->algorithm = algorithm; handle_new->algorithm_name = algorithm_name; handle_new->flags = flags; handle_new->module = module; *handle = handle_new; } else { /* Deallocate resources. */ if (module) _gcry_pk_module_release (module); } return gcry_error (err); } /* Destroys the handle HANDLE. */ void gcry_ac_close (gcry_ac_handle_t handle) { /* Release reference to pubkey module. */ if (handle) { _gcry_pk_module_release (handle->module); gcry_free (handle); } } /* * Key management. */ /* Creates a new key of type TYPE, consisting of the MPI values contained in the data set DATA and stores it in KEY. */ gcry_error_t gcry_ac_key_init (gcry_ac_key_t *key, gcry_ac_handle_t handle, gcry_ac_key_type_t type, gcry_ac_data_t data) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_ac_data_t data_new = NULL; gcry_sexp_t data_sexp = NULL; gcry_ac_key_t key_new = NULL; /* Allocate. */ key_new = gcry_malloc (sizeof (struct gcry_ac_key)); if (! key_new) err = gpg_err_code_from_errno (errno); if (! err) { /* Create S-expression from data set. */ err = gcry_ac_data_construct (ac_key_identifiers[type], 0, 0, handle->algorithm_name, data, &data_sexp); } if (! err) { /* Copy data set. */ err = gcry_ac_data_copy_internal (&data_new, data); } if (! err) { /* Done. */ key_new->data_sexp = data_sexp; key_new->data = data_new; key_new->type = type; *key = key_new; } else { /* Deallocate resources. */ if (key_new) gcry_free (key_new); if (data_sexp) gcry_sexp_release (data_sexp); } return gcry_error (err); } /* Generates a new key pair via the handle HANDLE of NBITS bits and stores it in KEY_PAIR. In case non-standard settings are wanted, a pointer to a structure of type gcry_ac_key_spec__t, matching the selected algorithm, can be given as KEY_SPEC. MISC_DATA is not used yet. */ gcry_error_t gcry_ac_key_pair_generate (gcry_ac_handle_t handle, unsigned int nbits, void *key_spec, gcry_ac_key_pair_t *key_pair, gcry_mpi_t **misc_data) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_ac_key_pair_t key_pair_new = NULL; gcry_sexp_t genkey_sexp_request = NULL; gcry_sexp_t genkey_sexp_reply = NULL; char *genkey_format = NULL; size_t genkey_format_n = 0; void **arg_list = NULL; size_t arg_list_n = 0; unsigned int i = 0; /* Allocate key pair. */ key_pair_new = gcry_malloc (sizeof (struct gcry_ac_key_pair)); if (! key_pair_new) err = gpg_err_code_from_errno (errno); if (! err) { /* Allocate keys. */ key_pair_new->secret = gcry_malloc (sizeof (struct gcry_ac_key)); key_pair_new->public = gcry_malloc (sizeof (struct gcry_ac_key)); if (! (key_pair_new->secret || key_pair_new->public)) err = gpg_err_code_from_errno (errno); else { key_pair_new->secret->type = GCRY_AC_KEY_SECRET; key_pair_new->public->type = GCRY_AC_KEY_PUBLIC; key_pair_new->secret->data_sexp = NULL; key_pair_new->public->data_sexp = NULL; key_pair_new->secret->data = NULL; key_pair_new->public->data = NULL; } } if (! err) { /* Calculate size of the format string, that is used for creating the request S-expression. */ genkey_format_n = 23; /* Respect any relevant algorithm specific commands. */ if (key_spec) for (i = 0; gcry_ac_key_generate_specs[i].algorithm; i++) if (handle->algorithm == gcry_ac_key_generate_specs[i].algorithm) genkey_format_n += 6; /* Create format string. */ genkey_format = gcry_malloc (genkey_format_n); if (! genkey_format) err = gpg_err_code_from_errno (errno); else { /* Fill format string. */ *genkey_format = 0; strcat (genkey_format, "(genkey(%s(nbits%d)"); if (key_spec) for (i = 0; gcry_ac_key_generate_specs[i].algorithm; i++) if (handle->algorithm == gcry_ac_key_generate_specs[i].algorithm) strcat (genkey_format, "(%s%m)"); strcat (genkey_format, "))"); } } if (! err) { /* Build list of argument pointers, the algorithm name and the nbits are needed always. */ arg_list_n = 2; /* Now the algorithm specific arguments. */ if (key_spec) for (i = 0; gcry_ac_key_generate_specs[i].algorithm; i++) if (handle->algorithm == gcry_ac_key_generate_specs[i].algorithm) arg_list_n += 2; /* Allocate list. */ arg_list = gcry_malloc (sizeof (void *) * arg_list_n); if (! arg_list) err = gpg_err_code_from_errno (errno); else { /* Fill argument list. */ int j; arg_list[0] = (void *) &handle->algorithm_name; arg_list[1] = (void *) &nbits; if (key_spec) for (j = 2, i = 0; gcry_ac_key_generate_specs[i].algorithm; i++) if (handle->algorithm == gcry_ac_key_generate_specs[i].algorithm) { /* Add name of this specification flag and the according member of the spec strucuture. */ arg_list[j++] = (void *)(&gcry_ac_key_generate_specs[i].name); arg_list[j++] = (void *) (((char *) key_spec) + gcry_ac_key_generate_specs[i].offset); } } } if (! err) /* Construct final request S-expression. */ err = gcry_err_code (gcry_sexp_build_array (&genkey_sexp_request, NULL, genkey_format, arg_list)); if (! err) /* Perform genkey operation. */ err = gcry_err_code (gcry_pk_genkey (&genkey_sexp_reply, genkey_sexp_request)); /* Split keys. */ if (! err) { key_pair_new->secret->data_sexp = gcry_sexp_find_token (genkey_sexp_reply, "private-key", 0); if (! key_pair_new->secret->data_sexp) err = GPG_ERR_INTERNAL; } if (! err) { key_pair_new->public->data_sexp = gcry_sexp_find_token (genkey_sexp_reply, "public-key", 0); if (! key_pair_new->public->data_sexp) err = GPG_ERR_INTERNAL; } /* Extract key material. */ if (! err) err = gcry_ac_data_extract ("private-key", handle->algorithm_name, key_pair_new->secret->data_sexp, &key_pair_new->secret->data); if (! err) err = gcry_ac_data_extract ("public-key", handle->algorithm_name, key_pair_new->public->data_sexp, &key_pair_new->public->data); /* Done. */ if (! err) *key_pair = key_pair_new; else { /* Deallocate resources. */ if (key_pair_new) { if (key_pair_new->secret) gcry_ac_key_destroy (key_pair_new->secret); if (key_pair_new->public) gcry_ac_key_destroy (key_pair_new->public); gcry_free (key_pair_new); } if (arg_list) gcry_free (arg_list); if (genkey_format) gcry_free (genkey_format); if (genkey_sexp_request) gcry_sexp_release (genkey_sexp_request); if (genkey_sexp_reply) gcry_sexp_release (genkey_sexp_reply); } return gcry_error (err); } /* Returns the key of type WHICH out of the key pair KEY_PAIR. */ gcry_ac_key_t gcry_ac_key_pair_extract (gcry_ac_key_pair_t key_pair, gcry_ac_key_type_t witch) { gcry_ac_key_t key = NULL; switch (witch) { case GCRY_AC_KEY_SECRET: key = key_pair->secret; break; case GCRY_AC_KEY_PUBLIC: key = key_pair->public; break; } return key; } /* Destroys the key KEY. */ void gcry_ac_key_destroy (gcry_ac_key_t key) { int i; if (key) { if (key->data) { for (i = 0; i < key->data->data_n; i++) if (key->data->data[i].mpi != NULL) gcry_mpi_release (key->data->data[i].mpi); gcry_free (key->data); } if (key->data_sexp) gcry_sexp_release (key->data_sexp); gcry_free (key); } } /* Destroys the key pair KEY_PAIR. */ void gcry_ac_key_pair_destroy (gcry_ac_key_pair_t key_pair) { if (key_pair) { gcry_ac_key_destroy (key_pair->secret); gcry_ac_key_destroy (key_pair->public); gcry_free (key_pair); } } /* Returns the data set contained in the key KEY. */ gcry_ac_data_t gcry_ac_key_data_get (gcry_ac_key_t key) { return key->data; } /* Verifies that the key KEY is sane via HANDLE. */ gcry_error_t gcry_ac_key_test (gcry_ac_handle_t handle, gcry_ac_key_t key) { gcry_err_code_t err; err = gcry_err_code (gcry_pk_testkey (key->data_sexp)); return gcry_error (err); } /* Stores the number of bits of the key KEY in NBITS via HANDLE. */ gcry_error_t gcry_ac_key_get_nbits (gcry_ac_handle_t handle, gcry_ac_key_t key, unsigned int *nbits) { gcry_err_code_t err = GPG_ERR_NO_ERROR; unsigned int n; n = gcry_pk_get_nbits (key->data_sexp); if (n) *nbits = n; else err = GPG_ERR_PUBKEY_ALGO; return gcry_error (err); } /* Writes the 20 byte long key grip of the key KEY to KEY_GRIP via HANDLE. */ gcry_error_t gcry_ac_key_get_grip (gcry_ac_handle_t handle, gcry_ac_key_t key, unsigned char *key_grip) { gcry_err_code_t err = GPG_ERR_NO_ERROR; unsigned char *ret; ret = gcry_pk_get_keygrip (key->data_sexp, key_grip); if (! ret) err = GPG_ERR_INV_OBJ; return gcry_error (err); } /* * Functions performing cryptographic operations. */ /* Encrypts the plain text MPI value DATA_PLAIN with the key public KEY under the control of the flags FLAGS and stores the resulting data set into DATA_ENCRYPTED. */ gcry_error_t gcry_ac_data_encrypt (gcry_ac_handle_t handle, unsigned int flags, gcry_ac_key_t key, gcry_mpi_t data_plain, gcry_ac_data_t *data_encrypted) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_sexp_t sexp_request = NULL; gcry_sexp_t sexp_reply = NULL; char *request_format = NULL; size_t request_format_n = 0; gcry_ac_data_t data; int i; if (key->type != GCRY_AC_KEY_PUBLIC) err = GPG_ERR_WRONG_KEY_USAGE; if (! err) { /* Calculate request format string. */ request_format_n += 23; for (i = 0; gcry_ac_flags[i].number; i++) if (flags & gcry_ac_flags[i].number) request_format_n += strlen (gcry_ac_flags[i].string) + 1; /* Allocate request format string. */ request_format = gcry_malloc (request_format_n); if (! request_format) err = gpg_err_code_from_errno (errno); } if (! err) { /* Fill format string. */ *request_format = 0; strcat (request_format, "(data(flags"); for (i = 0; gcry_ac_flags[i].number; i++) if (flags & gcry_ac_flags[i].number) { strcat (request_format, " "); strcat (request_format, gcry_ac_flags[i].string); } strcat (request_format, ")(value%m))"); /* Create S-expression. */ err = gcry_sexp_build (&sexp_request, NULL, request_format, data_plain); } if (! err) /* Encrypt. */ err = gcry_pk_encrypt (&sexp_reply, sexp_request, key->data_sexp); if (! err) /* Extract data. */ err = gcry_ac_data_extract ("enc-val", handle->algorithm_name, sexp_reply, &data); /* Deallocate resources. */ if (sexp_request) gcry_sexp_release (sexp_request); if (sexp_reply) gcry_sexp_release (sexp_reply); if (! err) /* Copy out. */ *data_encrypted = data; return gcry_error (err); } /* Decrypts the encrypted data contained in the data set DATA_ENCRYPTED with the secret key KEY under the control of the flags FLAGS and stores the resulting plain text MPI value in DATA_PLAIN. */ gcry_error_t gcry_ac_data_decrypt (gcry_ac_handle_t handle, unsigned int flags, gcry_ac_key_t key, gcry_mpi_t *data_plain, gcry_ac_data_t data_encrypted) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_mpi_t data_decrypted = NULL; gcry_sexp_t sexp_request = NULL; gcry_sexp_t sexp_reply = NULL; if (key->type != GCRY_AC_KEY_SECRET) err = GPG_ERR_WRONG_KEY_USAGE; if (! err) /* Create S-expression from data. */ err = gcry_ac_data_construct ("enc-val", 1, flags, handle->algorithm_name, data_encrypted, &sexp_request); if (! err) /* Decrypt. */ err = gcry_pk_decrypt (&sexp_reply, sexp_request, key->data_sexp); if (! err) { /* Extract plain text. */ gcry_sexp_t l; l = gcry_sexp_find_token (sexp_reply, "value", 0); if (! l) err = GPG_ERR_GENERAL; else { data_decrypted = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG); if (! data_decrypted) err = GPG_ERR_GENERAL; gcry_sexp_release (l); } } /* Done. */ if (err) { /* Deallocate resources. */ if (sexp_request) gcry_sexp_release (sexp_request); if (sexp_reply) gcry_sexp_release (sexp_reply); } else *data_plain = data_decrypted; return gcry_error (err); } /* Signs the data contained in DATA with the secret key KEY and stores the resulting signature data set in DATA_SIGNATURE. */ gcry_error_t gcry_ac_data_sign (gcry_ac_handle_t handle, gcry_ac_key_t key, gcry_mpi_t data, gcry_ac_data_t *data_signature) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_sexp_t sexp_request = NULL; gcry_sexp_t sexp_reply = NULL; gcry_ac_data_t ac_data; if (key->type != GCRY_AC_KEY_SECRET) err = GPG_ERR_WRONG_KEY_USAGE; if (! err) /* Create S-expression holding the data. */ err = gcry_sexp_build (&sexp_request, NULL, "(data(flags)(value%m))", data); if (! err) /* Sign. */ err = gcry_pk_sign (&sexp_reply, sexp_request, key->data_sexp); if (! err) /* Extract data. */ err = gcry_ac_data_extract ("sig-val", handle->algorithm_name, sexp_reply, &ac_data); /* Done. */ if (sexp_request) gcry_sexp_release (sexp_request); if (sexp_reply) gcry_sexp_release (sexp_reply); if (! err) *data_signature = ac_data; return gcry_error (err); } /* Verifies that the signature contained in the data set DATA_SIGNATURE is indeed the result of signing the data contained in DATA with the secret key belonging to the public key KEY. */ gcry_error_t gcry_ac_data_verify (gcry_ac_handle_t handle, gcry_ac_key_t key, gcry_mpi_t data, gcry_ac_data_t data_signature) { gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_sexp_t sexp_request = NULL; gcry_sexp_t sexp_data = NULL; if (key->type != GCRY_AC_KEY_PUBLIC) err = GPG_ERR_WRONG_KEY_USAGE; if (! err) /* Construct S-expression holding the signature data. */ err = gcry_ac_data_construct ("sig-val", 1, 0, handle->algorithm_name, data_signature, &sexp_request); if (! err) /* Construct S-expression holding the data. */ err = gcry_sexp_build (&sexp_data, NULL, "(data(flags)(value%m))", data); if (! err) /* Verify signature. */ err = gcry_pk_verify (sexp_request, sexp_data, key->data_sexp); /* Done. */ if (sexp_request) gcry_sexp_release (sexp_request); if (sexp_data) gcry_sexp_release (sexp_data); return gcry_error (err); } /* * General functions. */ /* Stores the textual representation of the algorithm whose id is given in ALGORITHM in NAME. */ gcry_error_t gcry_ac_id_to_name (gcry_ac_id_t algorithm, const char **name) { gcry_err_code_t err = GPG_ERR_NO_ERROR; const char *n; n = gcry_pk_algo_name (algorithm); if (*n) *name = n; else err = GPG_ERR_PUBKEY_ALGO; return gcry_error (err); } /* Stores the numeric ID of the algorithm whose textual representation is contained in NAME in ALGORITHM. */ gcry_error_t gcry_ac_name_to_id (const char *name, gcry_ac_id_t *algorithm) { gcry_err_code_t err = GPG_ERR_NO_ERROR; int algo; algo = gcry_pk_map_name (name); if (algo) *algorithm = algo; else err = GPG_ERR_PUBKEY_ALGO; return gcry_error (err); }