src/ChangeLog:
[libgcrypt.git] / cipher / ac.c
index d26d88f..5ecdc10 100644 (file)
@@ -1,5 +1,5 @@
 /* ac.c - Alternative interface for asymmetric cryptography.
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
  
    This file is part of Libgcrypt.
   
   
    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
- */
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.  */
 
 #include <config.h>
-#include <assert.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <stddef.h>
+#include <assert.h>
 
 #include "g10lib.h"
 #include "cipher.h"
+#include "mpi.h"
 
 \f
 
 /* 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.  */
+   on how many people prefer the ac interface.  */
 
 /* Mapping of flag numbers to the according strings as it is expected
    for S-expressions.  */
-struct number_string
+static struct number_string
 {
   int number;
   const char *string;
-} gcry_ac_flags[] =
+} ac_flags[] =
   {
-    { GCRY_AC_FLAG_DATA_NO_BLINDING, "no-blinding" },
-    { 0, NULL },
+    { GCRY_AC_FLAG_NO_BLINDING, "no-blinding" },
   };
 
 /* The positions in this list correspond to the values contained in
@@ -52,7 +52,7 @@ struct number_string
 static const char *ac_key_identifiers[] =
   {
     "private-key",
-    "public-key",
+    "public-key"
   };
 
 /* These specifications are needed for key-pair generation; the caller
@@ -68,7 +68,7 @@ struct gcry_ac_key_generate_spec
                                   structure at which the MPI value
                                   associated with this flag is to be
                                   found.  */
-} gcry_ac_key_generate_specs[] =
+} ac_key_generate_specs[] =
   {
     { GCRY_AC_RSA, "rsa-use-e", offsetof (gcry_ac_key_spec_rsa_t, e) },
     { 0 },
@@ -88,8 +88,10 @@ struct gcry_ac_handle
 /* A named MPI value.  */
 typedef struct gcry_ac_mpi
 {
-  const char *name;
-  gcry_mpi_t mpi;
+  const char *name_provided;   /* Provided name of MPI value. */
+  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.  */
@@ -99,15 +101,14 @@ struct gcry_ac_data
   unsigned int data_n;         /* Number of values in DATA.  */
 };
 
-/* The key in `native' ac form and as an S-expression. */
+/* A single key.  */
 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.  */
+/* A key pair.  */
 struct gcry_ac_key_pair
 {
   gcry_ac_key_t public;
@@ -116,1208 +117,3428 @@ struct gcry_ac_key_pair
 
 \f
 
-/*
- * Primitive functions for the manipulation of `data sets'.
+/* 
+ * Functions for working with data sets.
  */
 
-/* Return in AC_MPI a pointer to the named MPI contained in DATA that
-   is labelled with NAME or NULL in case there is no MPI with the that
-   name.  */
-static void
-gcry_ac_data_search (gcry_ac_data_t data,
-                    const char *name,
-                    gcry_ac_mpi_t **ac_mpi)
-{
-  gcry_ac_mpi_t *ac_mpi_found = NULL;
-  int i;
-
-  /* Search.  */
-  for (i = 0; i < data->data_n; i++)
-    if (! strcmp (name, data->data[i].name))
-      ac_mpi_found = &data->data[i];
-
-  *ac_mpi = ac_mpi_found;
-}
-
-/* Add MPI to DATA, with the label being NAME.  */
-static gcry_err_code_t
-gcry_ac_data_add (gcry_ac_data_t data,
-                 const char *name, gcry_mpi_t mpi)
+/* Creates a new, empty data set and store 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_mpi_t *ac_mpis = NULL;
-
-  /* Allocate.  */
-  ac_mpis = gcry_realloc (data->data,
-                          sizeof (gcry_ac_mpi_t) * (data->data_n + 1));
-  if (! ac_mpis)
-    err = gpg_err_code_from_errno (errno);
+  gcry_ac_data_t data_new;
+  gcry_error_t err;
 
-  if (! err)
+  data_new = gcry_malloc (sizeof (*data_new));
+  if (! data_new)
     {
-      /* Fill. */
-      if (ac_mpis != data->data)
-       data->data = ac_mpis;
-      data->data[data->data_n].name = name;
-      data->data[data->data_n].mpi = mpi;
-      data->data_n++;
+      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;
 }
 
-/* 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_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;
-  void *p = NULL;
-  int i = 0;
+  gcry_error_t err;
 
-  /* 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;
+  err = _gcry_ac_data_new (data);
+
+  return gcry_error (err);
+}
 
-  if (! err)
-    /* Allocate space for named MPIs.  */
-    err = _gcry_malloc (sizeof (gcry_ac_mpi_t) * data->data_n, 0,
-                       (void **) &data_new->data);
+/* 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);
+      }
+}
 
-  if (! err)
+/* Destroys the data set DATA.  */
+void
+_gcry_ac_data_destroy (gcry_ac_data_t data)
+{
+  if (data)
     {
-      /* 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;
+      ac_data_values_destroy (data);
+      gcry_free (data->data);
+      gcry_free (data);
+    }
+}
 
-         /* Name.  */
-         data_new->data[i].name = strdup (data->data[i].name);
-         if (! data_new->data[i].name)
-           err = gpg_err_code_from_errno (errno);
+void
+gcry_ac_data_destroy (gcry_ac_data_t data)
+{
+  return _gcry_ac_data_destroy (data);
+}
 
-         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);
-           }
-       }
-    }
+/* 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;
 
-  if (! err)
+  data_mpis_new = gcry_malloc (sizeof (*data_mpis_new) * data_mpis_n);
+  if (! data_mpis_new)
     {
-      /* Copy out.  */
-      *data_cp = data_new;
+      err = gcry_error_from_errno (errno);
+      goto out;
     }
-  else
+  memset (data_mpis_new, 0, sizeof (*data_mpis_new) * data_mpis_n);
+
+  err = 0;
+  for (i = 0; i < data_mpis_n; i++)
     {
-      /* Deallocate resources.  */
-      if (data_new)
+      /* Copy values.  */
+
+      if (data_mpis[i].name)
+       label = gcry_strdup (data_mpis[i].name);
+      else
+       label = gcry_strdup (data_mpis[i].name_provided);
+      mpi = gcry_mpi_copy (data_mpis[i].mpi);
+      if (! (label && mpi))
        {
-         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);
+         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;
 }
 
-\f
+/* 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;
+  gcry_ac_data_t data_new;
+  gcry_error_t err;
 
-/* 
- * Functions for converting data between the native ac and the
- * S-expression structure.
- */
+  /* Allocate data set.  */
+  data_new = gcry_malloc (sizeof (*data_new));
+  if (! data_new)
+    {
+      err = gcry_error_from_errno (errno);
+      goto out;
+    }
 
-/* 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:
+  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;
 
-     (IDENTIFIER <data to be ignored>
-                 (ALGORITHM <list of named MPI values>))
+ out:
+
+  if (err)
+    gcry_free (data_new);
+
+  return err;
+}
 
-  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_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;
-  gcry_sexp_t data_element_sexp = NULL;
-  gcry_sexp_t inner_data_sexp = NULL;
-  size_t inner_data_n;
+  return gcry_error (_gcry_ac_data_copy (data_cp, data));
+}
 
-  const char *name;
-  size_t name_n;
+/* 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;
+}
 
-  gcry_mpi_t data_elem_mpi = NULL;
-  char *data_elem_name = NULL;
+unsigned int
+gcry_ac_data_length (gcry_ac_data_t data)
+{
+  return _gcry_ac_data_length (data);
+}
 
-  gcry_ac_data_t data_new = NULL;
+/* 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;
 
-  int i = 0;
+  name_cp = NULL;
+  mpi_cp = NULL;
 
-  /* 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 (flags & ~(GCRY_AC_FLAG_DEALLOC | GCRY_AC_FLAG_COPY))
+    {
+      err = gcry_error (GPG_ERR_INV_ARG);
+      goto out;
     }
 
-  if (! err)
+  if (flags & GCRY_AC_FLAG_COPY)
     {
-      /* Allocate new data set.  */
-      data_new = gcry_malloc (sizeof (struct gcry_ac_data));
-      if (! data_new)
-       err = gpg_err_code_from_errno (errno);
-      else
+      /* Create copies.  */
+
+      name_cp = gcry_strdup (name);
+      mpi_cp = gcry_mpi_copy (mpi);
+      if (! (name_cp && mpi_cp))
        {
-         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);
+         err = gcry_error_from_errno (errno);
+         goto out;
        }
     }
 
-  if (! err)
+  /* Search for existing entry.  */
+  for (i = 0; i < data->data_n; i++)
+    if (! strcmp (name,
+                 data->data[i].name
+                 ? data->data[i].name : data->data[i].name_provided))
+      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
     {
-      /* Iterate through list of data elements and add them to the
-        data set.  */
+      /* Create a new entry.  */
+
+      gcry_ac_mpi_t *ac_mpis;
 
-      for (i = 1; i < inner_data_n; i++)
+      ac_mpis = gcry_realloc (data->data,
+                             sizeof (*data->data) * (data->data_n + 1));
+      if (! ac_mpis)
        {
-         data_new->data[i - 1].name = NULL;
-         data_new->data[i - 1].mpi = NULL;
+         err = gcry_error_from_errno (errno);
+         goto out;
+       }
 
-         /* 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 (data->data != ac_mpis)
+       data->data = ac_mpis;
+      data->data_n++;
+    }
 
-         if (! err)
-           {
-             /* Extract the name.  */
-             name = gcry_sexp_nth_data (data_element_sexp, 0, &name_n);
-             if (! name)
-               err = GPG_ERR_INTERNAL;
-           }
+  data->data[i].name_provided = name_cp ? NULL : name;
+  data->data[i].name = name_cp;
+  data->data[i].mpi = mpi_cp ? mpi_cp : mpi;
+  data->data[i].flags = flags;
+  err = 0;
 
-         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;
-           }
+ out:
 
-         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;
-               }
-           }
+  if (err)
+    {
+      gcry_mpi_release (mpi_cp);
+      gcry_free (name_cp);
+    }
 
-         /* Done.  */
+  return err;
+}
 
-         if (data_element_sexp)
-           gcry_sexp_release (data_element_sexp);
+gcry_error_t
+gcry_ac_data_set (gcry_ac_data_t data, unsigned int flags,
+                 const char *name, gcry_mpi_t mpi)
+{
+  return gcry_error (_gcry_ac_data_set (data, flags, name, mpi));
+}
 
-         if (! err)
-           {
-             data_new->data[i - 1].name = data_elem_name;
-             data_new->data[i - 1].mpi = data_elem_mpi;
-           }
-         else
-           break;
-       }
-    }
+/* 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 (! err)
+  if (flags & ~(GCRY_AC_FLAG_COPY))
     {
-      /* Copy out.  */
-      data_new->data_n = inner_data_n - 1;
-      *data = data_new;
+      err = gcry_error (GPG_ERR_INV_ARG);
+      goto out;
     }
-  else
+
+  for (i = 0; i < data->data_n; i++)
+    if (! strcmp (name,
+                 data->data[i].name ?
+                 data->data[i].name : data->data[i].name_provided))
+      break;
+  if (i == data->data_n)
     {
-      /* Deallocate resources.  */
+      err = gcry_error (GPG_ERR_NOT_FOUND);
+      goto out;
+    }
 
-      if (data_new)
+  if (flags & GCRY_AC_FLAG_COPY)
+    {
+      mpi_return = gcry_mpi_copy (data->data[i].mpi);
+      if (! mpi_return)
        {
-         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);
+         err = gcry_error_from_errno (errno); /* FIXME? */
+         goto out;
        }
     }
+  else
+    mpi_return = data->data[i].mpi;
+
+  *mpi = mpi_return;
+  err = 0;
+
+ out:
 
   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_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_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_ERROR;
-  void **arg_list = NULL;
-
-  gcry_sexp_t data_sexp_new = NULL;
-
-  size_t data_format_n = 0;
-  char *data_format = NULL;
+  return gcry_error (_gcry_ac_data_get_name (data, flags, name, mpi));
+}
 
-  int i;
+/* 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;
 
-  /* 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;
+  name_cp = NULL;
+  mpi_cp = NULL;
 
-  if (! err)
+  if (flags & ~(GCRY_AC_FLAG_COPY))
     {
-      /* 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);
+      err = gcry_error (GPG_ERR_INV_ARG);
+      goto out;
     }
 
-  if (! err)
+  if (idx >= data->data_n)
     {
-      /* Construct the format string.  */
+      err = gcry_error (GPG_ERR_INV_ARG);
+      goto out;
+    }
 
-      *data_format = 0;
-      strcat (data_format, "(");
-      strcat (data_format, identifier);
-      if (include_flags)
+  if (flags & GCRY_AC_FLAG_COPY)
+    {
+      /* Return copies to the user.  */
+      if (name)
        {
-         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, ")");
+         if (data->data[idx].name_provided)
+           name_cp = gcry_strdup (data->data[idx].name_provided);
+         else
+           name_cp = gcry_strdup (data->data[idx].name);
+         if (! name_cp)
+           {
+             err = gcry_error_from_errno (errno);
+             goto out;
+           }
        }
-      strcat (data_format, "(");
-      strcat (data_format, algorithm);
-      for (i = 0; i < data->data_n; i++)
+      if (mpi)
        {
-         strcat (data_format, "(");
-         strcat (data_format, data->data[i].name);
-         strcat (data_format, "%m)");
+         mpi_cp = gcry_mpi_copy (data->data[idx].mpi);
+         if (! mpi_cp)
+           {
+             err = gcry_error_from_errno (errno);
+             goto out;
+           }
        }
-      strcat (data_format, "))");
-
-      /* Create final S-expression.  */
-      err = gcry_sexp_build_array (&data_sexp_new, NULL,
-                                  data_format, arg_list);
     }
 
+  if (name)
+    *name = name_cp ? name_cp : (data->data[idx].name
+                                ? data->data[idx].name
+                                : data->data[idx].name_provided);
+  if (mpi)
+    *mpi = mpi_cp ? mpi_cp : data->data[idx].mpi;
+  err = 0;
+
+ out:
+
   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);
+      gcry_mpi_release (mpi_cp);
+      gcry_free (name_cp);
     }
 
-  else
-    /* Copy-out.  */
-    *data_sexp = data_sexp_new;
-
   return err;
 }
 
-\f
-
-/* 
- * Functions for working with data sets.
- */
+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)
+{
+  return gcry_error (_gcry_ac_data_get_index (data, flags, idx, name, mpi));
+}
 
-/* Creates a new, empty data set and stores it in DATA.  */
+/* 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_new (gcry_ac_data_t *data)
+_gcry_ac_data_to_sexp (gcry_ac_data_t data, gcry_sexp_t *sexp,
+                      const char **identifiers)
 {
-  gcry_err_code_t err = GPG_ERR_NO_ERROR;
-  gcry_ac_data_t data_new = NULL;
+  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;
 
-  data_new = gcry_malloc (sizeof (struct gcry_ac_data));
-  if (! data_new)
-    err = gpg_err_code_from_errno (errno);
+  sexp_buffer_n = 1;
+  sexp_buffer = NULL;
+  arg_list = NULL;
+  err = 0;
 
-  if (! err)
+  /* 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++)
     {
-      data_new->data = NULL;
-      data_new->data_n = 0;
-      *data = data_new;
+      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;
 
-  return gcry_error (err);
-}
-
-/* Destroys the data set DATA.  */
-void
-gcry_ac_data_destroy (gcry_ac_data_t data)
-{
-  int i;
+  /* Allocate buffer.  */
 
-  for (i = 0; i < data->data_n; i++)
+  sexp_buffer = gcry_malloc (sexp_buffer_n);
+  if (! sexp_buffer)
     {
-      gcry_free ((void *) data->data[i].name);
-      gcry_mpi_release (data->data[i].mpi);
+      err = gcry_error_from_errno (errno);
+      goto out;
     }
-  gcry_free (data->data);
-  gcry_free (data);
-}
 
-/* Adds the value MPI to the data set DATA with the label NAME.  If
-   there is already a value with that label, it is replaced, otherwise
-   a new value is added. */
-gcry_error_t
-gcry_ac_data_set (gcry_ac_data_t data,
-                 const char *name, gcry_mpi_t mpi)
-{
-  gcry_err_code_t err = GPG_ERR_NO_ERROR;
-  gcry_ac_mpi_t *ac_mpi;
+  /* Fill buffer.  */
 
-  gcry_ac_data_search (data, name, &ac_mpi);
-  if (ac_mpi)
+  *sexp_buffer = 0;
+  sexp_buffer_n = 0;
+
+  /* Add identifiers: (<IDENTIFIER0>(<IDENTIFIER1>...)).  */
+  if (identifiers_n)
     {
-      /* An entry for NAME does already exist, replace it.  */
-      if (ac_mpi->mpi != mpi)
-       {
-         gcry_mpi_release (ac_mpi->mpi);
-         ac_mpi->mpi = mpi;
-       }
+      /* 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
     {
-      /* Create a new entry.  */
-
-      gcry_mpi_t mpi_cp = NULL;
-      char *name_cp = NULL;
-
-      name_cp = strdup (name);
-      if (name_cp)
-       mpi_cp = gcry_mpi_copy (mpi);
-      if (! (name_cp && mpi_cp))
-       err = gpg_err_code_from_errno (errno);
-
-      if (! err)
-       err = gcry_ac_data_add (data, name_cp, mpi_cp);
+      /* 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)
-       {
-         if (name_cp)
-           gcry_free (name_cp);
-         if (mpi_cp)
-           gcry_mpi_release (mpi_cp);
-       }
+       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, ")");
     }
 
-  return gcry_error (err);
+  /* 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 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_data_to_sexp (gcry_ac_data_t data, gcry_sexp_t *sexp,
+                     const char **identifiers)
 {
-  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  gcry_error_t err;
 
-  err = gcry_ac_data_copy_internal (data_cp, data);
+  err = _gcry_ac_data_to_sexp (data, sexp, identifiers);
 
   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)
+/* 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)
 {
-  return data->data_n;
+  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;
+
+  /* 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;
+           }
+       }
+      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;
+
+      string = NULL;
+      mpi = NULL;
+
+      gcry_sexp_release (sexp_tmp);
+      sexp_tmp = NULL;
+    }
+  if (err)
+    goto out;
+
+  *data_set = data_set_new;
+
+ out:
+
+  gcry_sexp_release (sexp_tmp);
+  gcry_mpi_release (mpi);
+  gcry_free (string);
+  
+  if (err)
+    gcry_ac_data_destroy (data_set_new);
+
+  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, const char *name,
-                      gcry_mpi_t *mpi)
+gcry_ac_data_from_sexp (gcry_ac_data_t *data_set, gcry_sexp_t sexp,
+                       const char **identifiers)
 {
-  gcry_err_code_t err = GPG_ERR_NO_DATA;
-  gcry_mpi_t mpi_found = NULL;
-  int i;
-  
-  for (i = 0; i < data->data_n && (! mpi_found); i++)
-    if (! strcmp (data->data[i].name, name))
-      {
-       mpi_found = data->data[i].mpi;
-       err = GPG_ERR_NO_ERROR;
-      }
+  gcry_error_t err;
 
-  if (! err)
-    *mpi = mpi_found;
+  err = _gcry_ac_data_from_sexp (data_set, sexp, identifiers);
 
   return gcry_error (err);
 }
 
-/* Stores in NAME and MPI the named MPI value contained in the data
-   set DATA with the index INDEX.  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 idx,
-                       const char **name, gcry_mpi_t *mpi)
+static void
+_gcry_ac_data_dump (const char *prefix, gcry_ac_data_t data)
 {
-  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  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 (idx < data->data_n)
+  mpi_buffer = NULL;
+
+  data_n = _gcry_ac_data_length (data);
+  for (i = 0; i < data_n; i++)
     {
-      if (name)
-       *name = data->data[idx].name;
-      if (mpi)
-       *mpi = data->data[idx].mpi;
+      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;
     }
-  else
-    err = GPG_ERR_NO_DATA;
 
-  return gcry_error (err);
+  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)
+_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;
 }
 
+void
+gcry_ac_data_clear (gcry_ac_data_t data)
+{
+  return _gcry_ac_data_clear (data);
+}
+
 \f
 
 /*
- * Handle management.
+ * Implementation of `ac io' objects.
  */
 
-/* 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)
+/* 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.  */
+static 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)
 {
-  gcry_err_code_t err = GPG_ERR_NO_ERROR;
-  gcry_module_t module = NULL;
-  gcry_ac_handle_t handle_new;
-  const char *algorithm_name;
+  memset (ac_io, 0, sizeof (*ac_io));
 
-  *handle = NULL;
+  assert ((mode == GCRY_AC_IO_READABLE) || (mode == GCRY_AC_IO_WRITABLE));
+  assert ((type == GCRY_AC_IO_STRING) || (type == GCRY_AC_IO_STRING));
 
-  /* Get name.  */
-  algorithm_name = _gcry_pk_aliased_algo_name (algorithm);
-  if (! *algorithm_name)
-    err = GPG_ERR_PUBKEY_ALGO;
+  ac_io->mode = mode;
+  ac_io->type = type;
 
-  if (! err)  /* Acquire reference to the pubkey module.  */
-    err = _gcry_pk_module_lookup (algorithm, &module);
-  
-  if (! err)
+  switch (mode)
     {
-      /* Allocate.  */
-      handle_new = gcry_malloc (sizeof (struct gcry_ac_handle));
-      if (! handle_new)
-       err = gpg_err_code_from_errno (errno);
+    case GCRY_AC_IO_READABLE:
+      switch (type)
+       {
+       case GCRY_AC_IO_STRING:
+         ac_io->readable.string.data = va_arg (ap, unsigned char *);
+         ac_io->readable.string.data_n = va_arg (ap, size_t);
+         break;
+
+       case GCRY_AC_IO_CALLBACK:
+         ac_io->readable.callback.cb = va_arg (ap, gcry_ac_data_read_cb_t);
+         ac_io->readable.callback.opaque = va_arg (ap, void *);
+         break;
+       }
+      break;
+    case GCRY_AC_IO_WRITABLE:
+      switch (type)
+       {
+       case GCRY_AC_IO_STRING:
+         ac_io->writable.string.data = va_arg (ap, unsigned char **);
+         ac_io->writable.string.data_n = va_arg (ap, size_t *);
+         break;
+
+       case GCRY_AC_IO_CALLBACK:
+         ac_io->writable.callback.cb = va_arg (ap, gcry_ac_data_write_cb_t);
+         ac_io->writable.callback.opaque = va_arg (ap, void *);
+         break;
+       }
+      break;
     }
+}
+
+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)
+{
+  _gcry_ac_io_init_va (ac_io, mode, type, ap);
+}
+
+/* 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. */
+static 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);
+}
+
+
+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);
+}
 
-  if (! err)
+/* 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;
+
+  assert (ac_io->mode == GCRY_AC_IO_WRITABLE);
+  err = 0;
+
+  switch (ac_io->type)
     {
-      /* Done.  */
-      handle_new->algorithm = algorithm;
-      handle_new->algorithm_name = algorithm_name;
-      handle_new->flags = flags;
-      handle_new->module = module;
-      *handle = handle_new;
+    case GCRY_AC_IO_STRING:
+      {
+       unsigned char *p;
+
+       if (*ac_io->writable.string.data)
+         {
+           p = gcry_realloc (*ac_io->writable.string.data,
+                             *ac_io->writable.string.data_n + buffer_n);
+           if (! p)
+             err = gcry_error_from_errno (errno);
+           else
+             {
+               if (*ac_io->writable.string.data != p)
+                 *ac_io->writable.string.data = p;
+               memcpy (p + *ac_io->writable.string.data_n, buffer, buffer_n);
+               *ac_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->writable.string.data = p;
+               *ac_io->writable.string.data_n = buffer_n;
+             }
+         }
+      }
+      break;
+
+    case GCRY_AC_IO_CALLBACK:
+      err = (*ac_io->writable.callback.cb) (ac_io->writable.callback.opaque,
+                                           buffer, buffer_n);
+      break;
     }
-  else
+
+  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;
+  
+  assert (ac_io->mode == GCRY_AC_IO_READABLE);
+  err = 0;
+
+  switch (ac_io->type)
     {
-      /* Deallocate resources.  */
-      if (module)
-       _gcry_pk_module_release (module);
+    case GCRY_AC_IO_STRING:
+      {
+       size_t bytes_available;
+       size_t bytes_to_read;
+       size_t bytes_wanted;
+
+       bytes_available = ac_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->readable.string.data + nread, bytes_to_read);
+       *buffer_n = bytes_to_read;
+       err = 0;
+       break;
+      }
+
+    case GCRY_AC_IO_CALLBACK:
+      err = (*ac_io->readable.callback.cb) (ac_io->readable.callback.opaque,
+                                           buffer, buffer_n);
+      break;
     }
 
-  return gcry_error (err);
+  return err;
 }
 
-/* Destroys the handle HANDLE.  */
-void
-gcry_ac_close (gcry_ac_handle_t handle)
+/* 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)
 {
-  /* Release reference to pubkey module.  */
-  if (handle)
+  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)
     {
-      _gcry_pk_module_release (handle->module);
-      gcry_free (handle);
+      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;
 }
 
 \f
 
 /* 
- * Key management.
+ * Functions for converting data between the native ac and the
+ * S-expression structure used by the pk interface.
  */
 
-/* Creates a new key of type TYPE, consisting of the MPI values
-   contained in the data set DATA and stores it in KEY.  */
+/* 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>)) */
 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)
+ac_data_extract (const char *identifier, const char *algorithm,
+                gcry_sexp_t sexp, 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;
+  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;
 
-  /* Allocate.  */
-  key_new = gcry_malloc (sizeof (struct gcry_ac_key));
-  if (! key_new)
-    err = gpg_err_code_from_errno (errno);
+  value_sexp = NULL;
+  data_sexp = NULL;
+  value_name = NULL;
+  value_mpi = NULL;
+  data_new = NULL;
 
-  if (! err)
+  /* 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))
     {
-      /* Create S-expression from data set.  */
-      err = gcry_ac_data_construct (ac_key_identifiers[type], 0, 0,
-                                    handle->algorithm_name, data, &data_sexp);
+      err = gcry_error (GPG_ERR_INV_SEXP);
+      goto out;
     }
 
-  if (! err)
+  /* Extract inner S-expression.  */
+  data_sexp = gcry_sexp_find_token (sexp, algorithm, 0);
+  if (! data_sexp)
     {
-      /* Copy data set.  */
-      err = gcry_ac_data_copy_internal (&data_new, data);
+      err = gcry_error (GPG_ERR_INV_SEXP);
+      goto out;
     }
 
-  if (! err)
-    {
-      /* Done.  */
-      key_new->data_sexp = data_sexp;
-      key_new->data = data_new;
-      key_new->type = type;
-      *key = key_new;
-    }
-  else
+  /* 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++)
     {
-      /* Deallocate resources.  */
-      if (key_new)
-       gcry_free (key_new);
-      if (data_sexp)
-       gcry_sexp_release (data_sexp);
-    }
+      /* 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;
+       }
 
-  return gcry_error (err);
+      /* 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++)
+    {
+      /* FIXME!! */
+      arg_list[(i * 2) + 0] = (data->data[i].name
+                              ? (void **) &data->data[i].name
+                              : (void **) &data->data[i].name_provided);
+      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;
+}
+
+\f
+
+/*
+ * 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;
+
+  /* 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;
+}
+
+gcry_error_t
+gcry_ac_open (gcry_ac_handle_t *handle,
+             gcry_ac_id_t algorithm, unsigned int flags)
+{
+  gcry_error_t err;
+
+  err = _gcry_ac_open (handle, algorithm, flags);
+
+  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);
+    }
+}
+
+void
+gcry_ac_close (gcry_ac_handle_t handle)
+{
+  _gcry_ac_close (handle);
+}
+
+\f
+
+/* 
+ * 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;
+
+  /* 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;
+}
+
+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_error_t err;
+
+  err = _gcry_ac_key_init (key, handle, type, data);
+
+  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_<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;
+
+  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;
+
+  /* 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);
+  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;
+}
+
+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_error_t err;
+
+  err = _gcry_ac_key_pair_generate (handle, nbits, key_spec, key_pair, misc_data);
+
+  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 which)
+{
+  gcry_ac_key_t key;
+
+  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;
+}
+
+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;
+
+  key = _gcry_ac_key_pair_extract (key_pair, which);
+
+  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 != NULL)
+              gcry_mpi_release (key->data->data[i].mpi);
+          gcry_free (key->data);
+        }
+      gcry_free (key);
+    }
+}
+
+void
+gcry_ac_key_destroy (gcry_ac_key_t key)
+{
+  _gcry_ac_key_destroy (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);
+    }
+}
+
+void
+gcry_ac_key_pair_destroy (gcry_ac_key_pair_t key_pair)
+{
+  _gcry_ac_key_pair_destroy (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;
+}
+
+gcry_ac_data_t
+gcry_ac_key_data_get (gcry_ac_key_t key)
+{
+  gcry_ac_data_t data;
+
+  data = _gcry_ac_key_data_get (key);
+
+  return 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;
+
+  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);
+}
+
+gcry_error_t
+gcry_ac_key_test (gcry_ac_handle_t handle, gcry_ac_key_t key)
+{
+  gcry_error_t err;
+
+  err = _gcry_ac_key_test (handle, key);
+
+  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;
+
+  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;
+}
+
+gcry_error_t
+gcry_ac_key_get_nbits (gcry_ac_handle_t handle,
+                      gcry_ac_key_t key, unsigned int *nbits)
+{
+  gcry_error_t err;
+
+  err = _gcry_ac_key_get_nbits (handle, key, nbits);
+
+  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_sexp_t key_sexp;
+  gcry_error_t err;
+  unsigned char *ret;
+
+  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;
+}
+
+gcry_error_t
+gcry_ac_key_get_grip (gcry_ac_handle_t handle,
+                     gcry_ac_key_t key, unsigned char *key_grip)
+{
+  gcry_error_t err;
+
+  err = _gcry_ac_key_get_grip (handle, key, key_grip);
+
+  return gcry_error (err);
+}
+
+
+\f
+
+/* 
+ * 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;
+
+  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;
+}
+
+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_error_t err;
+
+  err = _gcry_ac_data_encrypt (handle, flags, key, data_plain, data_encrypted);
+
+  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_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;
+
+  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);
+
+}
+
+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_error_t err;
+
+  err = _gcry_ac_data_decrypt (handle, flags, key, data_plain, data_encrypted);
+
+  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;
+
+  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);
+}
+
+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_error_t err;
+
+  err = _gcry_ac_data_sign (handle, key, data, data_signature);
+
+  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;
+
+  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);
+}
+
+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_error_t err;
+
+  err = _gcry_ac_data_verify (handle, key, data, data_signature);
+
+  return gcry_error (err);
+}
+
+\f
+
+
+/*
+ * 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;
+
+  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;
+
+  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;
+  
+  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)
+{
+  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)
+{
+  return ac_data_dencode (method, DATA_DECODE, flags, options,
+                         ac_io_read, ac_io_write);
+}
+
+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)
+{
+  gcry_error_t err;
+
+  err = _gcry_ac_data_encode (method, flags, options,
+                             ac_io_read, ac_io_write);
+
+  return gcry_error (err);
+}
+
+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)
+{
+  gcry_error_t err;
+
+  err = _gcry_ac_data_decode (method, flags, options,
+                             ac_io_read, ac_io_write);
+  
+  return gcry_error (err);
+}
+
+/* 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;
+
+  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 octet string (I2OSP). */
+void
+gcry_ac_mpi_to_os (gcry_mpi_t mpi, unsigned char *os, size_t os_n)
+{
+  _gcry_ac_mpi_to_os (mpi, os, os_n);
+}
+
+/* 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;
+
+  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;
+}
+
+gcry_error_t
+gcry_ac_mpi_to_os_alloc (gcry_mpi_t mpi, unsigned char **os, size_t *os_n)
+{
+  gcry_error_t err;
+
+  err = _gcry_ac_mpi_to_os_alloc (mpi, os, os_n);
+
+  return gcry_error (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;
+  
+  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? */
+}
+
+void
+gcry_ac_os_to_mpi (gcry_mpi_t mpi, unsigned char *os, size_t os_n)
+{
+  _gcry_ac_os_to_mpi (mpi, os, os_n);
+}
+
+\f
+
+/* 
+ * 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;
+
+  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;
 }
 
-/* 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.  */
-gcry_error_t
-gcry_ac_key_pair_generate (gcry_ac_handle_t handle,
-                          gcry_ac_key_pair_t *key_pair,
-                          unsigned int nbits,
-                          void *key_spec)
+/* 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_err_code_t err = GPG_ERR_NO_ERROR;
+  gcry_ac_emsa_pkcs_v1_5_t *options_em;
+  gcry_ac_ssa_pkcs_v1_5_t *options;
+  gcry_error_t err;
+  unsigned int k;
 
-  gcry_ac_key_pair_t key_pair_new = NULL;
+  options_em = opts_em;
+  options = opts;
 
-  gcry_sexp_t genkey_sexp_request = NULL;
-  gcry_sexp_t genkey_sexp_reply = NULL;
+  err = _gcry_ac_key_get_nbits (handle, key, &k);
+  if (err)
+    goto out;
 
-  char *genkey_format = NULL;
-  size_t genkey_format_n = 0;
+  k = (k + 7) / 8;
+  options_em->md = options->md;
+  options_em->em_n = k;
 
-  void **arg_list = NULL;
-  size_t arg_list_n = 0;
+ out:
 
-  unsigned int i = 0;
+  return err;
+}
 
-  /* 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);
+/* 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) }
+  };
 
-  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));
+/* 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;
 
-      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;
-       }
-    }
+  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;
 
-  if (! err)
-    {
-      /* Calculate size of the format string, that is used for
-        creating the request S-expression.  */
-      genkey_format_n = 23;
+  return ac_scheme;
+}
 
-      /* 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;
+/* 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;
 
-      /* 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, "))");
-       }
+  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;
 
-  if (! err)
-    {
-      /* Build list of argument pointers, the algorithm name and the
-        nbits are needed always.  */
-      arg_list_n = 2;
+  *opts_em = options_em;
 
-      /* 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;
+ out:
 
-      /* 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;
+  if (err)
+    free (options_em);
 
-         arg_list[0] = (void *) &handle->algorithm_name;
-         arg_list[1] = (void *) &nbits;
+  return err;
+}
 
-         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);
-               }
-       }
+/* 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;
     }
 
-  if (! err)
-    /* Construct final request S-expression.  */
-    err = gcry_err_code (gcry_sexp_build_array (&genkey_sexp_request, NULL,
-                                              genkey_format, arg_list));
+  err = _gcry_ac_data_get_index (data, GCRY_AC_FLAG_COPY, 0, NULL, &mpi_new);
+  if (err)
+    goto out;
+
+  *mpi = mpi_new;
 
-  if (! err)
-    /* Perform genkey operation.  */
-    err = gcry_err_code (gcry_pk_genkey (&genkey_sexp_reply,
-                                       genkey_sexp_request));
+ out:
+
+  return err;
+}
 
-  /* Split keys.  */
-  if (! 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;
+
+  data_encrypted = NULL;
+  mpi_encrypted = NULL;
+  mpi_plain = NULL;
+  opts_em = NULL;
+  buffer = NULL;
+  em = NULL;
+
+  scheme = ac_scheme_get (scheme_id);
+  if (! scheme)
     {
-      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;
+      err = gcry_error (GPG_ERR_NO_ENCRYPTION_SCHEME);
+      goto out;
     }
-  if (! err)
+
+  if (key->type != GCRY_AC_KEY_PUBLIC)
     {
-      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;
+      err = gcry_error (GPG_ERR_WRONG_KEY_USAGE);
+      goto out;
     }
 
-  /* 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);
+  err = ac_dencode_prepare (handle, key, opts, *scheme, &opts_em);
+  if (err)
+    goto out;
 
-  /* Done.  */
+  _gcry_ac_io_init (&io_em, GCRY_AC_IO_WRITABLE,
+                   GCRY_AC_IO_STRING, &em, &em_n);
 
-  if (! err)
-    *key_pair = key_pair_new;
-  else
-    {
-      /* Deallocate resources.  */
+  err = _gcry_ac_data_encode (scheme->scheme_encoding, 0, opts_em,
+                             io_message, &io_em);
+  if (err)
+    goto out;
 
-      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);
+  mpi_plain = gcry_mpi_snew (0);
+  gcry_ac_os_to_mpi (mpi_plain, em, em_n);
 
-         gcry_free (key_pair_new);
-       }
+  err = _gcry_ac_data_encrypt (handle, 0, key, mpi_plain, &data_encrypted);
+  if (err)
+    goto out;
 
-      if (arg_list)
-       gcry_free (arg_list);
+  err = ac_data_set_to_mpi (data_encrypted, &mpi_encrypted);
+  if (err)
+    goto out;
 
-      if (genkey_format)
-       gcry_free (genkey_format);
+  err = _gcry_ac_mpi_to_os_alloc (mpi_encrypted, &buffer, &buffer_n);
+  if (err)
+    goto out;
 
-      if (genkey_sexp_request)
-       gcry_sexp_release (genkey_sexp_request);
-      if (genkey_sexp_reply)
-       gcry_sexp_release (genkey_sexp_reply);
-    }
+  err = _gcry_ac_io_write (io_cipher, buffer, buffer_n);
 
-  return gcry_error (err);
+ 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;
 }
 
-/* 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_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_ac_key_t key = NULL;
-
-  switch (witch)
-    {
-    case GCRY_AC_KEY_SECRET:
-      key = key_pair->secret;
-      break;
+  gcry_error_t err;
 
-    case GCRY_AC_KEY_PUBLIC:
-      key = key_pair->public;
-      break;
-    }
+  err = _gcry_ac_data_encrypt_scheme (handle, scheme_id, flags, opts, key,
+                                     io_message, io_cipher);
 
-  return key;
+  return gcry_error (err);
 }
 
-/* Destroys the key KEY.  */
-void
-gcry_ac_key_destroy (gcry_ac_key_t key)
+/* 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)
 {
-  int i;
+  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;
+
+  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)
+  if (key->type != GCRY_AC_KEY_SECRET)
     {
-      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);
+      err = gcry_error (GPG_ERR_WRONG_KEY_USAGE);
+      goto out;
     }
-}
 
-/* Destroys the key pair KEY_PAIR.  */
-void
-gcry_ac_key_pair_destroy (gcry_ac_key_pair_t key_pair)
-{
-  if (key_pair)
+  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)
     {
-      gcry_ac_key_destroy (key_pair->secret);
-      gcry_ac_key_destroy (key_pair->public);
-      gcry_free (key_pair);
+      /* FIXME? */
+      err = gcry_error (GPG_ERR_CONFLICT);
+      goto out;
     }
-}
 
-/* 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;
-}
+  err = _gcry_ac_data_new (&data_encrypted);
+  if (err)
+    goto out;
 
-/* Verifies that the key KEY is sane.  */
-gcry_error_t
-gcry_ac_key_test (gcry_ac_key_t key)
-{
-  gcry_err_code_t err;
+  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_err_code (gcry_pk_testkey (key->data_sexp));
+  err = _gcry_ac_data_decrypt (handle, 0, key, &mpi_decrypted, data_encrypted);
+  if (err)
+    goto out;
 
-  return gcry_error (err);
-}
+  err = _gcry_ac_mpi_to_os_alloc (mpi_decrypted, &em, &em_n);
+  if (err)
+    goto out;
 
-/* Stores the number of bits of the key KEY in NBITS.  */
-gcry_error_t
-gcry_ac_key_get_nbits (gcry_ac_key_t key, unsigned int *nbits)
-{
-  gcry_err_code_t err = GPG_ERR_NO_ERROR;
-  unsigned int n;
+  err = ac_dencode_prepare (handle, key, opts, *scheme, &opts_em);
+  if (err)
+    goto out;
 
-  n = gcry_pk_get_nbits (key->data_sexp);
-  if (n)
-    *nbits = n;
-  else
-    err = GPG_ERR_PUBKEY_ALGO;
+  _gcry_ac_io_init (&io_em, GCRY_AC_IO_READABLE,
+                   GCRY_AC_IO_STRING, em, em_n);
 
-  return gcry_error (err);
+  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;
 }
 
-/* Writes the 20 byte long key grip of the key KEY to KEY_GRIP.  */
 gcry_error_t
-gcry_ac_key_get_grip (gcry_ac_key_t key, unsigned char *key_grip)
+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_err_code_t err = GPG_ERR_NO_ERROR;
-  unsigned char *ret;
+  gcry_error_t err;
 
-  ret = gcry_pk_get_keygrip (key->data_sexp, key_grip);
-  if (! ret)
-    err = GPG_ERR_INV_OBJ;
+  err = _gcry_ac_data_decrypt_scheme (handle, scheme_id, flags, opts, key,
+                                     io_cipher, io_message);
 
   return gcry_error (err);
-}
-
-\f
-
-/* 
- * 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.  */
+}  
+
+/* 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_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_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_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;
+  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;
+
+  data_signed = NULL;
+  mpi_signed = NULL;
+  opts_em = NULL;
+  buffer = NULL;
+  mpi = NULL;
+  em = NULL;
 
-  if (key->type != GCRY_AC_KEY_PUBLIC)
-    err = GPG_ERR_WRONG_KEY_USAGE;
+  if (key->type != GCRY_AC_KEY_SECRET)
+    {
+      err = gcry_error (GPG_ERR_WRONG_KEY_USAGE);
+      goto out;
+    }
 
-  if (! err)
+  scheme = ac_scheme_get (scheme_id);
+  if (! scheme)
     {
-      /* Calculate request format string.  */
+      /* FIXME: adjust api of scheme_get in respect to err codes.  */
+      err = gcry_error (GPG_ERR_NO_SIGNATURE_SCHEME);
+      goto out;
+    }
 
-      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;
+  err = ac_dencode_prepare (handle, key, opts, *scheme, &opts_em);
+  if (err)
+    goto out;
 
-      /* Allocate request format string.  */
-      request_format = gcry_malloc (request_format_n);
-      if (! request_format)
-       err = gpg_err_code_from_errno (errno);
-    }
+  _gcry_ac_io_init (&io_em, GCRY_AC_IO_WRITABLE,
+                   GCRY_AC_IO_STRING, &em, &em_n);
 
-  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))");
+  err = _gcry_ac_data_encode (scheme->scheme_encoding, 0, opts_em,
+                             io_message, &io_em);
+  if (err)
+    goto out;
 
-      /* Create S-expression.  */
-      err = gcry_sexp_build (&sexp_request, NULL,
-                            request_format, data_plain);
-    }
+  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;
 
-  if (! err)
-    /* Encrypt.  */
-    err = gcry_pk_encrypt (&sexp_reply, sexp_request, key->data_sexp);
+  err = ac_data_set_to_mpi (data_signed, &mpi_signed);
+  if (err)
+    goto out;
 
-  if (! err)
-    /* Extract data.  */
-    err = gcry_ac_data_extract ("enc-val", handle->algorithm_name,
-                               sexp_reply, &data);
+  err = _gcry_ac_mpi_to_os_alloc (mpi_signed, &buffer, &buffer_n);
+  if (err)
+    goto out;
 
-  /* Deallocate resources.  */
+  err = _gcry_ac_io_write (io_signature, buffer, buffer_n);
 
-  if (sexp_request)
-    gcry_sexp_release (sexp_request);
-  if (sexp_reply)
-    gcry_sexp_release (sexp_reply);
+ out:
 
-  if (! err)
-    /* Copy out.  */
-    *data_encrypted = data;
+  _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 gcry_error (err);
+  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_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_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;
+  gcry_error_t err;
 
-  if (key->type != GCRY_AC_KEY_SECRET)
-    err = GPG_ERR_WRONG_KEY_USAGE;
+  err = _gcry_ac_data_sign_scheme (handle, scheme_id, flags, opts, key,
+                                  io_message, io_signature);
 
-  if (! err)
-    /* Create S-expression from data.  */
-    err = gcry_ac_data_construct ("enc-val", 1, flags, handle->algorithm_name,
-                                 data_encrypted, &sexp_request);
+  return gcry_error (err);
+}
 
-  if (! err)
-    /* Decrypt.  */
-    err = gcry_pk_decrypt (&sexp_reply, sexp_request, key->data_sexp);
+/* 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;
+
+  mpi_signature = NULL;
+  elements_sig = NULL;
+  data_signed = NULL;
+  mpi_data = NULL;
+  opts_em = NULL;
+  em = NULL;
+  s = NULL;
 
-  if (! err)
+  if (key->type != GCRY_AC_KEY_PUBLIC)
     {
-      /* 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);
-       }
+      err = gcry_error (GPG_ERR_WRONG_KEY_USAGE);
+      goto out;
     }
 
-  /* Done.  */
-
-  if (err)
+  scheme = ac_scheme_get (scheme_id);
+  if (! scheme)
     {
-      /* Deallocate resources.  */
-      if (sexp_request)
-       gcry_sexp_release (sexp_request);
-      if (sexp_reply)
-       gcry_sexp_release (sexp_reply);
+      err = gcry_error (GPG_ERR_NO_SIGNATURE_SCHEME);
+      goto out;
     }
-  else
-    *data_plain = data_decrypted;
-
-  return gcry_error (err);
 
-}
+  err = ac_dencode_prepare (handle, key, opts, *scheme, &opts_em);
+  if (err)
+    goto out;
 
-/* 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;
+  _gcry_ac_io_init (&io_em, GCRY_AC_IO_WRITABLE,
+                   GCRY_AC_IO_STRING, &em, &em_n);
 
-  if (key->type != GCRY_AC_KEY_SECRET)
-    err = GPG_ERR_WRONG_KEY_USAGE;
+  err = _gcry_ac_data_encode (scheme->scheme_encoding, 0, opts_em,
+                             io_message, &io_em);
+  if (err)
+    goto out;
 
-  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);
+  mpi_data = gcry_mpi_new (0);
+  _gcry_ac_os_to_mpi (mpi_data, em, em_n);
 
-  if (! err)
-    /* Extract data.  */
-    err = gcry_ac_data_extract ("sig-val", handle->algorithm_name,
-                               sexp_reply, &ac_data);
+  err = _gcry_ac_io_read_all (io_signature, &s, &s_n);
+  if (err)
+    goto out;
 
-  /* Done.  */
+  mpi_signature = gcry_mpi_new (0);
+  _gcry_ac_os_to_mpi (mpi_signature, s, s_n);
 
-  if (sexp_request)
-    gcry_sexp_release (sexp_request);
-  if (sexp_reply)
-    gcry_sexp_release (sexp_reply);
+  err = _gcry_pk_get_elements (handle->algorithm, NULL, &elements_sig);
+  if (err)
+    goto out;
 
-  if (! err)
-    *data_signature = ac_data;
+  elements_sig_n = strlen (elements_sig);
+  if (elements_sig_n != 1)
+    {
+      /* FIXME? */
+      err = gcry_error (GPG_ERR_CONFLICT);
+      goto out;
+    }
 
-  return gcry_error (err);
-}
+  err = _gcry_ac_data_new (&data_signed);
+  if (err)
+    goto out;
 
-/* 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;
+  err = _gcry_ac_data_set (data_signed, GCRY_AC_FLAG_COPY | GCRY_AC_FLAG_DEALLOC,
+                          elements_sig, mpi_signature);
+  if (err)
+    goto out;
 
-  if (key->type != GCRY_AC_KEY_PUBLIC)
-    err = GPG_ERR_WRONG_KEY_USAGE;
+  gcry_mpi_release (mpi_signature);
+  mpi_signature = NULL;
+  
+  err = _gcry_ac_data_verify (handle, key, mpi_data, data_signed);
 
-  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);
+ out:
 
-  if (! err)
-    /* Construct S-expression holding the data.  */
-    err = gcry_sexp_build (&sexp_data, NULL,
-                          "(data(flags)(value%m))", data);
+  _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);
 
-  if (! err)
-    /* Verify signature.  */
-    err = gcry_pk_verify (sexp_request, sexp_data, key->data_sexp);
+  return err;
+}
 
-  /* Done.  */
+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_error_t err;
 
-  if (sexp_request)
-    gcry_sexp_release (sexp_request);
-  if (sexp_data)
-    gcry_sexp_release (sexp_data);
+  err = _gcry_ac_data_verify_scheme (handle, scheme_id, flags, opts, key,
+                                    io_message, io_signature);
 
   return gcry_error (err);
 }
 
-\f
+
 
 /* 
  * General functions.
@@ -1328,14 +3549,20 @@ gcry_ac_data_verify (gcry_ac_handle_t handle,
 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;
+  gcry_error_t err;
   const char *n;
 
   n = gcry_pk_algo_name (algorithm);
-  if (*n)
-    *name = n;
-  else
-    err = GPG_ERR_PUBKEY_ALGO;
+  if (! *n)
+    {
+      err = gcry_error (GPG_ERR_PUBKEY_ALGO);
+      goto out;
+    }
+
+  *name = n;
+  err = 0;
+
+ out:
 
   return gcry_error (err);
 }
@@ -1345,14 +3572,26 @@ gcry_ac_id_to_name (gcry_ac_id_t algorithm, const char **name)
 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;
+  gcry_error_t err;
   int algo;
 
   algo = gcry_pk_map_name (name);
-  if (algo)
-    *algorithm = algo;
-  else
-    err = GPG_ERR_PUBKEY_ALGO;
-    
+  if (! algo)
+    {
+      err = gcry_error (GPG_ERR_PUBKEY_ALGO);
+      goto out;
+    }
+
+  *algorithm = algo;
+  err = 0;
+
+ out:
+
   return gcry_error (err);
 }
+
+gcry_err_code_t
+_gcry_ac_init (void)
+{
+  return 0;
+}