Coverage Report

Created: 2026-01-09 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/common/pkscreening.c
Line
Count
Source
1
/* pkscreening.c - Screen public keys for vulnerabilities
2
 * Copyright (C) 2017 Werner Koch
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * This file is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as
8
 * published by the Free Software Foundation; either version 2.1 of
9
 * the License, or (at your option) any later version.
10
 *
11
 * This file is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
#include <config.h>
21
#include <stdlib.h>
22
23
#include "util.h"
24
#include "pkscreening.h"
25
26
27
/* Helper */
28
static inline gpg_error_t
29
my_error (gpg_err_code_t ec)
30
0
{
31
0
  return gpg_err_make (default_errsource, ec);
32
0
}
33
34
35
/* Emulation of the new gcry_mpi_get_ui function.  */
36
static gpg_error_t
37
my_mpi_get_ui (unsigned int *v, gcry_mpi_t a)
38
0
{
39
0
  gpg_error_t err;
40
0
  unsigned char buf[8];
41
0
  size_t n;
42
0
  int i, mul;
43
44
0
  if (gcry_mpi_cmp_ui (a, 16384) > 0)
45
0
    return my_error (GPG_ERR_ERANGE); /* Clearly too large for our purpose.  */
46
47
0
  err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, sizeof buf, &n, a);
48
0
  if (err)
49
0
    return err;
50
51
0
  *v = 0;
52
0
  for (i = n - 1, mul = 1; i >= 0; i--, mul *= 256)
53
0
    *v += mul * buf[i];
54
55
0
  return 0;
56
0
}
57
58
59
/* Detect whether the MODULUS of a public RSA key is affected by the
60
 * ROCA vulnerability as found in the Infinion RSA library
61
 * (CVE-2017-15361).  Returns 0 if not affected, GPG_ERR_TRUE if
62
 * affected, GPG_ERR_BAD_MPI if an opaque RSA was passed, or other
63
 * error codes if something weird happened  */
64
gpg_error_t
65
screen_key_for_roca (gcry_mpi_t modulus)
66
0
{
67
0
  static struct {
68
0
    unsigned int prime_ui;
69
0
    const char *print_hex;
70
0
    gcry_mpi_t prime;
71
0
    gcry_mpi_t print;
72
0
  } table[] = {
73
0
   { 3,   "0x6" },
74
0
   { 5,   "0x1E" },
75
0
   { 7,   "0x7E" },
76
0
   { 11,  "0x402" },
77
0
   { 13,  "0x161A" },
78
0
   { 17,  "0x1A316" },
79
0
   { 19,  "0x30AF2" },
80
0
   { 23,  "0x7FFFFE" },
81
0
   { 29,  "0x1FFFFFFE" },
82
0
   { 31,  "0x7FFFFFFE" },
83
0
   { 37,  "0x4000402"  },
84
0
   { 41,  "0x1FFFFFFFFFE" },
85
0
   { 43,  "0x7FFFFFFFFFE" },
86
0
   { 47,  "0x7FFFFFFFFFFE" },
87
0
   { 53,  "0x12DD703303AED2" },
88
0
   { 59,  "0x7FFFFFFFFFFFFFE" },
89
0
   { 61,  "0x1434026619900B0A" },
90
0
   { 67,  "0x7FFFFFFFFFFFFFFFE" },
91
0
   { 71,  "0x1164729716B1D977E" },
92
0
   { 73,  "0x147811A48004962078A" },
93
0
   { 79,  "0xB4010404000640502"   },
94
0
   { 83,  "0x7FFFFFFFFFFFFFFFFFFFE" },
95
0
   { 89,  "0x1FFFFFFFFFFFFFFFFFFFFFE" },
96
0
   { 97,  "0x1000000006000001800000002" },
97
0
   { 101, "0x1FFFFFFFFFFFFFFFFFFFFFFFFE" },
98
0
   { 103, "0x16380E9115BD964257768FE396" },
99
0
   { 107, "0x27816EA9821633397BE6A897E1A" },
100
0
   { 109, "0x1752639F4E85B003685CBE7192BA" },
101
0
   { 113, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
102
0
   { 127, "0x6CA09850C2813205A04C81430A190536" },
103
0
   { 131, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
104
0
   { 137, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
105
0
   { 139, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
106
0
   { 149, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
107
0
   { 151, "0x50C018BC00482458DAC35B1A2412003D18030A" },
108
0
   { 157, "0x161FB414D76AF63826461899071BD5BACA0B7E1A" },
109
0
   { 163, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
110
0
   { 167, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" }
111
0
  };
112
0
  gpg_error_t err;
113
0
  int i;
114
0
  gcry_mpi_t rem;
115
0
  unsigned int bitno;
116
117
  /* Initialize on the first call. */
118
0
  if (!table[0].prime)
119
0
    {
120
      /* We pass primes[i] to the call so that in case of a concurrent
121
       * second thread the already allocated space is reused.  */
122
0
      for (i = 0; i < DIM (table); i++)
123
0
        {
124
0
          table[i].prime = gcry_mpi_set_ui (table[i].prime, table[i].prime_ui);
125
0
          if (gcry_mpi_scan (&table[i].print, GCRYMPI_FMT_HEX,
126
0
                             table[i].print_hex, 0, NULL))
127
0
            BUG ();
128
0
        }
129
0
    }
130
131
  /* Check that it is not NULL or an opaque MPI.  */
132
0
  if (!modulus || gcry_mpi_get_flag (modulus, GCRYMPI_FLAG_OPAQUE))
133
0
    return my_error (GPG_ERR_BAD_MPI);
134
135
  /* We divide the modulus of an RSA public key by a set of small
136
   * PRIMEs and examine all the remainders.  If all the bits at the
137
   * index given by the remainder are set in the corresponding PRINT
138
   * masks the key is very likely vulnerable.  If any of the tested
139
   * bits is zero, the key is not vulnerable.  */
140
0
  rem = gcry_mpi_new (0);
141
0
  for (i = 0; i < DIM (table); i++)
142
0
    {
143
0
      gcry_mpi_mod (rem, modulus, table[i].prime);
144
0
      err = my_mpi_get_ui (&bitno, rem);
145
0
      if (gpg_err_code (err) == GPG_ERR_ERANGE)
146
0
        continue;
147
0
      if (err)
148
0
        goto leave;
149
0
      if (!gcry_mpi_test_bit (table[i].print, bitno))
150
0
        goto leave;  /* Not vulnerable.  */
151
0
    }
152
153
  /* Very likely vulnerable */
154
0
  err = my_error (GPG_ERR_TRUE);
155
156
0
 leave:
157
0
  gcry_mpi_release (rem);
158
0
  return err;
159
0
}