Coverage Report

Created: 2025-03-11 06:49

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