Coverage Report

Created: 2023-09-25 07:17

/src/neomutt/ncrypt/pgpmicalg.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Identify the hash algorithm from a PGP signature
4
 *
5
 * @authors
6
 * Copyright (C) 2001 Thomas Roessler <roessler@does-not-exist.org>
7
 *
8
 * @copyright
9
 * This program is free software: you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License as published by the Free Software
11
 * Foundation, either version 2 of the License, or (at your option) any later
12
 * version.
13
 *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT
15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU General Public License along with
20
 * this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 * @page crypt_pgpmicalg Identify the hash algorithm from a PGP signature
25
 *
26
 * Identify the Message Integrity Check algorithm (micalg) from a PGP signature
27
 */
28
29
#include "config.h"
30
#include <stdbool.h>
31
#include <stdio.h>
32
#include <string.h>
33
#include "mutt/lib.h"
34
#include "core/lib.h"
35
#include "pgpmicalg.h"
36
#include "handler.h"
37
#include "pgppacket.h"
38
39
/**
40
 * struct HashAlgorithm - PGP Hashing algorithm
41
 */
42
struct HashAlgorithm
43
{
44
  short id;         ///< Algorithm Id
45
  const char *name; ///< Algorithm name
46
};
47
48
/**
49
 * HashAlgorithms - PGP Hashing algorithms
50
 */
51
static const struct HashAlgorithm HashAlgorithms[] = {
52
  { 1, "pgp-md5" },     { 2, "pgp-sha1" },     { 3, "pgp-ripemd160" },
53
  { 5, "pgp-md2" },     { 6, "pgp-tiger192" }, { 7, "pgp-haval-5-160" },
54
  { 8, "pgp-sha256" },  { 9, "pgp-sha384" },   { 10, "pgp-sha512" },
55
  { 11, "pgp-sha224" }, { -1, NULL },
56
};
57
58
/**
59
 * pgp_hash_to_micalg - Lookup a hash name, given its id
60
 * @param id ID
61
 * @retval ptr Name of hash algorithm
62
 */
63
static const char *pgp_hash_to_micalg(short id)
64
0
{
65
0
  for (int i = 0; HashAlgorithms[i].id >= 0; i++)
66
0
    if (HashAlgorithms[i].id == id)
67
0
      return HashAlgorithms[i].name;
68
0
  return "x-unknown";
69
0
}
70
71
/**
72
 * pgp_dearmor - Unwrap an armoured PGP block
73
 * @param fp_in  File to read from
74
 * @param fp_out File to write to
75
 */
76
static void pgp_dearmor(FILE *fp_in, FILE *fp_out)
77
0
{
78
0
  char line[8192] = { 0 };
79
0
  char *r = NULL;
80
81
0
  struct State state = { 0 };
82
0
  state.fp_in = fp_in;
83
0
  state.fp_out = fp_out;
84
85
  /* find the beginning of ASCII armor */
86
87
0
  while ((r = fgets(line, sizeof(line), fp_in)))
88
0
  {
89
0
    if (mutt_strn_equal(line, "-----BEGIN", 10))
90
0
      break;
91
0
  }
92
0
  if (!r)
93
0
  {
94
0
    mutt_debug(LL_DEBUG1, "Can't find begin of ASCII armor\n");
95
0
    return;
96
0
  }
97
98
  /* skip the armor header */
99
100
0
  while ((r = fgets(line, sizeof(line), fp_in)))
101
0
  {
102
0
    SKIPWS(r);
103
0
    if (*r == '\0')
104
0
      break;
105
0
  }
106
0
  if (!r)
107
0
  {
108
0
    mutt_debug(LL_DEBUG1, "Armor header doesn't end\n");
109
0
    return;
110
0
  }
111
112
  /* actual data starts here */
113
0
  LOFF_T start = ftello(fp_in);
114
0
  if (start < 0)
115
0
    return;
116
117
  /* find the checksum */
118
119
0
  while ((r = fgets(line, sizeof(line), fp_in)))
120
0
  {
121
0
    if ((*line == '=') || mutt_strn_equal(line, "-----END", 8))
122
0
      break;
123
0
  }
124
0
  if (!r)
125
0
  {
126
0
    mutt_debug(LL_DEBUG1, "Can't find end of ASCII armor\n");
127
0
    return;
128
0
  }
129
130
0
  LOFF_T end = ftello(fp_in) - strlen(line);
131
0
  if (end < start)
132
0
  {
133
0
    mutt_debug(LL_DEBUG1, "end < start???\n");
134
0
    return;
135
0
  }
136
137
0
  if (!mutt_file_seek(fp_in, start, SEEK_SET))
138
0
  {
139
0
    return;
140
0
  }
141
142
0
  mutt_decode_base64(&state, end - start, false, ICONV_T_INVALID);
143
0
}
144
145
/**
146
 * pgp_mic_from_packet - Get the hash algorithm from a PGP packet
147
 * @param p   PGP packet
148
 * @param len Length of packet
149
 * @retval num Hash algorithm id
150
 */
151
static short pgp_mic_from_packet(unsigned char *p, size_t len)
152
0
{
153
  /* is signature? */
154
0
  if ((p[0] & 0x3f) != PT_SIG)
155
0
  {
156
0
    mutt_debug(LL_DEBUG1, "tag = %d, want %d\n", p[0] & 0x3f, PT_SIG);
157
0
    return -1;
158
0
  }
159
160
0
  if ((len >= 18) && (p[1] == 3))
161
0
  {
162
    /* version 3 signature */
163
0
    return (short) p[17];
164
0
  }
165
0
  else if ((len >= 5) && (p[1] == 4))
166
0
  {
167
    /* version 4 signature */
168
0
    return (short) p[4];
169
0
  }
170
0
  else
171
0
  {
172
0
    mutt_debug(LL_DEBUG1, "Bad signature packet\n");
173
0
    return -1;
174
0
  }
175
0
}
176
177
/**
178
 * pgp_find_hash - Find the hash algorithm of a file
179
 * @param fname File to read
180
 * @retval num Hash algorithm id
181
 */
182
static short pgp_find_hash(const char *fname)
183
0
{
184
0
  size_t l;
185
0
  short rc = -1;
186
187
0
  FILE *fp_out = mutt_file_mkstemp();
188
0
  if (!fp_out)
189
0
  {
190
0
    mutt_perror(_("Can't create temporary file"));
191
0
    goto bye;
192
0
  }
193
194
0
  FILE *fp_in = fopen(fname, "r");
195
0
  if (!fp_in)
196
0
  {
197
0
    mutt_perror("%s", fname);
198
0
    goto bye;
199
0
  }
200
201
0
  pgp_dearmor(fp_in, fp_out);
202
0
  rewind(fp_out);
203
204
0
  unsigned char *p = pgp_read_packet(fp_out, &l);
205
0
  if (p)
206
0
  {
207
0
    rc = pgp_mic_from_packet(p, l);
208
0
  }
209
0
  else
210
0
  {
211
0
    mutt_debug(LL_DEBUG1, "No packet\n");
212
0
  }
213
214
0
bye:
215
216
0
  mutt_file_fclose(&fp_in);
217
0
  mutt_file_fclose(&fp_out);
218
0
  pgp_release_packet();
219
0
  return rc;
220
0
}
221
222
/**
223
 * pgp_micalg - Find the hash algorithm of a file
224
 * @param fname File to read
225
 * @retval ptr Name of hash algorithm
226
 */
227
const char *pgp_micalg(const char *fname)
228
0
{
229
0
  return pgp_hash_to_micalg(pgp_find_hash(fname));
230
0
}