Andreas Krennmair:
[apps/madmutt.git] / pgpmicalg.c
1 /*
2  * Copyright (C) 2001 Thomas Roessler <roessler@does-not-exist.org>
3  * 
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public
15  * License along with this program; if not, write to the Free
16  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
17  * MA 02111, USA.
18  */
19
20 /* This module peeks at a PGP signature and figures out the hash
21  * algorithm.
22  */
23
24 #include "mutt.h"
25 #include "pgp.h"
26 #include "pgppacket.h"
27 #include "mime.h"
28 #include "charset.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 static struct 
36 {
37   short id;
38   const char *name;
39
40 HashAlgorithms[] = 
41 {
42   { 1,          "pgp-md5"               },
43   { 2,          "pgp-sha1"              },
44   { 3,          "pgp-ripemd160"         },
45   { 5,          "pgp-md2"               },
46   { 6,          "pgp-tiger192"          },
47   { 7,          "pgp-haval-5-160"       },
48   { 8,          "pgp-sha256"            },
49   { 9,          "pgp-sha384"            },
50   { 10,         "pgp-sha512"            },
51   { -1,         NULL }
52 };
53
54 static const char *pgp_hash_to_micalg (short id)
55 {
56   int i;
57   
58   for (i = 0; HashAlgorithms[i].id >= 0; i++)
59     if (HashAlgorithms[i].id == id)
60       return HashAlgorithms[i].name;
61   return "x-unknown";
62 }
63
64 static void pgp_dearmor (FILE *in, FILE *out)
65 {
66   char line[HUGE_STRING];
67   long start;
68   long end;
69   char *r;
70
71   STATE state;
72   
73   memset (&state, 0, sizeof (STATE));
74   state.fpin = in;
75   state.fpout = out;
76   
77   /* find the beginning of ASCII armor */
78   
79   while ((r = fgets (line, sizeof (line), in)) != NULL)
80   {
81     if (!strncmp (line, "-----BEGIN", 10))
82       break;
83   }
84   if (r == NULL)
85   {
86     dprint (1, (debugfile, "pgp_dearmor: Can't find begin of ASCII armor.\n"));
87     return;
88   }
89
90   /* skip the armor header */
91   
92   while ((r = fgets (line, sizeof (line), in)) != NULL)
93   {
94     SKIPWS (r);
95     if (!*r) break;
96   }
97   if (r == NULL)
98   {
99     dprint (1, (debugfile, "pgp_dearmor: Armor header doesn't end.\n"));
100     return;
101   }
102   
103   /* actual data starts here */
104   start = ftell (in);
105   
106   /* find the checksum */
107   
108   while ((r = fgets (line, sizeof (line), in)) != NULL)
109   {
110     if (*line == '=' || !strncmp (line, "-----END", 8))
111       break;
112   }
113   if (r == NULL)
114   {
115     dprint (1, (debugfile, "pgp_dearmor: Can't find end of ASCII armor.\n"));
116     return;
117   }
118   
119   if ((end = ftell (in) - strlen (line)) < start)
120   {
121     dprint (1, (debugfile, "pgp_dearmor: end < start???\n"));
122     return;
123   }
124   
125   if (fseek (in, start, SEEK_SET) == -1)
126   {
127     dprint (1, (debugfile, "pgp_dearmor: Can't seekto start.\n"));
128     return;
129   }
130
131   mutt_decode_base64 (&state, end - start, 0, (iconv_t) -1);
132 }
133
134 static short pgp_mic_from_packet (unsigned char *p, size_t len)
135 {
136   /* is signature? */
137   if ((p[0] & 0x3f) != PT_SIG)
138   {
139     dprint (1, (debugfile, "pgp_mic_from_packet: tag = %d, want %d.\n",
140                 p[0]&0x3f, PT_SIG));
141     return -1;
142   }
143   
144   if (len >= 18 && p[1] == 3)
145     /* version 3 signature */
146     return (short) p[17];
147   else if (len >= 5 && p[1] == 4)
148     /* version 4 signature */
149     return (short) p[4];
150   else
151   {
152     dprint (1, (debugfile, "pgp_mic_from_packet: Bad signature packet.\n"));
153     return -1;
154   }
155 }
156
157 static short pgp_find_hash (const char *fname)
158 {
159   FILE *in = NULL;
160   FILE *out = NULL;
161   
162   char tempfile[_POSIX_PATH_MAX];
163   
164   unsigned char *p;
165   size_t l;
166   
167   short rv = -1;
168   
169   mutt_mktemp (tempfile);
170   if ((out = safe_fopen (tempfile, "w+")) == NULL)
171   {
172     mutt_perror (tempfile);
173     goto bye;
174   }
175   unlink (tempfile);
176   
177   if ((in = fopen (fname, "r")) == NULL)
178   {
179     mutt_perror (fname);
180     goto bye;
181   }
182   
183   pgp_dearmor (in, out);
184   rewind (out);
185
186   if ((p = pgp_read_packet (out, &l)) != NULL)
187   {
188     rv = pgp_mic_from_packet (p, l);
189   }
190   else
191   {
192     dprint (1, (debugfile, "pgp_find_hash: No packet.\n"));
193   }
194   
195   bye:
196   
197   safe_fclose (&in);
198   safe_fclose (&out);
199   pgp_release_packet ();
200   return rv;
201 }
202
203 const char *pgp_micalg (const char *fname)
204 {
205   return pgp_hash_to_micalg (pgp_find_hash (fname));
206 }
207