/* 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 . */ #include #include #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; }