| /* 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 <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <config.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stddef.h> |
| |
| #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 "(<IDENTIFIER>)". */ |
| 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 "(<LABEL> %m)". */ |
| sexp_buffer_n += 1 + strlen (label) + 4; |
| } |
| if (err) |
| goto out; |
| |
| /* Allocate buffer. */ |
| |
| sexp_buffer = gcry_malloc (sexp_buffer_n); |
| if (! sexp_buffer) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Fill buffer. */ |
| |
| *sexp_buffer = 0; |
| sexp_buffer_n = 0; |
| |
| /* Add identifiers: (<IDENTIFIER0>(<IDENTIFIER1>...)). */ |
| if (identifiers_n) |
| { |
| /* Add nested identifier lists as usual. */ |
| for (i = 0; i < identifiers_n; i++) |
| sexp_buffer_n += sprintf (sexp_buffer + sexp_buffer_n, "(%s", |
| identifiers[i]); |
| } |
| else |
| { |
| /* Add special list. */ |
| sexp_buffer_n += sprintf (sexp_buffer + sexp_buffer_n, "("); |
| } |
| |
| /* Add MPI list. */ |
| arg_list = gcry_malloc (sizeof (*arg_list) * (data_n + 1)); |
| if (! arg_list) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| for (i = 0; i < data_n; i++) |
| { |
| err = gcry_ac_data_get_index (data, 0, i, &label, &mpi); |
| if (err) |
| break; |
| sexp_buffer_n += sprintf (sexp_buffer + sexp_buffer_n, |
| "(%s %%m)", label); |
| arg_list[i] = &data->data[i].mpi; |
| } |
| if (err) |
| goto out; |
| |
| if (identifiers_n) |
| { |
| /* Add closing braces for identifier lists as usual. */ |
| for (i = 0; i < identifiers_n; i++) |
| sexp_buffer_n += sprintf (sexp_buffer + sexp_buffer_n, ")"); |
| } |
| else |
| { |
| /* Add closing braces for special list. */ |
| sexp_buffer_n += sprintf (sexp_buffer + sexp_buffer_n, ")"); |
| } |
| |
| /* Construct. */ |
| err = gcry_sexp_build_array (&sexp_new, NULL, sexp_buffer, arg_list); |
| if (err) |
| goto out; |
| |
| *sexp = sexp_new; |
| |
| out: |
| |
| gcry_free (sexp_buffer); |
| gcry_free (arg_list); |
| |
| return err; |
| } |
| |
| /* Create a new data set, which is to be stored in DATA_SET, from the |
| S-Expression SEXP, according to the identifiers contained in |
| IDENTIFIERS. */ |
| gcry_error_t |
| _gcry_ac_data_from_sexp (gcry_ac_data_t *data_set, gcry_sexp_t sexp, |
| const char **identifiers) |
| { |
| gcry_ac_data_t data_set_new; |
| gcry_error_t err; |
| gcry_sexp_t sexp_cur; |
| gcry_sexp_t sexp_tmp; |
| gcry_mpi_t mpi; |
| char *string; |
| const char *data; |
| size_t data_n; |
| size_t sexp_n; |
| unsigned int i; |
| int skip_name; |
| |
| data_set_new = NULL; |
| sexp_cur = sexp; |
| sexp_tmp = NULL; |
| string = NULL; |
| mpi = NULL; |
| err = 0; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| /* Process S-expression/identifiers. */ |
| |
| if (identifiers) |
| { |
| for (i = 0; identifiers[i]; i++) |
| { |
| /* Next identifier. Extract first data item from |
| SEXP_CUR. */ |
| data = gcry_sexp_nth_data (sexp_cur, 0, &data_n); |
| |
| if (! ((data_n == strlen (identifiers[i])) |
| && (! strncmp (data, identifiers[i], data_n)))) |
| { |
| /* Identifier mismatch -> error. */ |
| err = gcry_error (GPG_ERR_INV_SEXP); |
| break; |
| } |
| |
| /* Identifier matches. Now we have to distinguish two |
| cases: |
| |
| (i) we are at the last identifier: |
| leave loop |
| |
| (ii) we are not at the last identifier: |
| extract next element, which is supposed to be a |
| sublist. */ |
| |
| if (! identifiers[i + 1]) |
| /* Last identifier. */ |
| break; |
| else |
| { |
| /* Not the last identifier, extract next sublist. */ |
| |
| sexp_tmp = gcry_sexp_nth (sexp_cur, 1); |
| if (! sexp_tmp) |
| { |
| /* Missing sublist. */ |
| err = gcry_error (GPG_ERR_INV_SEXP); |
| break; |
| } |
| |
| /* Release old SEXP_CUR, in case it is not equal to the |
| original SEXP. */ |
| if (sexp_cur != sexp) |
| gcry_sexp_release (sexp_cur); |
| |
| /* Make SEXP_CUR point to the new current sublist. */ |
| sexp_cur = sexp_tmp; |
| sexp_tmp = NULL; |
| } |
| } |
| if (err) |
| goto out; |
| |
| if (i) |
| { |
| /* We have at least one identifier in the list, this means |
| the the list of named MPI values is prefixed, this means |
| that we need to skip the first item (the list name), when |
| processing the MPI values. */ |
| skip_name = 1; |
| } |
| else |
| { |
| /* Since there is no identifiers list, the list of named MPI |
| values is not prefixed with a list name, therefore the |
| offset to use is zero. */ |
| skip_name = 0; |
| } |
| } |
| else |
| /* Since there is no identifiers list, the list of named MPI |
| values is not prefixed with a list name, therefore the offset |
| to use is zero. */ |
| skip_name = 0; |
| |
| /* Create data set from S-expression data. */ |
| |
| err = gcry_ac_data_new (&data_set_new); |
| if (err) |
| goto out; |
| |
| /* Figure out amount of named MPIs in SEXP_CUR. */ |
| if (sexp_cur) |
| sexp_n = gcry_sexp_length (sexp_cur) - skip_name; |
| else |
| sexp_n = 0; |
| |
| /* Extracte the named MPIs sequentially. */ |
| for (i = 0; i < sexp_n; i++) |
| { |
| /* Store next S-Expression pair, which is supposed to consist of |
| a name and an MPI value, in SEXP_TMP. */ |
| |
| sexp_tmp = gcry_sexp_nth (sexp_cur, i + skip_name); |
| if (! sexp_tmp) |
| { |
| err = gcry_error (GPG_ERR_INV_SEXP); |
| break; |
| } |
| |
| /* Extract name from current S-Expression pair. */ |
| data = gcry_sexp_nth_data (sexp_tmp, 0, &data_n); |
| string = gcry_malloc (data_n + 1); |
| if (! string) |
| { |
| err = gcry_error_from_errno (errno); |
| break; |
| } |
| memcpy (string, data, data_n); |
| string[data_n] = 0; |
| |
| /* Extract MPI value. */ |
| mpi = gcry_sexp_nth_mpi (sexp_tmp, 1, 0); |
| if (! mpi) |
| { |
| err = gcry_error (GPG_ERR_INV_SEXP); /* FIXME? */ |
| break; |
| } |
| |
| /* Store named MPI in data_set_new. */ |
| err = gcry_ac_data_set (data_set_new, GCRY_AC_FLAG_DEALLOC, string, mpi); |
| if (err) |
| break; |
| |
| /* gcry_free (string); */ |
| string = NULL; |
| /* gcry_mpi_release (mpi); */ |
| mpi = NULL; |
| |
| gcry_sexp_release (sexp_tmp); |
| sexp_tmp = NULL; |
| } |
| if (err) |
| goto out; |
| |
| *data_set = data_set_new; |
| |
| out: |
| |
| if (sexp_cur != sexp) |
| gcry_sexp_release (sexp_cur); |
| gcry_sexp_release (sexp_tmp); |
| gcry_mpi_release (mpi); |
| gcry_free (string); |
| |
| if (err) |
| gcry_ac_data_destroy (data_set_new); |
| |
| return err; |
| } |
| |
| |
| static void |
| _gcry_ac_data_dump (const char *prefix, gcry_ac_data_t data) |
| { |
| unsigned char *mpi_buffer; |
| size_t mpi_buffer_n; |
| unsigned int data_n; |
| gcry_error_t err; |
| const char *name; |
| gcry_mpi_t mpi; |
| unsigned int i; |
| |
| if (! data) |
| return; |
| |
| if (fips_mode ()) |
| return; |
| |
| mpi_buffer = NULL; |
| |
| data_n = _gcry_ac_data_length (data); |
| for (i = 0; i < data_n; i++) |
| { |
| err = gcry_ac_data_get_index (data, 0, i, &name, &mpi); |
| if (err) |
| { |
| log_error ("failed to dump data set"); |
| break; |
| } |
| |
| err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &mpi_buffer, &mpi_buffer_n, mpi); |
| if (err) |
| { |
| log_error ("failed to dump data set"); |
| break; |
| } |
| |
| log_printf ("%s%s%s: %s\n", |
| prefix ? prefix : "", |
| prefix ? ": " : "" |
| , name, mpi_buffer); |
| |
| gcry_free (mpi_buffer); |
| mpi_buffer = NULL; |
| } |
| |
| gcry_free (mpi_buffer); |
| } |
| |
| /* Dump the named MPI values contained in the data set DATA to |
| Libgcrypt's logging stream. */ |
| void |
| gcry_ac_data_dump (const char *prefix, gcry_ac_data_t data) |
| { |
| _gcry_ac_data_dump (prefix, data); |
| } |
| |
| /* Destroys any values contained in the data set DATA. */ |
| void |
| _gcry_ac_data_clear (gcry_ac_data_t data) |
| { |
| ac_data_values_destroy (data); |
| gcry_free (data->data); |
| data->data = NULL; |
| data->data_n = 0; |
| } |
| |
| |
| |
| /* |
| * Implementation of `ac io' objects. |
| */ |
| |
| /* Initialize AC_IO according to MODE, TYPE and the variable list of |
| arguments AP. The list of variable arguments to specify depends on |
| the given TYPE. */ |
| void |
| _gcry_ac_io_init_va (gcry_ac_io_t *ac_io, |
| gcry_ac_io_mode_t mode, gcry_ac_io_type_t type, va_list ap) |
| { |
| memset (ac_io, 0, sizeof (*ac_io)); |
| |
| if (fips_mode ()) |
| return; |
| |
| gcry_assert ((mode == GCRY_AC_IO_READABLE) || (mode == GCRY_AC_IO_WRITABLE)); |
| gcry_assert ((type == GCRY_AC_IO_STRING) || (type == GCRY_AC_IO_STRING)); |
| |
| ac_io->mode = mode; |
| ac_io->type = type; |
| |
| switch (mode) |
| { |
| case GCRY_AC_IO_READABLE: |
| switch (type) |
| { |
| case GCRY_AC_IO_STRING: |
| ac_io->io.readable.string.data = va_arg (ap, unsigned char *); |
| ac_io->io.readable.string.data_n = va_arg (ap, size_t); |
| break; |
| |
| case GCRY_AC_IO_CALLBACK: |
| ac_io->io.readable.callback.cb = va_arg (ap, gcry_ac_data_read_cb_t); |
| ac_io->io.readable.callback.opaque = va_arg (ap, void *); |
| break; |
| } |
| break; |
| case GCRY_AC_IO_WRITABLE: |
| switch (type) |
| { |
| case GCRY_AC_IO_STRING: |
| ac_io->io.writable.string.data = va_arg (ap, unsigned char **); |
| ac_io->io.writable.string.data_n = va_arg (ap, size_t *); |
| break; |
| |
| case GCRY_AC_IO_CALLBACK: |
| ac_io->io.writable.callback.cb = va_arg (ap, gcry_ac_data_write_cb_t); |
| ac_io->io.writable.callback.opaque = va_arg (ap, void *); |
| break; |
| } |
| break; |
| } |
| } |
| |
| /* Initialize AC_IO according to MODE, TYPE and the variable list of |
| arguments. The list of variable arguments to specify depends on |
| the given TYPE. */ |
| void |
| _gcry_ac_io_init (gcry_ac_io_t *ac_io, |
| gcry_ac_io_mode_t mode, gcry_ac_io_type_t type, ...) |
| { |
| va_list ap; |
| |
| va_start (ap, type); |
| _gcry_ac_io_init_va (ac_io, mode, type, ap); |
| va_end (ap); |
| } |
| |
| |
| /* Write to the IO object AC_IO BUFFER_N bytes from BUFFER. Return |
| zero on success or error code. */ |
| static gcry_error_t |
| _gcry_ac_io_write (gcry_ac_io_t *ac_io, unsigned char *buffer, size_t buffer_n) |
| { |
| gcry_error_t err; |
| |
| gcry_assert (ac_io->mode == GCRY_AC_IO_WRITABLE); |
| err = 0; |
| |
| switch (ac_io->type) |
| { |
| case GCRY_AC_IO_STRING: |
| { |
| unsigned char *p; |
| |
| if (*ac_io->io.writable.string.data) |
| { |
| p = gcry_realloc (*ac_io->io.writable.string.data, |
| *ac_io->io.writable.string.data_n + buffer_n); |
| if (! p) |
| err = gcry_error_from_errno (errno); |
| else |
| { |
| if (*ac_io->io.writable.string.data != p) |
| *ac_io->io.writable.string.data = p; |
| memcpy (p + *ac_io->io.writable.string.data_n, buffer, buffer_n); |
| *ac_io->io.writable.string.data_n += buffer_n; |
| } |
| } |
| else |
| { |
| if (gcry_is_secure (buffer)) |
| p = gcry_malloc_secure (buffer_n); |
| else |
| p = gcry_malloc (buffer_n); |
| if (! p) |
| err = gcry_error_from_errno (errno); |
| else |
| { |
| memcpy (p, buffer, buffer_n); |
| *ac_io->io.writable.string.data = p; |
| *ac_io->io.writable.string.data_n = buffer_n; |
| } |
| } |
| } |
| break; |
| |
| case GCRY_AC_IO_CALLBACK: |
| err = (*ac_io->io.writable.callback.cb) (ac_io->io.writable.callback.opaque, |
| buffer, buffer_n); |
| break; |
| } |
| |
| return err; |
| } |
| |
| /* Read *BUFFER_N bytes from the IO object AC_IO into BUFFER; NREAD |
| bytes have already been read from the object; on success, store the |
| amount of bytes read in *BUFFER_N; zero bytes read means EOF. |
| Return zero on success or error code. */ |
| static gcry_error_t |
| _gcry_ac_io_read (gcry_ac_io_t *ac_io, |
| unsigned int nread, unsigned char *buffer, size_t *buffer_n) |
| { |
| gcry_error_t err; |
| |
| gcry_assert (ac_io->mode == GCRY_AC_IO_READABLE); |
| err = 0; |
| |
| switch (ac_io->type) |
| { |
| case GCRY_AC_IO_STRING: |
| { |
| size_t bytes_available; |
| size_t bytes_to_read; |
| size_t bytes_wanted; |
| |
| bytes_available = ac_io->io.readable.string.data_n - nread; |
| bytes_wanted = *buffer_n; |
| |
| if (bytes_wanted > bytes_available) |
| bytes_to_read = bytes_available; |
| else |
| bytes_to_read = bytes_wanted; |
| |
| memcpy (buffer, ac_io->io.readable.string.data + nread, bytes_to_read); |
| *buffer_n = bytes_to_read; |
| err = 0; |
| break; |
| } |
| |
| case GCRY_AC_IO_CALLBACK: |
| err = (*ac_io->io.readable.callback.cb) |
| (ac_io->io.readable.callback.opaque, buffer, buffer_n); |
| break; |
| } |
| |
| return err; |
| } |
| |
| /* Read all data available from the IO object AC_IO into newly |
| allocated memory, storing an appropriate pointer in *BUFFER and the |
| amount of bytes read in *BUFFER_N. Return zero on success or error |
| code. */ |
| static gcry_error_t |
| _gcry_ac_io_read_all (gcry_ac_io_t *ac_io, unsigned char **buffer, size_t *buffer_n) |
| { |
| unsigned char *buffer_new; |
| size_t buffer_new_n; |
| unsigned char buf[BUFSIZ]; |
| size_t buf_n; |
| unsigned char *p; |
| gcry_error_t err; |
| |
| buffer_new = NULL; |
| buffer_new_n = 0; |
| |
| while (1) |
| { |
| buf_n = sizeof (buf); |
| err = _gcry_ac_io_read (ac_io, buffer_new_n, buf, &buf_n); |
| if (err) |
| break; |
| |
| if (buf_n) |
| { |
| p = gcry_realloc (buffer_new, buffer_new_n + buf_n); |
| if (! p) |
| { |
| err = gcry_error_from_errno (errno); |
| break; |
| } |
| |
| if (buffer_new != p) |
| buffer_new = p; |
| |
| memcpy (buffer_new + buffer_new_n, buf, buf_n); |
| buffer_new_n += buf_n; |
| } |
| else |
| break; |
| } |
| if (err) |
| goto out; |
| |
| *buffer_n = buffer_new_n; |
| *buffer = buffer_new; |
| |
| out: |
| |
| if (err) |
| gcry_free (buffer_new); |
| |
| return err; |
| } |
| |
| /* Read data chunks from the IO object AC_IO until EOF, feeding them |
| to the callback function CB. Return zero on success or error |
| code. */ |
| static gcry_error_t |
| _gcry_ac_io_process (gcry_ac_io_t *ac_io, |
| gcry_ac_data_write_cb_t cb, void *opaque) |
| { |
| unsigned char buffer[BUFSIZ]; |
| unsigned int nread; |
| size_t buffer_n; |
| gcry_error_t err; |
| |
| nread = 0; |
| |
| while (1) |
| { |
| buffer_n = sizeof (buffer); |
| err = _gcry_ac_io_read (ac_io, nread, buffer, &buffer_n); |
| if (err) |
| break; |
| if (buffer_n) |
| { |
| err = (*cb) (opaque, buffer, buffer_n); |
| if (err) |
| break; |
| nread += buffer_n; |
| } |
| else |
| break; |
| } |
| |
| return err; |
| } |
| |
| |
| |
| /* |
| * Functions for converting data between the native ac and the |
| * S-expression structure used by the pk interface. |
| */ |
| |
| /* 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 <list of named MPI values>)) */ |
| static gcry_error_t |
| ac_data_extract (const char *identifier, const char *algorithm, |
| gcry_sexp_t sexp, gcry_ac_data_t *data) |
| { |
| gcry_error_t err; |
| gcry_sexp_t value_sexp; |
| gcry_sexp_t data_sexp; |
| size_t data_sexp_n; |
| gcry_mpi_t value_mpi; |
| char *value_name; |
| const char *data_raw; |
| size_t data_raw_n; |
| gcry_ac_data_t data_new; |
| unsigned int i; |
| |
| value_sexp = NULL; |
| data_sexp = NULL; |
| value_name = NULL; |
| value_mpi = NULL; |
| data_new = NULL; |
| |
| /* Verify that the S-expression contains the correct identifier. */ |
| data_raw = gcry_sexp_nth_data (sexp, 0, &data_raw_n); |
| if ((! data_raw) || strncmp (identifier, data_raw, data_raw_n)) |
| { |
| err = gcry_error (GPG_ERR_INV_SEXP); |
| goto out; |
| } |
| |
| /* Extract inner S-expression. */ |
| data_sexp = gcry_sexp_find_token (sexp, algorithm, 0); |
| if (! data_sexp) |
| { |
| err = gcry_error (GPG_ERR_INV_SEXP); |
| goto out; |
| } |
| |
| /* Count data elements. */ |
| data_sexp_n = gcry_sexp_length (data_sexp); |
| data_sexp_n--; |
| |
| /* Allocate new data set. */ |
| err = _gcry_ac_data_new (&data_new); |
| if (err) |
| goto out; |
| |
| /* Iterate through list of data elements and add them to the data |
| set. */ |
| for (i = 0; i < data_sexp_n; i++) |
| { |
| /* Get the S-expression of the named MPI, that contains the name |
| and the MPI value. */ |
| value_sexp = gcry_sexp_nth (data_sexp, i + 1); |
| if (! value_sexp) |
| { |
| err = gcry_error (GPG_ERR_INV_SEXP); |
| break; |
| } |
| |
| /* Extract the name. */ |
| data_raw = gcry_sexp_nth_data (value_sexp, 0, &data_raw_n); |
| if (! data_raw) |
| { |
| err = gcry_error (GPG_ERR_INV_SEXP); |
| break; |
| } |
| |
| /* Extract the MPI value. */ |
| value_mpi = gcry_sexp_nth_mpi (value_sexp, 1, GCRYMPI_FMT_USG); |
| if (! value_mpi) |
| { |
| err = gcry_error (GPG_ERR_INTERNAL); /* FIXME? */ |
| break; |
| } |
| |
| /* Duplicate the name. */ |
| value_name = gcry_malloc (data_raw_n + 1); |
| if (! value_name) |
| { |
| err = gcry_error_from_errno (errno); |
| break; |
| } |
| strncpy (value_name, data_raw, data_raw_n); |
| value_name[data_raw_n] = 0; |
| |
| err = _gcry_ac_data_set (data_new, GCRY_AC_FLAG_DEALLOC, value_name, value_mpi); |
| if (err) |
| break; |
| |
| gcry_sexp_release (value_sexp); |
| value_sexp = NULL; |
| value_name = NULL; |
| value_mpi = NULL; |
| } |
| if (err) |
| goto out; |
| |
| /* Copy out. */ |
| *data = data_new; |
| |
| out: |
| |
| /* Deallocate resources. */ |
| if (err) |
| { |
| _gcry_ac_data_destroy (data_new); |
| gcry_mpi_release (value_mpi); |
| gcry_free (value_name); |
| gcry_sexp_release (value_sexp); |
| } |
| gcry_sexp_release (data_sexp); |
| |
| 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 <list of named MPI values>)) */ |
| static gcry_error_t |
| ac_data_construct (const char *identifier, int include_flags, |
| unsigned int flags, const char *algorithm, |
| gcry_ac_data_t data, gcry_sexp_t *sexp) |
| { |
| unsigned int data_length; |
| gcry_sexp_t sexp_new; |
| gcry_error_t err; |
| size_t sexp_format_n; |
| char *sexp_format; |
| void **arg_list; |
| unsigned int i; |
| |
| arg_list = NULL; |
| sexp_new = NULL; |
| sexp_format = NULL; |
| |
| /* We build a list of arguments to pass to |
| gcry_sexp_build_array(). */ |
| data_length = _gcry_ac_data_length (data); |
| arg_list = gcry_malloc (sizeof (*arg_list) * (data_length * 2)); |
| if (! arg_list) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Fill list with MPIs. */ |
| for (i = 0; i < data_length; i++) |
| { |
| char **nameaddr = &data->data[i].name; |
| |
| arg_list[(i * 2) + 0] = nameaddr; |
| arg_list[(i * 2) + 1] = &data->data[i].mpi; |
| } |
| |
| /* Calculate size of format string. */ |
| sexp_format_n = (3 |
| + (include_flags ? 7 : 0) |
| + (algorithm ? (2 + strlen (algorithm)) : 0) |
| + strlen (identifier)); |
| |
| for (i = 0; i < data_length; i++) |
| /* Per-element sizes. */ |
| sexp_format_n += 6; |
| |
| if (include_flags) |
| /* Add flags. */ |
| for (i = 0; i < DIM (ac_flags); i++) |
| if (flags & ac_flags[i].number) |
| sexp_format_n += strlen (ac_flags[i].string) + 1; |
| |
| /* Done. */ |
| sexp_format = gcry_malloc (sexp_format_n); |
| if (! sexp_format) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Construct the format string. */ |
| |
| *sexp_format = 0; |
| strcat (sexp_format, "("); |
| strcat (sexp_format, identifier); |
| if (include_flags) |
| { |
| strcat (sexp_format, "(flags"); |
| for (i = 0; i < DIM (ac_flags); i++) |
| if (flags & ac_flags[i].number) |
| { |
| strcat (sexp_format, " "); |
| strcat (sexp_format, ac_flags[i].string); |
| } |
| strcat (sexp_format, ")"); |
| } |
| if (algorithm) |
| { |
| strcat (sexp_format, "("); |
| strcat (sexp_format, algorithm); |
| } |
| for (i = 0; i < data_length; i++) |
| strcat (sexp_format, "(%s%m)"); |
| if (algorithm) |
| strcat (sexp_format, ")"); |
| strcat (sexp_format, ")"); |
| |
| /* Create final S-expression. */ |
| err = gcry_sexp_build_array (&sexp_new, NULL, sexp_format, arg_list); |
| if (err) |
| goto out; |
| |
| *sexp = sexp_new; |
| |
| out: |
| |
| /* Deallocate resources. */ |
| gcry_free (sexp_format); |
| gcry_free (arg_list); |
| if (err) |
| gcry_sexp_release (sexp_new); |
| |
| return err; |
| } |
| |
| |
| |
| /* |
| * Handle management. |
| */ |
| |
| /* Creates a new handle for the algorithm ALGORITHM and stores 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_ac_handle_t handle_new; |
| const char *algorithm_name; |
| gcry_module_t module; |
| gcry_error_t err; |
| |
| *handle = NULL; |
| module = NULL; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| /* Get name. */ |
| algorithm_name = _gcry_pk_aliased_algo_name (algorithm); |
| if (! algorithm_name) |
| { |
| err = gcry_error (GPG_ERR_PUBKEY_ALGO); |
| goto out; |
| } |
| |
| /* Acquire reference to the pubkey module. */ |
| err = _gcry_pk_module_lookup (algorithm, &module); |
| if (err) |
| goto out; |
| |
| /* Allocate. */ |
| handle_new = gcry_malloc (sizeof (*handle_new)); |
| if (! handle_new) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Done. */ |
| handle_new->algorithm = algorithm; |
| handle_new->algorithm_name = algorithm_name; |
| handle_new->flags = flags; |
| handle_new->module = module; |
| *handle = handle_new; |
| |
| out: |
| |
| /* Deallocate resources. */ |
| if (err) |
| _gcry_pk_module_release (module); |
| |
| return 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. |
| */ |
| |
| /* Initialize a key from a given data set. */ |
| /* FIXME/Damn: the argument HANDLE is not only unnecessary, it is |
| completely WRONG here. */ |
| 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_ac_data_t data_new; |
| gcry_ac_key_t key_new; |
| gcry_error_t err; |
| |
| (void)handle; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| /* Allocate. */ |
| key_new = gcry_malloc (sizeof (*key_new)); |
| if (! key_new) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Copy data set. */ |
| err = _gcry_ac_data_copy (&data_new, data); |
| if (err) |
| goto out; |
| |
| /* Done. */ |
| key_new->data = data_new; |
| key_new->type = type; |
| *key = key_new; |
| |
| out: |
| |
| if (err) |
| /* Deallocate resources. */ |
| gcry_free (key_new); |
| |
| return 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_<algorithm>_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_sexp_t genkey_sexp_request; |
| gcry_sexp_t genkey_sexp_reply; |
| gcry_ac_data_t key_data_secret; |
| gcry_ac_data_t key_data_public; |
| gcry_ac_key_pair_t key_pair_new; |
| gcry_ac_key_t key_secret; |
| gcry_ac_key_t key_public; |
| gcry_sexp_t key_sexp; |
| gcry_error_t err; |
| char *genkey_format; |
| size_t genkey_format_n; |
| void **arg_list; |
| size_t arg_list_n; |
| unsigned int i; |
| unsigned int j; |
| |
| (void)misc_data; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| key_data_secret = NULL; |
| key_data_public = NULL; |
| key_secret = NULL; |
| key_public = NULL; |
| genkey_format = NULL; |
| arg_list = NULL; |
| genkey_sexp_request = NULL; |
| genkey_sexp_reply = NULL; |
| key_sexp = NULL; |
| |
| /* Allocate key pair. */ |
| key_pair_new = gcry_malloc (sizeof (struct gcry_ac_key_pair)); |
| if (! key_pair_new) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Allocate keys. */ |
| key_secret = gcry_malloc (sizeof (*key_secret)); |
| if (! key_secret) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| key_public = gcry_malloc (sizeof (*key_public)); |
| if (! key_public) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Calculate size of the format string, that is used for creating |
| the request S-expression. */ |
| genkey_format_n = 22; |
| |
| /* Respect any relevant algorithm specific commands. */ |
| if (key_spec) |
| for (i = 0; i < DIM (ac_key_generate_specs); i++) |
| if (handle->algorithm == ac_key_generate_specs[i].algorithm) |
| genkey_format_n += 6; |
| |
| /* Create format string. */ |
| genkey_format = gcry_malloc (genkey_format_n); |
| if (! genkey_format) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Fill format string. */ |
| *genkey_format = 0; |
| strcat (genkey_format, "(genkey(%s(nbits%d)"); |
| if (key_spec) |
| for (i = 0; i < DIM (ac_key_generate_specs); i++) |
| if (handle->algorithm == ac_key_generate_specs[i].algorithm) |
| strcat (genkey_format, "(%s%m)"); |
| strcat (genkey_format, "))"); |
| |
| /* Build list of argument pointers, the algorithm name and the nbits |
| are always needed. */ |
| arg_list_n = 2; |
| |
| /* Now the algorithm specific arguments. */ |
| if (key_spec) |
| for (i = 0; i < DIM (ac_key_generate_specs); i++) |
| if (handle->algorithm == ac_key_generate_specs[i].algorithm) |
| arg_list_n += 2; |
| |
| /* Allocate list. */ |
| arg_list = gcry_malloc (sizeof (*arg_list) * arg_list_n); |
| if (! arg_list) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| arg_list[0] = (void *) &handle->algorithm_name; |
| arg_list[1] = (void *) &nbits; |
| if (key_spec) |
| for (j = 2, i = 0; i < DIM (ac_key_generate_specs); i++) |
| if (handle->algorithm == ac_key_generate_specs[i].algorithm) |
| { |
| /* Add name of this specification flag and the |
| according member of the spec strucuture. */ |
| arg_list[j++] = (void *)(&ac_key_generate_specs[i].name); |
| arg_list[j++] = (void *) |
| (((char *) key_spec) |
| + ac_key_generate_specs[i].offset); |
| /* FIXME: above seems to suck. */ |
| } |
| |
| /* Construct final request S-expression. */ |
| err = gcry_sexp_build_array (&genkey_sexp_request, |
| NULL, genkey_format, arg_list); |
| if (err) |
| goto out; |
| |
| /* Perform genkey operation. */ |
| err = gcry_pk_genkey (&genkey_sexp_reply, genkey_sexp_request); |
| if (err) |
| goto out; |
| |
| key_sexp = gcry_sexp_find_token (genkey_sexp_reply, "private-key", 0); |
| if (! key_sexp) |
| { |
| err = gcry_error (GPG_ERR_INTERNAL); |
| goto out; |
| } |
| err = ac_data_extract ("private-key", handle->algorithm_name, |
| key_sexp, &key_data_secret); |
| if (err) |
| goto out; |
| |
| gcry_sexp_release (key_sexp); |
| key_sexp = gcry_sexp_find_token (genkey_sexp_reply, "public-key", 0); |
| if (! key_sexp) |
| { |
| err = gcry_error (GPG_ERR_INTERNAL); |
| goto out; |
| } |
| err = ac_data_extract ("public-key", handle->algorithm_name, |
| key_sexp, &key_data_public); |
| if (err) |
| goto out; |
| |
| /* Done. */ |
| |
| key_secret->type = GCRY_AC_KEY_SECRET; |
| key_secret->data = key_data_secret; |
| key_public->type = GCRY_AC_KEY_PUBLIC; |
| key_public->data = key_data_public; |
| key_pair_new->secret = key_secret; |
| key_pair_new->public = key_public; |
| *key_pair = key_pair_new; |
| |
| out: |
| |
| /* Deallocate resources. */ |
| |
| gcry_free (genkey_format); |
| gcry_free (arg_list); |
| gcry_sexp_release (genkey_sexp_request); |
| gcry_sexp_release (genkey_sexp_reply); |
| gcry_sexp_release (key_sexp); |
| if (err) |
| { |
| _gcry_ac_data_destroy (key_data_secret); |
| _gcry_ac_data_destroy (key_data_public); |
| gcry_free (key_secret); |
| gcry_free (key_public); |
| gcry_free (key_pair_new); |
| } |
| |
| return 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 which) |
| { |
| gcry_ac_key_t key; |
| |
| if (fips_mode ()) |
| return NULL; |
| |
| switch (which) |
| { |
| case GCRY_AC_KEY_SECRET: |
| key = key_pair->secret; |
| break; |
| |
| case GCRY_AC_KEY_PUBLIC: |
| key = key_pair->public; |
| break; |
| |
| default: |
| key = NULL; |
| break; |
| } |
| |
| return key; |
| } |
| |
| /* Destroys the key KEY. */ |
| void |
| _gcry_ac_key_destroy (gcry_ac_key_t key) |
| { |
| unsigned int i; |
| |
| if (key) |
| { |
| if (key->data) |
| { |
| for (i = 0; i < key->data->data_n; i++) |
| { |
| if (key->data->data[i].mpi) |
| gcry_mpi_release (key->data->data[i].mpi); |
| if (key->data->data[i].name) |
| gcry_free (key->data->data[i].name); |
| } |
| gcry_free (key->data->data); |
| gcry_free (key->data); |
| } |
| 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) |
| { |
| if (fips_mode ()) |
| return NULL; |
| 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_sexp_t key_sexp; |
| gcry_error_t err; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| key_sexp = NULL; |
| err = ac_data_construct (ac_key_identifiers[key->type], 0, 0, |
| handle->algorithm_name, key->data, &key_sexp); |
| if (err) |
| goto out; |
| |
| err = gcry_pk_testkey (key_sexp); |
| |
| out: |
| |
| gcry_sexp_release (key_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_sexp_t key_sexp; |
| gcry_error_t err; |
| unsigned int n; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| key_sexp = NULL; |
| |
| err = ac_data_construct (ac_key_identifiers[key->type], |
| 0, 0, handle->algorithm_name, key->data, &key_sexp); |
| if (err) |
| goto out; |
| |
| n = gcry_pk_get_nbits (key_sexp); |
| if (! n) |
| { |
| err = gcry_error (GPG_ERR_PUBKEY_ALGO); |
| goto out; |
| } |
| |
| *nbits = n; |
| |
| out: |
| |
| gcry_sexp_release (key_sexp); |
| |
| return 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_sexp_t key_sexp; |
| gcry_error_t err; |
| unsigned char *ret; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| key_sexp = NULL; |
| err = ac_data_construct (ac_key_identifiers[key->type], 0, 0, |
| handle->algorithm_name, key->data, &key_sexp); |
| if (err) |
| goto out; |
| |
| ret = gcry_pk_get_keygrip (key_sexp, key_grip); |
| if (! ret) |
| { |
| err = gcry_error (GPG_ERR_INV_OBJ); |
| goto out; |
| } |
| |
| err = 0; |
| |
| out: |
| |
| gcry_sexp_release (key_sexp); |
| |
| return 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_ac_data_t data_encrypted_new; |
| gcry_ac_data_t data_value; |
| gcry_sexp_t sexp_request; |
| gcry_sexp_t sexp_reply; |
| gcry_sexp_t sexp_key; |
| gcry_error_t err; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| data_encrypted_new = NULL; |
| sexp_request = NULL; |
| sexp_reply = NULL; |
| data_value = NULL; |
| sexp_key = NULL; |
| |
| if (key->type != GCRY_AC_KEY_PUBLIC) |
| { |
| err = gcry_error (GPG_ERR_WRONG_KEY_USAGE); |
| goto out; |
| } |
| |
| err = ac_data_construct (ac_key_identifiers[key->type], 0, 0, |
| handle->algorithm_name, key->data, &sexp_key); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_new (&data_value); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_set (data_value, 0, "value", data_plain); |
| if (err) |
| goto out; |
| |
| err = ac_data_construct ("data", 1, flags, handle->algorithm_name, |
| data_value, &sexp_request); |
| if (err) |
| goto out; |
| |
| /* FIXME: error vs. errcode? */ |
| |
| err = gcry_pk_encrypt (&sexp_reply, sexp_request, sexp_key); |
| if (err) |
| goto out; |
| |
| /* Extract data. */ |
| err = ac_data_extract ("enc-val", handle->algorithm_name, |
| sexp_reply, &data_encrypted_new); |
| if (err) |
| goto out; |
| |
| *data_encrypted = data_encrypted_new; |
| |
| out: |
| |
| /* Deallocate resources. */ |
| |
| gcry_sexp_release (sexp_request); |
| gcry_sexp_release (sexp_reply); |
| gcry_sexp_release (sexp_key); |
| _gcry_ac_data_destroy (data_value); |
| |
| return 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_mpi_t data_decrypted; |
| gcry_sexp_t sexp_request; |
| gcry_sexp_t sexp_reply; |
| gcry_sexp_t sexp_value; |
| gcry_sexp_t sexp_key; |
| gcry_error_t err; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| sexp_request = NULL; |
| sexp_reply = NULL; |
| sexp_value = NULL; |
| sexp_key = NULL; |
| |
| if (key->type != GCRY_AC_KEY_SECRET) |
| { |
| err = gcry_error (GPG_ERR_WRONG_KEY_USAGE); |
| goto out; |
| } |
| |
| err = ac_data_construct (ac_key_identifiers[key->type], 0, 0, |
| handle->algorithm_name, key->data, &sexp_key); |
| if (err) |
| goto out; |
| |
| /* Create S-expression from data. */ |
| err = ac_data_construct ("enc-val", 1, flags, handle->algorithm_name, |
| data_encrypted, &sexp_request); |
| if (err) |
| goto out; |
| |
| /* Decrypt. */ |
| err = gcry_pk_decrypt (&sexp_reply, sexp_request, sexp_key); |
| if (err) |
| goto out; |
| |
| /* Extract plain text. */ |
| sexp_value = gcry_sexp_find_token (sexp_reply, "value", 0); |
| if (! sexp_value) |
| { |
| /* FIXME? */ |
| err = gcry_error (GPG_ERR_GENERAL); |
| goto out; |
| } |
| |
| data_decrypted = gcry_sexp_nth_mpi (sexp_value, 1, GCRYMPI_FMT_USG); |
| if (! data_decrypted) |
| { |
| err = gcry_error (GPG_ERR_GENERAL); |
| goto out; |
| } |
| |
| *data_plain = data_decrypted; |
| |
| out: |
| |
| /* Deallocate resources. */ |
| gcry_sexp_release (sexp_request); |
| gcry_sexp_release (sexp_reply); |
| gcry_sexp_release (sexp_value); |
| gcry_sexp_release (sexp_key); |
| |
| 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_ac_data_t data_signed; |
| gcry_ac_data_t data_value; |
| gcry_sexp_t sexp_request; |
| gcry_sexp_t sexp_reply; |
| gcry_sexp_t sexp_key; |
| gcry_error_t err; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| data_signed = NULL; |
| data_value = NULL; |
| sexp_request = NULL; |
| sexp_reply = NULL; |
| sexp_key = NULL; |
| |
| if (key->type != GCRY_AC_KEY_SECRET) |
| { |
| err = gcry_error (GPG_ERR_WRONG_KEY_USAGE); |
| goto out; |
| } |
| |
| err = ac_data_construct (ac_key_identifiers[key->type], 0, 0, |
| handle->algorithm_name, key->data, &sexp_key); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_new (&data_value); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_set (data_value, 0, "value", data); |
| if (err) |
| goto out; |
| |
| /* Create S-expression holding the data. */ |
| err = ac_data_construct ("data", 1, 0, NULL, data_value, &sexp_request); |
| if (err) |
| goto out; |
| |
| /* Sign. */ |
| err = gcry_pk_sign (&sexp_reply, sexp_request, sexp_key); |
| if (err) |
| goto out; |
| |
| /* Extract data. */ |
| err = ac_data_extract ("sig-val", handle->algorithm_name, |
| sexp_reply, &data_signed); |
| if (err) |
| goto out; |
| |
| /* Done. */ |
| *data_signature = data_signed; |
| |
| out: |
| |
| gcry_sexp_release (sexp_request); |
| gcry_sexp_release (sexp_reply); |
| gcry_sexp_release (sexp_key); |
| _gcry_ac_data_destroy (data_value); |
| |
| 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_sexp_t sexp_signature; |
| gcry_ac_data_t data_value; |
| gcry_sexp_t sexp_data; |
| gcry_sexp_t sexp_key; |
| gcry_error_t err; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| sexp_signature = NULL; |
| data_value = NULL; |
| sexp_data = NULL; |
| sexp_key = NULL; |
| |
| err = ac_data_construct ("public-key", 0, 0, |
| handle->algorithm_name, key->data, &sexp_key); |
| if (err) |
| goto out; |
| |
| if (key->type != GCRY_AC_KEY_PUBLIC) |
| { |
| err = gcry_error (GPG_ERR_WRONG_KEY_USAGE); |
| goto out; |
| } |
| |
| /* Construct S-expression holding the signature data. */ |
| err = ac_data_construct ("sig-val", 1, 0, handle->algorithm_name, |
| data_signature, &sexp_signature); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_new (&data_value); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_set (data_value, 0, "value", data); |
| if (err) |
| goto out; |
| |
| /* Construct S-expression holding the data. */ |
| err = ac_data_construct ("data", 1, 0, NULL, data_value, &sexp_data); |
| if (err) |
| goto out; |
| |
| /* Verify signature. */ |
| err = gcry_pk_verify (sexp_signature, sexp_data, sexp_key); |
| |
| out: |
| |
| gcry_sexp_release (sexp_signature); |
| gcry_sexp_release (sexp_data); |
| gcry_sexp_release (sexp_key); |
| _gcry_ac_data_destroy (data_value); |
| |
| return gcry_error (err); |
| } |
| |
| |
| |
| |
| /* |
| * Implementation of encoding methods (em). |
| */ |
| |
| /* Type for functions that encode or decode (hence the name) a |
| message. */ |
| typedef gcry_error_t (*gcry_ac_em_dencode_t) (unsigned int flags, |
| void *options, |
| gcry_ac_io_t *ac_io_read, |
| gcry_ac_io_t *ac_io_write); |
| |
| /* Fill the buffer BUFFER which is BUFFER_N bytes long with non-zero |
| random bytes of random level LEVEL. */ |
| static void |
| em_randomize_nonzero (unsigned char *buffer, size_t buffer_n, |
| gcry_random_level_t level) |
| { |
| unsigned char *buffer_rand; |
| unsigned int buffer_rand_n; |
| unsigned int zeros; |
| unsigned int i; |
| unsigned int j; |
| |
| for (i = 0; i < buffer_n; i++) |
| buffer[i] = 0; |
| |
| do |
| { |
| /* Count zeros. */ |
| for (i = zeros = 0; i < buffer_n; i++) |
| if (! buffer[i]) |
| zeros++; |
| |
| if (zeros) |
| { |
| /* Get random bytes. */ |
| buffer_rand_n = zeros + (zeros / 128); |
| buffer_rand = gcry_random_bytes_secure (buffer_rand_n, level); |
| |
| /* Substitute zeros with non-zero random bytes. */ |
| for (i = j = 0; zeros && (i < buffer_n) && (j < buffer_rand_n); i++) |
| if (! buffer[i]) |
| { |
| while ((j < buffer_rand_n) && (! buffer_rand[j])) |
| j++; |
| if (j < buffer_rand_n) |
| { |
| buffer[i] = buffer_rand[j++]; |
| zeros--; |
| } |
| else |
| break; |
| } |
| gcry_free (buffer_rand); |
| } |
| } |
| while (zeros); |
| } |
| |
| /* Encode a message according to the Encoding Method for Encryption |
| `PKCS-V1_5' (EME-PKCS-V1_5). */ |
| static gcry_error_t |
| eme_pkcs_v1_5_encode (unsigned int flags, void *opts, |
| gcry_ac_io_t *ac_io_read, |
| gcry_ac_io_t *ac_io_write) |
| { |
| gcry_ac_eme_pkcs_v1_5_t *options; |
| gcry_error_t err; |
| unsigned char *buffer; |
| unsigned char *ps; |
| unsigned char *m; |
| size_t m_n; |
| unsigned int ps_n; |
| unsigned int k; |
| |
| (void)flags; |
| |
| options = opts; |
| buffer = NULL; |
| m = NULL; |
| |
| err = _gcry_ac_io_read_all (ac_io_read, &m, &m_n); |
| if (err) |
| goto out; |
| |
| /* Figure out key length in bytes. */ |
| k = options->key_size / 8; |
| |
| if (m_n > k - 11) |
| { |
| /* Key is too short for message. */ |
| err = gcry_error (GPG_ERR_TOO_SHORT); |
| goto out; |
| } |
| |
| /* According to this encoding method, the first byte of the encoded |
| message is zero. This byte will be lost anyway, when the encoded |
| message is to be converted into an MPI, that's why we skip |
| it. */ |
| |
| /* Allocate buffer. */ |
| buffer = gcry_malloc (k - 1); |
| if (! buffer) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| /* Generate an octet string PS of length k - mLen - 3 consisting |
| of pseudorandomly generated nonzero octets. The length of PS |
| will be at least eight octets. */ |
| ps_n = k - m_n - 3; |
| ps = buffer + 1; |
| em_randomize_nonzero (ps, ps_n, GCRY_STRONG_RANDOM); |
| |
| /* Concatenate PS, the message M, and other padding to form an |
| encoded message EM of length k octets as: |
| |
| EM = 0x00 || 0x02 || PS || 0x00 || M. */ |
| |
| buffer[0] = 0x02; |
| buffer[ps_n + 1] = 0x00; |
| memcpy (buffer + ps_n + 2, m, m_n); |
| |
| err = _gcry_ac_io_write (ac_io_write, buffer, k - 1); |
| |
| out: |
| |
| gcry_free (buffer); |
| gcry_free (m); |
| |
| return err; |
| } |
| |
| /* Decode a message according to the Encoding Method for Encryption |
| `PKCS-V1_5' (EME-PKCS-V1_5). */ |
| static gcry_error_t |
| eme_pkcs_v1_5_decode (unsigned int flags, void *opts, |
| gcry_ac_io_t *ac_io_read, |
| gcry_ac_io_t *ac_io_write) |
| { |
| gcry_ac_eme_pkcs_v1_5_t *options; |
| unsigned char *buffer; |
| unsigned char *em; |
| size_t em_n; |
| gcry_error_t err; |
| unsigned int i; |
| unsigned int k; |
| |
| (void)flags; |
| |
| options = opts; |
| buffer = NULL; |
| em = NULL; |
| |
| err = _gcry_ac_io_read_all (ac_io_read, &em, &em_n); |
| if (err) |
| goto out; |
| |
| /* Figure out key size. */ |
| k = options->key_size / 8; |
| |
| /* Search for zero byte. */ |
| for (i = 0; (i < em_n) && em[i]; i++); |
| |
| /* According to this encoding method, the first byte of the encoded |
| message should be zero. This byte is lost. */ |
| |
| if (! ((em_n >= 10) |
| && (em_n == (k - 1)) |
| && (em[0] == 0x02) |
| && (i < em_n) |
| && ((i - 1) >= 8))) |
| { |
| err = gcry_error (GPG_ERR_DECRYPT_FAILED); |
| goto out; |
| } |
| |
| i++; |
| buffer = gcry_malloc (em_n - i); |
| if (! buffer) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| memcpy (buffer, em + i, em_n - i); |
| err = _gcry_ac_io_write (ac_io_write, buffer, em_n - i); |
| |
| out: |
| |
| gcry_free (buffer); |
| gcry_free (em); |
| |
| return err; |
| } |
| |
| static gcry_error_t |
| emsa_pkcs_v1_5_encode_data_cb (void *opaque, |
| unsigned char *buffer, size_t buffer_n) |
| { |
| gcry_md_hd_t md_handle; |
| |
| md_handle = opaque; |
| gcry_md_write (md_handle, buffer, buffer_n); |
| |
| return 0; |
| } |
| |
| |
| /* Encode a message according to the Encoding Method for Signatures |
| with Appendix `PKCS-V1_5' (EMSA-PKCS-V1_5). */ |
| static gcry_error_t |
| emsa_pkcs_v1_5_encode (unsigned int flags, void *opts, |
| gcry_ac_io_t *ac_io_read, |
| gcry_ac_io_t *ac_io_write) |
| { |
| gcry_ac_emsa_pkcs_v1_5_t *options; |
| gcry_error_t err; |
| gcry_md_hd_t md; |
| unsigned char *t; |
| size_t t_n; |
| unsigned char *h; |
| size_t h_n; |
| unsigned char *ps; |
| size_t ps_n; |
| unsigned char *buffer; |
| size_t buffer_n; |
| unsigned char asn[100]; /* FIXME, always enough? */ |
| size_t asn_n; |
| unsigned int i; |
| |
| (void)flags; |
| |
| options = opts; |
| buffer = NULL; |
| md = NULL; |
| ps = NULL; |
| t = NULL; |
| |
| /* Create hashing handle and get the necessary information. */ |
| err = gcry_md_open (&md, options->md, 0); |
| if (err) |
| goto out; |
| |
| asn_n = DIM (asn); |
| err = gcry_md_algo_info (options->md, GCRYCTL_GET_ASNOID, asn, &asn_n); |
| if (err) |
| goto out; |
| |
| h_n = gcry_md_get_algo_dlen (options->md); |
| |
| err = _gcry_ac_io_process (ac_io_read, emsa_pkcs_v1_5_encode_data_cb, md); |
| if (err) |
| goto out; |
| |
| h = gcry_md_read (md, 0); |
| |
| /* Encode the algorithm ID for the hash function and the hash value |
| into an ASN.1 value of type DigestInfo with the Distinguished |
| Encoding Rules (DER), where the type DigestInfo has the syntax: |
| |
| DigestInfo ::== SEQUENCE { |
| digestAlgorithm AlgorithmIdentifier, |
| digest OCTET STRING |
| } |
| |
| The first field identifies the hash function and the second |
| contains the hash value. Let T be the DER encoding of the |
| DigestInfo value and let tLen be the length in octets of T. */ |
| |
| t_n = asn_n + h_n; |
| t = gcry_malloc (t_n); |
| if (! t) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| for (i = 0; i < asn_n; i++) |
| t[i] = asn[i]; |
| for (i = 0; i < h_n; i++) |
| t[asn_n + i] = h[i]; |
| |
| /* If emLen < tLen + 11, output "intended encoded message length |
| too short" and stop. */ |
| if (options->em_n < t_n + 11) |
| { |
| err = gcry_error (GPG_ERR_TOO_SHORT); |
| goto out; |
| } |
| |
| /* Generate an octet string PS consisting of emLen - tLen - 3 octets |
| with hexadecimal value 0xFF. The length of PS will be at least 8 |
| octets. */ |
| ps_n = options->em_n - t_n - 3; |
| ps = gcry_malloc (ps_n); |
| if (! ps) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| for (i = 0; i < ps_n; i++) |
| ps[i] = 0xFF; |
| |
| /* Concatenate PS, the DER encoding T, and other padding to form the |
| encoded message EM as: |
| |
| EM = 0x00 || 0x01 || PS || 0x00 || T. */ |
| |
| buffer_n = ps_n + t_n + 3; |
| buffer = gcry_malloc (buffer_n); |
| if (! buffer) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| buffer[0] = 0x00; |
| buffer[1] = 0x01; |
| for (i = 0; i < ps_n; i++) |
| buffer[2 + i] = ps[i]; |
| buffer[2 + ps_n] = 0x00; |
| for (i = 0; i < t_n; i++) |
| buffer[3 + ps_n + i] = t[i]; |
| |
| err = _gcry_ac_io_write (ac_io_write, buffer, buffer_n); |
| |
| out: |
| |
| gcry_md_close (md); |
| |
| gcry_free (buffer); |
| gcry_free (ps); |
| gcry_free (t); |
| |
| return err; |
| } |
| |
| /* `Actions' for data_dencode(). */ |
| typedef enum dencode_action |
| { |
| DATA_ENCODE, |
| DATA_DECODE, |
| } |
| dencode_action_t; |
| |
| /* Encode or decode a message according to the the encoding method |
| METHOD; ACTION specifies wether the message that is contained in |
| BUFFER_IN and of length BUFFER_IN_N should be encoded or decoded. |
| The resulting message will be stored in a newly allocated buffer in |
| BUFFER_OUT and BUFFER_OUT_N. */ |
| static gcry_error_t |
| ac_data_dencode (gcry_ac_em_t method, dencode_action_t action, |
| unsigned int flags, void *options, |
| gcry_ac_io_t *ac_io_read, |
| gcry_ac_io_t *ac_io_write) |
| { |
| struct |
| { |
| gcry_ac_em_t method; |
| gcry_ac_em_dencode_t encode; |
| gcry_ac_em_dencode_t decode; |
| } methods[] = |
| { |
| { GCRY_AC_EME_PKCS_V1_5, |
| eme_pkcs_v1_5_encode, eme_pkcs_v1_5_decode }, |
| { GCRY_AC_EMSA_PKCS_V1_5, |
| emsa_pkcs_v1_5_encode, NULL }, |
| }; |
| size_t methods_n; |
| gcry_error_t err; |
| unsigned int i; |
| |
| methods_n = sizeof (methods) / sizeof (*methods); |
| |
| for (i = 0; i < methods_n; i++) |
| if (methods[i].method == method) |
| break; |
| if (i == methods_n) |
| { |
| err = gcry_error (GPG_ERR_NOT_FOUND); /* FIXME? */ |
| goto out; |
| } |
| |
| err = 0; |
| switch (action) |
| { |
| case DATA_ENCODE: |
| if (methods[i].encode) |
| /* FIXME? */ |
| err = (*methods[i].encode) (flags, options, ac_io_read, ac_io_write); |
| break; |
| |
| case DATA_DECODE: |
| if (methods[i].decode) |
| /* FIXME? */ |
| err = (*methods[i].decode) (flags, options, ac_io_read, ac_io_write); |
| break; |
| |
| default: |
| err = gcry_error (GPG_ERR_INV_ARG); |
| break; |
| } |
| |
| out: |
| |
| return err; |
| } |
| |
| /* Encode a message according to the encoding method METHOD. OPTIONS |
| must be a pointer to a method-specific structure |
| (gcry_ac_em*_t). */ |
| gcry_error_t |
| _gcry_ac_data_encode (gcry_ac_em_t method, |
| unsigned int flags, void *options, |
| gcry_ac_io_t *ac_io_read, |
| gcry_ac_io_t *ac_io_write) |
| { |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| return ac_data_dencode (method, DATA_ENCODE, flags, options, |
| ac_io_read, ac_io_write); |
| } |
| |
| /* Dencode a message according to the encoding method METHOD. OPTIONS |
| must be a pointer to a method-specific structure |
| (gcry_ac_em*_t). */ |
| gcry_error_t |
| _gcry_ac_data_decode (gcry_ac_em_t method, |
| unsigned int flags, void *options, |
| gcry_ac_io_t *ac_io_read, |
| gcry_ac_io_t *ac_io_write) |
| { |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| return ac_data_dencode (method, DATA_DECODE, flags, options, |
| ac_io_read, ac_io_write); |
| } |
| |
| /* Convert an MPI into an octet string. */ |
| void |
| _gcry_ac_mpi_to_os (gcry_mpi_t mpi, unsigned char *os, size_t os_n) |
| { |
| unsigned long digit; |
| gcry_mpi_t base; |
| unsigned int i; |
| unsigned int n; |
| gcry_mpi_t m; |
| gcry_mpi_t d; |
| |
| if (fips_mode ()) |
| return; |
| |
| base = gcry_mpi_new (0); |
| gcry_mpi_set_ui (base, 256); |
| |
| n = 0; |
| m = gcry_mpi_copy (mpi); |
| while (gcry_mpi_cmp_ui (m, 0)) |
| { |
| n++; |
| gcry_mpi_div (m, NULL, m, base, 0); |
| } |
| |
| gcry_mpi_set (m, mpi); |
| d = gcry_mpi_new (0); |
| for (i = 0; (i < n) && (i < os_n); i++) |
| { |
| gcry_mpi_mod (d, m, base); |
| _gcry_mpi_get_ui (d, &digit); |
| gcry_mpi_div (m, NULL, m, base, 0); |
| os[os_n - i - 1] = (digit & 0xFF); |
| } |
| |
| for (; i < os_n; i++) |
| os[os_n - i - 1] = 0; |
| |
| gcry_mpi_release (base); |
| gcry_mpi_release (d); |
| gcry_mpi_release (m); |
| } |
| |
| /* Convert an MPI into an newly allocated octet string. */ |
| gcry_error_t |
| _gcry_ac_mpi_to_os_alloc (gcry_mpi_t mpi, unsigned char **os, size_t *os_n) |
| { |
| unsigned char *buffer; |
| size_t buffer_n; |
| gcry_error_t err; |
| unsigned int nbits; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| nbits = gcry_mpi_get_nbits (mpi); |
| buffer_n = (nbits + 7) / 8; |
| buffer = gcry_malloc (buffer_n); |
| if (! buffer) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| _gcry_ac_mpi_to_os (mpi, buffer, buffer_n); |
| *os = buffer; |
| *os_n = buffer_n; |
| err = 0; |
| |
| out: |
| |
| return err; |
| } |
| |
| |
| /* Convert an octet string into an MPI. */ |
| void |
| _gcry_ac_os_to_mpi (gcry_mpi_t mpi, unsigned char *os, size_t os_n) |
| { |
| unsigned int i; |
| gcry_mpi_t xi; |
| gcry_mpi_t x; |
| gcry_mpi_t a; |
| |
| if (fips_mode ()) |
| return; |
| |
| a = gcry_mpi_new (0); |
| gcry_mpi_set_ui (a, 1); |
| x = gcry_mpi_new (0); |
| gcry_mpi_set_ui (x, 0); |
| xi = gcry_mpi_new (0); |
| |
| for (i = 0; i < os_n; i++) |
| { |
| gcry_mpi_mul_ui (xi, a, os[os_n - i - 1]); |
| gcry_mpi_add (x, x, xi); |
| gcry_mpi_mul_ui (a, a, 256); |
| } |
| |
| gcry_mpi_release (xi); |
| gcry_mpi_release (a); |
| |
| gcry_mpi_set (mpi, x); |
| gcry_mpi_release (x); /* FIXME: correct? */ |
| } |
| |
| |
| |
| /* |
| * Implementation of Encryption Schemes (ES) and Signature Schemes |
| * with Appendix (SSA). |
| */ |
| |
| /* Schemes consist of two things: encoding methods and cryptographic |
| primitives. |
| |
| Since encoding methods are accessible through a common API with |
| method-specific options passed as an anonymous struct, schemes have |
| to provide functions that construct this method-specific structure; |
| this is what the functions of type `gcry_ac_dencode_prepare_t' are |
| there for. */ |
| |
| typedef gcry_error_t (*gcry_ac_dencode_prepare_t) (gcry_ac_handle_t handle, |
| gcry_ac_key_t key, |
| void *opts, |
| void *opts_em); |
| |
| /* The `dencode_prepare' function for ES-PKCS-V1_5. */ |
| static gcry_error_t |
| ac_es_dencode_prepare_pkcs_v1_5 (gcry_ac_handle_t handle, gcry_ac_key_t key, |
| void *opts, void *opts_em) |
| { |
| gcry_ac_eme_pkcs_v1_5_t *options_em; |
| unsigned int nbits; |
| gcry_error_t err; |
| |
| (void)opts; |
| |
| err = _gcry_ac_key_get_nbits (handle, key, &nbits); |
| if (err) |
| goto out; |
| |
| options_em = opts_em; |
| options_em->key_size = nbits; |
| |
| out: |
| |
| return err; |
| } |
| |
| /* The `dencode_prepare' function for SSA-PKCS-V1_5. */ |
| static gcry_error_t |
| ac_ssa_dencode_prepare_pkcs_v1_5 (gcry_ac_handle_t handle, gcry_ac_key_t key, |
| void *opts, void *opts_em) |
| { |
| gcry_ac_emsa_pkcs_v1_5_t *options_em; |
| gcry_ac_ssa_pkcs_v1_5_t *options; |
| gcry_error_t err; |
| unsigned int k; |
| |
| options_em = opts_em; |
| options = opts; |
| |
| err = _gcry_ac_key_get_nbits (handle, key, &k); |
| if (err) |
| goto out; |
| |
| k = (k + 7) / 8; |
| options_em->md = options->md; |
| options_em->em_n = k; |
| |
| out: |
| |
| return err; |
| } |
| |
| /* Type holding the information about each supported |
| Encryption/Signature Scheme. */ |
| typedef struct ac_scheme |
| { |
| gcry_ac_scheme_t scheme; |
| gcry_ac_em_t scheme_encoding; |
| gcry_ac_dencode_prepare_t dencode_prepare; |
| size_t options_em_n; |
| } ac_scheme_t; |
| |
| /* List of supported Schemes. */ |
| static ac_scheme_t ac_schemes[] = |
| { |
| { GCRY_AC_ES_PKCS_V1_5, GCRY_AC_EME_PKCS_V1_5, |
| ac_es_dencode_prepare_pkcs_v1_5, |
| sizeof (gcry_ac_eme_pkcs_v1_5_t) }, |
| { GCRY_AC_SSA_PKCS_V1_5, GCRY_AC_EMSA_PKCS_V1_5, |
| ac_ssa_dencode_prepare_pkcs_v1_5, |
| sizeof (gcry_ac_emsa_pkcs_v1_5_t) } |
| }; |
| |
| /* Lookup a scheme by it's ID. */ |
| static ac_scheme_t * |
| ac_scheme_get (gcry_ac_scheme_t scheme) |
| { |
| ac_scheme_t *ac_scheme; |
| unsigned int i; |
| |
| for (i = 0; i < DIM (ac_schemes); i++) |
| if (scheme == ac_schemes[i].scheme) |
| break; |
| if (i == DIM (ac_schemes)) |
| ac_scheme = NULL; |
| else |
| ac_scheme = ac_schemes + i; |
| |
| return ac_scheme; |
| } |
| |
| /* Prepares the encoding/decoding by creating an according option |
| structure. */ |
| static gcry_error_t |
| ac_dencode_prepare (gcry_ac_handle_t handle, gcry_ac_key_t key, void *opts, |
| ac_scheme_t scheme, void **opts_em) |
| { |
| gcry_error_t err; |
| void *options_em; |
| |
| options_em = gcry_malloc (scheme.options_em_n); |
| if (! options_em) |
| { |
| err = gcry_error_from_errno (errno); |
| goto out; |
| } |
| |
| err = (*scheme.dencode_prepare) (handle, key, opts, options_em); |
| if (err) |
| goto out; |
| |
| *opts_em = options_em; |
| |
| out: |
| |
| if (err) |
| free (options_em); |
| |
| return err; |
| } |
| |
| /* Convert a data set into a single MPI; currently, this is only |
| supported for data sets containing a single MPI. */ |
| static gcry_error_t |
| ac_data_set_to_mpi (gcry_ac_data_t data, gcry_mpi_t *mpi) |
| { |
| gcry_error_t err; |
| gcry_mpi_t mpi_new; |
| unsigned int elems; |
| |
| elems = _gcry_ac_data_length (data); |
| |
| if (elems != 1) |
| { |
| /* FIXME: I guess, we should be more flexible in this respect by |
| allowing the actual encryption/signature schemes to implement |
| this conversion mechanism. */ |
| err = gcry_error (GPG_ERR_CONFLICT); |
| goto out; |
| } |
| |
| err = _gcry_ac_data_get_index (data, GCRY_AC_FLAG_COPY, 0, NULL, &mpi_new); |
| if (err) |
| goto out; |
| |
| *mpi = mpi_new; |
| |
| out: |
| |
| return err; |
| } |
| |
| /* Encrypts the plain text message contained in M, which is of size |
| M_N, with the public key KEY_PUBLIC according to the Encryption |
| Scheme SCHEME_ID. HANDLE is used for accessing the low-level |
| cryptographic primitives. If OPTS is not NULL, it has to be an |
| anonymous structure specific to the chosen scheme (gcry_ac_es_*_t). |
| The encrypted message will be stored in C and C_N. */ |
| gcry_error_t |
| _gcry_ac_data_encrypt_scheme (gcry_ac_handle_t handle, |
| gcry_ac_scheme_t scheme_id, |
| unsigned int flags, void *opts, |
| gcry_ac_key_t key, |
| gcry_ac_io_t *io_message, |
| gcry_ac_io_t *io_cipher) |
| { |
| gcry_error_t err; |
| gcry_ac_io_t io_em; |
| unsigned char *em; |
| size_t em_n; |
| gcry_mpi_t mpi_plain; |
| gcry_ac_data_t data_encrypted; |
| gcry_mpi_t mpi_encrypted; |
| unsigned char *buffer; |
| size_t buffer_n; |
| void *opts_em; |
| ac_scheme_t *scheme; |
| |
| (void)flags; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| data_encrypted = NULL; |
| mpi_encrypted = NULL; |
| mpi_plain = NULL; |
| opts_em = NULL; |
| buffer = NULL; |
| em = NULL; |
| |
| scheme = ac_scheme_get (scheme_id); |
| if (! scheme) |
| { |
| err = gcry_error (GPG_ERR_NO_ENCRYPTION_SCHEME); |
| goto out; |
| } |
| |
| if (key->type != GCRY_AC_KEY_PUBLIC) |
| { |
| err = gcry_error (GPG_ERR_WRONG_KEY_USAGE); |
| goto out; |
| } |
| |
| err = ac_dencode_prepare (handle, key, opts, *scheme, &opts_em); |
| if (err) |
| goto out; |
| |
| _gcry_ac_io_init (&io_em, GCRY_AC_IO_WRITABLE, |
| GCRY_AC_IO_STRING, &em, &em_n); |
| |
| err = _gcry_ac_data_encode (scheme->scheme_encoding, 0, opts_em, |
| io_message, &io_em); |
| if (err) |
| goto out; |
| |
| mpi_plain = gcry_mpi_snew (0); |
| gcry_ac_os_to_mpi (mpi_plain, em, em_n); |
| |
| err = _gcry_ac_data_encrypt (handle, 0, key, mpi_plain, &data_encrypted); |
| if (err) |
| goto out; |
| |
| err = ac_data_set_to_mpi (data_encrypted, &mpi_encrypted); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_mpi_to_os_alloc (mpi_encrypted, &buffer, &buffer_n); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_io_write (io_cipher, buffer, buffer_n); |
| |
| out: |
| |
| gcry_ac_data_destroy (data_encrypted); |
| gcry_mpi_release (mpi_encrypted); |
| gcry_mpi_release (mpi_plain); |
| gcry_free (opts_em); |
| gcry_free (buffer); |
| gcry_free (em); |
| |
| return err; |
| } |
| |
| /* Decryptes the cipher message contained in C, which is of size C_N, |
| with the secret key KEY_SECRET according to the Encryption Scheme |
| SCHEME_ID. Handle is used for accessing the low-level |
| cryptographic primitives. If OPTS is not NULL, it has to be an |
| anonymous structure specific to the chosen scheme (gcry_ac_es_*_t). |
| The decrypted message will be stored in M and M_N. */ |
| gcry_error_t |
| _gcry_ac_data_decrypt_scheme (gcry_ac_handle_t handle, |
| gcry_ac_scheme_t scheme_id, |
| unsigned int flags, void *opts, |
| gcry_ac_key_t key, |
| gcry_ac_io_t *io_cipher, |
| gcry_ac_io_t *io_message) |
| { |
| gcry_ac_io_t io_em; |
| gcry_error_t err; |
| gcry_ac_data_t data_encrypted; |
| unsigned char *em; |
| size_t em_n; |
| gcry_mpi_t mpi_encrypted; |
| gcry_mpi_t mpi_decrypted; |
| void *opts_em; |
| ac_scheme_t *scheme; |
| char *elements_enc; |
| size_t elements_enc_n; |
| unsigned char *c; |
| size_t c_n; |
| |
| (void)flags; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| data_encrypted = NULL; |
| mpi_encrypted = NULL; |
| mpi_decrypted = NULL; |
| elements_enc = NULL; |
| opts_em = NULL; |
| em = NULL; |
| c = NULL; |
| |
| scheme = ac_scheme_get (scheme_id); |
| if (! scheme) |
| { |
| err = gcry_error (GPG_ERR_NO_ENCRYPTION_SCHEME); |
| goto out; |
| } |
| |
| if (key->type != GCRY_AC_KEY_SECRET) |
| { |
| err = gcry_error (GPG_ERR_WRONG_KEY_USAGE); |
| goto out; |
| } |
| |
| err = _gcry_ac_io_read_all (io_cipher, &c, &c_n); |
| if (err) |
| goto out; |
| |
| mpi_encrypted = gcry_mpi_snew (0); |
| gcry_ac_os_to_mpi (mpi_encrypted, c, c_n); |
| |
| err = _gcry_pk_get_elements (handle->algorithm, &elements_enc, NULL); |
| if (err) |
| goto out; |
| |
| elements_enc_n = strlen (elements_enc); |
| if (elements_enc_n != 1) |
| { |
| /* FIXME? */ |
| err = gcry_error (GPG_ERR_CONFLICT); |
| goto out; |
| } |
| |
| err = _gcry_ac_data_new (&data_encrypted); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_set (data_encrypted, GCRY_AC_FLAG_COPY | GCRY_AC_FLAG_DEALLOC, |
| elements_enc, mpi_encrypted); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_decrypt (handle, 0, key, &mpi_decrypted, data_encrypted); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_mpi_to_os_alloc (mpi_decrypted, &em, &em_n); |
| if (err) |
| goto out; |
| |
| err = ac_dencode_prepare (handle, key, opts, *scheme, &opts_em); |
| if (err) |
| goto out; |
| |
| _gcry_ac_io_init (&io_em, GCRY_AC_IO_READABLE, |
| GCRY_AC_IO_STRING, em, em_n); |
| |
| err = _gcry_ac_data_decode (scheme->scheme_encoding, 0, opts_em, |
| &io_em, io_message); |
| if (err) |
| goto out; |
| |
| out: |
| |
| _gcry_ac_data_destroy (data_encrypted); |
| gcry_mpi_release (mpi_encrypted); |
| gcry_mpi_release (mpi_decrypted); |
| free (elements_enc); |
| gcry_free (opts_em); |
| gcry_free (em); |
| gcry_free (c); |
| |
| return err; |
| } |
| |
| |
| /* Signs the message contained in M, which is of size M_N, with the |
| secret key KEY according to the Signature Scheme SCHEME_ID. Handle |
| is used for accessing the low-level cryptographic primitives. If |
| OPTS is not NULL, it has to be an anonymous structure specific to |
| the chosen scheme (gcry_ac_ssa_*_t). The signed message will be |
| stored in S and S_N. */ |
| gcry_error_t |
| _gcry_ac_data_sign_scheme (gcry_ac_handle_t handle, |
| gcry_ac_scheme_t scheme_id, |
| unsigned int flags, void *opts, |
| gcry_ac_key_t key, |
| gcry_ac_io_t *io_message, |
| gcry_ac_io_t *io_signature) |
| { |
| gcry_ac_io_t io_em; |
| gcry_error_t err; |
| gcry_ac_data_t data_signed; |
| unsigned char *em; |
| size_t em_n; |
| gcry_mpi_t mpi; |
| void *opts_em; |
| unsigned char *buffer; |
| size_t buffer_n; |
| gcry_mpi_t mpi_signed; |
| ac_scheme_t *scheme; |
| |
| (void)flags; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| data_signed = NULL; |
| mpi_signed = NULL; |
| opts_em = NULL; |
| buffer = NULL; |
| mpi = NULL; |
| em = NULL; |
| |
| if (key->type != GCRY_AC_KEY_SECRET) |
| { |
| err = gcry_error (GPG_ERR_WRONG_KEY_USAGE); |
| goto out; |
| } |
| |
| scheme = ac_scheme_get (scheme_id); |
| if (! scheme) |
| { |
| /* FIXME: adjust api of scheme_get in respect to err codes. */ |
| err = gcry_error (GPG_ERR_NO_SIGNATURE_SCHEME); |
| goto out; |
| } |
| |
| err = ac_dencode_prepare (handle, key, opts, *scheme, &opts_em); |
| if (err) |
| goto out; |
| |
| _gcry_ac_io_init (&io_em, GCRY_AC_IO_WRITABLE, |
| GCRY_AC_IO_STRING, &em, &em_n); |
| |
| err = _gcry_ac_data_encode (scheme->scheme_encoding, 0, opts_em, |
| io_message, &io_em); |
| if (err) |
| goto out; |
| |
| mpi = gcry_mpi_new (0); |
| _gcry_ac_os_to_mpi (mpi, em, em_n); |
| |
| err = _gcry_ac_data_sign (handle, key, mpi, &data_signed); |
| if (err) |
| goto out; |
| |
| err = ac_data_set_to_mpi (data_signed, &mpi_signed); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_mpi_to_os_alloc (mpi_signed, &buffer, &buffer_n); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_io_write (io_signature, buffer, buffer_n); |
| |
| out: |
| |
| _gcry_ac_data_destroy (data_signed); |
| gcry_mpi_release (mpi_signed); |
| gcry_mpi_release (mpi); |
| gcry_free (opts_em); |
| gcry_free (buffer); |
| gcry_free (em); |
| |
| return err; |
| } |
| |
| /* Verifies that the signature contained in S, which is of length S_N, |
| is indeed the result of signing the message contained in M, which |
| is of size M_N, with the secret key belonging to the public key |
| KEY_PUBLIC. If OPTS is not NULL, it has to be an anonymous |
| structure (gcry_ac_ssa_*_t) specific to the Signature Scheme, whose |
| ID is contained in SCHEME_ID. */ |
| gcry_error_t |
| _gcry_ac_data_verify_scheme (gcry_ac_handle_t handle, |
| gcry_ac_scheme_t scheme_id, |
| unsigned int flags, void *opts, |
| gcry_ac_key_t key, |
| gcry_ac_io_t *io_message, |
| gcry_ac_io_t *io_signature) |
| { |
| gcry_ac_io_t io_em; |
| gcry_error_t err; |
| gcry_ac_data_t data_signed; |
| unsigned char *em; |
| size_t em_n; |
| void *opts_em; |
| gcry_mpi_t mpi_signature; |
| gcry_mpi_t mpi_data; |
| ac_scheme_t *scheme; |
| char *elements_sig; |
| size_t elements_sig_n; |
| unsigned char *s; |
| size_t s_n; |
| |
| (void)flags; |
| |
| if (fips_mode ()) |
| return gpg_error (GPG_ERR_NOT_SUPPORTED); |
| |
| mpi_signature = NULL; |
| elements_sig = NULL; |
| data_signed = NULL; |
| mpi_data = NULL; |
| opts_em = NULL; |
| em = NULL; |
| s = NULL; |
| |
| if (key->type != GCRY_AC_KEY_PUBLIC) |
| { |
| err = gcry_error (GPG_ERR_WRONG_KEY_USAGE); |
| goto out; |
| } |
| |
| scheme = ac_scheme_get (scheme_id); |
| if (! scheme) |
| { |
| err = gcry_error (GPG_ERR_NO_SIGNATURE_SCHEME); |
| goto out; |
| } |
| |
| err = ac_dencode_prepare (handle, key, opts, *scheme, &opts_em); |
| if (err) |
| goto out; |
| |
| _gcry_ac_io_init (&io_em, GCRY_AC_IO_WRITABLE, |
| GCRY_AC_IO_STRING, &em, &em_n); |
| |
| err = _gcry_ac_data_encode (scheme->scheme_encoding, 0, opts_em, |
| io_message, &io_em); |
| if (err) |
| goto out; |
| |
| mpi_data = gcry_mpi_new (0); |
| _gcry_ac_os_to_mpi (mpi_data, em, em_n); |
| |
| err = _gcry_ac_io_read_all (io_signature, &s, &s_n); |
| if (err) |
| goto out; |
| |
| mpi_signature = gcry_mpi_new (0); |
| _gcry_ac_os_to_mpi (mpi_signature, s, s_n); |
| |
| err = _gcry_pk_get_elements (handle->algorithm, NULL, &elements_sig); |
| if (err) |
| goto out; |
| |
| elements_sig_n = strlen (elements_sig); |
| if (elements_sig_n != 1) |
| { |
| /* FIXME? */ |
| err = gcry_error (GPG_ERR_CONFLICT); |
| goto out; |
| } |
| |
| err = _gcry_ac_data_new (&data_signed); |
| if (err) |
| goto out; |
| |
| err = _gcry_ac_data_set (data_signed, GCRY_AC_FLAG_COPY | GCRY_AC_FLAG_DEALLOC, |
| elements_sig, mpi_signature); |
| if (err) |
| goto out; |
| |
| gcry_mpi_release (mpi_signature); |
| mpi_signature = NULL; |
| |
| err = _gcry_ac_data_verify (handle, key, mpi_data, data_signed); |
| |
| out: |
| |
| _gcry_ac_data_destroy (data_signed); |
| gcry_mpi_release (mpi_signature); |
| gcry_mpi_release (mpi_data); |
| free (elements_sig); |
| gcry_free (opts_em); |
| gcry_free (em); |
| gcry_free (s); |
| |
| return err; |
| } |
| |
| |
| /* |
| * General functions. |
| */ |
| |
| gcry_err_code_t |
| _gcry_ac_init (void) |
| { |
| if (fips_mode ()) |
| return GPG_ERR_NOT_SUPPORTED; |
| |
| return 0; |
| } |