Blame src/decrypt.c

Packit d7e8d0
/* decrypt.c - Decrypt function.
Packit d7e8d0
   Copyright (C) 2000 Werner Koch (dd9jn)
Packit d7e8d0
   Copyright (C) 2001, 2002, 2003, 2004, 2017 g10 Code GmbH
Packit d7e8d0
Packit d7e8d0
   This file is part of GPGME.
Packit d7e8d0
Packit d7e8d0
   GPGME is free software; you can redistribute it and/or modify it
Packit d7e8d0
   under the terms of the GNU Lesser General Public License as
Packit d7e8d0
   published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
   the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
   GPGME is distributed in the hope that it will be useful, but
Packit d7e8d0
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
   Lesser General Public License for more details.
Packit d7e8d0
Packit d7e8d0
   You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
   License along with this program; if not, write to the Free Software
Packit d7e8d0
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
Packit d7e8d0
   02111-1307, USA.  */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
#include <assert.h>
Packit d7e8d0
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
#include "gpgme.h"
Packit d7e8d0
#include "util.h"
Packit d7e8d0
#include "context.h"
Packit d7e8d0
#include "ops.h"
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
typedef struct
Packit d7e8d0
{
Packit d7e8d0
  struct _gpgme_op_decrypt_result result;
Packit d7e8d0
Packit d7e8d0
  /* The error code from a FAILURE status line or 0.  */
Packit d7e8d0
  gpg_error_t failure_code;
Packit d7e8d0
Packit d7e8d0
  int okay;
Packit d7e8d0
Packit d7e8d0
  /* A flag telling that the a decryption failed and an optional error
Packit d7e8d0
   * code to further specify the failure.  */
Packit d7e8d0
  int failed;
Packit d7e8d0
  gpg_error_t pkdecrypt_failed;
Packit d7e8d0
Packit d7e8d0
  /* At least one secret key is not available.  gpg issues NO_SECKEY
Packit d7e8d0
   * status lines for each key the message has been encrypted to but
Packit d7e8d0
   * that secret key is not available.  This can't be done for hidden
Packit d7e8d0
   * recipients, though.  We track it here to allow for a better error
Packit d7e8d0
   * message that the general DECRYPTION_FAILED. */
Packit d7e8d0
  int any_no_seckey;
Packit d7e8d0
Packit d7e8d0
  /* A pointer to the next pointer of the last recipient in the list.
Packit d7e8d0
     This makes appending new invalid signers painless while
Packit d7e8d0
     preserving the order.  */
Packit d7e8d0
  gpgme_recipient_t *last_recipient_p;
Packit d7e8d0
} *op_data_t;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
release_op_data (void *hook)
Packit d7e8d0
{
Packit d7e8d0
  op_data_t opd = (op_data_t) hook;
Packit d7e8d0
  gpgme_recipient_t recipient = opd->result.recipients;
Packit d7e8d0
Packit d7e8d0
  if (opd->result.unsupported_algorithm)
Packit d7e8d0
    free (opd->result.unsupported_algorithm);
Packit d7e8d0
Packit d7e8d0
  if (opd->result.file_name)
Packit d7e8d0
    free (opd->result.file_name);
Packit d7e8d0
Packit d7e8d0
  if (opd->result.session_key)
Packit d7e8d0
    free (opd->result.session_key);
Packit d7e8d0
Packit d7e8d0
  while (recipient)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_recipient_t next = recipient->next;
Packit d7e8d0
      free (recipient);
Packit d7e8d0
      recipient = next;
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_decrypt_result_t
Packit d7e8d0
gpgme_op_decrypt_result (gpgme_ctx_t ctx)
Packit d7e8d0
{
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx);
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err || !opd)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_SUC0 ("result=(null)");
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_debug_trace ())
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_recipient_t rcp;
Packit d7e8d0
Packit d7e8d0
      if (opd->result.unsupported_algorithm)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("result: unsupported_algorithm: %s",
Packit d7e8d0
		      opd->result.unsupported_algorithm);
Packit d7e8d0
	}
Packit d7e8d0
      if (opd->result.wrong_key_usage)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG ("result: wrong key usage");
Packit d7e8d0
	}
Packit d7e8d0
      rcp = opd->result.recipients;
Packit d7e8d0
      while (rcp)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, "
Packit d7e8d0
		      "status=%s", rcp->keyid, rcp->pubkey_algo,
Packit d7e8d0
		      gpg_strerror (rcp->status));
Packit d7e8d0
	  rcp = rcp->next;
Packit d7e8d0
	}
Packit d7e8d0
      if (opd->result.file_name)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("result: original file name: %s", opd->result.file_name);
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  TRACE_SUC1 ("result=%p", &opd->result);
Packit d7e8d0
  return &opd->result;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Parse the ARGS of an error status line and record some error
Packit d7e8d0
 * conditions at OPD.  Returns 0 on success.  */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_status_error (char *args, op_data_t opd)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  char *field[3];
Packit d7e8d0
  int nfields;
Packit d7e8d0
  char *args2;
Packit d7e8d0
Packit d7e8d0
  if (!args)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
  args2 = strdup (args); /* Split modifies the input string. */
Packit d7e8d0
  nfields = _gpgme_split_fields (args2, field, DIM (field));
Packit d7e8d0
  if (nfields < 1)
Packit d7e8d0
    {
Packit d7e8d0
      free (args2);
Packit d7e8d0
      return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing.  */
Packit d7e8d0
    }
Packit d7e8d0
  err = nfields < 2 ? 0 : atoi (field[1]);
Packit d7e8d0
Packit d7e8d0
  if (!strcmp (field[0], "decrypt.algorithm"))
Packit d7e8d0
    {
Packit d7e8d0
      if (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM
Packit d7e8d0
          && nfields > 2
Packit d7e8d0
          && strcmp (field[2], "?"))
Packit d7e8d0
        {
Packit d7e8d0
          opd->result.unsupported_algorithm = strdup (field[2]);
Packit d7e8d0
          if (!opd->result.unsupported_algorithm)
Packit d7e8d0
            {
Packit d7e8d0
              free (args2);
Packit d7e8d0
              return gpg_error_from_syserror ();
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
  else if (!strcmp (field[0], "decrypt.keyusage"))
Packit d7e8d0
    {
Packit d7e8d0
      if (gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
Packit d7e8d0
        opd->result.wrong_key_usage = 1;
Packit d7e8d0
    }
Packit d7e8d0
  else if (!strcmp (field[0], "pkdecrypt_failed"))
Packit d7e8d0
    {
Packit d7e8d0
      switch (gpg_err_code (err))
Packit d7e8d0
        {
Packit d7e8d0
        case GPG_ERR_CANCELED:
Packit d7e8d0
        case GPG_ERR_FULLY_CANCELED:
Packit d7e8d0
          /* It is better to return with a cancel error code than the
Packit d7e8d0
           * general decryption failed error code.  */
Packit d7e8d0
          opd->pkdecrypt_failed = gpg_err_make (gpg_err_source (err),
Packit d7e8d0
                                                GPG_ERR_CANCELED);
Packit d7e8d0
          break;
Packit d7e8d0
Packit d7e8d0
        case GPG_ERR_BAD_PASSPHRASE:
Packit d7e8d0
          /* A bad passphrase is severe enough that we return this
Packit d7e8d0
           * error code.  */
Packit d7e8d0
          opd->pkdecrypt_failed = err;
Packit d7e8d0
          break;
Packit d7e8d0
Packit d7e8d0
        default:
Packit d7e8d0
          /* For now all other error codes are ignored and the
Packit d7e8d0
           * standard DECRYPT_FAILED is returned.  */
Packit d7e8d0
          break;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
  free (args2);
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_enc_to (char *args, gpgme_recipient_t *recp, gpgme_protocol_t protocol)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_recipient_t rec;
Packit d7e8d0
  char *tail;
Packit d7e8d0
  int i;
Packit d7e8d0
Packit d7e8d0
  rec = malloc (sizeof (*rec));
Packit d7e8d0
  if (!rec)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  rec->next = NULL;
Packit d7e8d0
  rec->keyid = rec->_keyid;
Packit d7e8d0
  rec->status = 0;
Packit d7e8d0
Packit d7e8d0
  for (i = 0; i < sizeof (rec->_keyid) - 1; i++)
Packit d7e8d0
    {
Packit d7e8d0
      if (args[i] == '\0' || args[i] == ' ')
Packit d7e8d0
	break;
Packit d7e8d0
Packit d7e8d0
      rec->_keyid[i] = args[i];
Packit d7e8d0
    }
Packit d7e8d0
  rec->_keyid[i] = '\0';
Packit d7e8d0
Packit d7e8d0
  args = &args[i];
Packit d7e8d0
  if (*args != '\0' && *args != ' ')
Packit d7e8d0
    {
Packit d7e8d0
      free (rec);
Packit d7e8d0
      return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  while (*args == ' ')
Packit d7e8d0
    args++;
Packit d7e8d0
Packit d7e8d0
  if (*args)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (0);
Packit d7e8d0
      rec->pubkey_algo = _gpgme_map_pk_algo (strtol (args, &tail, 0), protocol);
Packit d7e8d0
      if (errno || args == tail || *tail != ' ')
Packit d7e8d0
	{
Packit d7e8d0
	  /* The crypto backend does not behave.  */
Packit d7e8d0
	  free (rec);
Packit d7e8d0
	  return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* FIXME: The key length is always 0 right now, so no need to parse
Packit d7e8d0
     it.  */
Packit d7e8d0
Packit d7e8d0
  *recp = rec;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
_gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
Packit d7e8d0
			       char *args)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_passphrase_status_handler (priv, code, args);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  switch (code)
Packit d7e8d0
    {
Packit d7e8d0
    case GPGME_STATUS_FAILURE:
Packit d7e8d0
      opd->failure_code = _gpgme_parse_failure (args);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_EOF:
Packit d7e8d0
      /* FIXME: These error values should probably be attributed to
Packit d7e8d0
	 the underlying crypto engine (as error source).  */
Packit d7e8d0
      if (opd->failed && opd->pkdecrypt_failed)
Packit d7e8d0
        return opd->pkdecrypt_failed;
Packit d7e8d0
      else if (opd->failed && opd->any_no_seckey)
Packit d7e8d0
	return gpg_error (GPG_ERR_NO_SECKEY);
Packit d7e8d0
      else if (opd->failed)
Packit d7e8d0
	return gpg_error (GPG_ERR_DECRYPT_FAILED);
Packit d7e8d0
      else if (!opd->okay)
Packit d7e8d0
	return gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
      else if (opd->failure_code)
Packit d7e8d0
        return opd->failure_code;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_DECRYPTION_INFO:
Packit d7e8d0
      /* Fixme: Provide a way to return the used symmetric algorithm. */
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_DECRYPTION_OKAY:
Packit d7e8d0
      opd->okay = 1;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_DECRYPTION_FAILED:
Packit d7e8d0
      opd->failed = 1;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_ERROR:
Packit d7e8d0
      /* Note that this is an informational status code which should
Packit d7e8d0
         not lead to an error return unless it is something not
Packit d7e8d0
         related to the backend.  */
Packit d7e8d0
      err = parse_status_error (args, opd);
Packit d7e8d0
      if (err)
Packit d7e8d0
        return err;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_ENC_TO:
Packit d7e8d0
      err = parse_enc_to (args, opd->last_recipient_p, ctx->protocol);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
Packit d7e8d0
      opd->last_recipient_p = &(*opd->last_recipient_p)->next;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_SESSION_KEY:
Packit d7e8d0
      if (opd->result.session_key)
Packit d7e8d0
        free (opd->result.session_key);
Packit d7e8d0
      opd->result.session_key = strdup(args);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_NO_SECKEY:
Packit d7e8d0
      {
Packit d7e8d0
	gpgme_recipient_t rec = opd->result.recipients;
Packit d7e8d0
	while (rec)
Packit d7e8d0
	  {
Packit d7e8d0
	    if (!strcmp (rec->keyid, args))
Packit d7e8d0
	      {
Packit d7e8d0
		rec->status = gpg_error (GPG_ERR_NO_SECKEY);
Packit d7e8d0
		break;
Packit d7e8d0
	      }
Packit d7e8d0
	    rec = rec->next;
Packit d7e8d0
	  }
Packit d7e8d0
	/* FIXME: Is this ok?  */
Packit d7e8d0
	if (!rec)
Packit d7e8d0
	  return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
        opd->any_no_seckey = 1;
Packit d7e8d0
      }
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_PLAINTEXT:
Packit d7e8d0
      err = _gpgme_parse_plaintext (args, &opd->result.file_name);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_INQUIRE_MAXLEN:
Packit d7e8d0
      if (ctx->status_cb && !ctx->full_status)
Packit d7e8d0
        {
Packit d7e8d0
          err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args);
Packit d7e8d0
          if (err)
Packit d7e8d0
            return err;
Packit d7e8d0
        }
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_DECRYPTION_COMPLIANCE_MODE:
Packit d7e8d0
      PARSE_COMPLIANCE_FLAGS (args, &opd->result);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    default:
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_progress_status_handler (priv, code, args);
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = _gpgme_decrypt_status_handler (priv, code, args);
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
_gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook,
Packit d7e8d0
			       sizeof (*opd), release_op_data);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  opd->last_recipient_p = &opd->result.recipients;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
_gpgme_decrypt_start (gpgme_ctx_t ctx, int synchronous,
Packit d7e8d0
                      gpgme_decrypt_flags_t flags,
Packit d7e8d0
                      gpgme_data_t cipher, gpgme_data_t plain)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  assert (!(flags & GPGME_DECRYPT_VERIFY));
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_reset (ctx, synchronous);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_decrypt_init_result (ctx);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  if (!cipher)
Packit d7e8d0
    return gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
  if (!plain)
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  if (ctx->passphrase_cb)
Packit d7e8d0
    {
Packit d7e8d0
      err = _gpgme_engine_set_command_handler
Packit d7e8d0
	(ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
Packit d7e8d0
Packit d7e8d0
  return _gpgme_engine_op_decrypt (ctx->engine,
Packit d7e8d0
                                   flags,
Packit d7e8d0
                                   cipher, plain,
Packit d7e8d0
                                   ctx->export_session_keys,
Packit d7e8d0
                                   ctx->override_session_key,
Packit d7e8d0
                                   ctx->auto_key_retrieve);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
Packit d7e8d0
			gpgme_data_t plain)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx,
Packit d7e8d0
	      "cipher=%p, plain=%p", cipher, plain);
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_decrypt_start (ctx, 0, 0, cipher, plain);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Decrypt ciphertext CIPHER within CTX and store the resulting
Packit d7e8d0
   plaintext in PLAIN.  */
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt", ctx,
Packit d7e8d0
	      "cipher=%p, plain=%p", cipher, plain);
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_decrypt_start (ctx, 1, 0, cipher, plain);
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = _gpgme_wait_one (ctx);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}