From ce32f6b6c8b583cd29fe52581d0428e95d4dec8a Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 6 Dec 2001 20:48:10 +0000 Subject: [PATCH] Decryption does now work --- sm/base64.c | 5 - sm/call-agent.c | 40 +++++-- sm/decrypt.c | 279 ++++++++++++++++++++++++++++++++++++++++++++---- sm/gpgsm.h | 5 + 4 files changed, 295 insertions(+), 34 deletions(-) diff --git a/sm/base64.c b/sm/base64.c index 1efe31d55..dca0fd14b 100644 --- a/sm/base64.c +++ b/sm/base64.c @@ -553,8 +553,3 @@ gpgsm_destroy_writer (Base64Context ctx) { xfree (ctx); } - - - - - diff --git a/sm/call-agent.c b/sm/call-agent.c index 0c0b48bf3..9aa6cc316 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -190,10 +190,12 @@ start_agent (void) log_debug ("connection to agent established\n"); - log_debug ("waiting for debugger .....\n"); - getchar (); - log_debug ("okay\n"); - + if (DBG_AGENT) + { + log_debug ("waiting for debugger [hit RETURN when ready] .....\n"); + getchar (); + log_debug ("... okay\n"); + } return 0; } @@ -288,8 +290,9 @@ gpgsm_agent_pkdecrypt (const char *keygrip, char line[LINELENGTH]; struct membuf data; struct cipher_parm_s cipher_parm; - size_t len; - + size_t n, len; + char *buf, *endp; + if (!keygrip || strlen(keygrip) != 40 || !ciphertext || !r_buf || !r_buflen) return GNUPG_Invalid_Value; *r_buf = NULL; @@ -321,7 +324,28 @@ gpgsm_agent_pkdecrypt (const char *keygrip, xfree (get_membuf (&data, &len)); return map_assuan_err (rc); } - *r_buf = get_membuf (&data, r_buflen); - return *r_buf? 0 : GNUPG_Out_Of_Core; + + put_membuf (&data, "", 1); /* make sure it is 0 terminated */ + buf = get_membuf (&data, &len); + if (!buf) + return seterr (Out_Of_Core); + assert (len); + len--; /* remove the terminating 0 */ + n = strtoul (buf, &endp, 10); + if (!n || *endp != ':') + return seterr (Invalid_Sexp); + endp++; + if (endp-buf+n > len) + return seterr (Invalid_Sexp); /* oops len does not match internal len*/ + memmove (buf, endp, n); + *r_buflen = n; + *r_buf = buf; + return 0; } + + + + + + diff --git a/sm/decrypt.c b/sm/decrypt.c index 6748b2ad1..1a18bf8d5 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -34,6 +34,21 @@ #include "keydb.h" #include "i18n.h" +struct decrypt_filter_parm_s { + int algo; + 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 bufferin in + libgcrypt (yet) */ + int helpblocklen; +}; + + static void print_integer (unsigned char *p) { @@ -49,6 +64,174 @@ print_integer (unsigned char *p) } } +/* 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, GCRY_CIPHER_MODE_CBC, 0); + 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 KSab writer just before the actual + 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; +} @@ -67,6 +250,9 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp) KEYDB_HANDLE kh; int recp; FILE *in_fp = NULL; + struct decrypt_filter_parm_s dfparm; + + memset (&dfparm, 0, sizeof dfparm); kh = keydb_new (0); if (!kh) @@ -130,6 +316,33 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp) if (stopreason == KSBA_SR_BEGIN_DATA || stopreason == KSBA_SR_DETACHED_DATA) { + int algo; + const char *algoid; + + algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/); + algo = gcry_cipher_map_name (algoid); + if (!algo) + { + log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?"); + rc = GNUPG_Unsupported_Algorithm; + goto leave; + } + dfparm.algo = algo; + 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; + } + for (recp=0; recp < 1; recp++) { char *issuer; @@ -164,8 +377,7 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp) if (rc) { log_debug ("failed to get cert: %s\n", gnupg_strerror (rc)); - goto oops; - } + goto oops; } hexkeygrip = gpgsm_get_keygrip_hexstring (cert); @@ -181,31 +393,54 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp) recp); else { - char *seskey; - size_t seskeylen; - log_debug ("recp %d - enc-val: `%s'\n", recp, enc_val); - - rc = gpgsm_agent_pkdecrypt (hexkeygrip, - enc_val, strlen (enc_val), - &seskey, &seskeylen); - if (rc) - log_debug ("problem: %s\n", gnupg_strerror (rc)); - else - { - unsigned char *p; - log_debug ("plaintext="); - for (p=seskey; seskeylen; seskeylen--, p++) - log_printf (" %02X", *p); - log_printf ("\n"); - } + rc = prepare_decryption (hexkeygrip, enc_val, + &dfparm); xfree (enc_val); + if (rc) + log_error ("decrypting session key failed: %s\n", + gnupg_strerror (rc)); + else + { /* 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) + { + log_error ("inconsistent padding\n"); + rc = seterr (Invalid_Data); + goto leave; + } } } } - - } while (stopreason != KSBA_SR_READY); @@ -217,6 +452,8 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp) keydb_release (kh); if (in_fp) fclose (in_fp); + if (dfparm.hd) + gcry_cipher_close (dfparm.hd); return rc; } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 14c5683a6..13337829b 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -167,5 +167,10 @@ int gpgsm_agent_pksign (const char *keygrip, size_t digestlen, int digestalgo, char **r_buf, size_t *r_buflen); +int gpgsm_agent_pkdecrypt (const char *keygrip, + const char *ciphertext, size_t ciphertextlen, + char **r_buf, size_t *r_buflen); + #endif /*GPGSM_H*/ +