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