1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-11-04 20:38:50 +01:00
gnupg/sm/decrypt.c

463 lines
13 KiB
C
Raw Normal View History

2001-12-06 00:48:01 +01:00
/* decrypt.c - Decrypt a message
* Copyright (C) 2001 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include <gcrypt.h>
#include <ksba.h>
#include "gpgsm.h"
#include "keydb.h"
#include "i18n.h"
2001-12-06 21:48:10 +01:00
struct decrypt_filter_parm_s {
int algo;
int mode;
2001-12-06 21:48:10 +01:00
int blklen;
GCRY_CIPHER_HD hd;
char iv[16];
size_t ivlen;
int any_data; /* dod we push anything through the filter at all? */
unsigned char lastblock[16]; /* to strip the padding we have to
keep this one */
char helpblock[16]; /* needed because there is no block buffering in
2001-12-06 21:48:10 +01:00
libgcrypt (yet) */
int helpblocklen;
};
2001-12-06 00:48:01 +01:00
static void
print_integer (unsigned char *p)
{
unsigned long len;
if (!p)
log_printf ("none");
else
{
len = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
for (p+=4; len; len--, p++)
log_printf ("%02X", *p);
}
}
2001-12-06 21:48:10 +01:00
/* decrypt the session key and fill in the parm structure. The
algo and the IV is expected to be already in PARM. */
static int
prepare_decryption (const char *hexkeygrip, const char *enc_val,
struct decrypt_filter_parm_s *parm)
{
char *seskey = NULL;
size_t n, seskeylen;
int rc;
rc = gpgsm_agent_pkdecrypt (hexkeygrip, enc_val, strlen (enc_val),
&seskey, &seskeylen);
if (rc)
{
log_error ("error decrypting session key: %s\n", gnupg_strerror (rc));
goto leave;
}
if (DBG_CRYPTO)
log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
n=0;
if (n + 7 > seskeylen )
{
rc = seterr (Invalid_Session_Key);
goto leave;
}
if (seskey[n] != 2 ) /* wrong block type version */
{
rc = seterr (Invalid_Session_Key);
goto leave;
}
for (n++; n < seskeylen && seskey[n]; n++) /* skip the random bytes */
;
n++; /* and the zero byte */
if (n >= seskeylen )
{
rc = seterr (Invalid_Session_Key);
goto leave;
}
if (DBG_CRYPTO)
log_printhex ("session key:", seskey+n, seskeylen-n);
parm->hd = gcry_cipher_open (parm->algo, parm->mode, 0);
2001-12-06 21:48:10 +01:00
if (!parm->hd)
{
rc = gcry_errno ();
log_error ("error creating decryptor: %s\n", gcry_strerror (rc));
rc = map_gcry_err (rc);
goto leave;
}
rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
if (rc == GCRYERR_WEAK_KEY)
{
log_info (_("WARNING: message was encrypted with "
"a weak key in the symmetric cipher.\n"));
rc = 0;
}
if (rc)
{
log_error("key setup failed: %s\n", gcry_strerror(rc) );
rc = map_gcry_err (rc);
goto leave;
}
gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen);
leave:
xfree (seskey);
return rc;
}
/* This function is called by the KSBA writer just before the actual
2001-12-06 21:48:10 +01:00
write is done. The function must take INLEN bytes from INBUF,
decrypt it and store it inoutbuf which has a maximum size of
maxoutlen. The valid bytes in outbuf should be return in outlen.
Due to different buffer sizes or different length of input and
output, it may happen that fewer bytes are process or fewer bytes
are written. */
static KsbaError
decrypt_filter (void *arg,
const void *inbuf, size_t inlen, size_t *inused,
void *outbuf, size_t maxoutlen, size_t *outlen)
{
struct decrypt_filter_parm_s *parm = arg;
int blklen = parm->blklen;
size_t orig_inlen = inlen;
/* fixme: Should we issue an error when we have not seen one full block? */
if (!inlen)
return KSBA_Bug;
if (maxoutlen < 2*parm->blklen)
return KSBA_Bug;
/* make some space becuase we will later need an extra block at the end */
maxoutlen -= blklen;
if (parm->helpblocklen)
{
int i, j;
for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++)
parm->helpblock[i] = ((const char*)inbuf)[j];
inlen -= j;
if (blklen > maxoutlen)
return KSBA_Bug;
if (i < blklen)
{
parm->helpblocklen = i;
*outlen = 0;
}
else
{
parm->helpblocklen = 0;
if (parm->any_data)
{
memcpy (outbuf, parm->lastblock, blklen);
*outlen =blklen;
}
else
*outlen = 0;
gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen,
parm->helpblock, blklen);
parm->any_data = 1;
}
*inused = orig_inlen - inlen;
return 0;
}
if (inlen > maxoutlen)
inlen = maxoutlen;
if (inlen % blklen)
{ /* store the remainder away */
parm->helpblocklen = inlen%blklen;
inlen = inlen/blklen*blklen;
memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen);
}
*inused = inlen + parm->helpblocklen;
if (inlen)
{
assert (inlen >= blklen);
if (parm->any_data)
{
gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen,
inbuf, inlen);
memcpy (outbuf, parm->lastblock, blklen);
memcpy (parm->lastblock,(char*)outbuf+inlen, blklen);
*outlen = inlen;
}
else
{
gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen);
*outlen = inlen - blklen;
parm->any_data = 1;
}
}
else
*outlen = 0;
return 0;
}
2001-12-06 00:48:01 +01:00
/* Perform a decrypt operation. */
int
gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
{
int rc;
KsbaError err;
Base64Context b64reader = NULL;
Base64Context b64writer = NULL;
KsbaReader reader;
KsbaWriter writer;
KsbaCMS cms = NULL;
KsbaStopReason stopreason;
KEYDB_HANDLE kh;
int recp;
FILE *in_fp = NULL;
2001-12-06 21:48:10 +01:00
struct decrypt_filter_parm_s dfparm;
memset (&dfparm, 0, sizeof dfparm);
2001-12-06 00:48:01 +01:00
kh = keydb_new (0);
if (!kh)
{
log_error (_("failed to allocated keyDB handle\n"));
rc = GNUPG_General_Error;
goto leave;
}
in_fp = fdopen ( dup (in_fd), "rb");
if (!in_fp)
{
log_error ("fdopen() failed: %s\n", strerror (errno));
rc = seterr (IO_Error);
goto leave;
}
rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, &reader);
if (rc)
{
log_error ("can't create reader: %s\n", gnupg_strerror (rc));
goto leave;
}
rc = gpgsm_create_writer (&b64reader, ctrl, out_fp, &writer);
if (rc)
{
log_error ("can't create writer: %s\n", gnupg_strerror (rc));
goto leave;
}
cms = ksba_cms_new ();
if (!cms)
{
rc = seterr (Out_Of_Core);
goto leave;
}
err = ksba_cms_set_reader_writer (cms, reader, writer);
if (err)
{
log_debug ("ksba_cms_set_reader_writer failed: %s\n",
ksba_strerror (err));
rc = map_ksba_err (err);
goto leave;
}
/* parser loop */
do
{
err = ksba_cms_parse (cms, &stopreason);
if (err)
{
log_debug ("ksba_cms_parse failed: %s\n", ksba_strerror (err));
rc = map_ksba_err (err);
goto leave;
}
if (stopreason == KSBA_SR_BEGIN_DATA
|| stopreason == KSBA_SR_DETACHED_DATA)
{
int algo, mode;
2001-12-06 21:48:10 +01:00
const char *algoid;
algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
algo = gcry_cipher_map_name (algoid);
mode = gcry_cipher_mode_from_oid (algoid);
if (!algo || !mode)
2001-12-06 21:48:10 +01:00
{
log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?");
rc = GNUPG_Unsupported_Algorithm;
goto leave;
}
dfparm.algo = algo;
dfparm.mode = mode;
2001-12-06 21:48:10 +01:00
dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
if (dfparm.blklen > sizeof (dfparm.helpblock))
return GNUPG_Bug;
rc = ksba_cms_get_content_enc_iv (cms,
dfparm.iv,
sizeof (dfparm.iv),
&dfparm.ivlen);
if (rc)
{
log_error ("error getting IV: %s\n", ksba_strerror (err));
rc = map_ksba_err (err);
goto leave;
}
2001-12-06 00:48:01 +01:00
for (recp=0; recp < 1; recp++)
{
char *issuer;
unsigned char *serial;
char *enc_val;
char *hexkeygrip = NULL;
err = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
if (err)
log_error ("recp %d - error getting info: %s\n",
recp, ksba_strerror (err));
else
{
KsbaCert cert = NULL;
log_debug ("recp %d - issuer: `%s'\n",
recp, issuer? issuer:"[NONE]");
log_debug ("recp %d - serial: ", recp);
print_integer (serial);
log_printf ("\n");
keydb_search_reset (kh);
rc = keydb_search_issuer_sn (kh, issuer, serial);
if (rc)
{
log_debug ("failed to find the certificate: %s\n",
gnupg_strerror(rc));
goto oops;
}
rc = keydb_get_cert (kh, &cert);
if (rc)
{
log_debug ("failed to get cert: %s\n", gnupg_strerror (rc));
2001-12-06 21:48:10 +01:00
goto oops; }
2001-12-06 00:48:01 +01:00
hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
oops:
xfree (issuer);
xfree (serial);
ksba_cert_release (cert);
}
enc_val = ksba_cms_get_enc_val (cms, recp);
if (!enc_val)
log_error ("recp %d - error getting encrypted session key\n",
recp);
else
{
log_debug ("recp %d - enc-val: `%s'\n",
recp, enc_val);
2001-12-06 21:48:10 +01:00
rc = prepare_decryption (hexkeygrip, enc_val,
&dfparm);
xfree (enc_val);
2001-12-06 00:48:01 +01:00
if (rc)
2001-12-06 21:48:10 +01:00
log_error ("decrypting session key failed: %s\n",
gnupg_strerror (rc));
2001-12-06 00:48:01 +01:00
else
2001-12-06 21:48:10 +01:00
{ /* setup the bulk decrypter */
ksba_writer_set_filter (writer,
decrypt_filter,
&dfparm);
}
}
}
}
else if (stopreason == KSBA_SR_END_DATA)
{
ksba_writer_set_filter (writer, NULL, NULL);
if (dfparm.any_data)
{ /* write the last block with padding removed */
int i, npadding = dfparm.lastblock[dfparm.blklen-1];
if (!npadding || npadding > dfparm.blklen)
{
log_error ("invalid padding with value %d\n", npadding);
rc = seterr (Invalid_Data);
goto leave;
}
rc = ksba_writer_write (writer,
dfparm.lastblock,
dfparm.blklen - npadding);
if (rc)
{
rc = map_ksba_err (rc);
goto leave;
}
for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
{
if (dfparm.lastblock[i] != npadding)
2001-12-06 00:48:01 +01:00
{
2001-12-06 21:48:10 +01:00
log_error ("inconsistent padding\n");
rc = seterr (Invalid_Data);
goto leave;
2001-12-06 00:48:01 +01:00
}
}
}
}
}
while (stopreason != KSBA_SR_READY);
leave:
ksba_cms_release (cms);
gpgsm_destroy_reader (b64reader);
gpgsm_destroy_writer (b64writer);
keydb_release (kh);
if (in_fp)
fclose (in_fp);
2001-12-06 21:48:10 +01:00
if (dfparm.hd)
gcry_cipher_close (dfparm.hd);
2001-12-06 00:48:01 +01:00
return rc;
}