2017-05-31 14:33:45 +02:00
|
|
|
/* compliance.c - Functions for compliance modi
|
|
|
|
* Copyright (C) 2017 g10 Code GmbH
|
2017-06-19 12:37:52 +02:00
|
|
|
* Copyright (C) 2017 Bundesamt für Sicherheit in der Informationstechnik
|
2017-05-31 14:33:45 +02:00
|
|
|
*
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
*
|
|
|
|
* This file is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of either
|
|
|
|
*
|
|
|
|
* - the GNU Lesser General Public License as published by the Free
|
|
|
|
* Software Foundation; either version 3 of the License, or (at
|
|
|
|
* your option) any later version.
|
|
|
|
*
|
|
|
|
* or
|
|
|
|
*
|
|
|
|
* - the GNU General Public License as published by the Free
|
|
|
|
* Software Foundation; either version 2 of the License, or (at
|
|
|
|
* your option) any later version.
|
|
|
|
*
|
|
|
|
* or both in parallel, as here.
|
|
|
|
*
|
|
|
|
* 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 General Public License
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <gcrypt.h>
|
|
|
|
|
|
|
|
#include "openpgpdefs.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "util.h"
|
2017-06-07 11:50:54 +02:00
|
|
|
#include "i18n.h"
|
2017-05-31 14:33:45 +02:00
|
|
|
#include "compliance.h"
|
|
|
|
|
2017-06-07 15:38:50 +02:00
|
|
|
static int initialized;
|
|
|
|
static int module;
|
|
|
|
|
2024-10-07 09:59:26 +02:00
|
|
|
|
|
|
|
/* The next variable and the code in get_compliance_cache should be
|
|
|
|
* removed after the software suite has been approved. */
|
|
|
|
static int assumed_de_vs_compliance = -1;
|
|
|
|
|
2021-11-18 20:44:14 +01:00
|
|
|
/* This value is used by DSA and RSA checks in addition to the hard
|
|
|
|
* coded length checks. It allows one to increase the required key length
|
2024-05-12 18:09:23 -04:00
|
|
|
* using a config file. */
|
2021-11-18 20:44:14 +01:00
|
|
|
static unsigned int min_compliant_rsa_length;
|
2021-04-01 13:08:15 +02:00
|
|
|
|
|
|
|
/* Return the address of a compliance cache variable for COMPLIANCE.
|
|
|
|
* If no such variable exists NULL is returned. FOR_RNG returns the
|
|
|
|
* cache variable for the RNG compliance check. */
|
|
|
|
static int *
|
|
|
|
get_compliance_cache (enum gnupg_compliance_mode compliance, int for_rng)
|
|
|
|
{
|
|
|
|
static int r_gnupg = -1, s_gnupg = -1;
|
|
|
|
static int r_rfc4880 = -1, s_rfc4880 = -1;
|
|
|
|
static int r_rfc2440 = -1, s_rfc2440 = -1;
|
|
|
|
static int r_pgp7 = -1, s_pgp7 = -1;
|
|
|
|
static int r_pgp8 = -1, s_pgp8 = -1;
|
|
|
|
static int r_de_vs = -1, s_de_vs = -1;
|
|
|
|
|
|
|
|
int *ptr = NULL;
|
|
|
|
|
|
|
|
switch (compliance)
|
|
|
|
{
|
|
|
|
case CO_GNUPG: ptr = for_rng? &r_gnupg : &s_gnupg ; break;
|
|
|
|
case CO_RFC4880: ptr = for_rng? &r_rfc4880 : &s_rfc4880; break;
|
|
|
|
case CO_RFC2440: ptr = for_rng? &r_rfc2440 : &s_rfc2440; break;
|
|
|
|
case CO_PGP7: ptr = for_rng? &r_pgp7 : &s_pgp7 ; break;
|
|
|
|
case CO_PGP8: ptr = for_rng? &r_pgp8 : &s_pgp8 ; break;
|
|
|
|
case CO_DE_VS: ptr = for_rng? &r_de_vs : &s_de_vs ; break;
|
|
|
|
}
|
|
|
|
|
2024-10-07 09:59:26 +02:00
|
|
|
/* Remove this code after approval. */
|
|
|
|
if (ptr && compliance == CO_DE_VS)
|
|
|
|
{
|
|
|
|
if (assumed_de_vs_compliance == -1)
|
|
|
|
{
|
|
|
|
const char *s = getenv ("GNUPG_ASSUME_COMPLIANCE");
|
|
|
|
assumed_de_vs_compliance = (s && !strcmp (s, "de-vs"));
|
|
|
|
}
|
|
|
|
if (assumed_de_vs_compliance)
|
|
|
|
*ptr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-01 13:08:15 +02:00
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-07 15:38:50 +02:00
|
|
|
/* Initializes the module. Must be called with the current
|
|
|
|
* GNUPG_MODULE_NAME. Checks a few invariants, and tunes the policies
|
|
|
|
* for the given module. */
|
|
|
|
void
|
|
|
|
gnupg_initialize_compliance (int gnupg_module_name)
|
|
|
|
{
|
|
|
|
log_assert (! initialized);
|
|
|
|
|
|
|
|
/* We accept both OpenPGP-style and gcrypt-style algorithm ids.
|
2023-01-12 20:52:27 +01:00
|
|
|
* Assert that they are compatible. At some places gcrypt ids are
|
|
|
|
* used which can't be encoded in an OpenPGP algo octet; we also
|
|
|
|
* assert this. */
|
2017-06-07 15:38:50 +02:00
|
|
|
log_assert ((int) GCRY_PK_RSA == (int) PUBKEY_ALGO_RSA);
|
|
|
|
log_assert ((int) GCRY_PK_RSA_E == (int) PUBKEY_ALGO_RSA_E);
|
|
|
|
log_assert ((int) GCRY_PK_RSA_S == (int) PUBKEY_ALGO_RSA_S);
|
|
|
|
log_assert ((int) GCRY_PK_ELG_E == (int) PUBKEY_ALGO_ELGAMAL_E);
|
|
|
|
log_assert ((int) GCRY_PK_DSA == (int) PUBKEY_ALGO_DSA);
|
|
|
|
log_assert ((int) GCRY_PK_ECC == (int) PUBKEY_ALGO_ECDH);
|
|
|
|
log_assert ((int) GCRY_PK_ELG == (int) PUBKEY_ALGO_ELGAMAL);
|
2023-01-12 20:52:27 +01:00
|
|
|
log_assert ((int) GCRY_PK_ECDSA > 255);
|
|
|
|
log_assert ((int) GCRY_PK_ECDH > 255);
|
|
|
|
log_assert ((int) GCRY_PK_EDDSA > 255);
|
2017-06-07 15:38:50 +02:00
|
|
|
log_assert ((int) GCRY_CIPHER_NONE == (int) CIPHER_ALGO_NONE);
|
|
|
|
log_assert ((int) GCRY_CIPHER_IDEA == (int) CIPHER_ALGO_IDEA);
|
|
|
|
log_assert ((int) GCRY_CIPHER_3DES == (int) CIPHER_ALGO_3DES);
|
|
|
|
log_assert ((int) GCRY_CIPHER_CAST5 == (int) CIPHER_ALGO_CAST5);
|
|
|
|
log_assert ((int) GCRY_CIPHER_BLOWFISH == (int) CIPHER_ALGO_BLOWFISH);
|
|
|
|
log_assert ((int) GCRY_CIPHER_AES == (int) CIPHER_ALGO_AES);
|
|
|
|
log_assert ((int) GCRY_CIPHER_AES192 == (int) CIPHER_ALGO_AES192);
|
|
|
|
log_assert ((int) GCRY_CIPHER_AES256 == (int) CIPHER_ALGO_AES256);
|
|
|
|
log_assert ((int) GCRY_CIPHER_TWOFISH == (int) CIPHER_ALGO_TWOFISH);
|
|
|
|
log_assert ((int) GCRY_MD_MD5 == (int) DIGEST_ALGO_MD5);
|
|
|
|
log_assert ((int) GCRY_MD_SHA1 == (int) DIGEST_ALGO_SHA1);
|
|
|
|
log_assert ((int) GCRY_MD_RMD160 == (int) DIGEST_ALGO_RMD160);
|
|
|
|
log_assert ((int) GCRY_MD_SHA256 == (int) DIGEST_ALGO_SHA256);
|
|
|
|
log_assert ((int) GCRY_MD_SHA384 == (int) DIGEST_ALGO_SHA384);
|
|
|
|
log_assert ((int) GCRY_MD_SHA512 == (int) DIGEST_ALGO_SHA512);
|
|
|
|
log_assert ((int) GCRY_MD_SHA224 == (int) DIGEST_ALGO_SHA224);
|
|
|
|
|
|
|
|
switch (gnupg_module_name)
|
|
|
|
{
|
|
|
|
case GNUPG_MODULE_NAME_GPGSM:
|
|
|
|
case GNUPG_MODULE_NAME_GPG:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
log_assert (!"no policies for this module");
|
|
|
|
}
|
|
|
|
|
|
|
|
module = gnupg_module_name;
|
|
|
|
initialized = 1;
|
|
|
|
}
|
|
|
|
|
2017-05-31 14:33:45 +02:00
|
|
|
/* Return true if ALGO with a key of KEYLENGTH is compliant to the
|
2017-06-06 14:48:01 +02:00
|
|
|
* given COMPLIANCE mode. If KEY is not NULL, various bits of
|
2017-05-31 14:33:45 +02:00
|
|
|
* information will be extracted from it. If CURVENAME is not NULL, it
|
|
|
|
* is assumed to be the already computed. ALGO may be either an
|
|
|
|
* OpenPGP-style pubkey_algo_t, or a gcrypt-style enum gcry_pk_algos,
|
|
|
|
* both are compatible from the point of view of this function. */
|
|
|
|
int
|
|
|
|
gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
|
2020-07-03 15:47:55 +02:00
|
|
|
unsigned int algo_flags,
|
2017-06-19 12:37:52 +02:00
|
|
|
gcry_mpi_t key[], unsigned int keylength,
|
|
|
|
const char *curvename)
|
2017-05-31 14:33:45 +02:00
|
|
|
{
|
2024-04-15 13:25:07 +02:00
|
|
|
enum { is_rsa, is_dsa, is_elg, is_ecc, is_kem } algotype;
|
2017-05-30 14:30:24 +02:00
|
|
|
int result = 0;
|
2017-05-31 14:33:45 +02:00
|
|
|
|
2017-06-19 11:00:04 +02:00
|
|
|
if (! initialized)
|
|
|
|
return 0;
|
2017-06-07 15:38:50 +02:00
|
|
|
|
2017-05-31 14:33:45 +02:00
|
|
|
switch (algo)
|
|
|
|
{
|
|
|
|
case PUBKEY_ALGO_RSA:
|
|
|
|
case PUBKEY_ALGO_RSA_E:
|
|
|
|
case PUBKEY_ALGO_RSA_S:
|
|
|
|
algotype = is_rsa;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_DSA:
|
2017-05-30 14:30:24 +02:00
|
|
|
algotype = is_dsa;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_ELGAMAL_E:
|
2017-07-27 13:56:38 +02:00
|
|
|
algotype = is_elg;
|
2017-05-31 14:33:45 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_ECDH:
|
|
|
|
case PUBKEY_ALGO_ECDSA:
|
|
|
|
case PUBKEY_ALGO_EDDSA:
|
2023-01-12 20:52:27 +01:00
|
|
|
case GCRY_PK_ECDSA:
|
|
|
|
case GCRY_PK_ECDH:
|
|
|
|
case GCRY_PK_EDDSA:
|
2017-05-31 14:33:45 +02:00
|
|
|
algotype = is_ecc;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_ELGAMAL:
|
2017-07-27 13:56:38 +02:00
|
|
|
return 0; /* Signing with Elgamal is not at all supported. */
|
2017-05-31 14:33:45 +02:00
|
|
|
|
2024-04-15 13:25:07 +02:00
|
|
|
case PUBKEY_ALGO_KYBER:
|
|
|
|
algotype = is_kem;
|
|
|
|
break;
|
|
|
|
|
2017-05-31 14:33:45 +02:00
|
|
|
default: /* Unknown. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (compliance == CO_DE_VS)
|
|
|
|
{
|
|
|
|
char *curve = NULL;
|
|
|
|
|
|
|
|
switch (algotype)
|
|
|
|
{
|
2017-07-27 13:56:38 +02:00
|
|
|
case is_elg:
|
2017-05-31 14:33:45 +02:00
|
|
|
result = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case is_rsa:
|
2021-11-18 20:44:14 +01:00
|
|
|
result = ((keylength == 2048
|
|
|
|
|| keylength == 3072
|
|
|
|
|| keylength == 4096)
|
|
|
|
&& keylength >= min_compliant_rsa_length);
|
2020-07-14 12:25:50 +02:00
|
|
|
/* Although rsaPSS was not part of the original evaluation
|
|
|
|
* we got word that we can claim compliance. */
|
|
|
|
(void)algo_flags;
|
2017-05-31 14:33:45 +02:00
|
|
|
break;
|
|
|
|
|
2017-05-30 14:30:24 +02:00
|
|
|
case is_dsa:
|
|
|
|
if (key)
|
|
|
|
{
|
2017-06-19 17:50:02 +02:00
|
|
|
size_t P = gcry_mpi_get_nbits (key[0]);
|
|
|
|
size_t Q = gcry_mpi_get_nbits (key[1]);
|
|
|
|
result = (Q == 256
|
2021-11-18 20:44:14 +01:00
|
|
|
&& (P == 2048 || P == 3072)
|
|
|
|
&& P >= min_compliant_rsa_length);
|
2017-05-30 14:30:24 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-05-31 14:33:45 +02:00
|
|
|
case is_ecc:
|
|
|
|
if (!curvename && key)
|
|
|
|
{
|
|
|
|
curve = openpgp_oid_to_str (key[0]);
|
|
|
|
curvename = openpgp_oid_to_curve (curve, 0);
|
|
|
|
if (!curvename)
|
|
|
|
curvename = curve;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = (curvename
|
2017-06-19 17:50:02 +02:00
|
|
|
&& (algo == PUBKEY_ALGO_ECDH
|
2023-01-12 20:52:27 +01:00
|
|
|
|| algo == PUBKEY_ALGO_ECDSA
|
|
|
|
|| algo == GCRY_PK_ECDH
|
|
|
|
|| algo == GCRY_PK_ECDSA)
|
2017-05-31 14:33:45 +02:00
|
|
|
&& (!strcmp (curvename, "brainpoolP256r1")
|
|
|
|
|| !strcmp (curvename, "brainpoolP384r1")
|
|
|
|
|| !strcmp (curvename, "brainpoolP512r1")));
|
|
|
|
break;
|
|
|
|
|
2024-04-15 13:25:07 +02:00
|
|
|
case is_kem:
|
|
|
|
result = 0;
|
|
|
|
break;
|
|
|
|
|
2017-05-31 14:33:45 +02:00
|
|
|
default:
|
|
|
|
result = 0;
|
|
|
|
}
|
|
|
|
xfree (curve);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = 1; /* Assume compliance. */
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-06 16:01:40 +02:00
|
|
|
/* Return true if ALGO with the given KEYLENGTH is allowed in the
|
|
|
|
* given COMPLIANCE mode. USE specifies for which use case the
|
|
|
|
* predicate is evaluated. This way policies can be strict in what
|
|
|
|
* they produce, and liberal in what they accept. */
|
|
|
|
int
|
|
|
|
gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance,
|
2020-07-03 15:47:55 +02:00
|
|
|
enum pk_use_case use, int algo,
|
|
|
|
unsigned int algo_flags, gcry_mpi_t key[],
|
2017-06-06 16:01:40 +02:00
|
|
|
unsigned int keylength, const char *curvename)
|
|
|
|
{
|
2017-07-27 16:22:36 +02:00
|
|
|
int result = 0;
|
|
|
|
|
2017-06-19 11:00:04 +02:00
|
|
|
if (! initialized)
|
|
|
|
return 1;
|
|
|
|
|
2023-11-08 17:09:04 +01:00
|
|
|
/* Map the the generic ECC algo to ECDSA if requested. */
|
|
|
|
if ((algo_flags & PK_ALGO_FLAG_ECC18)
|
|
|
|
&& algo == GCRY_PK_ECC
|
|
|
|
&& (use == PK_USE_VERIFICATION
|
|
|
|
|| use == PK_USE_SIGNING))
|
|
|
|
algo = GCRY_PK_ECDSA;
|
|
|
|
|
2017-06-06 16:01:40 +02:00
|
|
|
switch (compliance)
|
|
|
|
{
|
|
|
|
case CO_DE_VS:
|
|
|
|
switch (algo)
|
|
|
|
{
|
|
|
|
case PUBKEY_ALGO_RSA:
|
|
|
|
case PUBKEY_ALGO_RSA_E:
|
|
|
|
case PUBKEY_ALGO_RSA_S:
|
|
|
|
switch (use)
|
|
|
|
{
|
|
|
|
case PK_USE_DECRYPTION:
|
2017-07-27 16:22:36 +02:00
|
|
|
case PK_USE_VERIFICATION:
|
|
|
|
result = 1;
|
|
|
|
break;
|
2017-07-27 13:56:38 +02:00
|
|
|
case PK_USE_ENCRYPTION:
|
2017-06-06 16:01:40 +02:00
|
|
|
case PK_USE_SIGNING:
|
2021-11-18 20:44:14 +01:00
|
|
|
result = ((keylength == 2048
|
|
|
|
|| keylength == 3072
|
|
|
|
|| keylength == 4096)
|
|
|
|
&& keylength >= min_compliant_rsa_length);
|
2017-07-27 16:22:36 +02:00
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
default:
|
|
|
|
log_assert (!"reached");
|
|
|
|
}
|
2017-07-27 16:22:36 +02:00
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
|
|
|
|
case PUBKEY_ALGO_DSA:
|
2017-07-27 16:22:36 +02:00
|
|
|
if (use == PK_USE_VERIFICATION)
|
|
|
|
result = 1;
|
|
|
|
else if (use == PK_USE_SIGNING && key)
|
2017-06-06 16:01:40 +02:00
|
|
|
{
|
2017-06-19 17:50:02 +02:00
|
|
|
size_t P = gcry_mpi_get_nbits (key[0]);
|
|
|
|
size_t Q = gcry_mpi_get_nbits (key[1]);
|
2021-11-18 20:44:14 +01:00
|
|
|
result = (Q == 256
|
|
|
|
&& (P == 2048 || P == 3072)
|
|
|
|
&& keylength >= min_compliant_rsa_length);
|
2017-07-27 16:22:36 +02:00
|
|
|
}
|
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
|
|
|
|
case PUBKEY_ALGO_ELGAMAL:
|
|
|
|
case PUBKEY_ALGO_ELGAMAL_E:
|
2017-07-27 16:22:36 +02:00
|
|
|
result = (use == PK_USE_DECRYPTION);
|
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
|
2023-11-08 17:09:04 +01:00
|
|
|
case PUBKEY_ALGO_ECDH: /* Same value as GCRY_PK_ECC, i.e. 18 */
|
2023-01-12 20:52:27 +01:00
|
|
|
case GCRY_PK_ECDH:
|
2017-07-27 13:56:38 +02:00
|
|
|
if (use == PK_USE_DECRYPTION)
|
2017-07-27 16:22:36 +02:00
|
|
|
result = 1;
|
2017-07-27 13:56:38 +02:00
|
|
|
else if (use == PK_USE_ENCRYPTION)
|
|
|
|
{
|
|
|
|
char *curve = NULL;
|
|
|
|
|
|
|
|
if (!curvename && key)
|
|
|
|
{
|
|
|
|
curve = openpgp_oid_to_str (key[0]);
|
|
|
|
curvename = openpgp_oid_to_curve (curve, 0);
|
|
|
|
if (!curvename)
|
|
|
|
curvename = curve;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = (curvename
|
|
|
|
&& (!strcmp (curvename, "brainpoolP256r1")
|
|
|
|
|| !strcmp (curvename, "brainpoolP384r1")
|
|
|
|
|| !strcmp (curvename, "brainpoolP512r1")));
|
|
|
|
|
|
|
|
xfree (curve);
|
|
|
|
}
|
2017-07-27 16:22:36 +02:00
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
|
|
|
|
case PUBKEY_ALGO_ECDSA:
|
2023-01-12 20:52:27 +01:00
|
|
|
case GCRY_PK_ECDSA:
|
2017-07-27 16:22:36 +02:00
|
|
|
if (use == PK_USE_VERIFICATION)
|
|
|
|
result = 1;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *curve = NULL;
|
2017-06-06 16:01:40 +02:00
|
|
|
|
2017-07-27 16:22:36 +02:00
|
|
|
if (! curvename && key)
|
2017-06-06 16:01:40 +02:00
|
|
|
{
|
|
|
|
curve = openpgp_oid_to_str (key[0]);
|
|
|
|
curvename = openpgp_oid_to_curve (curve, 0);
|
|
|
|
if (!curvename)
|
|
|
|
curvename = curve;
|
|
|
|
}
|
|
|
|
|
2017-07-27 16:22:36 +02:00
|
|
|
result = (use == PK_USE_SIGNING
|
|
|
|
&& curvename
|
|
|
|
&& (!strcmp (curvename, "brainpoolP256r1")
|
|
|
|
|| !strcmp (curvename, "brainpoolP384r1")
|
|
|
|
|| !strcmp (curvename, "brainpoolP512r1")));
|
|
|
|
xfree (curve);
|
|
|
|
}
|
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_EDDSA:
|
2023-01-20 11:02:02 +01:00
|
|
|
if (use == PK_USE_VERIFICATION)
|
|
|
|
result = 1;
|
|
|
|
else /* We may not create such signatures in de-vs mode. */
|
|
|
|
result = 0;
|
2017-07-27 16:22:36 +02:00
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
|
|
|
|
default:
|
2017-07-27 16:22:36 +02:00
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
}
|
2017-07-27 16:22:36 +02:00
|
|
|
break;
|
2017-06-06 16:01:40 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
/* The default policy is to allow all algorithms. */
|
2017-07-27 16:22:36 +02:00
|
|
|
result = 1;
|
2017-06-06 16:01:40 +02:00
|
|
|
}
|
|
|
|
|
2017-07-27 16:22:36 +02:00
|
|
|
return result;
|
2017-06-06 16:01:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-07 16:09:07 +02:00
|
|
|
/* Return true if (CIPHER, MODE) is compliant to the given COMPLIANCE mode. */
|
2017-05-30 14:30:24 +02:00
|
|
|
int
|
2017-06-07 16:09:07 +02:00
|
|
|
gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance,
|
|
|
|
cipher_algo_t cipher,
|
|
|
|
enum gcry_cipher_modes mode)
|
2017-05-30 14:30:24 +02:00
|
|
|
{
|
2017-06-19 11:00:04 +02:00
|
|
|
if (! initialized)
|
|
|
|
return 0;
|
2017-06-07 15:38:50 +02:00
|
|
|
|
2017-05-30 14:30:24 +02:00
|
|
|
switch (compliance)
|
|
|
|
{
|
|
|
|
case CO_DE_VS:
|
|
|
|
switch (cipher)
|
|
|
|
{
|
|
|
|
case CIPHER_ALGO_AES:
|
|
|
|
case CIPHER_ALGO_AES192:
|
|
|
|
case CIPHER_ALGO_AES256:
|
|
|
|
case CIPHER_ALGO_3DES:
|
2017-06-07 16:09:07 +02:00
|
|
|
switch (module)
|
|
|
|
{
|
|
|
|
case GNUPG_MODULE_NAME_GPG:
|
|
|
|
return mode == GCRY_CIPHER_MODE_CFB;
|
|
|
|
case GNUPG_MODULE_NAME_GPGSM:
|
|
|
|
return mode == GCRY_CIPHER_MODE_CBC;
|
|
|
|
}
|
|
|
|
log_assert (!"reached");
|
|
|
|
|
2017-05-30 14:30:24 +02:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
log_assert (!"reached");
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_assert (!"reached");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-06 16:01:40 +02:00
|
|
|
/* Return true if CIPHER is allowed in the given COMPLIANCE mode. If
|
|
|
|
* PRODUCER is true, the predicate is evaluated for the producer, if
|
|
|
|
* false for the consumer. This way policies can be strict in what
|
|
|
|
* they produce, and liberal in what they accept. */
|
|
|
|
int
|
|
|
|
gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer,
|
|
|
|
cipher_algo_t cipher,
|
|
|
|
enum gcry_cipher_modes mode)
|
|
|
|
{
|
2017-06-19 11:00:04 +02:00
|
|
|
if (! initialized)
|
|
|
|
return 1;
|
|
|
|
|
2017-06-06 16:01:40 +02:00
|
|
|
switch (compliance)
|
|
|
|
{
|
|
|
|
case CO_DE_VS:
|
|
|
|
switch (cipher)
|
|
|
|
{
|
|
|
|
case CIPHER_ALGO_AES:
|
|
|
|
case CIPHER_ALGO_AES192:
|
|
|
|
case CIPHER_ALGO_AES256:
|
|
|
|
case CIPHER_ALGO_3DES:
|
|
|
|
switch (module)
|
|
|
|
{
|
|
|
|
case GNUPG_MODULE_NAME_GPG:
|
2017-06-19 12:37:52 +02:00
|
|
|
return (mode == GCRY_CIPHER_MODE_NONE
|
|
|
|
|| mode == GCRY_CIPHER_MODE_CFB);
|
2017-06-06 16:01:40 +02:00
|
|
|
case GNUPG_MODULE_NAME_GPGSM:
|
2017-06-19 12:37:52 +02:00
|
|
|
return (mode == GCRY_CIPHER_MODE_NONE
|
2021-06-02 19:14:37 +02:00
|
|
|
|| mode == GCRY_CIPHER_MODE_CBC
|
|
|
|
|| (mode == GCRY_CIPHER_MODE_GCM && !producer));
|
2017-06-06 16:01:40 +02:00
|
|
|
}
|
|
|
|
log_assert (!"reached");
|
|
|
|
|
|
|
|
case CIPHER_ALGO_BLOWFISH:
|
|
|
|
case CIPHER_ALGO_CAMELLIA128:
|
|
|
|
case CIPHER_ALGO_CAMELLIA192:
|
|
|
|
case CIPHER_ALGO_CAMELLIA256:
|
|
|
|
case CIPHER_ALGO_CAST5:
|
|
|
|
case CIPHER_ALGO_IDEA:
|
|
|
|
case CIPHER_ALGO_TWOFISH:
|
|
|
|
return (module == GNUPG_MODULE_NAME_GPG
|
2017-06-19 12:37:52 +02:00
|
|
|
&& (mode == GCRY_CIPHER_MODE_NONE
|
|
|
|
|| mode == GCRY_CIPHER_MODE_CFB)
|
2017-06-06 16:01:40 +02:00
|
|
|
&& ! producer);
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
log_assert (!"reached");
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* The default policy is to allow all algorithms. */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_assert (!"reached");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-06 14:48:01 +02:00
|
|
|
/* Return true if DIGEST is compliant to the given COMPLIANCE mode. */
|
2017-05-30 14:30:24 +02:00
|
|
|
int
|
2017-06-19 17:42:50 +02:00
|
|
|
gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance,
|
|
|
|
digest_algo_t digest)
|
2017-05-30 14:30:24 +02:00
|
|
|
{
|
2017-06-19 11:00:04 +02:00
|
|
|
if (! initialized)
|
|
|
|
return 0;
|
2017-06-07 15:38:50 +02:00
|
|
|
|
2017-05-30 14:30:24 +02:00
|
|
|
switch (compliance)
|
|
|
|
{
|
|
|
|
case CO_DE_VS:
|
|
|
|
switch (digest)
|
|
|
|
{
|
|
|
|
case DIGEST_ALGO_SHA256:
|
|
|
|
case DIGEST_ALGO_SHA384:
|
|
|
|
case DIGEST_ALGO_SHA512:
|
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
log_assert (!"reached");
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_assert (!"reached");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-06 16:01:40 +02:00
|
|
|
/* Return true if DIGEST is allowed in the given COMPLIANCE mode. If
|
|
|
|
* PRODUCER is true, the predicate is evaluated for the producer, if
|
|
|
|
* false for the consumer. This way policies can be strict in what
|
|
|
|
* they produce, and liberal in what they accept. */
|
|
|
|
int
|
|
|
|
gnupg_digest_is_allowed (enum gnupg_compliance_mode compliance, int producer,
|
|
|
|
digest_algo_t digest)
|
|
|
|
{
|
2017-06-19 11:00:04 +02:00
|
|
|
if (! initialized)
|
|
|
|
return 1;
|
|
|
|
|
2017-06-06 16:01:40 +02:00
|
|
|
switch (compliance)
|
|
|
|
{
|
|
|
|
case CO_DE_VS:
|
|
|
|
switch (digest)
|
|
|
|
{
|
|
|
|
case DIGEST_ALGO_SHA256:
|
|
|
|
case DIGEST_ALGO_SHA384:
|
|
|
|
case DIGEST_ALGO_SHA512:
|
|
|
|
return 1;
|
|
|
|
case DIGEST_ALGO_SHA1:
|
|
|
|
case DIGEST_ALGO_SHA224:
|
|
|
|
case DIGEST_ALGO_RMD160:
|
|
|
|
return ! producer;
|
|
|
|
case DIGEST_ALGO_MD5:
|
|
|
|
return ! producer && module == GNUPG_MODULE_NAME_GPGSM;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
log_assert (!"reached");
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* The default policy is to allow all algorithms. */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_assert (!"reached");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-17 15:52:26 +02:00
|
|
|
/* Return True if the random number generator is compliant in
|
|
|
|
* COMPLIANCE mode. */
|
|
|
|
int
|
|
|
|
gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance)
|
|
|
|
{
|
2021-04-01 13:08:15 +02:00
|
|
|
int *result;
|
|
|
|
int res;
|
2017-07-17 15:52:26 +02:00
|
|
|
|
2023-11-08 17:09:04 +01:00
|
|
|
/* #warning debug code ahead */
|
|
|
|
/* return 1; */
|
|
|
|
|
2021-04-01 13:08:15 +02:00
|
|
|
result = get_compliance_cache (compliance, 1);
|
|
|
|
|
|
|
|
if (result && *result != -1)
|
|
|
|
res = *result; /* Use cached result. */
|
2017-07-17 15:52:26 +02:00
|
|
|
else if (compliance == CO_DE_VS)
|
|
|
|
{
|
2021-01-28 15:48:08 +01:00
|
|
|
/* We also check whether the library is at all compliant. */
|
2021-04-01 13:08:15 +02:00
|
|
|
res = gnupg_gcrypt_is_compliant (compliance);
|
2021-01-28 15:48:08 +01:00
|
|
|
|
|
|
|
/* In DE_VS mode under Windows we also require that the JENT RNG
|
|
|
|
* is active. Check it here. */
|
2017-07-17 15:52:26 +02:00
|
|
|
#ifdef HAVE_W32_SYSTEM
|
2021-04-01 13:08:15 +02:00
|
|
|
if (res == 1)
|
2021-01-28 15:48:08 +01:00
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
const char *fields[5];
|
|
|
|
|
|
|
|
buf = gcry_get_config (0, "rng-type");
|
|
|
|
if (buf
|
|
|
|
&& split_fields_colon (buf, fields, DIM (fields)) >= 5
|
|
|
|
&& atoi (fields[4]) > 0)
|
|
|
|
; /* Field 5 > 0 := Jent is active. */
|
|
|
|
else
|
|
|
|
result = 0; /* Force non-compliance. */
|
|
|
|
gcry_free (buf);
|
|
|
|
}
|
|
|
|
#endif /*HAVE_W32_SYSTEM*/
|
|
|
|
}
|
|
|
|
else
|
2021-04-01 13:08:15 +02:00
|
|
|
res = 1;
|
2021-01-28 15:48:08 +01:00
|
|
|
|
2021-04-01 13:08:15 +02:00
|
|
|
if (result)
|
|
|
|
*result = res;
|
|
|
|
|
|
|
|
return res;
|
2021-01-28 15:48:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Return true if the used Libgcrypt is compliant in COMPLIANCE
|
|
|
|
* mode. */
|
|
|
|
int
|
|
|
|
gnupg_gcrypt_is_compliant (enum gnupg_compliance_mode compliance)
|
|
|
|
{
|
2021-04-01 13:08:15 +02:00
|
|
|
int *result;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
result = get_compliance_cache (compliance, 0);
|
2021-01-28 15:48:08 +01:00
|
|
|
|
2021-04-01 13:08:15 +02:00
|
|
|
if (result && *result != -1)
|
|
|
|
res = *result; /* Use cached result. */
|
2021-01-28 15:48:08 +01:00
|
|
|
else if (compliance == CO_DE_VS)
|
|
|
|
{
|
|
|
|
int is19orlater = !!gcry_check_version ("1.9.0");
|
|
|
|
|
|
|
|
/* A compliant version of GnuPG requires Libgcrypt >= 1.8.1 and
|
|
|
|
* less than 1.9.0. Version 1.9.0 requires a re-evaluation and
|
|
|
|
* can thus not be used for de-vs. */
|
|
|
|
if (gcry_check_version ("1.8.1") && !is19orlater)
|
2021-04-01 13:08:15 +02:00
|
|
|
res = 1; /* Compliant version of Libgcrypt. */
|
2021-01-28 15:48:08 +01:00
|
|
|
else if (is19orlater)
|
|
|
|
{
|
|
|
|
/* Libgcrypt might be nice enough to tell us whether it is
|
|
|
|
* compliant. */
|
|
|
|
char *buf;
|
|
|
|
const char *fields[3];
|
|
|
|
|
|
|
|
buf = gcry_get_config (0, "compliance");
|
|
|
|
if (buf
|
|
|
|
&& split_fields_colon (buf, fields, DIM (fields)) >= 2
|
|
|
|
&& strstr (fields[1], "de-vs"))
|
2021-04-01 13:08:15 +02:00
|
|
|
res = 1; /* Compliant. */
|
2021-01-28 15:48:08 +01:00
|
|
|
else
|
2021-04-01 13:08:15 +02:00
|
|
|
res = 0; /* Non-compliant. */
|
2021-01-28 15:48:08 +01:00
|
|
|
gcry_free (buf);
|
|
|
|
}
|
2017-07-17 15:52:26 +02:00
|
|
|
else
|
2021-04-01 13:08:15 +02:00
|
|
|
res = 0; /* Non-compliant version of Libgcrypt. */
|
2017-07-17 15:52:26 +02:00
|
|
|
}
|
|
|
|
else
|
2021-04-01 13:08:15 +02:00
|
|
|
res = 1;
|
2017-07-17 15:52:26 +02:00
|
|
|
|
2021-04-01 13:08:15 +02:00
|
|
|
if (result)
|
|
|
|
*result = res;
|
|
|
|
|
|
|
|
return res;
|
2017-07-17 15:52:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-31 14:33:45 +02:00
|
|
|
const char *
|
|
|
|
gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance)
|
|
|
|
{
|
|
|
|
switch (compliance)
|
|
|
|
{
|
|
|
|
case CO_GNUPG:
|
|
|
|
return "8";
|
|
|
|
case CO_RFC4880:
|
|
|
|
case CO_RFC2440:
|
|
|
|
case CO_PGP7:
|
|
|
|
case CO_PGP8:
|
|
|
|
log_assert (!"no status code assigned for this compliance mode");
|
|
|
|
case CO_DE_VS:
|
2024-10-21 17:07:28 +02:00
|
|
|
return assumed_de_vs_compliance > 0 ? "2023" : "23";
|
2017-05-31 14:33:45 +02:00
|
|
|
}
|
|
|
|
log_assert (!"invalid compliance mode");
|
|
|
|
}
|
2017-06-07 11:50:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* Parse the value of --compliance. Returns the value corresponding
|
|
|
|
* to the given STRING according to OPTIONS of size LENGTH, or -1
|
|
|
|
* indicating that the lookup was unsuccessful, or the list of options
|
|
|
|
* was printed. If quiet is false, an additional hint to use 'help'
|
|
|
|
* is printed on unsuccessful lookups. */
|
|
|
|
int
|
|
|
|
gnupg_parse_compliance_option (const char *string,
|
|
|
|
struct gnupg_compliance_option options[],
|
|
|
|
size_t length,
|
|
|
|
int quiet)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (! ascii_strcasecmp (string, "help"))
|
|
|
|
{
|
2017-06-19 17:42:50 +02:00
|
|
|
log_info (_("valid values for option '%s':\n"), "--compliance");
|
2017-06-07 11:50:54 +02:00
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
log_info (" %s\n", options[i].keyword);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
if (! ascii_strcasecmp (string, options[i].keyword))
|
|
|
|
return options[i].value;
|
|
|
|
|
2017-06-19 17:42:50 +02:00
|
|
|
log_error (_("invalid value for option '%s'\n"), "--compliance");
|
2017-06-07 11:50:54 +02:00
|
|
|
if (! quiet)
|
2017-06-19 17:42:50 +02:00
|
|
|
log_info (_("(use \"help\" to list choices)\n"));
|
2017-06-07 11:50:54 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2017-06-07 12:33:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* Return the command line option for the given COMPLIANCE mode. */
|
|
|
|
const char *
|
|
|
|
gnupg_compliance_option_string (enum gnupg_compliance_mode compliance)
|
|
|
|
{
|
|
|
|
switch (compliance)
|
|
|
|
{
|
|
|
|
case CO_GNUPG: return "--compliance=gnupg";
|
|
|
|
case CO_RFC4880: return "--compliance=openpgp";
|
|
|
|
case CO_RFC2440: return "--compliance=rfc2440";
|
|
|
|
case CO_PGP7: return "--compliance=pgp7";
|
|
|
|
case CO_PGP8: return "--compliance=pgp8";
|
|
|
|
case CO_DE_VS: return "--compliance=de-vs";
|
|
|
|
}
|
|
|
|
|
|
|
|
log_assert (!"invalid compliance mode");
|
|
|
|
}
|
2021-11-18 20:44:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* Set additional infos for example taken from config files at startup. */
|
|
|
|
void
|
|
|
|
gnupg_set_compliance_extra_info (unsigned int min_rsa)
|
|
|
|
{
|
|
|
|
min_compliant_rsa_length = min_rsa;
|
|
|
|
}
|