Nico Golde:
[apps/madmutt.git] / pgpmicalg.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 2001 Thomas Roessler <roessler@does-not-exist.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 /* This module peeks at a PGP signature and figures out the hash
11  * algorithm.
12  */
13
14 #if HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #include "mutt.h"
19 #include "pgp.h"
20 #include "pgppacket.h"
21 #include "mime.h"
22 #include "charset.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28
29 static struct {
30   short id;
31   const char *name;
32 } HashAlgorithms[] = {
33   {
34   1, "pgp-md5"}, {
35   2, "pgp-sha1"}, {
36   3, "pgp-ripemd160"}, {
37   5, "pgp-md2"}, {
38   6, "pgp-tiger192"}, {
39   7, "pgp-haval-5-160"}, {
40   8, "pgp-sha256"}, {
41   9, "pgp-sha384"}, {
42   10, "pgp-sha512"}, {
43   -1, NULL}
44 };
45
46 static const char *pgp_hash_to_micalg (short id)
47 {
48   int i;
49
50   for (i = 0; HashAlgorithms[i].id >= 0; i++)
51     if (HashAlgorithms[i].id == id)
52       return HashAlgorithms[i].name;
53   return "x-unknown";
54 }
55
56 static void pgp_dearmor (FILE * in, FILE * out)
57 {
58   char line[HUGE_STRING];
59   long start;
60   long end;
61   char *r;
62
63   STATE state;
64
65   memset (&state, 0, sizeof (STATE));
66   state.fpin = in;
67   state.fpout = out;
68
69   /* find the beginning of ASCII armor */
70
71   while ((r = fgets (line, sizeof (line), in)) != NULL) {
72     if (!strncmp (line, "-----BEGIN", 10))
73       break;
74   }
75   if (r == NULL) {
76     dprint (1,
77             (debugfile, "pgp_dearmor: Can't find begin of ASCII armor.\n"));
78     return;
79   }
80
81   /* skip the armor header */
82
83   while ((r = fgets (line, sizeof (line), in)) != NULL) {
84     SKIPWS (r);
85     if (!*r)
86       break;
87   }
88   if (r == NULL) {
89     dprint (1, (debugfile, "pgp_dearmor: Armor header doesn't end.\n"));
90     return;
91   }
92
93   /* actual data starts here */
94   start = ftell (in);
95
96   /* find the checksum */
97
98   while ((r = fgets (line, sizeof (line), in)) != NULL) {
99     if (*line == '=' || !strncmp (line, "-----END", 8))
100       break;
101   }
102   if (r == NULL) {
103     dprint (1, (debugfile, "pgp_dearmor: Can't find end of ASCII armor.\n"));
104     return;
105   }
106
107   if ((end = ftell (in) - mutt_strlen (line)) < start) {
108     dprint (1, (debugfile, "pgp_dearmor: end < start???\n"));
109     return;
110   }
111
112   if (fseek (in, start, SEEK_SET) == -1) {
113     dprint (1, (debugfile, "pgp_dearmor: Can't seekto start.\n"));
114     return;
115   }
116
117   mutt_decode_base64 (&state, end - start, 0, (iconv_t) - 1);
118 }
119
120 static short pgp_mic_from_packet (unsigned char *p, size_t len)
121 {
122   /* is signature? */
123   if ((p[0] & 0x3f) != PT_SIG) {
124     dprint (1, (debugfile, "pgp_mic_from_packet: tag = %d, want %d.\n",
125                 p[0] & 0x3f, PT_SIG));
126     return -1;
127   }
128
129   if (len >= 18 && p[1] == 3)
130     /* version 3 signature */
131     return (short) p[17];
132   else if (len >= 5 && p[1] == 4)
133     /* version 4 signature */
134     return (short) p[4];
135   else {
136     dprint (1, (debugfile, "pgp_mic_from_packet: Bad signature packet.\n"));
137     return -1;
138   }
139 }
140
141 static short pgp_find_hash (const char *fname)
142 {
143   FILE *in = NULL;
144   FILE *out = NULL;
145
146   char tempfile[_POSIX_PATH_MAX];
147
148   unsigned char *p;
149   size_t l;
150
151   short rv = -1;
152
153   mutt_mktemp (tempfile);
154   if ((out = safe_fopen (tempfile, "w+")) == NULL) {
155     mutt_perror (tempfile);
156     goto bye;
157   }
158   unlink (tempfile);
159
160   if ((in = fopen (fname, "r")) == NULL) {
161     mutt_perror (fname);
162     goto bye;
163   }
164
165   pgp_dearmor (in, out);
166   rewind (out);
167
168   if ((p = pgp_read_packet (out, &l)) != NULL) {
169     rv = pgp_mic_from_packet (p, l);
170   }
171   else {
172     dprint (1, (debugfile, "pgp_find_hash: No packet.\n"));
173   }
174
175 bye:
176
177   safe_fclose (&in);
178   safe_fclose (&out);
179   pgp_release_packet ();
180   return rv;
181 }
182
183 const char *pgp_micalg (const char *fname)
184 {
185   return pgp_hash_to_micalg (pgp_find_hash (fname));
186 }