/*
* Copyright (C) 2001 Thomas Roessler <roessler@does-not-exist.org>
*
* This program 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.
*
* This program 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/* This module peeks at a PGP signature and figures out the hash
* algorithm.
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include "mutt.h"
#include "pgp.h"
#include "pgppacket.h"
#include "charset.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
static const struct
{
short id;
const char *name;
}
HashAlgorithms[] =
{
{ 1, "pgp-md5" },
{ 2, "pgp-sha1" },
{ 3, "pgp-ripemd160" },
{ 5, "pgp-md2" },
{ 6, "pgp-tiger192" },
{ 7, "pgp-haval-5-160" },
{ 8, "pgp-sha256" },
{ 9, "pgp-sha384" },
{ 10, "pgp-sha512" },
{ 11, "pgp-sha224" },
{ -1, NULL }
};
static const char *pgp_hash_to_micalg (short id)
{
int i;
for (i = 0; HashAlgorithms[i].id >= 0; i++)
if (HashAlgorithms[i].id == id)
return HashAlgorithms[i].name;
return "x-unknown";
}
static void pgp_dearmor (FILE *in, FILE *out)
{
char line[HUGE_STRING];
LOFF_T start;
LOFF_T end;
char *r;
STATE state;
memset (&state, 0, sizeof (STATE));
state.fpin = in;
state.fpout = out;
/* find the beginning of ASCII armor */
while ((r = fgets (line, sizeof (line), in)) != NULL)
{
if (!strncmp (line, "-----BEGIN", 10))
break;
}
if (r == NULL)
{
dprint (1, (debugfile, "pgp_dearmor: Can't find begin of ASCII armor.\n"));
return;
}
/* skip the armor header */
while ((r = fgets (line, sizeof (line), in)) != NULL)
{
SKIPWS (r);
if (!*r) break;
}
if (r == NULL)
{
dprint (1, (debugfile, "pgp_dearmor: Armor header doesn't end.\n"));
return;
}
/* actual data starts here */
start = ftello (in);
/* find the checksum */
while ((r = fgets (line, sizeof (line), in)) != NULL)
{
if (*line == '=' || !strncmp (line, "-----END", 8))
break;
}
if (r == NULL)
{
dprint (1, (debugfile, "pgp_dearmor: Can't find end of ASCII armor.\n"));
return;
}
if ((end = ftello (in) - strlen (line)) < start)
{
dprint (1, (debugfile, "pgp_dearmor: end < start???\n"));
return;
}
if (fseeko (in, start, SEEK_SET) == -1)
{
dprint (1, (debugfile, "pgp_dearmor: Can't seekto start.\n"));
return;
}
mutt_decode_base64 (&state, end - start, 0, (iconv_t) -1);
}
static short pgp_mic_from_packet (unsigned char *p, size_t len)
{
/* is signature? */
if ((p[0] & 0x3f) != PT_SIG)
{
dprint (1, (debugfile, "pgp_mic_from_packet: tag = %d, want %d.\n",
p[0]&0x3f, PT_SIG));
return -1;
}
if (len >= 18 && p[1] == 3)
/* version 3 signature */
return (short) p[17];
else if (len >= 5 && p[1] == 4)
/* version 4 signature */
return (short) p[4];
else
{
dprint (1, (debugfile, "pgp_mic_from_packet: Bad signature packet.\n"));
return -1;
}
}
static short pgp_find_hash (const char *fname)
{
FILE *in = NULL;
FILE *out = NULL;
BUFFER *tempfile = NULL;
unsigned char *p;
size_t l;
short rv = -1;
tempfile = mutt_buffer_pool_get ();
mutt_buffer_mktemp (tempfile);
if ((out = safe_fopen (mutt_b2s (tempfile), "w+")) == NULL)
{
mutt_perror (mutt_b2s (tempfile));
goto bye;
}
unlink (mutt_b2s (tempfile));
if ((in = fopen (fname, "r")) == NULL)
{
mutt_perror (fname);
goto bye;
}
pgp_dearmor (in, out);
rewind (out);
if ((p = pgp_read_packet (out, &l)) != NULL)
{
rv = pgp_mic_from_packet (p, l);
}
else
{
dprint (1, (debugfile, "pgp_find_hash: No packet.\n"));
}
bye:
mutt_buffer_pool_release (&tempfile);
safe_fclose (&in);
safe_fclose (&out);
pgp_release_packet ();
return rv;
}
const char *pgp_micalg (const char *fname)
{
return pgp_hash_to_micalg (pgp_find_hash (fname));
}