gnupg/common/pkscreening.c

160 lines
4.8 KiB
C

/* pkscreening.c - Screen public keys for vulnerabilities
* Copyright (C) 2017 Werner Koch
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include "util.h"
#include "pkscreening.h"
/* Helper */
static inline gpg_error_t
my_error (gpg_err_code_t ec)
{
return gpg_err_make (default_errsource, ec);
}
/* Emulation of the new gcry_mpi_get_ui function. */
static gpg_error_t
my_mpi_get_ui (unsigned int *v, gcry_mpi_t a)
{
gpg_error_t err;
unsigned char buf[8];
size_t n;
int i, mul;
if (gcry_mpi_cmp_ui (a, 16384) > 0)
return my_error (GPG_ERR_ERANGE); /* Clearly too large for our purpose. */
err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, sizeof buf, &n, a);
if (err)
return err;
*v = 0;
for (i = n - 1, mul = 1; i >= 0; i--, mul *= 256)
*v += mul * buf[i];
return 0;
}
/* Detect whether the MODULUS of a public RSA key is affected by the
* ROCA vulnerability as found in the Infinion RSA library
* (CVE-2017-15361). Returns 0 if not affected, GPG_ERR_TRUE if
* affected, GPG_ERR_BAD_MPI if an opaque RSA was passed, or other
* error codes if something weird happened */
gpg_error_t
screen_key_for_roca (gcry_mpi_t modulus)
{
static struct {
unsigned int prime_ui;
const char *print_hex;
gcry_mpi_t prime;
gcry_mpi_t print;
} table[] = {
{ 3, "0x6" },
{ 5, "0x1E" },
{ 7, "0x7E" },
{ 11, "0x402" },
{ 13, "0x161A" },
{ 17, "0x1A316" },
{ 19, "0x30AF2" },
{ 23, "0x7FFFFE" },
{ 29, "0x1FFFFFFE" },
{ 31, "0x7FFFFFFE" },
{ 37, "0x4000402" },
{ 41, "0x1FFFFFFFFFE" },
{ 43, "0x7FFFFFFFFFE" },
{ 47, "0x7FFFFFFFFFFE" },
{ 53, "0x12DD703303AED2" },
{ 59, "0x7FFFFFFFFFFFFFE" },
{ 61, "0x1434026619900B0A" },
{ 67, "0x7FFFFFFFFFFFFFFFE" },
{ 71, "0x1164729716B1D977E" },
{ 73, "0x147811A48004962078A" },
{ 79, "0xB4010404000640502" },
{ 83, "0x7FFFFFFFFFFFFFFFFFFFE" },
{ 89, "0x1FFFFFFFFFFFFFFFFFFFFFE" },
{ 97, "0x1000000006000001800000002" },
{ 101, "0x1FFFFFFFFFFFFFFFFFFFFFFFFE" },
{ 103, "0x16380E9115BD964257768FE396" },
{ 107, "0x27816EA9821633397BE6A897E1A" },
{ 109, "0x1752639F4E85B003685CBE7192BA" },
{ 113, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
{ 127, "0x6CA09850C2813205A04C81430A190536" },
{ 131, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
{ 137, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
{ 139, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
{ 149, "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
{ 151, "0x50C018BC00482458DAC35B1A2412003D18030A" },
{ 157, "0x161FB414D76AF63826461899071BD5BACA0B7E1A" },
{ 163, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" },
{ 167, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" }
};
gpg_error_t err;
int i;
gcry_mpi_t rem;
unsigned int bitno;
/* Initialize on the first call. */
if (!table[0].prime)
{
/* We pass primes[i] to the call so that in case of a concurrent
* second thread the already allocated space is reused. */
for (i = 0; i < DIM (table); i++)
{
table[i].prime = gcry_mpi_set_ui (table[i].prime, table[i].prime_ui);
if (gcry_mpi_scan (&table[i].print, GCRYMPI_FMT_HEX,
table[i].print_hex, 0, NULL))
BUG ();
}
}
/* Check that it is not NULL or an opaque MPI. */
if (!modulus || gcry_mpi_get_flag (modulus, GCRYMPI_FLAG_OPAQUE))
return my_error (GPG_ERR_BAD_MPI);
/* We divide the modulus of an RSA public key by a set of small
* PRIMEs and examine all the remainders. If all the bits at the
* index given by the remainder are set in the corresponding PRINT
* masks the key is very likely vulnerable. If any of the tested
* bits is zero, the key is not vulnerable. */
rem = gcry_mpi_new (0);
for (i = 0; i < DIM (table); i++)
{
gcry_mpi_mod (rem, modulus, table[i].prime);
err = my_mpi_get_ui (&bitno, rem);
if (gpg_err_code (err) == GPG_ERR_ERANGE)
continue;
if (err)
goto leave;
if (!gcry_mpi_test_bit (table[i].print, bitno))
goto leave; /* Not vulnerable. */
}
/* Very likely vulnerable */
err = my_error (GPG_ERR_TRUE);
leave:
gcry_mpi_release (rem);
return err;
}