Nico Golde:
[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 #if HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include "mutt.h"
29 #include "pgp.h"
30 #include "pgppacket.h"
31 #include "mime.h"
32 #include "charset.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38
39 static struct 
40 {
41   short id;
42   const char *name;
43
44 HashAlgorithms[] = 
45 {
46   { 1,          "pgp-md5"               },
47   { 2,          "pgp-sha1"              },
48   { 3,          "pgp-ripemd160"         },
49   { 5,          "pgp-md2"               },
50   { 6,          "pgp-tiger192"          },
51   { 7,          "pgp-haval-5-160"       },
52   { 8,          "pgp-sha256"            },
53   { 9,          "pgp-sha384"            },
54   { 10,         "pgp-sha512"            },
55   { -1,         NULL }
56 };
57
58 static const char *pgp_hash_to_micalg (short id)
59 {
60   int i;
61   
62   for (i = 0; HashAlgorithms[i].id >= 0; i++)
63     if (HashAlgorithms[i].id == id)
64       return HashAlgorithms[i].name;
65   return "x-unknown";
66 }
67
68 static void pgp_dearmor (FILE *in, FILE *out)
69 {
70   char line[HUGE_STRING];
71   long start;
72   long end;
73   char *r;
74
75   STATE state;
76   
77   memset (&state, 0, sizeof (STATE));
78   state.fpin = in;
79   state.fpout = out;
80   
81   /* find the beginning of ASCII armor */
82   
83   while ((r = fgets (line, sizeof (line), in)) != NULL)
84   {
85     if (!strncmp (line, "-----BEGIN", 10))
86       break;
87   }
88   if (r == NULL)
89   {
90     dprint (1, (debugfile, "pgp_dearmor: Can't find begin of ASCII armor.\n"));
91     return;
92   }
93
94   /* skip the armor header */
95   
96   while ((r = fgets (line, sizeof (line), in)) != NULL)
97   {
98     SKIPWS (r);
99     if (!*r) break;
100   }
101   if (r == NULL)
102   {
103     dprint (1, (debugfile, "pgp_dearmor: Armor header doesn't end.\n"));
104     return;
105   }
106   
107   /* actual data starts here */
108   start = ftell (in);
109   
110   /* find the checksum */
111   
112   while ((r = fgets (line, sizeof (line), in)) != NULL)
113   {
114     if (*line == '=' || !strncmp (line, "-----END", 8))
115       break;
116   }
117   if (r == NULL)
118   {
119     dprint (1, (debugfile, "pgp_dearmor: Can't find end of ASCII armor.\n"));
120     return;
121   }
122   
123   if ((end = ftell (in) - strlen (line)) < start)
124   {
125     dprint (1, (debugfile, "pgp_dearmor: end < start???\n"));
126     return;
127   }
128   
129   if (fseek (in, start, SEEK_SET) == -1)
130   {
131     dprint (1, (debugfile, "pgp_dearmor: Can't seekto start.\n"));
132     return;
133   }
134
135   mutt_decode_base64 (&state, end - start, 0, (iconv_t) -1);
136 }
137
138 static short pgp_mic_from_packet (unsigned char *p, size_t len)
139 {
140   /* is signature? */
141   if ((p[0] & 0x3f) != PT_SIG)
142   {
143     dprint (1, (debugfile, "pgp_mic_from_packet: tag = %d, want %d.\n",
144                 p[0]&0x3f, PT_SIG));
145     return -1;
146   }
147   
148   if (len >= 18 && p[1] == 3)
149     /* version 3 signature */
150     return (short) p[17];
151   else if (len >= 5 && p[1] == 4)
152     /* version 4 signature */
153     return (short) p[4];
154   else
155   {
156     dprint (1, (debugfile, "pgp_mic_from_packet: Bad signature packet.\n"));
157     return -1;
158   }
159 }
160
161 static short pgp_find_hash (const char *fname)
162 {
163   FILE *in = NULL;
164   FILE *out = NULL;
165   
166   char tempfile[_POSIX_PATH_MAX];
167   
168   unsigned char *p;
169   size_t l;
170   
171   short rv = -1;
172   
173   mutt_mktemp (tempfile);
174   if ((out = safe_fopen (tempfile, "w+")) == NULL)
175   {
176     mutt_perror (tempfile);
177     goto bye;
178   }
179   unlink (tempfile);
180   
181   if ((in = fopen (fname, "r")) == NULL)
182   {
183     mutt_perror (fname);
184     goto bye;
185   }
186   
187   pgp_dearmor (in, out);
188   rewind (out);
189
190   if ((p = pgp_read_packet (out, &l)) != NULL)
191   {
192     rv = pgp_mic_from_packet (p, l);
193   }
194   else
195   {
196     dprint (1, (debugfile, "pgp_find_hash: No packet.\n"));
197   }
198   
199   bye:
200   
201   safe_fclose (&in);
202   safe_fclose (&out);
203   pgp_release_packet ();
204   return rv;
205 }
206
207 const char *pgp_micalg (const char *fname)
208 {
209   return pgp_hash_to_micalg (pgp_find_hash (fname));
210 }
211