diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.gitignore b/.gitignore index 870812f..552f393 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,17 @@ libgcrypt-1.4.5-hobbled.tar.bz2 /libgcrypt-1.9.2-hobbled.tar.xz /libgcrypt-1.9.3-hobbled.tar.xz /libgcrypt-1.9.4-hobbled.tar.xz +/libgcrypt-1.10.0.tar.bz2 +/libgcrypt-1.10.0.tar.bz2.sig +/libgcrypt-1.10.1.tar.bz2 +/libgcrypt-1.10.1.tar.bz2.sig +/libgcrypt-1.10.2.tar.bz2 +/libgcrypt-1.10.2.tar.bz2.sig +/libgcrypt-1.10.3.tar.bz2 +/libgcrypt-1.10.3.tar.bz2.sig +/libgcrypt-1.11.0.tar.bz2 +/libgcrypt-1.11.0.tar.bz2.sig +/libgcrypt-1.11.1.tar.bz2 +/libgcrypt-1.11.1.tar.bz2.sig +/libgcrypt-1.11.2.tar.bz2 +/libgcrypt-1.11.2.tar.bz2.sig diff --git a/curves.c b/curves.c deleted file mode 100644 index 1b190d1..0000000 --- a/curves.c +++ /dev/null @@ -1,271 +0,0 @@ -/* curves.c - ECC curves regression tests - * Copyright (C) 2011 Free Software Foundation, Inc. - * - * This file is part of Libgcrypt. - * - * Libgcrypt 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. - * - * Libgcrypt 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 Lesser 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include -#include -#include - -#include "../src/gcrypt-int.h" - - -#define PGM "curves" -#include "t-common.h" - -/* Number of curves defined in ../cipger/ecc-curves.c */ -#define N_CURVES 20 - -/* A real world sample public key. */ -static char const sample_key_1[] = -"(public-key\n" -" (ecdsa\n" -" (p #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF#)\n" -" (a #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC#)\n" -" (b #5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B#)\n" -" (g #046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" - "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5#)\n" -" (n #00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551#)\n" -" (h #000000000000000000000000000000000000000000000000000000000000000001#)\n" -" (q #0442B927242237639A36CE9221B340DB1A9AB76DF2FE3E171277F6A4023DED146EE" - "86525E38CCECFF3FB8D152CC6334F70D23A525175C1BCBDDE6E023B2228770E#)\n" -" ))"; -static char const sample_key_1_curve[] = "NIST P-256"; -static unsigned int sample_key_1_nbits = 256; - -static void -list_curves (void) -{ - int idx; - const char *name; - unsigned int nbits; - - for (idx=0; (name = gcry_pk_get_curve (NULL, idx, &nbits)); idx++) - { - if (verbose) - printf ("%s - %u bits\n", name, nbits); - } - if (idx != N_CURVES) - fail ("expected %d curves but got %d\n", N_CURVES, idx); - if (gcry_pk_get_curve (NULL, -1, NULL)) - fail ("curve iteration failed\n"); -} - - -static void -check_matching (void) -{ - gpg_error_t err; - gcry_sexp_t key; - const char *name; - unsigned int nbits; - - err = gcry_sexp_new (&key, sample_key_1, 0, 1); - if (err) - die ("parsing s-expression string failed: %s\n", gpg_strerror (err)); - name = gcry_pk_get_curve (key, 0, &nbits); - if (!name) - fail ("curve name not found for sample_key_1\n"); - else if (strcmp (name, sample_key_1_curve)) - fail ("expected curve name %s but got %s for sample_key_1\n", - sample_key_1_curve, name); - else if (nbits != sample_key_1_nbits) - fail ("expected curve size %u but got %u for sample_key_1\n", - sample_key_1_nbits, nbits); - - gcry_sexp_release (key); -} - - -static void -check_get_params (void) -{ - static struct { - int algo; - const char *name; - int error_expected; - } tv[] = - { - { GCRY_PK_ECC, "Ed25519" }, - { GCRY_PK_ECC, "1.3.6.1.4.1.11591.15.1" }, - { GCRY_PK_ECC, "1.3.101.112" }, - - { GCRY_PK_ECC, "Curve25519" }, - { GCRY_PK_ECC, "1.3.6.1.4.1.3029.1.5.1" }, - { GCRY_PK_ECC, "1.3.101.110" }, - { GCRY_PK_ECC, "X25519" }, - - { GCRY_PK_ECC, "Ed448" }, - { GCRY_PK_ECC, "X448" }, - { GCRY_PK_ECC, "1.3.101.113" }, - { GCRY_PK_ECC, "1.3.101.111" }, - - { GCRY_PK_ECC, "NIST P-192" }, - { GCRY_PK_ECC, "1.2.840.10045.3.1.1" }, - { GCRY_PK_ECC, "prime192v1" }, - { GCRY_PK_ECC, "secp192r1" }, - { GCRY_PK_ECC, "nistp192" }, - - { GCRY_PK_ECC, "NIST P-224" }, - { GCRY_PK_ECC, "secp224r1" }, - { GCRY_PK_ECC, "1.3.132.0.33" }, - { GCRY_PK_ECC, "nistp224" }, - - { GCRY_PK_ECC, "NIST P-256" }, - { GCRY_PK_ECC, "1.2.840.10045.3.1.7" }, - { GCRY_PK_ECC, "prime256v1" }, - { GCRY_PK_ECC, "secp256r1" }, - { GCRY_PK_ECC, "nistp256" }, - - { GCRY_PK_ECC, "NIST P-384" }, - { GCRY_PK_ECC, "secp384r1" }, - { GCRY_PK_ECC, "1.3.132.0.34" }, - { GCRY_PK_ECC, "nistp384" }, - - { GCRY_PK_ECC, "NIST P-521" }, - { GCRY_PK_ECC, "secp521r1" }, - { GCRY_PK_ECC, "1.3.132.0.35" }, - { GCRY_PK_ECC, "nistp521" }, - - { GCRY_PK_ECC, "GOST2001-test" }, - { GCRY_PK_ECC, "1.2.643.2.2.35.0" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-A" }, - { GCRY_PK_ECC, "1.2.643.2.2.35.1" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-B" }, - { GCRY_PK_ECC, "1.2.643.2.2.35.2" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-C" }, - { GCRY_PK_ECC, "1.2.643.2.2.35.3" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-A" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-XchA" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-C" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-XchB" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-A" }, - { GCRY_PK_ECC, "1.2.643.2.2.36.0" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-C" }, - { GCRY_PK_ECC, "1.2.643.2.2.36.1" }, - - /* Noet that GOST2012-256-tc26-A" is only in the curve alias - * list but has no parameter entry. */ - { GCRY_PK_ECC, "GOST2001-CryptoPro-A" }, - { GCRY_PK_ECC, "1.2.643.7.1.2.1.1.2" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-A" }, - { GCRY_PK_ECC, "GOST2012-256-tc26-B" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-B" }, - { GCRY_PK_ECC, "1.2.643.7.1.2.1.1.3" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-B" }, - { GCRY_PK_ECC, "GOST2012-256-tc26-C" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-C" }, - { GCRY_PK_ECC, "1.2.643.7.1.2.1.1.4" }, - { GCRY_PK_ECC, "GOST2001-CryptoPro-C" }, - { GCRY_PK_ECC, "GOST2012-256-tc26-D" }, - - { GCRY_PK_ECC, "GOST2012-512-test" }, - { GCRY_PK_ECC, "GOST2012-test" }, - { GCRY_PK_ECC, "GOST2012-512-test" }, - { GCRY_PK_ECC, "1.2.643.7.1.2.1.2.0" }, - { GCRY_PK_ECC, "GOST2012-512-tc26-A" }, - { GCRY_PK_ECC, "GOST2012-tc26-A" }, - { GCRY_PK_ECC, "GOST2012-512-tc26-B" }, - { GCRY_PK_ECC, "GOST2012-tc26-B" }, - { GCRY_PK_ECC, "GOST2012-512-tc26-A" }, - { GCRY_PK_ECC, "1.2.643.7.1.2.1.2.1" }, - { GCRY_PK_ECC, "GOST2012-512-tc26-B" }, - { GCRY_PK_ECC, "1.2.643.7.1.2.1.2.2" }, - { GCRY_PK_ECC, "GOST2012-512-tc26-C" }, - { GCRY_PK_ECC, "1.2.643.7.1.2.1.2.3" }, - - { GCRY_PK_ECC, "secp256k1" }, - { GCRY_PK_ECC, "1.3.132.0.10" }, - - { GCRY_PK_ECC, "sm2p256v1" }, - { GCRY_PK_ECC, "1.2.156.10197.1.301" }, - - /* Check also the ECC algo mapping. */ - { GCRY_PK_ECDSA, "Ed25519" }, - { GCRY_PK_EDDSA, "Ed25519" }, - { GCRY_PK_ECDH, "Ed25519" }, - { GCRY_PK_ECDSA, "Curve25519" }, - { GCRY_PK_EDDSA, "Curve25519" }, - { GCRY_PK_ECDH, "Curve25519" }, - { GCRY_PK_ECC, "NoSuchCurve", 1 }, - { GCRY_PK_RSA, "rsa", 1 }, - { GCRY_PK_ELG, "elg", 1 }, - { GCRY_PK_DSA, "dsa", 1 } - }; - int idx; - gcry_sexp_t param; - const char *name; - - param = gcry_pk_get_param (GCRY_PK_ECDSA, sample_key_1_curve); - if (!param) - fail ("error gerring parameters for `%s'\n", sample_key_1_curve); - - name = gcry_pk_get_curve (param, 0, NULL); - if (!name) - fail ("get_param: curve name not found for sample_key_1\n"); - else if (strcmp (name, sample_key_1_curve)) - fail ("get_param: expected curve name %s but got %s for sample_key_1\n", - sample_key_1_curve, name); - - gcry_sexp_release (param); - - /* Some simple tests */ - for (idx=0; idx < DIM (tv); idx++) - { - param = gcry_pk_get_param (tv[idx].algo, tv[idx].name); - if (!param) - { - if (!tv[idx].error_expected) - fail ("get_param: test %d (%s) failed\n", idx, tv[idx].name); - } - else - { - if (tv[idx].error_expected) - fail ("get_param: test %d (%s) failed (error expected)\n", - idx, tv[idx].name); - } - gcry_sexp_release (param); - } -} - - -int -main (int argc, char **argv) -{ - if (argc > 1 && !strcmp (argv[1], "--verbose")) - verbose = 1; - else if (argc > 1 && !strcmp (argv[1], "--debug")) - verbose = debug = 1; - - if (!gcry_check_version (GCRYPT_VERSION)) - die ("version mismatch\n"); - - xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0)); - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); - if (debug) - xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u, 0)); - list_curves (); - check_matching (); - check_get_params (); - - return error_count ? 1 : 0; -} diff --git a/ecc-curves.c b/ecc-curves.c deleted file mode 100644 index 5236b19..0000000 --- a/ecc-curves.c +++ /dev/null @@ -1,1483 +0,0 @@ -/* ecc-curves.c - Elliptic Curve parameter mangement - * Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc. - * Copyright (C) 2013 g10 Code GmbH - * - * This file is part of Libgcrypt. - * - * Libgcrypt 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. - * - * Libgcrypt 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 Lesser 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 -#include -#include - -#include "g10lib.h" -#include "mpi.h" -#include "mpi-internal.h" -#include "cipher.h" -#include "context.h" -#include "ec-context.h" -#include "pubkey-internal.h" -#include "ecc-common.h" - - -static gpg_err_code_t -point_from_keyparam (gcry_mpi_point_t *r_a, - gcry_sexp_t keyparam, const char *name, mpi_ec_t ec); - -/* This tables defines aliases for curve names. */ -static const struct -{ - const char *name; /* Our name. */ - const char *other; /* Other name. */ -} curve_aliases[] = - { - { "Ed25519", "1.3.6.1.4.1.11591.15.1" }, /* OpenPGP */ - { "Ed25519", "1.3.101.112" }, /* rfc8410 */ - - { "Curve25519", "1.3.6.1.4.1.3029.1.5.1" }, /* OpenPGP */ - { "Curve25519", "1.3.101.110" }, /* rfc8410 */ - { "Curve25519", "X25519" }, /* rfc8410 */ - - { "Ed448", "1.3.101.113" }, /* rfc8410 */ - { "X448", "1.3.101.111" }, /* rfc8410 */ - - { "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID */ - { "NIST P-192", "prime192v1" }, /* X9.62 name. */ - { "NIST P-192", "secp192r1" }, /* SECP name. */ - { "NIST P-192", "nistp192" }, /* rfc5656. */ - - { "NIST P-224", "secp224r1" }, - { "NIST P-224", "1.3.132.0.33" }, /* SECP OID. */ - { "NIST P-224", "nistp224" }, /* rfc5656. */ - - { "NIST P-256", "1.2.840.10045.3.1.7" }, /* From NIST SP 800-78-1. */ - { "NIST P-256", "prime256v1" }, - { "NIST P-256", "secp256r1" }, - { "NIST P-256", "nistp256" }, /* rfc5656. */ - - { "NIST P-384", "secp384r1" }, - { "NIST P-384", "1.3.132.0.34" }, - { "NIST P-384", "nistp384" }, /* rfc5656. */ - - { "NIST P-521", "secp521r1" }, - { "NIST P-521", "1.3.132.0.35" }, - { "NIST P-521", "nistp521" }, /* rfc5656. */ - - { "GOST2001-test", "1.2.643.2.2.35.0" }, - { "GOST2001-CryptoPro-A", "1.2.643.2.2.35.1" }, - { "GOST2001-CryptoPro-B", "1.2.643.2.2.35.2" }, - { "GOST2001-CryptoPro-C", "1.2.643.2.2.35.3" }, - { "GOST2001-CryptoPro-A", "GOST2001-CryptoPro-XchA" }, - { "GOST2001-CryptoPro-C", "GOST2001-CryptoPro-XchB" }, - { "GOST2001-CryptoPro-A", "1.2.643.2.2.36.0" }, - { "GOST2001-CryptoPro-C", "1.2.643.2.2.36.1" }, - - { "GOST2012-256-tc26-A", "1.2.643.7.1.2.1.1.1" }, - { "GOST2001-CryptoPro-A", "1.2.643.7.1.2.1.1.2" }, - { "GOST2001-CryptoPro-A", "GOST2012-256-tc26-B" }, - { "GOST2001-CryptoPro-B", "1.2.643.7.1.2.1.1.3" }, - { "GOST2001-CryptoPro-B", "GOST2012-256-tc26-C" }, - { "GOST2001-CryptoPro-C", "1.2.643.7.1.2.1.1.4" }, - { "GOST2001-CryptoPro-C", "GOST2012-256-tc26-D" }, - - { "GOST2012-512-test", "GOST2012-test" }, - { "GOST2012-512-test", "1.2.643.7.1.2.1.2.0" }, - { "GOST2012-512-tc26-A", "GOST2012-tc26-A" }, - { "GOST2012-512-tc26-B", "GOST2012-tc26-B" }, - { "GOST2012-512-tc26-A", "1.2.643.7.1.2.1.2.1" }, - { "GOST2012-512-tc26-B", "1.2.643.7.1.2.1.2.2" }, - { "GOST2012-512-tc26-C", "1.2.643.7.1.2.1.2.3" }, - - { "secp256k1", "1.3.132.0.10" }, - - { "sm2p256v1", "1.2.156.10197.1.301" }, - - { NULL, NULL} - }; - - -typedef struct -{ - const char *desc; /* Description of the curve. */ - unsigned int nbits; /* Number of bits. */ - unsigned int fips:1; /* True if this is a FIPS140-2 approved curve. */ - - /* The model describing this curve. This is mainly used to select - the group equation. */ - enum gcry_mpi_ec_models model; - - /* The actual ECC dialect used. This is used for curve specific - optimizations and to select encodings etc. */ - enum ecc_dialects dialect; - - const char *p; /* The prime defining the field. */ - const char *a, *b; /* The coefficients. For Twisted Edwards - Curves b is used for d. For Montgomery - Curves (a,b) has ((A-2)/4,B^-1). */ - const char *n; /* The order of the base point. */ - const char *g_x, *g_y; /* Base point. */ - unsigned int h; /* Cofactor. */ -} ecc_domain_parms_t; - - -/* This static table defines all available curves. */ -static const ecc_domain_parms_t domain_parms[] = - { - { - /* (-x^2 + y^2 = 1 + dx^2y^2) */ - "Ed25519", 255, 0, - MPI_EC_EDWARDS, ECC_DIALECT_ED25519, - "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", - "-0x01", - "-0x2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A", - "0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED", - "0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A", - "0x6666666666666666666666666666666666666666666666666666666666666658", - 8 - }, - { - /* (y^2 = x^3 + 486662*x^2 + x) */ - "Curve25519", 255, 0, - MPI_EC_MONTGOMERY, ECC_DIALECT_STANDARD, - "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", - "0x01DB41", - "0x01", - "0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED", - "0x0000000000000000000000000000000000000000000000000000000000000009", - "0x20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9", - 8 - /* Note: As per RFC-7748 errata eid4730 the g_y value should be - * "0x5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14" - * but that breaks the keygrip. The new value is recovered in - * the function _gcry_ecc_fill_in_curve. See bug #4712. - */ - }, - { - /* (x^2 + y^2 = 1 + dx^2y^2) */ - "Ed448", 448, 0, - MPI_EC_EDWARDS, ECC_DIALECT_SAFECURVE, - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "0x01", - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6756", - "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3", - "0x4F1970C66BED0DED221D15A622BF36DA9E146570470F1767EA6DE324" - "A3D3A46412AE1AF72AB66511433B80E18B00938E2626A82BC70CC05E", - "0x693F46716EB6BC248876203756C9C7624BEA73736CA3984087789C1E" - "05A0C2D73AD3FF1CE67C39C4FDBD132C4ED7C8AD9808795BF230FA14", - 4, - }, - { - /* (y^2 = x^3 + 156326*x^2 + x) */ - "X448", 448, 0, - MPI_EC_MONTGOMERY, ECC_DIALECT_SAFECURVE, - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "0x98A9", - "0x01", - "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3", - "0x00000000000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000000005", - "0x7D235D1295F5B1F66C98AB6E58326FCECBAE5D34F55545D060F75DC2" - "8DF3F6EDB8027E2346430D211312C4B150677AF76FD7223D457B5B1A", - 4, - }, -#if 0 /* No real specs yet found. */ - { - /* x^2 + y^2 = 1 + 3617x^2y^2 mod 2^414 - 17 */ - "Curve3617", - "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF", - MPI_EC_EDWARDS, 0, - "0x01", - "0x0e21", - "0x07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB3CC92414CF" - "706022B36F1C0338AD63CF181B0E71A5E106AF79", - "0x1A334905141443300218C0631C326E5FCD46369F44C03EC7F57FF35498A4AB4D" - "6D6BA111301A73FAA8537C64C4FD3812F3CBC595", - "0x22", - 8 - }, -#endif /*0*/ - { - "NIST P-192", 192, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xfffffffffffffffffffffffffffffffeffffffffffffffff", - "0xfffffffffffffffffffffffffffffffefffffffffffffffc", - "0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", - "0xffffffffffffffffffffffff99def836146bc9b1b4d22831", - - "0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", - "0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811", - 1 - }, - { - "NIST P-224", 224, 1, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xffffffffffffffffffffffffffffffff000000000000000000000001", - "0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe", - "0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", - "0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d" , - - "0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", - "0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", - 1 - }, - { - "NIST P-256", 256, 1, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff", - "0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc", - "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", - "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", - - "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", - "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", - 1 - }, - { - "NIST P-384", 384, 1, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" - "ffffffff0000000000000000ffffffff", - "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" - "ffffffff0000000000000000fffffffc", - "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a" - "c656398d8a2ed19d2a85c8edd3ec2aef", - "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf" - "581a0db248b0a77aecec196accc52973", - - "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38" - "5502f25dbf55296c3a545e3872760ab7", - "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0" - "0a60b1ce1d7e819d7a431d7c90ea0e5f", - 1 - }, - { - "NIST P-521", 521, 1, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", - "0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef10" - "9e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", - "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "fffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409", - - "0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d" - "3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", - "0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e" - "662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", - 1 - }, - - { - "GOST2001-test", 256, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0x8000000000000000000000000000000000000000000000000000000000000431", - "0x0000000000000000000000000000000000000000000000000000000000000007", - "0x5fbff498aa938ce739b8e022fbafef40563f6e6a3472fc2a514c0ce9dae23b7e", - "0x8000000000000000000000000000000150fe8a1892976154c59cfc193accf5b3", - - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x08e2a8a0e65147d4bd6316030e16d19c85c97f0a9ca267122b96abbcea7e8fc8", - 1 - }, - { - "GOST2001-CryptoPro-A", 256, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd97", - "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd94", - "0x00000000000000000000000000000000000000000000000000000000000000a6", - "0xffffffffffffffffffffffffffffffff6c611070995ad10045841b09b761b893", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x8d91e471e0989cda27df505a453f2b7635294f2ddf23e3b122acc99c9e9f1e14", - 1 - }, - { - "GOST2001-CryptoPro-B", 256, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0x8000000000000000000000000000000000000000000000000000000000000c99", - "0x8000000000000000000000000000000000000000000000000000000000000c96", - "0x3e1af419a269a5f866a7d3c25c3df80ae979259373ff2b182f49d4ce7e1bbc8b", - "0x800000000000000000000000000000015f700cfff1a624e5e497161bcc8a198f", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x3fa8124359f96680b83d1c3eb2c070e5c545c9858d03ecfb744bf8d717717efc", - 1 - }, - { - "GOST2001-CryptoPro-C", 256, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0x9b9f605f5a858107ab1ec85e6b41c8aacf846e86789051d37998f7b9022d759b", - "0x9b9f605f5a858107ab1ec85e6b41c8aacf846e86789051d37998f7b9022d7598", - "0x000000000000000000000000000000000000000000000000000000000000805a", - "0x9b9f605f5a858107ab1ec85e6b41c8aa582ca3511eddfb74f02f3a6598980bb9", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x41ece55743711a8c3cbf3783cd08c0ee4d4dc440d4641a8f366e550dfdb3bb67", - 1 - }, - { - "GOST2012-256-A", 256, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd97", - "0xc2173f1513981673af4892c23035a27ce25e2013bf95aa33b22c656f277e7335", - "0x295f9bae7428ed9ccc20e7c359a9d41a22fccd9108e17bf7ba9337a6f8ae9513", - "0x400000000000000000000000000000000fd8cddfc87b6635c115af556c360c67", - "0x91e38443a5e82c0d880923425712b2bb658b9196932e02c78b2582fe742daa28", - "0x32879423ab1a0375895786c4bb46e9565fde0b5344766740af268adb32322e5c", - 4 - }, - { - "GOST2012-512-test", 511, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0x4531acd1fe0023c7550d267b6b2fee80922b14b2ffb90f04d4eb7c09b5d2d15d" - "f1d852741af4704a0458047e80e4546d35b8336fac224dd81664bbf528be6373", - "0x0000000000000000000000000000000000000000000000000000000000000007", - "0x1cff0806a31116da29d8cfa54e57eb748bc5f377e49400fdd788b649eca1ac4" - "361834013b2ad7322480a89ca58e0cf74bc9e540c2add6897fad0a3084f302adc", - "0x4531acd1fe0023c7550d267b6b2fee80922b14b2ffb90f04d4eb7c09b5d2d15d" - "a82f2d7ecb1dbac719905c5eecc423f1d86e25edbe23c595d644aaf187e6e6df", - - "0x24d19cc64572ee30f396bf6ebbfd7a6c5213b3b3d7057cc825f91093a68cd762" - "fd60611262cd838dc6b60aa7eee804e28bc849977fac33b4b530f1b120248a9a", - "0x2bb312a43bd2ce6e0d020613c857acddcfbf061e91e5f2c3f32447c259f39b2" - "c83ab156d77f1496bf7eb3351e1ee4e43dc1a18b91b24640b6dbb92cb1add371e", - 1 - }, - { - "GOST2012-512-tc26-A", 512, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc7", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc4", - "0xe8c2505dedfc86ddc1bd0b2b6667f1da34b82574761cb0e879bd081cfd0b6265" - "ee3cb090f30d27614cb4574010da90dd862ef9d4ebee4761503190785a71c760", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "27e69532f48d89116ff22b8d4e0560609b4b38abfad2b85dcacdb1411f10b275", - "0x0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000003", - "0x7503cfe87a836ae3a61b8816e25450e6ce5e1c93acf1abc1778064fdcbefa921" - "df1626be4fd036e93d75e6a50e3a41e98028fe5fc235f5b889a589cb5215f2a4", - 1 - }, - { - "GOST2012-512-tc26-B", 512, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0x8000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000006f", - "0x8000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000006c", - "0x687d1b459dc841457e3e06cf6f5e2517b97c7d614af138bcbf85dc806c4b289f" - "3e965d2db1416d217f8b276fad1ab69c50f78bee1fa3106efb8ccbc7c5140116", - "0x8000000000000000000000000000000000000000000000000000000000000001" - "49a1ec142565a545acfdb77bd9d40cfa8b996712101bea0ec6346c54374f25bd", - "0x0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000002", - "0x1a8f7eda389b094c2c071e3647a8940f3c123b697578c213be6dd9e6c8ec7335" - "dcb228fd1edf4a39152cbcaaf8c0398828041055f94ceeec7e21340780fe41bd", - 1 - }, - { - "GOST2012-512-tc26-C", 512, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc7", - "0xdc9203e514a721875485a529d2c722fb187bc8980eb866644de41c68e1430645" - "46e861c0e2c9edd92ade71f46fcf50ff2ad97f951fda9f2a2eb6546f39689bd3", - "0xb4c4ee28cebc6c2c8ac12952cf37f16ac7efb6a9f69f4b57ffda2e4f0de5ade0" - "38cbc2fff719d2c18de0284b8bfef3b52b8cc7a5f5bf0a3c8d2319a5312557e1", - "0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "c98cdba46506ab004c33a9ff5147502cc8eda9e7a769a12694623cef47f023ed", - "0xe2e31edfc23de7bdebe241ce593ef5de2295b7a9cbaef021d385f7074cea043a" - "a27272a7ae602bf2a7b9033db9ed3610c6fb85487eae97aac5bc7928c1950148", - "0xf5ce40d95b5eb899abbccff5911cb8577939804d6527378b8c108c3d2090ff9be" - "18e2d33e3021ed2ef32d85822423b6304f726aa854bae07d0396e9a9addc40f", - 4 - }, - - { - "secp256k1", 256, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000007", - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", - "0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", - "0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", - 1 - }, - - { - "sm2p256v1", 256, 0, - MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, - "0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff", - "0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc", - "0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93", - "0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123", - "0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7", - "0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0", - 1 - }, - - { NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } - }; - - - - -/* Return a copy of POINT. */ -static gcry_mpi_point_t -point_copy (gcry_mpi_point_t point) -{ - gcry_mpi_point_t newpoint; - - if (point) - { - newpoint = mpi_point_new (0); - point_set (newpoint, point); - } - else - newpoint = NULL; - return newpoint; -} - - -/* Helper to scan a hex string. */ -static gcry_mpi_t -scanval (const char *string) -{ - gpg_err_code_t rc; - gcry_mpi_t val; - - rc = _gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL); - if (rc) - log_fatal ("scanning ECC parameter failed: %s\n", gpg_strerror (rc)); - return val; -} - - -/* Return the index of the domain_parms table for a curve with NAME. - Return -1 if not found. */ -static int -find_domain_parms_idx (const char *name) -{ - int idx, aliasno; - - /* First check our native curves. */ - for (idx = 0; domain_parms[idx].desc; idx++) - if (!strcmp (name, domain_parms[idx].desc)) - return idx; - - /* If not found consult the alias table. */ - if (!domain_parms[idx].desc) - { - for (aliasno = 0; curve_aliases[aliasno].name; aliasno++) - if (!strcmp (name, curve_aliases[aliasno].other)) - break; - if (curve_aliases[aliasno].name) - { - for (idx = 0; domain_parms[idx].desc; idx++) - if (!strcmp (curve_aliases[aliasno].name, domain_parms[idx].desc)) - return idx; - } - } - - return -1; -} - - -/* Generate the crypto system setup. This function takes the NAME of - a curve or the desired number of bits and stores at R_CURVE the - parameters of the named curve or those of a suitable curve. If - R_NBITS is not NULL, the chosen number of bits is stored there. - NULL may be given for R_CURVE, if the value is not required and for - example only a quick test for availability is desired. Note that - the curve fields should be initialized to zero because fields which - are not NULL are skipped. */ -gpg_err_code_t -_gcry_ecc_fill_in_curve (unsigned int nbits, const char *name, - elliptic_curve_t *curve, unsigned int *r_nbits) -{ - int idx; - const char *resname = NULL; /* Set to a found curve name. */ - - if (name) - idx = find_domain_parms_idx (name); - else - { - for (idx = 0; domain_parms[idx].desc; idx++) - if (nbits == domain_parms[idx].nbits - && domain_parms[idx].model == MPI_EC_WEIERSTRASS) - break; - if (!domain_parms[idx].desc) - idx = -1; - } - if (idx < 0) - return GPG_ERR_UNKNOWN_CURVE; - - resname = domain_parms[idx].desc; - - /* In fips mode we only support NIST curves. Note that it is - possible to bypass this check by specifying the curve parameters - directly. */ - if (fips_mode () && !domain_parms[idx].fips ) - return GPG_ERR_NOT_SUPPORTED; - - switch (domain_parms[idx].model) - { - case MPI_EC_WEIERSTRASS: - case MPI_EC_EDWARDS: - case MPI_EC_MONTGOMERY: - break; - default: - return GPG_ERR_BUG; - } - - - if (r_nbits) - *r_nbits = domain_parms[idx].nbits; - - if (curve) - { - curve->model = domain_parms[idx].model; - curve->dialect = domain_parms[idx].dialect; - if (!curve->p) - curve->p = scanval (domain_parms[idx].p); - if (!curve->a) - { - curve->a = scanval (domain_parms[idx].a); - if (curve->a->sign) - { - mpi_resize (curve->a, curve->p->nlimbs); - _gcry_mpih_sub_n (curve->a->d, curve->p->d, - curve->a->d, curve->p->nlimbs); - curve->a->nlimbs = curve->p->nlimbs; - curve->a->sign = 0; - } - } - if (!curve->b) - { - curve->b = scanval (domain_parms[idx].b); - if (curve->b->sign) - { - mpi_resize (curve->b, curve->p->nlimbs); - _gcry_mpih_sub_n (curve->b->d, curve->p->d, - curve->b->d, curve->p->nlimbs); - curve->b->nlimbs = curve->p->nlimbs; - curve->b->sign = 0; - } - } - if (!curve->n) - curve->n = scanval (domain_parms[idx].n); - if (!curve->G.x) - curve->G.x = scanval (domain_parms[idx].g_x); - if (!curve->G.y) - curve->G.y = scanval (domain_parms[idx].g_y); - curve->h = domain_parms[idx].h; - - /* - * In the constants of domain_parms, we defined Curve25519 - * domain parameters as the ones in RFC-7748 before the errata - * (eid4730). To keep the computation having exact same values, - * we recover the new value of g_y, here. - */ - if (!strcmp (resname, "Curve25519")) - mpi_sub (curve->G.y, curve->p, curve->G.y); - - if (!curve->G.z) - curve->G.z = mpi_alloc_set_ui (1); - if (!curve->name) - curve->name = resname; - } - - return 0; -} - - -/* Give the name of the curve NAME, store the curve parameters into P, - A, B, G, and N if they point to NULL value. Note that G is - returned in standard uncompressed format. Also update MODEL and - DIALECT if they are not NULL. */ -gpg_err_code_t -_gcry_ecc_update_curve_param (const char *name, - enum gcry_mpi_ec_models *model, - enum ecc_dialects *dialect, - gcry_mpi_t *p, gcry_mpi_t *a, gcry_mpi_t *b, - gcry_mpi_t *g, gcry_mpi_t *n) -{ - int idx; - - idx = find_domain_parms_idx (name); - if (idx < 0) - return GPG_ERR_UNKNOWN_CURVE; - - if (g) - { - char *buf; - size_t len; - - len = 4; - len += strlen (domain_parms[idx].g_x+2); - len += strlen (domain_parms[idx].g_y+2); - len++; - buf = xtrymalloc (len); - if (!buf) - return gpg_err_code_from_syserror (); - strcpy (stpcpy (stpcpy (buf, "0x04"), domain_parms[idx].g_x+2), - domain_parms[idx].g_y+2); - _gcry_mpi_release (*g); - *g = scanval (buf); - xfree (buf); - } - if (model) - *model = domain_parms[idx].model; - if (dialect) - *dialect = domain_parms[idx].dialect; - if (p) - { - _gcry_mpi_release (*p); - *p = scanval (domain_parms[idx].p); - } - if (a) - { - _gcry_mpi_release (*a); - *a = scanval (domain_parms[idx].a); - } - if (b) - { - _gcry_mpi_release (*b); - *b = scanval (domain_parms[idx].b); - } - if (n) - { - _gcry_mpi_release (*n); - *n = scanval (domain_parms[idx].n); - } - return 0; -} - - -/* Return the name matching the parameters in PKEY. This works only - with curves described by the Weierstrass equation. */ -const char * -_gcry_ecc_get_curve (gcry_sexp_t keyparms, int iterator, unsigned int *r_nbits) -{ - gpg_err_code_t rc; - const char *result = NULL; - elliptic_curve_t E; - gcry_mpi_point_t G = NULL; - gcry_mpi_t tmp = NULL; - int idx; - - memset (&E, 0, sizeof E); - - if (r_nbits) - *r_nbits = 0; - - if (!keyparms) - { - idx = iterator; - if (idx >= 0 && idx < DIM (domain_parms)) - { - result = domain_parms[idx].desc; - if (r_nbits) - *r_nbits = domain_parms[idx].nbits; - } - return result; - } - - - /* - * Extract the curve parameters.. - */ - rc = gpg_err_code (sexp_extract_param (keyparms, NULL, "pabn", - &E.p, &E.a, &E.b, &E.n, NULL)); - if (rc == GPG_ERR_NO_OBJ) - { - /* This might be the second use case of checking whether a - specific curve given by name is supported. */ - gcry_sexp_t l1; - char *name; - - l1 = sexp_find_token (keyparms, "curve", 5); - if (!l1) - goto leave; /* No curve name parameter. */ - - name = sexp_nth_string (l1, 1); - sexp_release (l1); - if (!name) - goto leave; /* Name missing or out of core. */ - - idx = find_domain_parms_idx (name); - xfree (name); - if (idx >= 0) /* Curve found. */ - { - result = domain_parms[idx].desc; - if (r_nbits) - *r_nbits = domain_parms[idx].nbits; - } - return result; - } - - if (rc) - goto leave; - - rc = point_from_keyparam (&G, keyparms, "g", NULL); - if (rc) - goto leave; - - _gcry_mpi_point_init (&E.G); - _gcry_mpi_point_set (&E.G, G->x, G->y, G->z); - - for (idx = 0; domain_parms[idx].desc; idx++) - { - mpi_free (tmp); - tmp = scanval (domain_parms[idx].p); - if (mpi_cmp (tmp, E.p)) - continue; - - mpi_free (tmp); - tmp = scanval (domain_parms[idx].a); - if (tmp->sign) - { - if (!mpi_cmpabs (tmp, E.a)) - /* For backward compatibility to <= libgcrypt 1.8, we - allow this match to support existing keys in SEXP. */ - ; - else - { - mpi_resize (tmp, E.p->nlimbs); - _gcry_mpih_sub_n (tmp->d, E.p->d, - tmp->d, E.p->nlimbs); - tmp->nlimbs = E.p->nlimbs; - tmp->sign = 0; - if (mpi_cmp (tmp, E.a)) - continue; - } - } - else if (mpi_cmp (tmp, E.a)) - continue; - - mpi_free (tmp); - tmp = scanval (domain_parms[idx].b); - if (tmp->sign) - { - if (!mpi_cmpabs (tmp, E.b)) - /* Same for backward compatibility, see above. */ - ; - else - { - mpi_resize (tmp, E.p->nlimbs); - _gcry_mpih_sub_n (tmp->d, E.p->d, - tmp->d, E.p->nlimbs); - tmp->nlimbs = E.p->nlimbs; - tmp->sign = 0; - if (mpi_cmp (tmp, E.b)) - continue; - } - } - else if (mpi_cmp (tmp, E.b)) - continue; - - mpi_free (tmp); - tmp = scanval (domain_parms[idx].n); - if (mpi_cmp (tmp, E.n)) - continue; - - mpi_free (tmp); - tmp = scanval (domain_parms[idx].g_x); - if (mpi_cmp (tmp, E.G.x)) - continue; - - mpi_free (tmp); - tmp = scanval (domain_parms[idx].g_y); - if (mpi_cmp (tmp, E.G.y)) - continue; - - result = domain_parms[idx].desc; - if (r_nbits) - *r_nbits = domain_parms[idx].nbits; - break; - } - - leave: - _gcry_mpi_point_release (G); - _gcry_mpi_release (tmp); - _gcry_mpi_release (E.p); - _gcry_mpi_release (E.a); - _gcry_mpi_release (E.b); - _gcry_mpi_point_free_parts (&E.G); - _gcry_mpi_release (E.n); - return result; -} - - -/* Helper to extract an MPI from key parameters. */ -static gpg_err_code_t -mpi_from_keyparam (gcry_mpi_t *r_a, gcry_sexp_t keyparam, const char *name, - int opaque) -{ - gcry_err_code_t ec = 0; - gcry_sexp_t l1; - - l1 = sexp_find_token (keyparam, name, 0); - if (l1) - { - *r_a = sexp_nth_mpi (l1, 1, opaque? GCRYMPI_FMT_OPAQUE : GCRYMPI_FMT_USG); - sexp_release (l1); - if (!*r_a) - ec = GPG_ERR_INV_OBJ; - } - return ec; -} - -/* Helper to extract a point from key parameters. If no parameter - with NAME is found, the functions tries to find a non-encoded point - by appending ".x", ".y" and ".z" to NAME. ".z" is in this case - optional and defaults to 1. EC is the context which at this point - may not be fully initialized. */ -static gpg_err_code_t -point_from_keyparam (gcry_mpi_point_t *r_a, - gcry_sexp_t keyparam, const char *name, mpi_ec_t ec) -{ - gcry_err_code_t rc; - gcry_sexp_t l1; - gcry_mpi_point_t point; - - l1 = sexp_find_token (keyparam, name, 0); - if (l1) - { - gcry_mpi_t a; - - a = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_OPAQUE); - sexp_release (l1); - if (!a) - return GPG_ERR_INV_OBJ; - - point = mpi_point_new (0); - rc = _gcry_mpi_ec_decode_point (point, a, ec); - mpi_free (a); - if (rc) - { - mpi_point_release (point); - return rc; - } - } - else - { - char *tmpname; - gcry_mpi_t x = NULL; - gcry_mpi_t y = NULL; - gcry_mpi_t z = NULL; - - tmpname = xtrymalloc (strlen (name) + 2 + 1); - if (!tmpname) - return gpg_err_code_from_syserror (); - strcpy (stpcpy (tmpname, name), ".x"); - rc = mpi_from_keyparam (&x, keyparam, tmpname, 0); - if (rc) - { - xfree (tmpname); - return rc; - } - strcpy (stpcpy (tmpname, name), ".y"); - rc = mpi_from_keyparam (&y, keyparam, tmpname, 0); - if (rc) - { - mpi_free (x); - xfree (tmpname); - return rc; - } - strcpy (stpcpy (tmpname, name), ".z"); - rc = mpi_from_keyparam (&z, keyparam, tmpname, 0); - if (rc) - { - mpi_free (y); - mpi_free (x); - xfree (tmpname); - return rc; - } - if (!z) - z = mpi_set_ui (NULL, 1); - if (x && y) - point = mpi_point_snatch_set (NULL, x, y, z); - else - { - mpi_free (x); - mpi_free (y); - mpi_free (z); - point = NULL; - } - xfree (tmpname); - } - - if (point) - *r_a = point; - return 0; -} - - - -static gpg_err_code_t -mpi_ec_get_elliptic_curve (elliptic_curve_t *E, int *r_flags, - gcry_sexp_t keyparam, const char *curvename) -{ - gpg_err_code_t errc; - unsigned int nbits; - gcry_sexp_t l1; - - errc = _gcry_pk_util_get_nbits (keyparam, &nbits); - if (errc) - return errc; - - E->model = MPI_EC_WEIERSTRASS; - E->dialect = ECC_DIALECT_STANDARD; - E->h = 1; - - if (keyparam) - { - /* Parse an optional flags list. */ - l1 = sexp_find_token (keyparam, "flags", 0); - if (l1) - { - int flags = 0; - - errc = _gcry_pk_util_parse_flaglist (l1, &flags, NULL); - sexp_release (l1); - l1 = NULL; - if (errc) - goto leave; - - *r_flags |= flags; - } - - /* Parse the deprecated optional transient-key flag. */ - l1 = sexp_find_token (keyparam, "transient-key", 0); - if (l1) - { - *r_flags |= PUBKEY_FLAG_TRANSIENT_KEY; - sexp_release (l1); - } - - /* Check whether a curve name was given. */ - l1 = sexp_find_token (keyparam, "curve", 5); - - /* If we don't have a curve name or if override parameters have - explicitly been requested, parse them. */ - if (!l1 || (*r_flags & PUBKEY_FLAG_PARAM)) - { - gcry_mpi_point_t G = NULL; - gcry_mpi_t cofactor = NULL; - - errc = mpi_from_keyparam (&E->p, keyparam, "p", 0); - if (errc) - goto leave; - errc = mpi_from_keyparam (&E->a, keyparam, "a", 0); - if (errc) - goto leave; - errc = mpi_from_keyparam (&E->b, keyparam, "b", 0); - if (errc) - goto leave; - errc = point_from_keyparam (&G, keyparam, "g", NULL); - if (errc) - goto leave; - if (G) - { - _gcry_mpi_point_init (&E->G); - mpi_point_set (&E->G, G->x, G->y, G->z); - mpi_point_set (G, NULL, NULL, NULL); - mpi_point_release (G); - } - errc = mpi_from_keyparam (&E->n, keyparam, "n", 0); - if (errc) - goto leave; - errc = mpi_from_keyparam (&cofactor, keyparam, "h", 0); - if (errc) - goto leave; - if (cofactor) - { - mpi_get_ui (&E->h, cofactor); - mpi_free (cofactor); - } - } - } - else - l1 = NULL; /* No curvename. */ - - /* Check whether a curve parameter is available and use that to fill - in missing values. If no curve parameter is available try an - optional provided curvename. If only the curvename has been - given use that one. */ - if (l1 || curvename || nbits) - { - char *name; - - if (l1) - { - name = sexp_nth_string (l1, 1); - sexp_release (l1); - if (!name) - { - errc = GPG_ERR_INV_OBJ; /* Name missing or out of core. */ - goto leave; - } - } - else - name = NULL; - - errc = _gcry_ecc_fill_in_curve (nbits, name? name : curvename, E, NULL); - xfree (name); - if (errc) - goto leave; - } - - leave: - return errc; -} - -static gpg_err_code_t -mpi_ec_setup_elliptic_curve (mpi_ec_t ec, int flags, - elliptic_curve_t *E, gcry_sexp_t keyparam) -{ - gpg_err_code_t errc = 0; - - ec->G = mpi_point_snatch_set (NULL, E->G.x, E->G.y, E->G.z); - E->G.x = NULL; - E->G.y = NULL; - E->G.z = NULL; - ec->n = E->n; - E->n = NULL; - ec->h = E->h; - ec->name = E->name; - - /* Now that we know the curve name we can look for the public key - Q. point_from_keyparam needs to know the curve parameters so - that it is able to use the correct decompression. Parsing - the private key D could have been done earlier but it is less - surprising if we do it here as well. */ - if (keyparam) - { - int is_opaque_bytes = ((ec->dialect == ECC_DIALECT_ED25519 - && (flags & PUBKEY_FLAG_EDDSA)) - || (ec->dialect == ECC_DIALECT_SAFECURVE)); - - errc = point_from_keyparam (&ec->Q, keyparam, "q", ec); - if (errc) - return errc; - errc = mpi_from_keyparam (&ec->d, keyparam, "d", is_opaque_bytes); - - /* Size of opaque bytes should match size of P. */ - if (!errc && ec->d && is_opaque_bytes) - { - unsigned int n = mpi_get_nbits (ec->d); - unsigned int len; - - len = (ec->nbits+7)/8; - /* EdDSA requires additional bit for sign. */ - if ((ec->nbits%8) == 0 && ec->model == MPI_EC_EDWARDS) - len++; - - if ((n+7)/8 != len) - { - if (ec->dialect == ECC_DIALECT_ED25519) - { - /* - * GnuPG (<= 2.2) or OpenPGP implementations with no - * SOS support may remove zeros at the beginning. - * Recover those zeros. - */ - /* - * Also, GnuPG (<= 2.2) may add additional zero at - * the beginning, when private key is moved from - * OpenPGP to gpg-agent. Remove such a zero-prefix. - */ - const unsigned char *buf; - unsigned char *value; - - buf = mpi_get_opaque (ec->d, &n); - if (!buf) - return GPG_ERR_INV_OBJ; - - value = xtrymalloc_secure (len); - if (!value) - return gpg_err_code_from_syserror (); - - if ((n+7)/8 < len) - /* Recover zeros. */ - { - memset (value, 0, len - (n+7)/8); - memcpy (value + len - (n+7)/8, buf, (n+7)/8); - } - else if ((n+7)/8 == len + 1) - /* Remove a zero. */ - memcpy (value, buf+1, len); - else - { - xfree (value); - return GPG_ERR_INV_OBJ; - } - - mpi_set_opaque (ec->d, value, len*8); - } - else - { - if (DBG_CIPHER) - log_debug ("scalar size (%d) != prime size (%d)", - (n+7)/8, len); - - errc = GPG_ERR_INV_OBJ; - } - } - } - } - - return errc; -} - -gpg_err_code_t -_gcry_mpi_ec_internal_new (mpi_ec_t *r_ec, int *r_flags, const char *name_op, - gcry_sexp_t keyparam, const char *curvename) -{ - gpg_err_code_t errc; - elliptic_curve_t E; - mpi_ec_t ec; - - *r_ec = NULL; - - memset (&E, 0, sizeof E); - errc = mpi_ec_get_elliptic_curve (&E, r_flags, keyparam, curvename); - if (errc) - goto leave; - - ec = _gcry_mpi_ec_p_internal_new (E.model, E.dialect, *r_flags, - E.p, E.a, E.b); - if (!ec) - goto leave; - - errc = mpi_ec_setup_elliptic_curve (ec, *r_flags, &E, keyparam); - if (errc) - { - _gcry_mpi_ec_free (ec); - goto leave; - } - else - *r_ec = ec; - - if (!errc && DBG_CIPHER) - { - gcry_mpi_t mpi_q = NULL; - gcry_sexp_t l1; - char msg[80]; - - l1 = sexp_find_token (keyparam, "q", 0); - if (l1) - { - mpi_q = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_OPAQUE); - sexp_release (l1); - } - - log_debug ("%s info: %s/%s%s\n", name_op, - _gcry_ecc_model2str (ec->model), - _gcry_ecc_dialect2str (ec->dialect), - (*r_flags & PUBKEY_FLAG_EDDSA)? "+EdDSA" : ""); - if (ec->name) - log_debug ("%s name: %s\n", name_op, ec->name); - snprintf (msg, sizeof msg, "%s p", name_op); - log_printmpi (msg, ec->p); - snprintf (msg, sizeof msg, "%s a", name_op); - log_printmpi (msg, ec->a); - snprintf (msg, sizeof msg, "%s b", name_op); - log_printmpi (msg, ec->b); - snprintf (msg, sizeof msg, "%s g", name_op); - log_printpnt (msg, ec->G, NULL); - snprintf (msg, sizeof msg, "%s n", name_op); - log_printmpi (msg, ec->n); - log_debug ("%s h:+%02x\n", name_op, ec->h); - if (mpi_q) - { - snprintf (msg, sizeof msg, "%s q", name_op); - log_printmpi (msg, mpi_q); - mpi_free (mpi_q); - } - if (!fips_mode () && ec->d) - { - snprintf (msg, sizeof msg, "%s d", name_op); - log_printmpi (msg, ec->d); - } - } - - leave: - _gcry_ecc_curve_free (&E); - return errc; -} - -/* This function creates a new context for elliptic curve operations. - Either KEYPARAM or CURVENAME must be given. If both are given and - KEYPARAM has no curve parameter, CURVENAME is used to add missing - parameters. On success 0 is returned and the new context stored at - R_CTX. On error NULL is stored at R_CTX and an error code is - returned. The context needs to be released using - gcry_ctx_release. */ -gpg_err_code_t -_gcry_mpi_ec_new (gcry_ctx_t *r_ctx, - gcry_sexp_t keyparam, const char *curvename) -{ - gpg_err_code_t errc; - elliptic_curve_t E; - gcry_ctx_t ctx = NULL; - int flags = 0; - mpi_ec_t ec; - - *r_ctx = NULL; - - memset (&E, 0, sizeof E); - errc = mpi_ec_get_elliptic_curve (&E, &flags, keyparam, curvename); - if (errc) - goto leave; - - errc = _gcry_mpi_ec_p_new (&ctx, E.model, E.dialect, flags, E.p, E.a, E.b); - if (errc) - goto leave; - - ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC); - errc = mpi_ec_setup_elliptic_curve (ec, flags, &E, keyparam); - if (errc) - goto leave; - - *r_ctx = ctx; - ctx = NULL; - - leave: - _gcry_ecc_curve_free (&E); - _gcry_ctx_release (ctx); - return errc; -} - - -/* Return the parameters of the curve NAME as an S-expression. */ -gcry_sexp_t -_gcry_ecc_get_param_sexp (const char *name) -{ - elliptic_curve_t E; - gcry_mpi_t pkey[5]; - gcry_sexp_t result; - - memset (&E, 0, sizeof E); - if (_gcry_ecc_fill_in_curve (0, name, &E, NULL)) - return NULL; - - pkey[0] = E.p; - pkey[1] = E.a; - pkey[2] = E.b; - pkey[3] = _gcry_ecc_ec2os (E.G.x, E.G.y, E.p); - pkey[4] = E.n; - - if (sexp_build (&result, NULL, - "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)))", - pkey[0], pkey[1], pkey[2], pkey[3], pkey[4], E.h)) - result = NULL; - - _gcry_ecc_curve_free (&E); - _gcry_mpi_release (pkey[3]); - - return result; -} - - -/* Return an MPI (or opaque MPI) described by NAME and the context EC. - If COPY is true a copy is returned, if not a const MPI may be - returned. In any case mpi_free must be used. */ -gcry_mpi_t -_gcry_ecc_get_mpi (const char *name, mpi_ec_t ec, int copy) -{ - if (!*name) - return NULL; - - if (!strcmp (name, "p") && ec->p) - return mpi_is_const (ec->p) && !copy? ec->p : mpi_copy (ec->p); - if (!strcmp (name, "a") && ec->a) - return mpi_is_const (ec->a) && !copy? ec->a : mpi_copy (ec->a); - if (!strcmp (name, "b") && ec->b) - return mpi_is_const (ec->b) && !copy? ec->b : mpi_copy (ec->b); - if (!strcmp (name, "n") && ec->n) - return mpi_is_const (ec->n) && !copy? ec->n : mpi_copy (ec->n); - if (!strcmp (name, "h")) - { - gcry_mpi_t h = _gcry_mpi_get_const (ec->h); - - return !copy? h : mpi_set (NULL, h); - } - if (!strcmp (name, "d") && ec->d) - return mpi_is_const (ec->d) && !copy? ec->d : mpi_copy (ec->d); - - /* Return a requested point coordinate. */ - if (!strcmp (name, "g.x") && ec->G && ec->G->x) - return mpi_is_const (ec->G->x) && !copy? ec->G->x : mpi_copy (ec->G->x); - if (!strcmp (name, "g.y") && ec->G && ec->G->y) - return mpi_is_const (ec->G->y) && !copy? ec->G->y : mpi_copy (ec->G->y); - if (!strcmp (name, "q.x") && ec->Q && ec->Q->x) - return mpi_is_const (ec->Q->x) && !copy? ec->Q->x : mpi_copy (ec->Q->x); - if (!strcmp (name, "q.y") && ec->Q && ec->Q->y) - return mpi_is_const (ec->Q->y) && !copy? ec->Q->y : mpi_copy (ec->Q->y); - - /* If the base point has been requested, return it in standard - encoding. */ - if (!strcmp (name, "g") && ec->G) - return _gcry_mpi_ec_ec2os (ec->G, ec); - - /* If the public key has been requested, return it by default in - standard uncompressed encoding or if requested in other - encodings. */ - if (*name == 'q' && (!name[1] || name[1] == '@')) - { - /* If only the private key is given, compute the public key. */ - if (!ec->Q) - ec->Q = _gcry_ecc_compute_public (NULL, ec); - - if (!ec->Q) - return NULL; - - if (name[1] != '@') - return _gcry_mpi_ec_ec2os (ec->Q, ec); - - if (!strcmp (name+2, "eddsa") && ec->model == MPI_EC_EDWARDS) - { - unsigned char *encpk; - unsigned int encpklen; - - if (!_gcry_ecc_eddsa_encodepoint (ec->Q, ec, NULL, NULL, 0, - &encpk, &encpklen)) - return mpi_set_opaque (NULL, encpk, encpklen*8); - } - } - - return NULL; -} - - -/* Return a point described by NAME and the context EC. */ -gcry_mpi_point_t -_gcry_ecc_get_point (const char *name, mpi_ec_t ec) -{ - if (!strcmp (name, "g") && ec->G) - return point_copy (ec->G); - if (!strcmp (name, "q")) - { - /* If only the private key is given, compute the public key. */ - if (!ec->Q) - ec->Q = _gcry_ecc_compute_public (NULL, ec); - - if (ec->Q) - return point_copy (ec->Q); - } - - return NULL; -} - - -/* Store the MPI NEWVALUE into the context EC under NAME. */ -gpg_err_code_t -_gcry_ecc_set_mpi (const char *name, gcry_mpi_t newvalue, mpi_ec_t ec) -{ - gpg_err_code_t rc = 0; - - if (!*name) - ; - else if (!strcmp (name, "p")) - { - mpi_free (ec->p); - ec->p = mpi_copy (newvalue); - _gcry_mpi_ec_get_reset (ec); - } - else if (!strcmp (name, "a")) - { - mpi_free (ec->a); - ec->a = mpi_copy (newvalue); - _gcry_mpi_ec_get_reset (ec); - } - else if (!strcmp (name, "b")) - { - mpi_free (ec->b); - ec->b = mpi_copy (newvalue); - } - else if (!strcmp (name, "n")) - { - mpi_free (ec->n); - ec->n = mpi_copy (newvalue); - } - else if (!strcmp (name, "h")) - { - mpi_get_ui (&ec->h, newvalue); - } - else if (*name == 'q' && (!name[1] || name[1] == '@')) - { - if (newvalue) - { - if (!ec->Q) - ec->Q = mpi_point_new (0); - rc = _gcry_mpi_ec_decode_point (ec->Q, newvalue, ec); - } - if (rc || !newvalue) - { - _gcry_mpi_point_release (ec->Q); - ec->Q = NULL; - } - /* Note: We assume that Q matches d and thus do not reset d. */ - } - else if (!strcmp (name, "d")) - { - mpi_free (ec->d); - ec->d = mpi_copy (newvalue); - if (ec->d) - { - /* We need to reset the public key because it may not - anymore match. */ - _gcry_mpi_point_release (ec->Q); - ec->Q = NULL; - } - } - else - rc = GPG_ERR_UNKNOWN_NAME; - - return rc; -} - - -/* Store the point NEWVALUE into the context EC under NAME. */ -gpg_err_code_t -_gcry_ecc_set_point (const char *name, gcry_mpi_point_t newvalue, mpi_ec_t ec) -{ - if (!strcmp (name, "g")) - { - _gcry_mpi_point_release (ec->G); - ec->G = point_copy (newvalue); - } - else if (!strcmp (name, "q")) - { - _gcry_mpi_point_release (ec->Q); - ec->Q = point_copy (newvalue); - } - else - return GPG_ERR_UNKNOWN_NAME; - - return 0; -} diff --git a/hobble-libgcrypt b/hobble-libgcrypt deleted file mode 100755 index 1aa88dc..0000000 --- a/hobble-libgcrypt +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -# Quit out if anything fails. -set -e -x - -# Clean out patent-or-otherwise-encumbered code. -# EC: ????????? ??/??/2015 - -rm -f cipher/ecc-curves.c -rm -f tests/curves.c -rm -f tests/keygrip.c diff --git a/keygrip.c b/keygrip.c deleted file mode 100644 index 56fbba8..0000000 --- a/keygrip.c +++ /dev/null @@ -1,341 +0,0 @@ -/* keygrip.c - verifies that keygrips are calculated as expected - * Copyright (C) 2005 Free Software Foundation, Inc. - * - * This file is part of Libgcrypt. - * - * Libgcrypt 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. - * - * Libgcrypt 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 Lesser 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#define PGM "keygrip" -#include "t-common.h" - -static int repetitions; - - - -static void -print_hex (const char *text, const void *buf, size_t n) -{ - const unsigned char *p = buf; - - fputs (text, stdout); - for (; n; n--, p++) - printf ("%02X", *p); - putchar ('\n'); -} - - - - -static struct -{ - int algo; - const char *key; - const unsigned char grip[20]; -} key_grips[] = - { - { - GCRY_PK_RSA, - "(private-key" - " (rsa" - " (n #00B6B509596A9ECABC939212F891E656A626BA07DA8521A9CAD4C08E640C04052FBB87F424EF1A0275A48A9299AC9DB69ABE3D0124E6C756B1F7DFB9B842D6251AEA6EE85390495CADA73D671537FCE5850A932F32BAB60AB1AC1F852C1F83C625E7A7D70CDA9EF16D5C8E47739D77DF59261ABE8454807FF441E143FBD37F8545#)" - " (e #010001#)" - " (d #077AD3DE284245F4806A1B82B79E616FBDE821C82D691A65665E57B5FAD3F34E67F401E7BD2E28699E89D9C496CF821945AE83AC7A1231176A196BA6027E77D85789055D50404A7A2A95B1512F91F190BBAEF730ED550D227D512F89C0CDB31AC06FA9A19503DDF6B66D0B42B9691BFD6140EC1720FFC48AE00C34796DC899E5#)" - " (p #00D586C78E5F1B4BF2E7CD7A04CA091911706F19788B93E44EE20AAF462E8363E98A72253ED845CCBF2481BB351E8557C85BCFFF0DABDBFF8E26A79A0938096F27#)" - " (q #00DB0CDF60F26F2A296C88D6BF9F8E5BE45C0DDD713C96CC73EBCB48B061740943F21D2A93D6E42A7211E7F02A95DCED6C390A67AD21ECF739AE8A0CA46FF2EBB3#)" - " (u #33149195F16912DB20A48D020DBC3B9E3881B39D722BF79378F6340F43148A6E9FC5F53E2853B7387BA4443BA53A52FCA8173DE6E85B42F9783D4A7817D0680B#)))", - "\x32\xCF\xFA\x85\xB1\x79\x1F\xBB\x26\x14\xE9\x1A\xFD\xF3\xAF\xE3\x32\x08\x2E\x25" - }, - { - GCRY_PK_DSA, - " (public-key" - " (dsa" - " (p #0084E4C626E16005770BD9509ABF7354492E85B8C0060EFAAAEC617F725B592FAA59DF5460575F41022776A9718CE62EDD542AB73C7720869EBDBC834D174ADCD7136827DF51E2613545A25CA573BC502A61B809000B6E35F5EB7FD6F18C35678C23EA1C3638FB9CFDBA2800EE1B62F41A4479DE824F2834666FBF8DC5B53C2617#)" - " (q #00B0E6F710051002A9F425D98A677B18E0E5B038AB#)" - " (g #44370CEE0FE8609994183DBFEBA7EEA97D466838BCF65EFF506E35616DA93FA4E572A2F08886B74977BC00CA8CD3DBEA7AEB7DB8CBB180E6975E0D2CA76E023E6DE9F8CCD8826EBA2F72B8516532F6001DEFFAE76AA5E59E0FA33DBA3999B4E92D1703098CDEDCC416CF008801964084CDE1980132B2B78CB4CE9C15A559528B#)" - " (y #3D5DD14AFA2BF24A791E285B90232213D0E3BA74AB1109E768AED19639A322F84BB7D959E2BA92EF73DE4C7F381AA9F4053CFA3CD4527EF9043E304E5B95ED0A3A5A9D590AA641C13DB2B6E32B9B964A6A2C730DD3EA7C8E13F7A140AFF1A91CE375E9B9B960384779DC4EA180FA1F827C52288F366C0770A220F50D6D8FD6F6#)))", - "\x04\xA3\x4F\xA0\x2B\x03\x94\xD7\x32\xAD\xD5\x9B\x50\xAF\xDB\x5D\x57\x22\xA6\x10" - - }, - { - GCRY_PK_DSA, - "(private-key" - " (dsa" - " (p #0084E4C626E16005770BD9509ABF7354492E85B8C0060EFAAAEC617F725B592FAA59DF5460575F41022776A9718CE62EDD542AB73C7720869EBDBC834D174ADCD7136827DF51E2613545A25CA573BC502A61B809000B6E35F5EB7FD6F18C35678C23EA1C3638FB9CFDBA2800EE1B62F41A4479DE824F2834666FBF8DC5B53C2617#)" - " (q #00B0E6F710051002A9F425D98A677B18E0E5B038AB#)" - " (g #44370CEE0FE8609994183DBFEBA7EEA97D466838BCF65EFF506E35616DA93FA4E572A2F08886B74977BC00CA8CD3DBEA7AEB7DB8CBB180E6975E0D2CA76E023E6DE9F8CCD8826EBA2F72B8516532F6001DEFFAE76AA5E59E0FA33DBA3999B4E92D1703098CDEDCC416CF008801964084CDE1980132B2B78CB4CE9C15A559528B#)" - " (y #3D5DD14AFA2BF24A791E285B90232213D0E3BA74AB1109E768AED19639A322F84BB7D959E2BA92EF73DE4C7F381AA9F4053CFA3CD4527EF9043E304E5B95ED0A3A5A9D590AA641C13DB2B6E32B9B964A6A2C730DD3EA7C8E13F7A140AFF1A91CE375E9B9B960384779DC4EA180FA1F827C52288F366C0770A220F50D6D8FD6F6#)" - " (x #0087F9E91BFBCC1163DE71ED86D557708E32F8ADDE#)))", - "\x04\xA3\x4F\xA0\x2B\x03\x94\xD7\x32\xAD\xD5\x9B\x50\xAF\xDB\x5D\x57\x22\xA6\x10" - }, - { - GCRY_PK_ECDSA, - "(public-key" - " (ecdsa(flags param)" - " (p #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF#)" - " (a #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC#)" - " (b #5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B#)" - " (g #046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5#)" - " (n #00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551#)" - " (h #000000000000000000000000000000000000000000000000000000000000000001#)" - " (q #04C8A4CEC2E9A9BC8E173531A67B0840DF345C32E261ADD780E6D83D56EFADFD5DE872F8B854819B59543CE0B7F822330464FBC4E6324DADDCD9D059554F63B344#)))", - "\xE6\xDF\x94\x2D\xBD\x8C\x77\x05\xA3\xDD\x41\x6E\xFC\x04\x01\xDB\x31\x0E\x99\xB6" - }, - { - GCRY_PK_ECDSA, - "(public-key" - " (ecdsa(flags param)" - " (p #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF#)" - " (curve \"NIST P-256\")" - " (b #5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B#)" - " (g #046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5#)" - " (n #00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551#)" - " (h #000000000000000000000000000000000000000000000000000000000000000001#)" - " (q #04C8A4CEC2E9A9BC8E173531A67B0840DF345C32E261ADD780E6D83D56EFADFD5DE872F8B854819B59543CE0B7F822330464FBC4E6324DADDCD9D059554F63B344#)))", - "\xE6\xDF\x94\x2D\xBD\x8C\x77\x05\xA3\xDD\x41\x6E\xFC\x04\x01\xDB\x31\x0E\x99\xB6" - }, - { - GCRY_PK_ECDSA, - "(public-key" - " (ecdsa" - " (p #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF#)" - " (curve \"NIST P-256\")" - " (b #5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B#)" - " (g #046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5#)" - " (n #00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551#)" - " (h #000000000000000000000000000000000000000000000000000000000000000001#)" - " (q #04C8A4CEC2E9A9BC8E173531A67B0840DF345C32E261ADD780E6D83D56EFADFD5DE872F8B854819B59543CE0B7F822330464FBC4E6324DADDCD9D059554F63B344#)))", - "\xE6\xDF\x94\x2D\xBD\x8C\x77\x05\xA3\xDD\x41\x6E\xFC\x04\x01\xDB\x31\x0E\x99\xB6" - }, - { - GCRY_PK_ECDSA, - "(public-key" - " (ecdsa" - " (curve secp256r1)" - " (q #04C8A4CEC2E9A9BC8E173531A67B0840DF345C32E261ADD780E6D83D56EFADFD5DE872F8B854819B59543CE0B7F822330464FBC4E6324DADDCD9D059554F63B344#)))", - "\xE6\xDF\x94\x2D\xBD\x8C\x77\x05\xA3\xDD\x41\x6E\xFC\x04\x01\xDB\x31\x0E\x99\xB6" - }, - { - GCRY_PK_ECC, - "(public-key" - " (ecc" - " (curve secp256r1)" - " (q #04C8A4CEC2E9A9BC8E173531A67B0840DF345C32E261ADD780E6D83D56EFADFD5DE872F8B854819B59543CE0B7F822330464FBC4E6324DADDCD9D059554F63B344#)))", - "\xE6\xDF\x94\x2D\xBD\x8C\x77\x05\xA3\xDD\x41\x6E\xFC\x04\x01\xDB\x31\x0E\x99\xB6" - }, - { /* Ed25519 standard */ - GCRY_PK_ECC, - "(public-key" - " (ecc" - " (curve Ed25519)" - " (q #04" - " 1CC662926E7EFF4982B7FB8B928E61CD74CCDD85277CC57196C3AD20B611085F" - " 47BD24842905C049257673B3F5249524E0A41FAA17B25B818D0F97E625F1A1D0#)" - " ))", - "\x0C\xCA\xB2\xFD\x48\x9A\x33\x40\x2C\xE8" - "\xE0\x4A\x1F\xB2\x45\xEA\x80\x3D\x0A\xF1" - }, - { /* Ed25519+EdDSA */ - GCRY_PK_ECC, - "(public-key" - " (ecc" - " (curve Ed25519)(flags eddsa)" - " (q #773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB#)" - " ))", - "\x9D\xB6\xC6\x4A\x38\x83\x0F\x49\x60\x70" - "\x17\x89\x47\x55\x20\xBE\x8C\x82\x1F\x47" - }, - { /* Ed25519+EdDSA (with compression prefix) */ - GCRY_PK_ECC, - "(public-key" - " (ecc" - " (curve Ed25519)(flags eddsa)" - " (q #40" - " 773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB#)" - " ))", - "\x9D\xB6\xC6\x4A\x38\x83\x0F\x49\x60\x70" - "\x17\x89\x47\x55\x20\xBE\x8C\x82\x1F\x47" - }, - { /* Ed25519+EdDSA (same but uncompressed)*/ - GCRY_PK_ECC, - "(public-key" - " (ecc" - " (curve Ed25519)(flags eddsa)" - " (q #04" - " 629ad237d1ed04dcd4abe1711dd699a1cf51b1584c4de7a4ef8b8a640180b26f" - " 5bb7c29018ece0f46b01f2960e99041a5779afe7e2292b65f9d51f8c84723e77#)" - " ))", - "\x9D\xB6\xC6\x4A\x38\x83\x0F\x49\x60\x70" - "\x17\x89\x47\x55\x20\xBE\x8C\x82\x1F\x47" - }, - { /* Cv25519 */ - GCRY_PK_ECC, - "(public-key" - " (ecc" - " (curve Curve25519)(flags djb-tweak)" - " (q #40" - " 918C1733127F6BF2646FAE3D081A18AE77111C903B906310B077505EFFF12740#)" - " ))", - "\x0F\x89\xA5\x65\xD3\xEA\x18\x7C\xE8\x39" - "\x33\x23\x98\xF5\xD4\x80\x67\x7D\xF4\x9C" - }, - { /* Random key */ - GCRY_PK_RSA, - "(shadowed-private-key" - " (rsa" - " (n #00B493C79928398DA9D99AC0E949FE6EB62F683CB974FFFBFBC01066F5C9A89B" - " D3DC48EAD7C65F36EA943C2B2C865C26C4884FF9EDFDA8C99C855B737D77EEF6" - " B85DBC0CCEC0E900C1F89A6893A2A93E8B31028469B6927CEB2F08687E547C68" - " 6B0A2F7E50A194FF7AB7637E03DE0912EF7F6E5F1EC37625BD1620CCC2E7A564" - " 31E168CDAFBD1D9E61AE47A69A6FA03EF22F844528A710B2392F262B95A3078C" - " F321DC8325F92A5691EF69F34FD0DE0B22C79D29DC87723FCADE463829E8E5F7" - " D196D73D6C9C180F6A6A0DDBF7B9D8F7FA293C36163B12199EF6A1A95CAE4051" - " E3069C522CC6C4A7110F663A5DAD20F66C13A1674D050088208FAE4F33B3AB51" - " 03#)" - " (e #00010001#)" - " (shadowed t1-v1" - " (#D2760001240102000005000123350000# OPENPGP.1)" - ")))", - "\xE5\x6E\xE6\xEE\x5A\x2F\xDC\x3E\x98\x9D" - "\xFE\x49\xDA\xF5\x67\x43\xE3\x27\x28\x33" - } - }; - - -static void -check (void) -{ - unsigned char buf[20]; - unsigned char *ret; - gcry_error_t err; - gcry_sexp_t sexp; - unsigned int i; - int repn; - - for (i = 0; i < (sizeof (key_grips) / sizeof (*key_grips)); i++) - { - if (gcry_pk_test_algo (key_grips[i].algo)) - { - if (verbose) - fprintf (stderr, "algo %d not available; test skipped\n", - key_grips[i].algo); - continue; - } - err = gcry_sexp_sscan (&sexp, NULL, key_grips[i].key, - strlen (key_grips[i].key)); - if (err) - die ("scanning data %d failed: %s\n", i, gpg_strerror (err)); - - if (debug) - info ("check(%d): s-exp='%s'\n", i, key_grips[i].key); - - for (repn=0; repn < repetitions; repn++) - { - ret = gcry_pk_get_keygrip (sexp, buf); - if (!ret) - die ("gcry_pk_get_keygrip failed for %d\n", i); - - if ( memcmp (key_grips[i].grip, buf, sizeof (buf)) ) - { - print_hex ("keygrip: ", buf, sizeof buf); - die ("keygrip for %d does not match\n", i); - } - else if (debug && !repn) - print_hex ("keygrip: ", buf, sizeof buf); - } - - gcry_sexp_release (sexp); - } -} - - - -static void -progress_handler (void *cb_data, const char *what, int printchar, - int current, int total) -{ - (void)cb_data; - (void)what; - (void)current; - (void)total; - - putchar (printchar); -} - -int -main (int argc, char **argv) -{ - int last_argc = -1; - - if (argc) - { argc--; argv++; } - - while (argc && last_argc != argc ) - { - last_argc = argc; - if (!strcmp (*argv, "--")) - { - argc--; argv++; - break; - } - else if (!strcmp (*argv, "--verbose")) - { - verbose = 1; - argc--; argv++; - } - else if (!strcmp (*argv, "--debug")) - { - verbose = 1; - debug = 1; - argc--; argv++; - } - else if (!strcmp (*argv, "--repetitions")) - { - argc--; argv++; - if (argc) - { - repetitions = atoi(*argv); - argc--; argv++; - } - } - } - - if (repetitions < 1) - repetitions = 1; - - if (!gcry_check_version (GCRYPT_VERSION)) - die ("version mismatch\n"); - - gcry_set_progress_handler (progress_handler, NULL); - - xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0)); - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); - if (debug) - xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u, 0)); - - check (); - - return 0; -} diff --git a/libgcrypt-1.10.1-annobin.patch b/libgcrypt-1.10.1-annobin.patch new file mode 100644 index 0000000..c333c4e --- /dev/null +++ b/libgcrypt-1.10.1-annobin.patch @@ -0,0 +1,22 @@ +diff -rup libgcrypt.orig/src/Makefile.am libgcrypt-1.8.3/src/Makefile.am +--- libgcrypt.orig/src/Makefile.am 2021-10-18 16:36:20.914025497 +0100 ++++ libgcrypt-1.8.3/src/Makefile.am 2021-10-19 12:23:08.652960618 +0100 +@@ -108,6 +108,7 @@ endif !HAVE_W32_SYSTEM + + + libgcrypt_la_LDFLAGS = $(no_undefined) $(export_symbols) $(extra_ltoptions) \ ++ -Wc,-fplugin=annobin \ + $(libgcrypt_version_script_cmd) -version-info \ + @LIBGCRYPT_LT_CURRENT@:@LIBGCRYPT_LT_REVISION@:@LIBGCRYPT_LT_AGE@ + libgcrypt_la_DEPENDENCIES = \ +Only in libgcrypt-1.8.3/src: Makefile.am.annobin +diff -rup libgcrypt.orig/src/Makefile.in libgcrypt-1.8.3/src/Makefile.in +--- libgcrypt.orig/src/Makefile.in 2021-10-18 16:36:20.914025497 +0100 ++++ libgcrypt-1.8.3/src/Makefile.in 2021-10-19 12:23:37.791875325 +0100 +@@ -483,6 +483,7 @@ gcrypt_hwf_modules = @GCRYPT_HWF_MODULES + @HAVE_W32_SYSTEM_TRUE@gcrypt_deps = $(gcrypt_res) libgcrypt.def + @HAVE_W32_SYSTEM_FALSE@gcrypt_res_ldflag = + libgcrypt_la_LDFLAGS = $(no_undefined) $(export_symbols) $(extra_ltoptions) \ ++ -Wc,-fplugin=annobin \ + $(libgcrypt_version_script_cmd) -version-info \ + @LIBGCRYPT_LT_CURRENT@:@LIBGCRYPT_LT_REVISION@:@LIBGCRYPT_LT_AGE@ diff --git a/libgcrypt-1.11.0-marvin.patch b/libgcrypt-1.11.0-marvin.patch new file mode 100644 index 0000000..78b6809 --- /dev/null +++ b/libgcrypt-1.11.0-marvin.patch @@ -0,0 +1,2122 @@ +From 984e245fd21ff6e0058403f620f760013a744a3a Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Fri, 7 Jun 2024 14:42:33 +0200 +Subject: [PATCH 1/7] rsa: Do not accept invalid PKCS#1.5 padding when + deciphering + +The current code can accept 0-length padding when deciphering +PKCS#1.5 ciphertext. This is against the specification and hopefully +nobody depends on this. + +-- +* cipher/rsa-common.c (_gcry_rsa_pkcs1_decode_for_enc): Fail for too + short padding. +* src/const-time.h (ct_lt_s): New. + +Signed-off-by: Jakub Jelen +--- + cipher/rsa-common.c | 2 ++ + src/const-time.h | 6 ++++++ + 2 files changed, 8 insertions(+) + +diff --git a/cipher/rsa-common.c b/cipher/rsa-common.c +index 1920eedd..e45cb30d 100644 +--- a/cipher/rsa-common.c ++++ b/cipher/rsa-common.c +@@ -247,6 +247,8 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, + + failed |= not_found; + n0 += ct_is_zero (not_found); /* Skip the zero byte. */ ++ /* the valid padding is at least 8 bytes -- the plaintext needs to start at index 11 or later */ ++ failed |= ct_lt_s (n0, 11); + + /* To avoid an extra allocation we reuse the frame buffer. The only + caller of this function will anyway free the result soon. */ +diff --git a/src/const-time.h b/src/const-time.h +index 46eb187d..245967eb 100644 +--- a/src/const-time.h ++++ b/src/const-time.h +@@ -34,6 +34,12 @@ extern volatile unsigned int _gcry_ct_vone; + #endif + + ++static inline size_t ++ct_lt_s (size_t a, size_t b) ++{ ++ return ((a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(size_t) * 8 - 1)); ++} ++ + /* + * Return 0 if A is 0 and return 1 otherwise. + */ +-- +2.46.0 + + +From aca5bd25e819b2596754fdc6ecb1844462a5b4f0 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Wed, 10 Apr 2024 15:18:39 +0200 +Subject: [PATCH 2/7] rsa: Constant time blinding removal + +* cipher/rsa.c (secret_blinded): Use constant time mulm implementation +* configure.ac: New configure option --enable-marvin-workaround +* mpi/Makefile.am: Build constant time multiplication +* mpi/mpi-internal.h: Add constant time function prototypes +* mpi/mpi-mul-cs.c: New file with constant time multiplication and + modulo. +* mpi/mpi-mul.c (_gcry_mpi_mul_sec): New function. + (_gcry_mpi_mod_sec): New function. + (_gcry_mpi_mod_sec): New function. + (_gcry_mpi_reverse_sec): New function. + (_gcry_mpi_mulm_sec): New function. +* src/gcrypt-int.h: Add new functions. + +-- + +The current MPI code is not constant time, potentially leaking plaintext +when the attacker can observe enough decipher operations using RSA +PKCS#1.5. This is described as a Marvin Attack: + +https://eprint.iacr.org/2023/1442 + +Note, that this code is tested to be constant time only with the -O3 +optimization level. + +Signed-off-by: Jakub Jelen +--- + cipher/rsa.c | 4 + + configure.ac | 16 +++ + mpi/Makefile.am | 4 + + mpi/mpi-internal.h | 6 ++ + mpi/mpi-mul-cs.c | 263 +++++++++++++++++++++++++++++++++++++++++++++ + mpi/mpi-mul.c | 169 +++++++++++++++++++++++++++++ + src/gcrypt-int.h | 6 ++ + 7 files changed, 468 insertions(+) + create mode 100644 mpi/mpi-mul-cs.c + +diff --git a/cipher/rsa.c b/cipher/rsa.c +index c7a809f4..2f735f9c 100644 +--- a/cipher/rsa.c ++++ b/cipher/rsa.c +@@ -1197,7 +1197,11 @@ secret_blinded (gcry_mpi_t output, gcry_mpi_t input, + /* Undo blinding. Here we calculate: y = (x * r^-1) mod n, where x + * is the blinded decrypted data, ri is the modular multiplicative + * inverse of r and n is the RSA modulus. */ ++#ifdef WITH_MARVIN_WORKAROUND ++ mpi_mulm_sec (output, output, ri, sk->n); ++#else + mpi_mulm (output, output, ri, sk->n); ++#endif /* WITH_MARVIN_WORKAROUND */ + + _gcry_mpi_release (r); + _gcry_mpi_release (ri); +diff --git a/configure.ac b/configure.ac +index 4c45de22..3f63b88e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -603,6 +603,22 @@ AC_ARG_ENABLE(jent-support, + jentsupport=$enableval,jentsupport=yes) + AC_MSG_RESULT($jentsupport) + ++AC_MSG_CHECKING([whether a Marvin workaround is requested]) ++AC_ARG_ENABLE(marvin-workaround, ++ AS_HELP_STRING([--enable-marvin-workaround], ++ [Enable Marvin workaround for constant time PKCS1.5 depadding]), ++ [with_marvin_workaround="$enableval"], ++ [with_marvin_workaround=no]) ++AC_MSG_RESULT($with_marvin_workaround) ++if test "$with_marvin_workaround" = no ; then ++ WITH_MARVIN_WORKAROUND='' ++else ++ AC_DEFINE(WITH_MARVIN_WORKAROUND,1, ++ [Define to provide constant time PKCS1.5 depadding]) ++fi ++AM_CONDITIONAL(WITH_MARVIN_WORKAROUND, test "x$with_marvin_workaround" != xno) ++AC_SUBST(WITH_MARVIN_WORKAROUND) ++ + # Implementation of the --disable-padlock-support switch. + AC_MSG_CHECKING([whether padlock support is requested]) + AC_ARG_ENABLE(padlock-support, +diff --git a/mpi/Makefile.am b/mpi/Makefile.am +index 6fe63fce..85679e08 100644 +--- a/mpi/Makefile.am ++++ b/mpi/Makefile.am +@@ -181,3 +181,7 @@ EXTRA_libmpi_la_SOURCES = \ + asm-common-aarch64.h \ + asm-common-amd64.h \ + asm-common-i386.h ++ ++if WITH_MARVIN_WORKAROUND ++libmpi_la_SOURCES += mpi-mul-cs.c ++endif +diff --git a/mpi/mpi-internal.h b/mpi/mpi-internal.h +index 935bf3e1..39de5161 100644 +--- a/mpi/mpi-internal.h ++++ b/mpi/mpi-internal.h +@@ -239,6 +239,12 @@ void _gcry_mpih_mul_karatsuba_case( mpi_ptr_t prodp, + mpi_ptr_t vp, mpi_size_t vsize, + struct karatsuba_ctx *ctx ); + ++#ifdef WITH_MARVIN_WORKAROUND ++/*-- mpih-mul-cs.c --*/ ++void mul_cs(mpi_limb_t *ret, mpi_limb_t *a, mpi_limb_t *b, size_t n, mpi_limb_t *tmp); ++void mod_cs(mpi_limb_t *ret, mpi_limb_t *a, size_t anum, mpi_limb_t *mod, size_t modnum, mpi_limb_t *tmp); ++size_t mod_limb_numb(size_t anum, size_t modnum); ++#endif /* WITH_MARVIN_WORKAROUND */ + + /*-- mpih-mul_1.c (or xxx/cpu/ *.S) --*/ + mpi_limb_t _gcry_mpih_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, +diff --git a/mpi/mpi-mul-cs.c b/mpi/mpi-mul-cs.c +new file mode 100644 +index 00000000..3f77ac94 +--- /dev/null ++++ b/mpi/mpi-mul-cs.c +@@ -0,0 +1,263 @@ ++/* Copyright (c) 2024, Hubert Kario, Red Hat ++ * Released under BSD 2-Clause License, see LICENSE for details ++ */ ++#include ++#include ++#include ++#include ++#include ++#include "mpi-internal.h" ++#include "longlong.h" ++ ++/* For multiplication we're using schoolbook multiplication, ++ * so if we have two numbers, each with 6 "digits" (words) ++ * the multiplication is calculated as follows: ++ * A B C D E F ++ * x I J K L M N ++ * -------------- ++ * N*F ++ * N*E ++ * N*D ++ * N*C ++ * N*B ++ * N*A ++ * M*F ++ * M*E ++ * M*D ++ * M*C ++ * M*B ++ * M*A ++ * L*F ++ * L*E ++ * L*D ++ * L*C ++ * L*B ++ * L*A ++ * K*F ++ * K*E ++ * K*D ++ * K*C ++ * K*B ++ * K*A ++ * J*F ++ * J*E ++ * J*D ++ * J*C ++ * J*B ++ * J*A ++ * I*F ++ * I*E ++ * I*D ++ * I*C ++ * I*B ++ * + I*A ++ * ========================== ++ * N*B N*D N*F ++ * + N*A N*C N*E ++ * + M*B M*D M*F ++ * + M*A M*C M*E ++ * + L*B L*D L*F ++ * + L*A L*C L*E ++ * + K*B K*D K*F ++ * + K*A K*C K*E ++ * + J*B J*D J*F ++ * + J*A J*C J*E ++ * + I*B I*D I*F ++ * + I*A I*C I*E ++ * ++ * 1+1 1+3 1+5 ++ * 1+0 1+2 1+4 ++ * 0+1 0+3 0+5 ++ * 0+0 0+2 0+4 ++ * ++ * 0 1 2 3 4 5 6 ++ * which requires n^2 multiplications and 2n full length additions ++ * as we can keep every other result of limb multiplication in two separate ++ * limbs ++ */ ++ ++typedef mpi_limb_t limb_t; ++#if BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG_LONG ++#define LIMB_BIT_SIZE 64 ++#define LIMB_BYTE_SIZE 8 ++#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG ++#define LIMB_BIT_SIZE 32 ++#define LIMB_BYTE_SIZE 4 ++/* if we're on a 32 bit platform */ ++#else ++#define LIMB_BIT_SIZE 16 ++#define LIMB_BYTE_SIZE 2 ++/* ++ * if the compiler doesn't have either a 128bit data type nor a "return ++ * high 64 bits of multiplication" ++ */ ++#endif ++ ++/* add two limbs with carry in, return carry out */ ++static limb_t ++_add_limb (limb_t *ret, limb_t a, limb_t b, limb_t carry) ++{ ++ limb_t carry1, carry2, t; ++ add_ssaaaa (carry1, t, 0, a, 0, carry); ++ add_ssaaaa (carry2, t, 0, b, 0, t); ++ *ret = t; ++ return carry1 + carry2; ++} ++ ++/* add two numbers of the same size, return overflow ++ * ++ * add a to b, place result in ret; all arrays need to be n limbs long ++ * return overflow from addition (0 or 1) ++ */ ++static limb_t ++add (limb_t *ret, limb_t *a, limb_t *b, size_t n) ++{ ++ limb_t c = 0; ++ for (ssize_t i = n - 1; i > -1; i--) ++ { ++ c = _add_limb (&ret[i], a[i], b[i], c); ++ } ++ return c; ++} ++ ++/* multiply two numbers of the same size ++ * ++ * multiply a by b, place result in ret; a and b need to be n limbs long ++ * ret needs to be 2*n limbs long, tmp needs to be 2 * n 2 limbs ++ * long ++ */ ++void ++mul_cs (limb_t *ret, limb_t *a, limb_t *b, size_t n, limb_t *tmp) ++{ ++ limb_t *r_odd, *r_even; ++ r_odd = tmp; ++ r_even = &tmp[2 * n]; ++ ++ for (size_t i = 0; i < 2 * n; i++) ++ { ++ ret[i] = 0; ++ } ++ ++ for (size_t i=0; i> 1); ++ shift_in = shift_out; ++ } ++} ++ ++/* copy from either a or b to ret based on flag ++ * when flag == 0, then copies from b ++ * when flag == 1, then copies from a ++ */ ++static void ++cselect (limb_t flag, limb_t *ret, limb_t *a, limb_t *b, size_t n) ++{ ++ /* would be more efficient with non volatile mask, but then gcc ++ * generates code with jumps */ ++ limb_t mask1 = ct_limb_gen_mask (flag); ++ limb_t mask2 = ct_limb_gen_inv_mask (flag); ++ for (size_t i = 0; i < n; i++) ++ { ++ ret[i] = (mask1 & a[i]) | (mask2 & b[i]); ++ } ++} ++ ++static limb_t ++_sub_limb (limb_t *ret, limb_t a, limb_t b, limb_t borrow) ++{ ++ limb_t borrow1, borrow2, t; ++ sub_ddmmss (borrow1, t, 0, a, 0, borrow); ++ sub_ddmmss (borrow2, t, 0, t, 0, b); ++ *ret = t; ++ return -(borrow1 + borrow2); ++} ++ ++/* place the result of a - b into ret, return the borrow bit. ++ * All arrays need to be n limbs long ++ */ ++static limb_t ++sub (limb_t *ret, limb_t *a, limb_t *b, size_t n) ++{ ++ limb_t borrow = 0; ++ for (ssize_t i=n-1; i>-1; i--) ++ { ++ borrow = _sub_limb (&ret[i], a[i], b[i], borrow); ++ } ++ return borrow; ++} ++ ++/* return the number of limbs necessary to allocate for the mod() tmp operand */ ++size_t ++mod_limb_numb (size_t anum, size_t modnum) ++{ ++ return (anum + modnum) * 3; ++} ++ ++/* calculate a % mod, place the result in ret ++ * size of a is defined by anum, size of ret and mod is modnum, ++ * size of tmp is returned by mod_limb_numb() ++ */ ++void ++mod_cs (limb_t *ret, limb_t *a, size_t anum, limb_t *mod, size_t modnum, limb_t *tmp) ++{ ++ limb_t *atmp, *modtmp, *rettmp; ++ limb_t res; ++ ++ memset (tmp, 0, mod_limb_numb(anum, modnum) * LIMB_BYTE_SIZE); ++ ++ atmp = tmp; ++ modtmp = &tmp[anum+modnum]; ++ rettmp = &tmp[(anum+modnum)*2]; ++ ++ for (size_t i=modnum; inlimbs < v->nlimbs) ++ { /* Swap U and V. */ ++ usize = v->nlimbs; ++ usign = v->sign; ++ usecure = mpi_is_secure (v); ++ up = v->d; ++ vsize = u->nlimbs; ++ vsign = u->sign; ++ vsecure = mpi_is_secure (u); ++ vp = u->d; ++ } ++ else ++ { ++ usize = u->nlimbs; ++ usign = u->sign; ++ usecure = mpi_is_secure (u); ++ up = u->d; ++ vsize = v->nlimbs; ++ vsign = v->sign; ++ vsecure = mpi_is_secure (v); ++ vp = v->d; ++ } ++ sign_product = usign ^ vsign; ++ wp = w->d; ++ ++ /* make sure u and v have the same length by extending the limbs to the larger one, now u */ ++ if (usize != vsize) ++ { ++ mpi_limb_t *tmp_vp = mpi_alloc_limb_space (usize, vsecure); ++ clean_vp = 1; ++ MPN_ZERO (tmp_vp, (usize - vsize)); ++ MPN_COPY (tmp_vp + (usize - vsize), vp, vsize); ++ vsize = usize; ++ vp = tmp_vp; ++ } ++ ++ /* w == u */ ++ /* Ensure W has space enough to store the result. */ ++ wsize = usize + vsize; ++ if (!mpi_is_secure (w) && (mpi_is_secure (u) || mpi_is_secure (v))) ++ { ++ /* w is not allocated in secure space but u or v is. To make sure ++ * that no temporary results are stored in w, we temporary use ++ * a newly allocated limb space for w */ ++ wp = mpi_alloc_limb_space( wsize, 1 ); ++ assign_wp = 2; /* mark it as 2 so that we can later copy it back to ++ * normal memory */ ++ } ++ else if (w->alloced < wsize ) ++ { ++ if (wp == up || wp == vp) ++ { ++ wp = mpi_alloc_limb_space (wsize, mpi_is_secure (w)); ++ assign_wp = 1; ++ } ++ else ++ { ++ mpi_resize(w, wsize ); ++ wp = w->d; ++ } ++ } ++ else ++ { /* Make U and V not overlap with W. */ ++ if (wp == up) ++ { ++ /* W and U are identical. Allocate temporary space for U. */ ++ tmp_limb_nlimbs = usize; ++ up = tmp_limb = mpi_alloc_limb_space (usize, usecure); ++ /* Is V identical too? Keep it identical with U. */ ++ if (wp == vp) ++ vp = up; ++ /* Copy to the temporary space. */ ++ MPN_COPY (up, wp, usize); ++ } ++ else if (wp == vp) ++ { ++ /* W and V are identical. Allocate temporary space for V. */ ++ tmp_limb_nlimbs = vsize; ++ vp = tmp_limb = mpi_alloc_limb_space (vsize, vsecure); ++ /* Copy to the temporary space. */ ++ MPN_COPY (vp, wp, vsize); ++ } ++ } ++ ++ if (!vsize) ++ wsize = 0; ++ else ++ { ++ mpi_limb_t *tmp = mpi_alloc_limb_space (wsize * 2, mpi_is_secure (w)); ++ mul_cs (wp, up, vp, vsize, tmp); ++ _gcry_mpi_free_limb_space (tmp, wsize * 2); ++ } ++ ++ if (clean_vp) ++ { ++ _gcry_mpi_free_limb_space (vp, vsize); ++ } ++ if (assign_wp) ++ { ++ if (assign_wp == 2) ++ { ++ /* copy the temp wp from secure memory back to normal memory */ ++ mpi_ptr_t tmp_wp = mpi_alloc_limb_space (wsize, 0); ++ MPN_COPY (tmp_wp, wp, wsize); ++ _gcry_mpi_free_limb_space (wp, 0); ++ wp = tmp_wp; ++ } ++ _gcry_mpi_assign_limb_space (w, wp, wsize); ++ } ++ w->nlimbs = wsize; ++ w->sign = sign_product; ++ if (tmp_limb) ++ _gcry_mpi_free_limb_space (tmp_limb, tmp_limb_nlimbs); ++} ++#endif /* WITH_MARVIN_WORKAROUND */ + + void + _gcry_mpi_mulm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m) +@@ -221,3 +348,45 @@ _gcry_mpi_mulm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m) + if (temp_m) + mpi_free(temp_m); + } ++ ++#ifdef WITH_MARVIN_WORKAROUND ++static void ++_gcry_mpi_mod_sec (gcry_mpi_t ret, gcry_mpi_t a, gcry_mpi_t mod) ++{ ++ size_t asize = a->nlimbs; ++ size_t modsize = mod->nlimbs; ++ size_t tmp_size = mod_limb_numb (asize, modsize); ++ mpi_limb_t *tmp_limb = mpi_alloc_limb_space (tmp_size, mpi_is_secure(a)); ++ mod_cs (ret->d, a->d, asize, mod->d, modsize, tmp_limb); ++ /* cut the length to the mod size */ ++ ret->nlimbs = mod->nlimbs; ++ _gcry_mpi_free_limb_space (tmp_limb, tmp_size); ++} ++ ++/* The constant time code uses different order of the limbs ... */ ++static void ++_gcry_mpi_reverse_sec (gcry_mpi_t w) ++{ ++ for (size_t i = 0; i < w->nlimbs/2; i++) ++ { ++ mpi_limb_t t = w->d[i]; ++ w->d[i] = w->d[w->nlimbs - i - 1]; ++ w->d[w->nlimbs - i - 1] = t; ++ } ++} ++ ++void ++_gcry_mpi_mulm_sec (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m) ++{ ++ /* w == u */ ++ _gcry_mpi_reverse_sec (u); ++ _gcry_mpi_reverse_sec (v); ++ _gcry_mpi_reverse_sec (m); ++ _gcry_mpi_mul_sec (w, u, v); ++ _gcry_mpi_mod_sec (w, w, m); ++ ++ /* get them back to the order the rest of the code expects */ ++ _gcry_mpi_reverse_sec (w); /* -- this is the result */ ++ _gcry_mpi_reverse_sec (m); /* -- this might be still used by the calling function */ ++} ++#endif /* WITH_MARVIN_WORKAROUND */ +diff --git a/src/gcrypt-int.h b/src/gcrypt-int.h +index 1b449281..7e9e9894 100644 +--- a/src/gcrypt-int.h ++++ b/src/gcrypt-int.h +@@ -453,6 +453,9 @@ void _gcry_mpi_subm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m); + void _gcry_mpi_mul (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v); + void _gcry_mpi_mul_ui (gcry_mpi_t w, gcry_mpi_t u, unsigned long v ); + void _gcry_mpi_mulm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m); ++#ifdef WITH_MARVIN_WORKAROUND ++void _gcry_mpi_mulm_sec (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m); ++#endif /* WITH_MARVIN_WORKAROUND */ + void _gcry_mpi_mul_2exp (gcry_mpi_t w, gcry_mpi_t u, unsigned long cnt); + void _gcry_mpi_div (gcry_mpi_t q, gcry_mpi_t r, + gcry_mpi_t dividend, gcry_mpi_t divisor, int round); +@@ -548,6 +551,9 @@ int _gcry_mpi_get_flag (gcry_mpi_t a, enum gcry_mpi_flag flag); + #define mpi_mul_2exp(w,u,v) _gcry_mpi_mul_2exp ((w),(u),(v)) + #define mpi_mul(w,u,v) _gcry_mpi_mul ((w),(u),(v)) + #define mpi_mulm(w,u,v,m) _gcry_mpi_mulm ((w),(u),(v),(m)) ++#ifdef WITH_MARVIN_WORKAROUND ++#define mpi_mulm_sec(w,u,v,m) _gcry_mpi_mulm_sec ((w),(u),(v),(m)) ++#endif /* WITH_MARVIN_WORKAROUND */ + #define mpi_powm(w,b,e,m) _gcry_mpi_powm ( (w), (b), (e), (m) ) + #define mpi_tdiv(q,r,a,m) _gcry_mpi_div ( (q), (r), (a), (m), 0) + #define mpi_fdiv(q,r,a,m) _gcry_mpi_div ( (q), (r), (a), (m), -1) +-- +2.46.0 + + +From 1b7b995c5f4cd48a3b98915826431efb41a927ce Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Fri, 26 Apr 2024 16:39:18 +0200 +Subject: [PATCH 3/7] Constant time conversion of the message to the SEXP + +* cipher/rsa.c (rsa_decrypt): Use constant time conversion to SEXP after + decryption. +* src/const-time.c (_gcry_ct_memcpy): Constant time memcpy. New. +* src/const-time.h (ct_lt): New. + (ct_char_gen{,_inv}_mask): New. +* src/sexp.c (do_vsexp_sscan): Introduce constant time version of %b +-- + +The current conversion of the message back to the SEXP buffer leaks the +length of the decrypted message. This introduces a constant-time variant +that should run for the same time for any input length. + +Signed-off-by: Jakub Jelen +--- + cipher/rsa.c | 4 ++++ + src/const-time.c | 28 ++++++++++++++++++++++++++-- + src/const-time.h | 12 ++++++++++++ + src/sexp.c | 39 +++++++++++++++++++++++++++++++++------ + 4 files changed, 75 insertions(+), 8 deletions(-) + +diff --git a/cipher/rsa.c b/cipher/rsa.c +index 2f735f9c..52f2aa89 100644 +--- a/cipher/rsa.c ++++ b/cipher/rsa.c +@@ -1519,7 +1519,11 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) + rc = _gcry_rsa_pkcs1_decode_for_enc (&unpad, &unpadlen, nbits, plain); + mpi_free (plain); + plain = NULL; ++#ifdef WITH_MARVIN_WORKAROUND ++ rc_sexp = sexp_build (&result, NULL, "(value %c)", (int)unpadlen, unpad, (nbits + 7) / 8); ++#else + rc_sexp = sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad); ++#endif /* WITH_MARVIN_WORKAROUND */ + *r_plain = sexp_null_cond (result, ct_is_not_zero (rc)); + dummy = sexp_null_cond (result, ct_is_zero (rc)); + sexp_release (dummy); +diff --git a/src/const-time.c b/src/const-time.c +index 0fb53a07..cecb1937 100644 +--- a/src/const-time.c ++++ b/src/const-time.c +@@ -77,8 +77,8 @@ _gcry_ct_memmov_cond (void *dst, const void *src, size_t len, + unsigned long op_enable) + { + /* Note: dual mask with AND/OR used for EM leakage mitigation */ +- unsigned char mask1 = ct_ulong_gen_mask(op_enable); +- unsigned char mask2 = ct_ulong_gen_inv_mask(op_enable); ++ volatile unsigned char mask1 = ct_ulong_gen_mask(op_enable); ++ volatile unsigned char mask2 = ct_ulong_gen_inv_mask(op_enable); + unsigned char *b_dst = dst; + const unsigned char *b_src = src; + size_t i; +@@ -86,3 +86,27 @@ _gcry_ct_memmov_cond (void *dst, const void *src, size_t len, + for (i = 0; i < len; i++) + b_dst[i] = (b_dst[i] & mask2) | (b_src[i] & mask1); + } ++ ++/* ++ * Copy LEN bytes from memory area SRC to memory area DST. The ++ * bytes are read up to the BUFFER_LEN size to keep the operation ++ * not dependent on the message length (both SRC and DST buffers ++ * need to have this size!). ++ */ ++void ++_gcry_ct_memcpy (void *dst, const void *src, size_t len, size_t buffer_len) ++{ ++ unsigned char mask_a, mask_b; ++ unsigned char *b_dst = dst; ++ const unsigned char *b_src = src; ++ unsigned int writing; ++ size_t i; ++ ++ for (i = 0; i < buffer_len; i++) ++ { ++ writing = ct_lt (i, len); ++ mask_b = ct_uchar_gen_inv_mask (writing); ++ mask_a = ct_uchar_gen_mask (writing); ++ b_dst[i] = (b_src[i] & mask_a) | (b_dst[i] & mask_b); ++ } ++} +diff --git a/src/const-time.h b/src/const-time.h +index 245967eb..74055a4b 100644 +--- a/src/const-time.h ++++ b/src/const-time.h +@@ -26,6 +26,7 @@ + #define ct_not_memequal _gcry_ct_not_memequal + #define ct_memequal _gcry_ct_memequal + #define ct_memmov_cond _gcry_ct_memmov_cond ++#define ct_memcpy _gcry_ct_memcpy + + + #ifndef HAVE_GCC_ASM_VOLATILE_MEMORY +@@ -33,6 +34,14 @@ extern volatile unsigned int _gcry_ct_vzero; + extern volatile unsigned int _gcry_ct_vone; + #endif + ++/* ++ * Return 1 if A < B and return 0 otherwise. ++ */ ++static inline int ++ct_lt (unsigned int a, unsigned int b) ++{ ++ return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(unsigned int)*8 - 1); ++} + + static inline size_t + ct_lt_s (size_t a, size_t b) +@@ -112,6 +121,7 @@ unsigned int _gcry_ct_memequal (const void *b1, const void *b2, size_t len); + DEFINE_CT_TYPE_GEN_MASK(uintptr, uintptr_t) + DEFINE_CT_TYPE_GEN_MASK(ulong, unsigned long) + DEFINE_CT_TYPE_GEN_MASK(int16, int16_t) ++DEFINE_CT_TYPE_GEN_MASK(uchar, unsigned char) + + /* + * Return all bits set if A is 0 and return 1 otherwise. +@@ -137,6 +147,7 @@ DEFINE_CT_TYPE_GEN_MASK(int16, int16_t) + DEFINE_CT_TYPE_GEN_INV_MASK(uintptr, uintptr_t) + DEFINE_CT_TYPE_GEN_INV_MASK(ulong, unsigned long) + DEFINE_CT_TYPE_GEN_INV_MASK(int16, int16_t) ++DEFINE_CT_TYPE_GEN_INV_MASK(uchar, unsigned char) + + /* + * Return A when OP_ENABLED=1 +@@ -172,5 +183,6 @@ sexp_null_cond (gcry_sexp_t w, unsigned long op_enable) + */ + void _gcry_ct_memmov_cond (void *dst, const void *src, size_t len, + unsigned long op_enable); ++void _gcry_ct_memcpy (void *dst, const void *src, size_t len, size_t buffer_len); + + #endif /*GCRY_CONST_TIME_H*/ +diff --git a/src/sexp.c b/src/sexp.c +index c77d0212..8e6c847f 100644 +--- a/src/sexp.c ++++ b/src/sexp.c +@@ -31,6 +31,7 @@ + + #define GCRYPT_NO_MPI_MACROS 1 + #include "g10lib.h" ++#include "const-time.h" + + + /* Notes on the internal memory layout. +@@ -1079,6 +1080,10 @@ unquote_string (const char *string, size_t length, unsigned char *buf) + * %d - integer stored as string (no autoswitch to secure allocation) + * %b - memory buffer; this takes _two_ arguments: an integer with the + * length of the buffer and a pointer to the buffer. ++ * %c - memory buffer same as %b, but written in constant time; this ++ * takes _three_ arguments: an integer with the length of the buffer, ++ * a pointer to the buffer and the maximum length of the buffer ++ * to avoid potential side channel leaking the buffer length. + * %S - Copy an gcry_sexp_t here. The S-expression needs to be a + * regular one, starting with a parenthesis. + * (no autoswitch to secure allocation) +@@ -1204,7 +1209,7 @@ do_vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff, + { + switch (*p) + { +- case 'b': case 't': case 'v': case 'n': case 'f': ++ case 'b': case 'c': case 't': case 'v': case 'n': case 'f': + case 'r': case '"': case '\'': case '\\': + quoted_esc = 0; + break; +@@ -1538,14 +1543,27 @@ do_vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff, + memcpy (c.pos, astr, alen); + c.pos += alen; + } +- else if (*p == 'b') ++ else if (*p == 'b' || *p == 'c') + { + /* Insert a memory buffer. */ + const char *astr; +- int alen; ++ int alen, buflen = 0, reallen = 0; + + ARG_NEXT (alen, int); + ARG_NEXT (astr, const char *); ++ if (*p == 'c') ++ { ++ ARG_NEXT (buflen, int); ++ if (buflen < alen) ++ { ++ *erroff = p - buffer; ++ err = GPG_ERR_INV_ARG; ++ goto leave; ++ } ++ /* Do all the calculations with the buflen */ ++ reallen = alen; ++ alen = buflen; ++ } + + if (alen < 0) + { +@@ -1578,9 +1596,18 @@ do_vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff, + } + + *c.pos++ = ST_DATA; +- STORE_LEN (c.pos, alen); +- memcpy (c.pos, astr, alen); +- c.pos += alen; ++ if (*p == 'c') ++ { ++ STORE_LEN (c.pos, reallen); ++ ct_memcpy (c.pos, astr, reallen, buflen); ++ c.pos += reallen; ++ } ++ else ++ { ++ STORE_LEN (c.pos, alen); ++ memcpy (c.pos, astr, alen); ++ c.pos += alen; ++ } + } + else if (*p == 'd') + { +-- +2.46.0 + + +From 14008d6f9f3985461ea50a6760391086c9bbe72b Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Thu, 30 May 2024 17:50:36 +0200 +Subject: [PATCH 5/7] rsa: Implement constant-time conversion of MPI to string + +-- +* cipher/rsa-common.c (_gcry_rsa_pkcs1_decode_for_enc): Replace + _gcry_mpi_print() with constant time operation. + +The _gcry_mpi_print() does a lot of housekeeping on the provided MPI +including skipping all the leading zeroes. If the ciphertext decrypted +to many leading zeroes, the output from this function is much shorter +(could be even all zeroes), which would immediately fail. + +This implements the dummy conversion of the MPI with the knowledge that +the encrypted data will always have the fixed length, providing constant +time operation. + +Signed-off-by: Jakub Jelen +--- + cipher/rsa-common.c | 35 ++++++++++++++++++++++++++++++++++- + 1 file changed, 34 insertions(+), 1 deletion(-) + +diff --git a/cipher/rsa-common.c b/cipher/rsa-common.c +index e45cb30d..a61d82e3 100644 +--- a/cipher/rsa-common.c ++++ b/cipher/rsa-common.c +@@ -28,6 +28,7 @@ + #include "cipher.h" + #include "pubkey-internal.h" + #include "const-time.h" ++#include "bufhelp.h" + + + /* Turn VALUE into an octet string and store it in an allocated buffer +@@ -199,7 +200,6 @@ gpg_err_code_t + _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, + unsigned int nbits, gcry_mpi_t value) + { +- gcry_error_t err; + unsigned char *frame = NULL; + size_t nframe = (nbits+7) / 8; + size_t n, n0; +@@ -208,6 +208,36 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, + + *r_result = NULL; + ++ { ++#ifdef WITH_MARVIN_WORKAROUND ++ /* Allocate more to fit the whole MPI and allow moving it later to the right place */ ++ size_t alloc_len = value->nlimbs * BYTES_PER_MPI_LIMB; ++ if ( !(frame = xtrymalloc_secure (alloc_len))) ++ return gpg_err_code_from_syserror (); ++ ++ /* shovel the MPI content to the buffer as it is */ ++ unsigned char *p = frame; ++ for (int i = value->nlimbs - 1; i >= 0; i--) ++ { ++ mpi_limb_t *alimb = &value->d[i]; ++#if BYTES_PER_MPI_LIMB == 4 ++ buf_put_be32 (p, *alimb); ++ p += 4; ++#elif BYTES_PER_MPI_LIMB == 8 ++ buf_put_be64 (p, *alimb); ++ p += 8; ++#else ++# error please implement for this limb size. ++#endif ++ } ++ /* Remove leading zeroes, but not all! Keep the buffer to the nframe length! ++ * -- valid PKCS#1.5 padding will never have different lengths, than modulus */ ++ memmov_independently (frame, frame + (alloc_len - nframe), nframe, alloc_len); ++ n = 0; ++ failed |= ct_not_equal_byte (frame[n++], 0x00); ++#else ++ gcry_error_t err; ++ + if ( !(frame = xtrymalloc_secure (nframe))) + return gpg_err_code_from_syserror (); + +@@ -235,6 +265,9 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, + n = 0; + if (!frame[0]) + n++; ++#endif ++ } ++ + failed |= ct_not_equal_byte (frame[n++], 0x02); + + /* Find the terminating zero byte. */ +-- +2.46.0 + + +From 5c0b1707e48d21fa4d860b0bafdeaeb438885707 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Fri, 31 May 2024 15:03:29 +0200 +Subject: [PATCH 6/7] cipher: Use the constant time conversion also for OAEP + +-- +* cipher/rsa-common.c (mpi_to_string): New function + (_gcry_rsa_pkcs1_decode_for_enc): Use new function for MPI to string + conversion. + (_gcry_rsa_oaep_decode): Use the new function for MPI to string + conversion. +* cipher/rsa.c (rsa_decrypt): Use constant time conversion to S-exp for + OAEP padding. + +Signed-off-by: Jakub Jelen +--- + cipher/rsa-common.c | 75 +++++++++++++++++++++++++++++++-------------- + cipher/rsa.c | 4 +++ + 2 files changed, 56 insertions(+), 23 deletions(-) + +diff --git a/cipher/rsa-common.c b/cipher/rsa-common.c +index a61d82e3..82a9e083 100644 +--- a/cipher/rsa-common.c ++++ b/cipher/rsa-common.c +@@ -192,31 +192,26 @@ memmov_independently (void *dst, const void *src, size_t len, size_t buflen) + } + + +-/* Decode a plaintext in VALUE assuming pkcs#1 block type 2 padding. +- NBITS is the size of the secret key. On success the result is +- stored as a newly allocated buffer at R_RESULT and its valid length at +- R_RESULTLEN. On error NULL is stored at R_RESULT. */ +-gpg_err_code_t +-_gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, +- unsigned int nbits, gcry_mpi_t value) ++static unsigned char * ++mpi_to_string(gcry_mpi_t value, size_t nframe) + { + unsigned char *frame = NULL; +- size_t nframe = (nbits+7) / 8; +- size_t n, n0; +- unsigned int failed = 0; +- unsigned int not_found = 1; ++ unsigned char *p; ++ size_t noff; ++ /* Allocate memory to fit the whole MPI limbs and allow moving it later to the right place */ ++ size_t alloc_len = value->nlimbs * BYTES_PER_MPI_LIMB; + +- *r_result = NULL; ++ /* for too short input, we need to allocate at least modulus length (which might still be valid in case of OAEP) */ ++ noff = (alloc_len < nframe) ? nframe - alloc_len : 0; ++ alloc_len = alloc_len + noff; + +- { +-#ifdef WITH_MARVIN_WORKAROUND +- /* Allocate more to fit the whole MPI and allow moving it later to the right place */ +- size_t alloc_len = value->nlimbs * BYTES_PER_MPI_LIMB; + if ( !(frame = xtrymalloc_secure (alloc_len))) +- return gpg_err_code_from_syserror (); ++ return NULL; + ++ if (noff) ++ memset (frame, 0, noff); ++ p = frame + noff; + /* shovel the MPI content to the buffer as it is */ +- unsigned char *p = frame; + for (int i = value->nlimbs - 1; i >= 0; i--) + { + mpi_limb_t *alimb = &value->d[i]; +@@ -230,9 +225,34 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, + # error please implement for this limb size. + #endif + } +- /* Remove leading zeroes, but not all! Keep the buffer to the nframe length! +- * -- valid PKCS#1.5 padding will never have different lengths, than modulus */ ++ /* Move the MPI to the right place -- with the least significant bytes aligned to the modulus length ++ * -- this might keep some leading zeroes at the beginning of the buffer, but this might be valid for OAEP */ + memmov_independently (frame, frame + (alloc_len - nframe), nframe, alloc_len); ++ return frame; ++} ++ ++/* Decode a plaintext in VALUE assuming pkcs#1 block type 2 padding. ++ NBITS is the size of the secret key. On success the result is ++ stored as a newly allocated buffer at R_RESULT and its valid length at ++ R_RESULTLEN. On error NULL is stored at R_RESULT. */ ++gpg_err_code_t ++_gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, ++ unsigned int nbits, gcry_mpi_t value) ++{ ++ unsigned char *frame = NULL; ++ size_t nframe = (nbits+7) / 8; ++ size_t n, n0; ++ unsigned int failed = 0; ++ unsigned int not_found = 1; ++ ++ *r_result = NULL; ++ ++ { ++#ifdef WITH_MARVIN_WORKAROUND ++ frame = mpi_to_string(value, nframe); ++ if (frame == NULL) ++ return gpg_err_code_from_syserror (); ++ + n = 0; + failed |= ct_not_equal_byte (frame[n++], 0x00); + #else +@@ -265,7 +285,7 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, + n = 0; + if (!frame[0]) + n++; +-#endif ++#endif /* WITH_MARVIN_WORKAROUND */ + } + + failed |= ct_not_equal_byte (frame[n++], 0x02); +@@ -709,14 +729,23 @@ _gcry_rsa_oaep_decode (unsigned char **r_result, size_t *r_resultlen, + happen due to the leading zero in OAEP frames and due to the + following random octets (seed^mask) which may have leading zero + bytes. This all is needed to cope with our leading zeroes +- suppressing MPI implementation. The code implictly implements ++ suppressing MPI implementation. The code implicitly implements + Step 1b (bail out if NFRAME != N). */ ++#ifdef WITH_MARVIN_WORKAROUND ++ frame = mpi_to_string(value, nkey); ++ if (frame == NULL) ++ { ++ xfree (lhash); ++ return gpg_err_code_from_syserror (); ++ } ++#else + rc = octet_string_from_mpi (&frame, NULL, value, nkey); + if (rc) + { + xfree (lhash); + return GPG_ERR_ENCODING_PROBLEM; + } ++#endif /* WITH_MARVIN_WORKAROUND */ + nframe = nkey; + + /* Step 1c: Check that the key is long enough. */ +@@ -731,7 +760,7 @@ _gcry_rsa_oaep_decode (unsigned char **r_result, size_t *r_resultlen, + gcry_mpi_aprint above. */ + + /* Allocate space for SEED and DB. */ +- seed = xtrymalloc_secure (nframe - 1); ++ seed = xtrymalloc_secure (nframe); + if (!seed) + { + rc = gpg_err_code_from_syserror (); +diff --git a/cipher/rsa.c b/cipher/rsa.c +index 52f2aa89..c196724c 100644 +--- a/cipher/rsa.c ++++ b/cipher/rsa.c +@@ -1537,7 +1537,11 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) + plain, ctx.label, ctx.labellen); + mpi_free (plain); + plain = NULL; ++#ifdef WITH_MARVIN_WORKAROUND ++ rc_sexp = sexp_build (&result, NULL, "(value %c)", (int)unpadlen, unpad, (nbits + 7) / 8); ++#else + rc_sexp = sexp_build (&result, NULL, "(value %b)", (int)unpadlen, unpad); ++#endif /* WITH_MARVIN_WORKAROUND */ + *r_plain = sexp_null_cond (result, ct_is_not_zero (rc)); + dummy = sexp_null_cond (result, ct_is_zero (rc)); + sexp_release (dummy); +-- +2.46.0 + + +From 4dffeaeadf792931aee61e6f51f59cf911c0c1f5 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Mon, 29 Apr 2024 19:06:17 +0200 +Subject: [PATCH 7/7] Implement implicit rejection for PKCS#1.5 decipher + +-- +* cipher/pubkey-internal.h + (_gcry_rsa_pkcs1_decode_for_enc_implicit_rejection): New. +* pubkey-util.c (_gcry_pk_util_parse_flaglist): Parse the new flag + no-implicit-rejection. +* cipher/rsa-common.c (rsa_prf): New function. + (_gcry_rsa_pkcs1_decode_for_enc_implicit_rejection): New function. +* cipher/rsa.c (rsa_decrypt): Handle implicit rejection. +* src/cipher.h (pk_encoding): New flag. +* tests/pkcs1v2.c (extract_cmp_data): Allow quiet test. + (check_v15crypt): Add support for implicit rejection. +* tests/pkcs1v2-v15c.h: Add test vectors for implicit rejection. + +Signed-off-by: Jakub Jelen +--- + cipher/pubkey-internal.h | 8 ++ + cipher/pubkey-util.c | 9 ++ + cipher/rsa-common.c | 181 +++++++++++++++++++++++ + cipher/rsa.c | 81 +++++++++++ + src/cipher.h | 37 ++--- + src/const-time.c | 2 +- + src/const-time.h | 3 +- + tests/pkcs1v2-v15c.h | 304 +++++++++++++++++++++++++++++++++++++++ + tests/pkcs1v2.c | 124 +++++++++++----- + 9 files changed, 692 insertions(+), 57 deletions(-) + +diff --git a/cipher/pubkey-internal.h b/cipher/pubkey-internal.h +index acf45105..f66574fa 100644 +--- a/cipher/pubkey-internal.h ++++ b/cipher/pubkey-internal.h +@@ -55,6 +55,14 @@ _gcry_rsa_pkcs1_encode_for_enc (gcry_mpi_t *r_result, unsigned int nbits, + gpg_err_code_t + _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, + unsigned int nbits, gcry_mpi_t value); ++#ifdef WITH_MARVIN_WORKAROUND ++gpg_err_code_t ++_gcry_rsa_pkcs1_decode_for_enc_implicit_rejection (unsigned char **r_result, ++ size_t *r_resultlen, ++ unsigned int nbits, ++ gcry_mpi_t value, ++ unsigned char *kdk); ++#endif /* WITH_MARVIN_WORKAROUND */ + gpg_err_code_t + _gcry_rsa_pkcs1_encode_raw_for_sig (gcry_mpi_t *r_result, unsigned int nbits, + const unsigned char *value, size_t valuelen); +diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c +index 68defea6..e760dd4c 100644 +--- a/cipher/pubkey-util.c ++++ b/cipher/pubkey-util.c +@@ -193,6 +193,15 @@ _gcry_pk_util_parse_flaglist (gcry_sexp_t list, + rc = GPG_ERR_INV_FLAG; + break; + ++#ifdef WITH_MARVIN_WORKAROUND ++ case 21: ++ if (!memcmp (s, "no-implicit-rejection", 21)) ++ flags |= PUBKEY_FLAG_NO_IMPLICIT_REJECTION; ++ else if (!igninvflag) ++ rc = GPG_ERR_INV_FLAG; ++ break; ++#endif /* WITH_MARVIN_WORKAROUND */ ++ + default: + if (!igninvflag) + rc = GPG_ERR_INV_FLAG; +diff --git a/cipher/rsa-common.c b/cipher/rsa-common.c +index 82a9e083..e776a309 100644 +--- a/cipher/rsa-common.c ++++ b/cipher/rsa-common.c +@@ -318,6 +318,187 @@ _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen, + } + + ++#ifdef WITH_MARVIN_WORKAROUND ++#define SHA256_LEN 32 ++static gcry_err_code_t ++rsa_prf(unsigned char *out, size_t out_len, const char *label, size_t label_len, unsigned char *kdk, size_t nbits) ++{ ++ gcry_err_code_t rc = GPG_ERR_NO_ERROR; ++ size_t pos; ++ uint16_t iter = 0; ++ unsigned char be_iter[2]; ++ unsigned char be_bitlen[2]; ++ gcry_md_hd_t hd; ++ gcry_error_t err; ++ ++ be_bitlen[0] = (nbits >> 8) & 0xff; ++ be_bitlen[1] = nbits & 0xff; ++ ++ err = _gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); ++ if (err) ++ { ++ return GPG_ERR_INTERNAL; ++ } ++ err = _gcry_md_setkey (hd, kdk, SHA256_LEN); ++ if (err) ++ { ++ _gcry_md_close (hd); ++ return GPG_ERR_INTERNAL; ++ } ++ for (pos = 0; pos < out_len; pos += SHA256_LEN, iter++) ++ { ++ unsigned char *tmp; ++ ++ _gcry_md_reset(hd); ++ ++ be_iter[0] = (iter >> 8) & 0xff; ++ be_iter[1] = iter & 0xff; ++ _gcry_md_write (hd, be_iter, sizeof (be_iter)); ++ _gcry_md_write (hd, label, label_len); ++ _gcry_md_write (hd, be_bitlen, sizeof (be_bitlen)); ++ ++ tmp = _gcry_md_read(hd, 0); ++ if (pos + SHA256_LEN > out_len) ++ { ++ memcpy(out + pos, tmp, out_len - pos); ++ } ++ else ++ { ++ memcpy(out + pos, tmp, SHA256_LEN); ++ } ++ } ++ _gcry_md_close (hd); ++ return rc; ++} ++ ++ ++/* Decode a plaintext in VALUE assuming pkcs#1 block type 2 padding. ++ NBITS is the size of the secret key. On success the result is ++ stored as a newly allocated buffer at R_RESULT and its valid length at ++ R_RESULTLEN. On error R_RESULT contains bogus value of random length. ++ For more information, see description of Implicit Rejection in Marvin paper ++ https://people.redhat.com/~hkario/marvin/marvin-attack-paper.pdf */ ++#define MAX_LEN_GEN_TRIES 128 ++gpg_err_code_t ++_gcry_rsa_pkcs1_decode_for_enc_implicit_rejection (unsigned char **r_result, ++ size_t *r_resultlen, ++ unsigned int nbits, ++ gcry_mpi_t value, ++ unsigned char *kdk) ++{ ++ unsigned char *frame = NULL; ++ size_t nframe = (nbits+7) / 8; ++ size_t n, n0; ++ unsigned int failed = 0; ++ unsigned int not_found = 1; ++ int i, j; ++ uint16_t len_mask; ++ uint16_t max_sep_offset; ++ unsigned char *synthetic = NULL; ++ unsigned long synthethic_length; ++ uint16_t len_candidate; ++ unsigned char candidate_lengths[MAX_LEN_GEN_TRIES * sizeof(len_candidate)]; ++ int synth_msg_index = 0, msg_index; ++ ++ *r_result = NULL; ++ ++ synthetic = xtrymalloc(nframe); ++ if (synthetic == NULL) ++ { ++ free (synthetic); ++ return gpg_err_code_from_syserror (); ++ } ++ ++ if (rsa_prf(synthetic, nframe, "message", 7, kdk, nframe * 8) < 0) ++ { ++ xfree (synthetic); ++ return GPG_ERR_INTERNAL; ++ } ++ ++ /* decide how long the random message should be */ ++ if (rsa_prf(candidate_lengths, sizeof(candidate_lengths), ++ "length", 6, kdk, MAX_LEN_GEN_TRIES * sizeof(len_candidate) * 8) < 0) ++ { ++ xfree (synthetic); ++ return GPG_ERR_INTERNAL; ++ } ++ ++ len_mask = max_sep_offset = nframe - 2 - 8; ++ len_mask |= len_mask >> 1; ++ len_mask |= len_mask >> 2; ++ len_mask |= len_mask >> 4; ++ len_mask |= len_mask >> 8; ++ ++ synthethic_length = 0; ++ for (i = 0; i < MAX_LEN_GEN_TRIES * (int)sizeof(len_candidate); ++ i += sizeof(len_candidate)) ++ { ++ len_candidate = (candidate_lengths[i] << 8) | candidate_lengths[i + 1]; ++ len_candidate &= len_mask; ++ ++ synthethic_length = ct_ulong_select( ++ len_candidate, synthethic_length, ++ ct_lt(len_candidate, max_sep_offset)); ++ } ++ ++ synth_msg_index = nframe - synthethic_length; ++ ++ /* we have alternative message ready, check the real one */ ++ /* mostly copy from _gcry_rsa_pkcs1_decode_for_enc */ ++ frame = mpi_to_string(value, nframe); ++ if (frame == NULL) ++ { ++ xfree (synthetic); ++ return gpg_err_code_from_syserror (); ++ } ++ ++ /* FRAME = 0x00 || 0x02 || PS || 0x00 || M */ ++ n = 0; ++ failed |= ct_not_equal_byte (frame[n++], 0x00); ++ failed |= ct_not_equal_byte (frame[n++], 0x02); ++ ++ /* Find the terminating zero byte. */ ++ n0 = n; ++ for (; n < nframe; n++) ++ { ++ not_found &= ct_not_equal_byte (frame[n], 0x00); ++ n0 += not_found; ++ } ++ ++ failed |= not_found; ++ n0 += ct_is_zero (not_found); /* Skip the zero byte. */ ++ /* the valid padding is at least 8 bytes -- the plaintext needs to start at index 11 or later */ ++ failed |= ct_lt_s (n0, 11); ++ ++ msg_index = ct_ulong_select (synth_msg_index, n0, failed); ++ ++ /* To avoid the need memmove (O(N*log(N))), we use separate buffer where we move either ++ * of the messages. We can now also use the msg_index safely as its timing is not ++ * possible to distinguish valid and invalid padding */ ++ for (i = msg_index, j = 0; i < nframe && j < nframe; i++, j++) ++ { ++ frame[j] = ct_uchar_select (synthetic[i], frame[i], failed); ++ } ++ ++ *r_resultlen = j; ++ *r_result = frame; ++ ++ if (DBG_CIPHER) ++ { ++ if (!failed) ++ log_printhex ("value extracted from PKCS#1 block type 2 encoded data", ++ frame, nframe - n0); ++ else ++ log_printhex ("Synthetic value from implicit rejection", ++ synthetic, synthethic_length); ++ } ++ ++ xfree (synthetic); ++ return 0; ++} ++#endif /* WITH_MARVIN_WORKAROUND */ ++ ++ + /* Encode {VALUE,VALUELEN} for an NBITS keys and hash algorithm ALGO + using the pkcs#1 block type 1 padding. On success the result is + stored as a new MPI at R_RESULT. On error the value at R_RESULT is +diff --git a/cipher/rsa.c b/cipher/rsa.c +index c196724c..9c1ef1f7 100644 +--- a/cipher/rsa.c ++++ b/cipher/rsa.c +@@ -1436,6 +1436,7 @@ rsa_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) + } + + ++#define SHA256_LEN 32 + static gcry_err_code_t + rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) + +@@ -1451,6 +1452,9 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) + unsigned int nbits = rsa_get_nbits (keyparms); + gcry_sexp_t result = NULL; + gcry_sexp_t dummy = NULL; ++#ifdef WITH_MARVIN_WORKAROUND ++ unsigned char kdk[SHA256_LEN]; ++#endif /* WITH_MARVIN_WORKAROUND */ + + rc = rsa_check_keysize (nbits); + if (rc) +@@ -1498,6 +1502,71 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) + mpi_normalize (data); + mpi_fdiv_r (data, data, sk.n); + ++#ifdef WITH_MARVIN_WORKAROUND ++ /* Implicit rejection: Derive KDK */ ++ if (ctx.encoding == PUBKEY_ENC_PKCS1) ++ { ++ unsigned char *buf, *tmp; ++ unsigned int nbytes = (mpi_get_nbits (sk.n)+7)/8; ++ unsigned char key[SHA256_LEN]; ++ gcry_md_hd_t hd; ++ ++ /* (a) Convert `d` to big endian representation, left padded to the length of `n` */ ++ rc = _gcry_mpi_to_octet_string (&buf, NULL, sk.d, nbytes); ++ if (rc) ++ { ++ rc = GPG_ERR_INTERNAL; ++ goto leave; ++ } ++ ++ /* (b) Hash it using SHA-256 */ ++ rc = _gcry_md_open (&hd, GCRY_MD_SHA256, 0); ++ if (rc) ++ { ++ xfree (buf); ++ rc = GPG_ERR_INTERNAL; ++ goto leave; ++ } ++ _gcry_md_write (hd, buf, nbytes); ++ /* keep the buffer around -- it will be needed for the ciphertext */ ++ tmp = _gcry_md_read (hd, GCRY_MD_SHA256); ++ memcpy(key, tmp, SHA256_LEN); ++ _gcry_md_close (hd); ++ ++ /* (c) Use the hash as a SHA-256 HMAC key and ciphertext as a message */ ++ rc = _gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); ++ if (rc) ++ { ++ xfree (buf); ++ rc = GPG_ERR_INTERNAL; ++ goto leave; ++ } ++ rc = _gcry_md_setkey (hd, key, sizeof (key)); ++ if (rc) ++ { ++ xfree (buf); ++ _gcry_md_close (hd); ++ rc = GPG_ERR_INTERNAL; ++ goto leave; ++ } ++ ++ /* We need to convert the ciphertext to string, padded to the length of modulus */ ++ rc = _gcry_mpi_to_octet_string (NULL, buf, data, nbytes); ++ if (rc) ++ { ++ xfree (buf); ++ _gcry_md_close (hd); ++ rc = GPG_ERR_INTERNAL; ++ goto leave; ++ } ++ _gcry_md_write (hd, buf, nbytes); ++ xfree (buf); ++ buf = _gcry_md_read (hd, 0); ++ memcpy(kdk, buf, SHA256_LEN); ++ _gcry_md_close (hd); ++ } ++#endif /* WITH_MARVIN_WORKAROUND */ ++ + /* Allocate MPI for the plaintext. */ + plain = mpi_snew (nbits); + +@@ -1516,6 +1585,18 @@ rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) + switch (ctx.encoding) + { + case PUBKEY_ENC_PKCS1: ++#ifdef WITH_MARVIN_WORKAROUND ++ if (!(ctx.flags & PUBKEY_FLAG_NO_IMPLICIT_REJECTION)) ++ { ++ rc = _gcry_rsa_pkcs1_decode_for_enc_implicit_rejection (&unpad, &unpadlen, nbits, plain, kdk); ++ mpi_free (plain); ++ plain = NULL; ++ rc_sexp = sexp_build (&result, NULL, "(value %c)", (int)unpadlen, unpad, (nbits + 7) / 8); ++ *r_plain = result; ++ rc = ct_ulong_select (rc_sexp, rc, ct_is_zero (rc) & ct_is_not_zero (rc_sexp)); ++ break; ++ } ++#endif /* WITH_MARVIN_WORKAROUND */ + rc = _gcry_rsa_pkcs1_decode_for_enc (&unpad, &unpadlen, nbits, plain); + mpi_free (plain); + plain = NULL; +diff --git a/src/cipher.h b/src/cipher.h +index 0a2551fe..e522f39b 100644 +--- a/src/cipher.h ++++ b/src/cipher.h +@@ -26,24 +26,25 @@ + + #include "../random/random.h" + +-#define PUBKEY_FLAG_NO_BLINDING (1 << 0) +-#define PUBKEY_FLAG_RFC6979 (1 << 1) +-#define PUBKEY_FLAG_FIXEDLEN (1 << 2) +-#define PUBKEY_FLAG_LEGACYRESULT (1 << 3) +-#define PUBKEY_FLAG_RAW_FLAG (1 << 4) +-#define PUBKEY_FLAG_TRANSIENT_KEY (1 << 5) +-#define PUBKEY_FLAG_USE_X931 (1 << 6) +-#define PUBKEY_FLAG_USE_FIPS186 (1 << 7) +-#define PUBKEY_FLAG_USE_FIPS186_2 (1 << 8) +-#define PUBKEY_FLAG_PARAM (1 << 9) +-#define PUBKEY_FLAG_COMP (1 << 10) +-#define PUBKEY_FLAG_NOCOMP (1 << 11) +-#define PUBKEY_FLAG_EDDSA (1 << 12) +-#define PUBKEY_FLAG_GOST (1 << 13) +-#define PUBKEY_FLAG_NO_KEYTEST (1 << 14) +-#define PUBKEY_FLAG_DJB_TWEAK (1 << 15) +-#define PUBKEY_FLAG_SM2 (1 << 16) +-#define PUBKEY_FLAG_PREHASH (1 << 17) ++#define PUBKEY_FLAG_NO_BLINDING (1 << 0) ++#define PUBKEY_FLAG_RFC6979 (1 << 1) ++#define PUBKEY_FLAG_FIXEDLEN (1 << 2) ++#define PUBKEY_FLAG_LEGACYRESULT (1 << 3) ++#define PUBKEY_FLAG_RAW_FLAG (1 << 4) ++#define PUBKEY_FLAG_TRANSIENT_KEY (1 << 5) ++#define PUBKEY_FLAG_USE_X931 (1 << 6) ++#define PUBKEY_FLAG_USE_FIPS186 (1 << 7) ++#define PUBKEY_FLAG_USE_FIPS186_2 (1 << 8) ++#define PUBKEY_FLAG_PARAM (1 << 9) ++#define PUBKEY_FLAG_COMP (1 << 10) ++#define PUBKEY_FLAG_NOCOMP (1 << 11) ++#define PUBKEY_FLAG_EDDSA (1 << 12) ++#define PUBKEY_FLAG_GOST (1 << 13) ++#define PUBKEY_FLAG_NO_KEYTEST (1 << 14) ++#define PUBKEY_FLAG_DJB_TWEAK (1 << 15) ++#define PUBKEY_FLAG_SM2 (1 << 16) ++#define PUBKEY_FLAG_PREHASH (1 << 17) ++#define PUBKEY_FLAG_NO_IMPLICIT_REJECTION (1 << 18) + + + enum pk_operation +diff --git a/src/const-time.c b/src/const-time.c +index cecb1937..2b6a4b4e 100644 +--- a/src/const-time.c ++++ b/src/const-time.c +@@ -96,7 +96,7 @@ _gcry_ct_memmov_cond (void *dst, const void *src, size_t len, + void + _gcry_ct_memcpy (void *dst, const void *src, size_t len, size_t buffer_len) + { +- unsigned char mask_a, mask_b; ++ volatile unsigned char mask_a, mask_b; + unsigned char *b_dst = dst; + const unsigned char *b_src = src; + unsigned int writing; +diff --git a/src/const-time.h b/src/const-time.h +index 74055a4b..5e656e15 100644 +--- a/src/const-time.h ++++ b/src/const-time.h +@@ -40,7 +40,7 @@ extern volatile unsigned int _gcry_ct_vone; + static inline int + ct_lt (unsigned int a, unsigned int b) + { +- return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(unsigned int)*8 - 1); ++ return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(unsigned int) * 8 - 1); + } + + static inline size_t +@@ -164,6 +164,7 @@ DEFINE_CT_TYPE_GEN_INV_MASK(uchar, unsigned char) + DEFINE_CT_TYPE_SELECT_FUNC(uintptr, uintptr_t) + DEFINE_CT_TYPE_SELECT_FUNC(ulong, unsigned long) + DEFINE_CT_TYPE_SELECT_FUNC(int16, int16_t) ++DEFINE_CT_TYPE_SELECT_FUNC(uchar, unsigned char) + + /* + * Return NULL when OP_ENABLED=1 +diff --git a/tests/pkcs1v2-v15c.h b/tests/pkcs1v2-v15c.h +index 26c7b238..cb750d4f 100644 +--- a/tests/pkcs1v2-v15c.h ++++ b/tests/pkcs1v2-v15c.h +@@ -21,6 +21,7 @@ + const char *mesg; + const char *seed; + const char *encr; ++ const char *synt; + } m[20]; + } tbl[] = + { +@@ -3915,5 +3916,308 @@ + "25db1dd892b71bb74a5cf68263d8fd58f1a48e6c2fcb8c0b71a251cfc1a20157" + } + } ++ }, ++ { ++ "A 2048-bit RSA Test vectors for the Bleichenbacher workaround", ++ "c8cc839714098da56caa23640f93dc8997c16372968fc1b0c6df5113c1c94e8b21e48ad2297e65419011b4e6d8f5e73b1b78b257400321d1ef6b602d4ec8ce8d141c94905eb4ad306639a49206534b6e7f2607423e97dffd133c88d721399defbc7e96ccdcbd7f3aae1fe892712bfb4929817d511666440a1facb7a208f5ea165910add8a3f2d497202360ccb632024f0d07169c1918f316f794b143aef54ec87522a4c02978f9689980bffbf649c307e81819bff88409638d48bd94be152b59ff649fa0bd629d0ffa1813c3abf4b56bd3c2ea5465dffa14589292a9d8a24ad26be7ee0510741b6382d43c83d5bfa40a46613d062be445517dbcaf0cb4e1a769", ++ "010001", ++ "1455010e0f2d587663a666a6ff1ccdbbf0edd8100646d02a023922908992c4ad39e5565929726ef6508c3a71158ef0b6ff751d39d07580bb2d2f063210442d0603ff50dbbd7b35fe2c9bb19a47a1af85a4c24901e02ca8b58b7919b20edf32aacfbf51adb4bc4b61b9b7e968caa4d570f70ef18d8063228893e47d439efca793259bcf2cd108a3d8688cdf078e7ac799969f2339d2c1f522b969684629a933baaec2681625eab84f4e56f4447e9d88fb9a199cf71023e0e257b14441b33c84d3bc67ca8031d2612618103a7a0a40844262f75d8890cd616e51f9035488fd6e099de8ff6d65a4ff118254807c9f58d2fbba8ba151dc8c68be349c977a204e04c1", ++ { ++ { ++ "1. a random positive test case", ++ NULL, ++ "", ++ "8bfe264e85d3bdeaa6b8851b8e3b956ee3d226fd3f69063a86880173a273d9f283b2eebdd1ed35f7e02d91c571981b6737d5320bd8396b0f3ad5b019daec1b0aab3cbbc026395f4fd14f13673f2dfc81f9b660ec26ac381e6db3299b4e460b43fab9955df2b3cfaa20e900e19c856238fd371899c2bf2ce8c868b76754e5db3b036533fd603746be13c10d4e3e6022ebc905d20c2a7f32b215a4cd53b3f44ca1c327d2c2b651145821c08396c89071f665349c25e44d2733cd9305985ceef6430c3cf57af5fa224089221218fa34737c79c446d28a94c41c96e4e92ac53fbcf384dea8419ea089f8784445a492c812eb0d409467f75afd7d4d1078886205a066", ++ "6c6f72656d20697073756d20646f6c6f722073697420616d6574" ++ },{ ++ "2. a random negative test case decrypting to empty", ++ NULL, ++ "", ++ "20aaa8adbbc593a924ba1c5c7990b5c2242ae4b99d0fe636a19a4cf754edbcee774e472fe028160ed42634f8864900cb514006da642cae6ae8c7d087caebcfa6dad1551301e130344989a1d462d4164505f6393933450c67bc6d39d8f5160907cabc251b737925a1cf21e5c6aa5781b7769f6a2a583d97cce008c0f8b6add5f0b2bd80bee60237aa39bb20719fe75749f4bc4e42466ef5a861ae3a92395c7d858d430bfe38040f445ea93fa2958b503539800ffa5ce5f8cf51fa8171a91f36cb4f4575e8de6b4d3f096ee140b938fd2f50ee13f0d050222e2a72b0a3069ff3a6738e82c87090caa5aed4fcbe882c49646aa250b98f12f83c8d528113614a29e7", ++ "" ++ },{ ++ "3. invalid decrypting to max length message", ++ NULL, ++ "", ++ "48cceab10f39a4db32f60074feea473cbcdb7accf92e150417f76b44756b190e843e79ec12aa85083a21f5437e7bad0a60482e601198f9d86923239c8786ee728285afd0937f7dde12717f28389843d7375912b07b991f4fdb0190fced8ba665314367e8c5f9d2981d0f5128feeb46cb50fc237e64438a86df198dd0209364ae3a842d77532b66b7ef263b83b1541ed671b120dfd660462e2107a4ee7b964e734a7bd68d90dda61770658a3c242948532da32648687e0318286473f675b412d6468f013f14d760a358dfcad3cda2afeec5e268a37d250c37f722f468a70dfd92d7294c3c1ee1e7f8843b7d16f9f37ef35748c3ae93aa155cdcdfeb4e78567303", ++ "22d850137b9eebe092b24f602dc5bb7918c16bd89ddbf20467b119d205f9c2e4bd7d2592cf1e532106e0f33557565923c73a02d4f09c0c22bea89148183e60317f7028b3aa1f261f91c979393101d7e15f4067e63979b32751658ef769610fe97cf9cef3278b3117d384051c3b1d82c251c2305418c8f6840530e631aad63e70e20e025bcd8efb54c92ec6d3b106a2f8e64eeff7d38495b0fc50c97138af4b1c0a67a1c4e27b077b8439332edfa8608dfeae653cd6a628ac550395f7e74390e42c11682234870925eeaa1fa71b76cf1f2ee3bda69f6717033ff8b7c95c9799e7a3bea5e7e4a1c359772fb6b1c6e6c516661dfe30c3" ++ },{ ++ "4. invalid decrypting to message with length specified by second to last value from PRF", ++ NULL, ++ "", ++ "1439e08c3f84c1a7fec74ce07614b20e01f6fa4e8c2a6cffdc3520d8889e5d9a950c6425798f85d4be38d300ea5695f13ecd4cb389d1ff5b82484b494d6280ab7fa78e645933981cb934cce8bfcd114cc0e6811eefa47aae20af638a1cd163d2d3366186d0a07df0c81f6c9f3171cf3561472e98a6006bf75ddb457bed036dcce199369de7d94ef2c68e8467ee0604eea2b3009479162a7891ba5c40cab17f49e1c438cb6eaea4f76ce23cce0e483ff0e96fa790ea15be67671814342d0a23f4a20262b6182e72f3a67cd289711503c85516a9ed225422f98b116f1ab080a80abd6f0216df88d8cfd67c139243be8dd78502a7aaf6bc99d7da71bcdf627e7354", ++ "0f9b" ++ },{ ++ "5. invalid decrypting to message with length specified by third to last value from PRF", ++ NULL, ++ "", ++ "1690ebcceece2ce024f382e467cf8510e74514120937978576caf684d4a02ad569e8d76cbe365a060e00779de2f0865ccf0d923de3b4783a4e2c74f422e2f326086c390b658ba47f31ab013aa80f468c71256e5fa5679b24e83cd82c3d1e05e398208155de2212993cd2b8bab6987cf4cc1293f19909219439d74127545e9ed8a706961b8ee2119f6bfacafbef91b75a789ba65b8b833bc6149cf49b5c4d2c6359f62808659ba6541e1cd24bf7f7410486b5103f6c0ea29334ea6f4975b17387474fe920710ea61568d7b7c0a7916acf21665ad5a31c4eabcde44f8fb6120d8457afa1f3c85d517cda364af620113ae5a3c52a048821731922737307f77a1081", ++ "4f02" ++ },{ ++ "6. positive test with 11 byte long value", ++ NULL, ++ "", ++ "6213634593332c485cef783ea2846e3d6e8b0e005cd8293eaebbaa5079712fd681579bdfbbda138ae4d9d952917a03c92398ec0cb2bb0c6b5a8d55061fed0d0d8d72473563152648cfe640b335dc95331c21cb133a91790fa93ae44497c128708970d2beeb77e8721b061b1c44034143734a77be8220877415a6dba073c3871605380542a9f25252a4babe8331cdd53cf828423f3cc70b560624d0581fb126b2ed4f4ed358f0eb8065cf176399ac1a846a31055f9ae8c9c24a1ba050bc20842125bc1753158f8065f3adb9cc16bfdf83816bdf38b624f12022c5a6fbfe29bc91542be8c0208a770bcd677dc597f5557dc2ce28a11bf3e3857f158717a33f6592", ++ "6c6f72656d20697073756d" ++ },{ ++ "7. positive test with 11 byte long value and zero padded ciphertext", ++ NULL, ++ "", ++ "00a2e8f114ea8d05d12dc843e3cc3b2edc8229ff2a028bda29ba9d55e3cd02911902fef1f42a075bf05e8016e8567213d6f260fa49e360779dd81aeea3e04c2cb567e0d72b98bf754014561b7511e083d20e0bfb9cd23f8a0d3c88900c49d2fcd5843ff0765607b2026f28202a87aa94678aed22a0c20724541394cd8f44e373eba1d2bae98f516c1e2ba3d86852d064f856b1daf24795e767a2b90396e50743e3150664afab131fe40ea405dcf572dd1079af1d3f0392ccadcca0a12740dbb213b925ca2a06b1bc1383e83a658c82ba2e7427342379084d5f66b544579f07664cb26edd4f10fd913fdbc0de05ef887d4d1ec1ac95652397ea7fd4e4759fda8b", ++ "6c6f72656d20697073756d" ++ },{ ++ "8. positive test with 11 byte long value and zero truncated ciphertext", ++ NULL, ++ "", ++ "a2e8f114ea8d05d12dc843e3cc3b2edc8229ff2a028bda29ba9d55e3cd02911902fef1f42a075bf05e8016e8567213d6f260fa49e360779dd81aeea3e04c2cb567e0d72b98bf754014561b7511e083d20e0bfb9cd23f8a0d3c88900c49d2fcd5843ff0765607b2026f28202a87aa94678aed22a0c20724541394cd8f44e373eba1d2bae98f516c1e2ba3d86852d064f856b1daf24795e767a2b90396e50743e3150664afab131fe40ea405dcf572dd1079af1d3f0392ccadcca0a12740dbb213b925ca2a06b1bc1383e83a658c82ba2e7427342379084d5f66b544579f07664cb26edd4f10fd913fdbc0de05ef887d4d1ec1ac95652397ea7fd4e4759fda8b", ++ "6c6f72656d20697073756d" ++ },{ ++ "9. positive test with 11 byte long value and double zero padded ciphertext", ++ NULL, ++ "", ++ "00001f71879b426127f7dead621f7380a7098cf7d22173aa27991b143c46d53383c209bd0c9c00d84078037e715f6b98c65005a77120070522ede51d472c87ef94b94ead4c5428ee108a345561658301911ec5a8f7dd43ed4a3957fd29fb02a3529bf63f8040d3953490939bd8f78b2a3404b6fb5ff70a4bfdaac5c541d6bcce49c9778cc390be24cbef1d1eca7e870457241d3ff72ca44f9f56bdf31a890fa5eb3a9107b603ccc9d06a5dd911a664c82b6abd4fe036f8db8d5a070c2d86386ae18d97adc1847640c211d91ff5c3387574a26f8ef27ca7f48d2dd1f0c7f14b81cc9d33ee6853031d3ecf10a914ffd90947909c8011fd30249219348ebff76bfc", ++ "6c6f72656d20697073756d" ++ },{ ++ "10. a random negative test that generates an 11 byte long message", ++ NULL, ++ "", ++ "5f02f4b1f46935c742ebe62b6f05aa0a3286aab91a49b34780adde6410ab46f7386e05748331864ac98e1da63686e4babe3a19ed40a7f5ceefb89179596aab07ab1015e03b8f825084dab028b6731288f2e511a4b314b6ea3997d2e8fe2825cef8897cbbdfb6c939d441d6e04948414bb69e682927ef8576c9a7090d4aad0e74c520d6d5ce63a154720f00b76de8cc550b1aa14f016d63a7b6d6eaa1f7dbe9e50200d3159b3d099c900116bf4eba3b94204f18b1317b07529751abf64a26b0a0bf1c8ce757333b3d673211b67cc0653f2fe2620d57c8b6ee574a0323a167eab1106d9bc7fd90d415be5f1e9891a0e6c709f4fc0404e8226f8477b4e939b36eb2", ++ "af9ac70191c92413cb9f2d" ++ },{ ++ "11. an otherwise correct plaintext, but with wrong first byte (0x01 instead of 0x00), generates a random 11 byte long plaintext", ++ NULL, ++ "", ++ "9b2ec9c0c917c98f1ad3d0119aec6be51ae3106e9af1914d48600ab6a2c0c0c8ae02a2dc3039906ff3aac904af32ec798fd65f3ad1afa2e69400e7c1de81f5728f3b3291f38263bc7a90a0563e43ce7a0d4ee9c0d8a716621ca5d3d081188769ce1b131af7d35b13dea99153579c86db31fe07d5a2c14d621b77854e48a8df41b5798563af489a291e417b6a334c63222627376118c02c53b6e86310f728734ffc86ef9d7c8bf56c0c841b24b82b59f51aee4526ba1c4268506d301e4ebc498c6aebb6fd5258c876bf900bac8ca4d309dd522f6a6343599a8bc3760f422c10c72d0ad527ce4af1874124ace3d99bb74db8d69d2528db22c3a37644640f95c05f", ++ "a1f8c9255c35cfba403ccc" ++ },{ ++ "12. an otherwise correct plaintext, but with wrong second byte (0x01 instead of 0x02), generates a random 11 byte long plaintext", ++ NULL, ++ "", ++ "782c2b59a21a511243820acedd567c136f6d3090c115232a82a5efb0b178285f55b5ec2d2bac96bf00d6592ea7cdc3341610c8fb07e527e5e2d20cfaf2c7f23e375431f45e998929a02f25fd95354c33838090bca838502259e92d86d568bc2cdb132fab2a399593ca60a015dc2bb1afcd64fef8a3834e17e5358d822980dc446e845b3ab4702b1ee41fe5db716d92348d5091c15d35a110555a35deb4650a5a1d2c98025d42d4544f8b32aa6a5e02dc02deaed9a7313b73b49b0d4772a3768b0ea0db5846ace6569cae677bf67fb0acf3c255dc01ec8400c963b6e49b1067728b4e563d7e1e1515664347b92ee64db7efb5452357a02fff7fcb7437abc2e579", ++ "e6d700309ca0ed62452254" ++ },{ ++ "13. an invalid ciphertext, with a zero byte in first byte of ciphertext, decrypts to a random 11 byte long synthethic plaintext", ++ NULL, ++ "", ++ "0096136621faf36d5290b16bd26295de27f895d1faa51c800dafce73d001d60796cd4e2ac3fa2162131d859cd9da5a0c8a42281d9a63e5f353971b72e36b5722e4ac444d77f892a5443deb3dca49fa732fe855727196e23c26eeac55eeced8267a209ebc0f92f4656d64a6c13f7f7ce544ebeb0f668fe3a6c0f189e4bcd5ea12b73cf63e0c8350ee130dd62f01e5c97a1e13f52fde96a9a1bc9936ce734fdd61f27b18216f1d6de87f49cf4f2ea821fb8efd1f92cdad529baf7e31aff9bff4074f2cad2b4243dd15a711adcf7de900851fbd6bcb53dac399d7c880531d06f25f7002e1aaf1722765865d2c2b902c7736acd27bc6cbd3e38b560e2eecf7d4b576", ++ "ba27b1842e7c21c0e7ef6a" ++ },{ ++ "14. an invalid ciphertext, with a zero byte removed from first byte of ciphertext, decrypts to a random 11 byte long synthethic plaintext", ++ NULL, ++ "", ++ "96136621faf36d5290b16bd26295de27f895d1faa51c800dafce73d001d60796cd4e2ac3fa2162131d859cd9da5a0c8a42281d9a63e5f353971b72e36b5722e4ac444d77f892a5443deb3dca49fa732fe855727196e23c26eeac55eeced8267a209ebc0f92f4656d64a6c13f7f7ce544ebeb0f668fe3a6c0f189e4bcd5ea12b73cf63e0c8350ee130dd62f01e5c97a1e13f52fde96a9a1bc9936ce734fdd61f27b18216f1d6de87f49cf4f2ea821fb8efd1f92cdad529baf7e31aff9bff4074f2cad2b4243dd15a711adcf7de900851fbd6bcb53dac399d7c880531d06f25f7002e1aaf1722765865d2c2b902c7736acd27bc6cbd3e38b560e2eecf7d4b576", ++ "ba27b1842e7c21c0e7ef6a" ++ },{ ++ "15. an invalid ciphertext, with two zero bytes in first bytes of ciphertext, decrypts to a random 11 byte long synthethic plaintext", ++ NULL, ++ "", ++ "0000587cccc6b264bdfe0dc2149a988047fa921801f3502ea64624c510c6033d2f427e3f136c26e88ea9f6519e86a542cec96aad1e5e9013c3cc203b6de15a69183050813af5c9ad79703136d4b92f50ce171eefc6aa7988ecf02f319ffc5eafd6ee7a137f8fce64b255bb1b8dd19cfe767d64fdb468b9b2e9e7a0c24dae03239c8c714d3f40b7ee9c4e59ac15b17e4d328f1100756bce17133e8e7493b54e5006c3cbcdacd134130c5132a1edebdbd01a0c41452d16ed7a0788003c34730d0808e7e14c797a21f2b45a8aa1644357fd5e988f99b017d9df37563a354c788dc0e2f9466045622fa3f3e17db63414d27761f57392623a2bef6467501c63e8d645", ++ "d5cf555b1d6151029a429a" ++ },{ ++ "16. an invalid ciphertext, with two zero bytes removed from first bytes of ciphertext, decrypts to a random 11 byte long synthethic plaintext", ++ NULL, ++ "", ++ "587cccc6b264bdfe0dc2149a988047fa921801f3502ea64624c510c6033d2f427e3f136c26e88ea9f6519e86a542cec96aad1e5e9013c3cc203b6de15a69183050813af5c9ad79703136d4b92f50ce171eefc6aa7988ecf02f319ffc5eafd6ee7a137f8fce64b255bb1b8dd19cfe767d64fdb468b9b2e9e7a0c24dae03239c8c714d3f40b7ee9c4e59ac15b17e4d328f1100756bce17133e8e7493b54e5006c3cbcdacd134130c5132a1edebdbd01a0c41452d16ed7a0788003c34730d0808e7e14c797a21f2b45a8aa1644357fd5e988f99b017d9df37563a354c788dc0e2f9466045622fa3f3e17db63414d27761f57392623a2bef6467501c63e8d645", ++ "d5cf555b1d6151029a429a" ++ },{ ++ "17. and invalid ciphertext, otherwise valid but starting with 000002, decrypts to random 11 byte long synthethic plaintext", ++ NULL, ++ "", ++ "1786550ce8d8433052e01ecba8b76d3019f1355b212ac9d0f5191b023325a7e7714b7802f8e9a17c4cb3cd3a84041891471b10ca1fcfb5d041d34c82e6d0011cf4dc76b90e9c2e0743590579d55bcd7857057152c4a8040361343d1d22ba677d62b011407c652e234b1d663af25e2386251d7409190f19fc8ec3f9374fdf1254633874ce2ec2bff40ad0cb473f9761ec7b68da45a4bd5e33f5d7dac9b9a20821df9406b653f78a95a6c0ea0a4d57f867e4db22c17bf9a12c150f809a7b72b6db86c22a8732241ebf3c6a4f2cf82671d917aba8bc61052b40ccddd743a94ea9b538175106201971cca9d136d25081739aaf6cd18b2aecf9ad320ea3f89502f955", ++ "3d4a054d9358209e9cbbb9" ++ },{ ++ "18. negative test with otherwise valid padding but a zero byte in first byte of padding", ++ NULL, ++ "", ++ "179598823812d2c58a7eb50521150a48bcca8b4eb53414018b6bca19f4801456c5e36a940037ac516b0d6412ba44ec6b4f268a55ef1c5ffbf18a2f4e3522bb7b6ed89774b79bffa22f7d3102165565642de0d43a955e96a1f2e80e5430671d7266eb4f905dc8ff5e106dc5588e5b0289e49a4913940e392a97062616d2bda38155471b7d360cfb94681c702f60ed2d4de614ea72bf1c53160e63179f6c5b897b59492bee219108309f0b7b8cb2b136c346a5e98b8b4b8415fb1d713bae067911e3057f1c335b4b7e39101eafd5d28f0189037e4334f4fdb9038427b1d119a6702aa8233319cc97d496cc289ae8c956ddc84042659a2d43d6aa22f12b81ab884e", ++ "1f037dd717b07d3e7f7359" ++ },{ ++ "19. negative test with otherwise valid padding but a zero byte at the eigth byte of padding", ++ NULL, ++ "", ++ "a7a340675a82c30e22219a55bc07cdf36d47d01834c1834f917f18b517419ce9de2a96460e745024436470ed85e94297b283537d52189c406a3f533cb405cc6a9dba46b482ce98b6e3dd52d8fce2237425617e38c11fbc46b61897ef200d01e4f25f5f6c4c5b38cd0de38ba11908b86595a8036a08a42a3d05b79600a97ac18ba368a08d6cf6ccb624f6e8002afc75599fba4de3d4f3ba7d208391ebe8d21f8282b18e2c10869eb2702e68f9176b42b0ddc9d763f0c86ba0ff92c957aaeab76d9ab8da52ea297ec11d92d770146faa1b300e0f91ef969b53e7d2907ffc984e9a9c9d11fb7d6cba91972059b46506b035efec6575c46d7114a6b935864858445f", ++ "63cb0bf65fc8255dd29e17" ++ },{ ++ "20. negative test with an otherwise valid plaintext but with missing separator byte", ++ NULL, ++ "", ++ "3d1b97e7aa34eaf1f4fc171ceb11dcfffd9a46a5b6961205b10b302818c1fcc9f4ec78bf18ea0cee7e9fa5b16fb4c611463b368b3312ac11cf9c06b7cf72b54e284848a508d3f02328c62c2999d0fb60929f81783c7a256891bc2ff4d91df2af96a24fc5701a1823af939ce6dbdc510608e3d41eec172ad2d51b9fc61b4217c923cadcf5bac321355ef8be5e5f090cdc2bd0c697d9058247db3ad613fdce87d2955a6d1c948a5160f93da21f731d74137f5d1f53a1923adb513d2e6e1589d44cc079f4c6ddd471d38ac82d20d8b1d21f8d65f3b6907086809f4123e08d86fb38729585de026a485d8f0e703fd4772f6668febf67df947b82195fa3867e3a3065", ++ "6f09a0b62699337c497b0b" ++ } ++ } ++ }, ++ { ++ "A 2049-bit RSA Test vectors for the Bleichenbacher workaround", ++ "0155f889556a1775f1c7a7786a50b18bc28c9e986ede5667cab39b84124e90eba75c1db083ac3e443bba94dc23560f75e3a81693a2a43bdc7426d8c4eafe68c85de0fe757f6e49bb9ed447e602430800dbb04ceb22e7fa57a18d338fb66026cdb467e70cc040e7d367ef403c7bf1e3df624650094631f21eafd2fb5bc915ff04379acd1112f732c0b46607c178d38a20f52eda509f2f9c0405d51069e80ccf941554d0470467505c3cf541ea0897dfc9f400cecb298ffc753372d9f6933af174cc40ed96d467031733b97f8cddd3f92bc3a03ea8576c417f24007b5e4f7501105b544de9fadcdffadf98dfb4bb05b8199f3f85acfd91f7a9a094b9a383f5049097", ++ "010001", ++ "0119c2b3f50a7ad6152679d7ff510958ac2d8ca6f0028592f332d55a16736178a8e67f17e705ce300e3e87547251006013f974d0a3db49ef344ca5a26a34c0450704d0e422e0ce23a69425c15fefb6f26e106eeff64cc8b9d7442e4da4e8c85008eaeb365859a2294fa3937bc26be56332e7d81e2c160ef635cc528aa7be55e633a723dbc1e16ba29e52b29aef2f9e5654fdc0666bb0fc254acbe80e63874f0f5f020782e3c9dcfc2520d0c9c4a7b634e4503fbb493e1aafeeb3f88bd7a13398725dae6fe399e775cd5d4cf09fc838347c4c98dab1a4883cce620513615afaa10a63368e6d7b79df4166ab162739ef515a4402ee1e0601c5a55bc71df0e30edf81", ++ { ++ { ++ "1. malformed that generates length specified by 3rd last value from PRF", ++ NULL, ++ "", ++ "00b26f6404b82649629f2704494282443776929122e279a9cf30b0c6fe8122a0a9042870d97cc8ef65490fe58f031eb2442352191f5fbc311026b5147d32df914599f38b825ebb824af0d63f2d541a245c5775d1c4b78630e4996cc5fe413d38455a776cf4edcc0aa7fccb31c584d60502ed2b77398f536e137ff7ba6430e9258e21c2db5b82f5380f566876110ac4c759178900fbad7ab70ea07b1daf7a1639cbb4196543a6cbe8271f35dddb8120304f6eef83059e1c5c5678710f904a6d760c4d1d8ad076be17904b9e69910040b47914a0176fb7eea0c06444a6c4b86d674d19a556a1de5490373cb01ce31bbd15a5633362d3d2cd7d4af1b4c5121288b894", ++ "42" ++ },{ ++ "2. simple positive test case", ++ NULL, ++ "", ++ "013300edbf0bb3571e59889f7ed76970bf6d57e1c89bbb6d1c3991d9df8e65ed54b556d928da7d768facb395bbcc81e9f8573b45cf8195dbd85d83a59281cddf4163aec11b53b4140053e3bd109f787a7c3cec31d535af1f50e0598d85d96d91ea01913d07097d25af99c67464ebf2bb396fb28a9233e56f31f7e105d71a23e9ef3b736d1e80e713d1691713df97334779552fc94b40dd733c7251bc522b673d3ec9354af3dd4ad44fa71c0662213a57ada1d75149697d0eb55c053aaed5ffd0b815832f454179519d3736fb4faf808416071db0d0f801aca8548311ee708c131f4be658b15f6b54256872c2903ac708bd43b017b073b5707bc84c2cd9da70e967", ++ "6c6f72656d20697073756d" ++ },{ ++ "3. positive test case with null padded ciphertext", ++ NULL, ++ "", ++ "0002aadf846a329fadc6760980303dbd87bfadfa78c2015ce4d6c5782fd9d3f1078bd3c0a2c5bfbdd1c024552e5054d98b5bcdc94e476dd280e64d650089326542ce7c61d4f1ab40004c2e6a88a883613568556a10f3f9edeab67ae8dddc1e6b0831c2793d2715de943f7ce34c5c05d1b09f14431fde566d17e76c9feee90d86a2c158616ec81dda0c642f58c0ba8fa4495843124a7235d46fb4069715a51bf710fd024259131ba94da73597ace494856c94e7a3ec261545793b0990279b15fa91c7fd13dbfb1df2f221dab9fa9f7c1d21e48aa49f6aaecbabf5ee76dc6c2af2317ffb4e303115386a97f8729afc3d0c89419669235f1a3a69570e0836c79fc162", ++ "6c6f72656d20697073756d" ++ },{ ++ "4. positive test case with null truncated ciphertext", ++ NULL, ++ "", ++ "02aadf846a329fadc6760980303dbd87bfadfa78c2015ce4d6c5782fd9d3f1078bd3c0a2c5bfbdd1c024552e5054d98b5bcdc94e476dd280e64d650089326542ce7c61d4f1ab40004c2e6a88a883613568556a10f3f9edeab67ae8dddc1e6b0831c2793d2715de943f7ce34c5c05d1b09f14431fde566d17e76c9feee90d86a2c158616ec81dda0c642f58c0ba8fa4495843124a7235d46fb4069715a51bf710fd024259131ba94da73597ace494856c94e7a3ec261545793b0990279b15fa91c7fd13dbfb1df2f221dab9fa9f7c1d21e48aa49f6aaecbabf5ee76dc6c2af2317ffb4e303115386a97f8729afc3d0c89419669235f1a3a69570e0836c79fc162", ++ "6c6f72656d20697073756d" ++ },{ ++ "5. positive test case with double null padded ciphertext", ++ NULL, ++ "", ++ "0000f36da3b72d8ff6ded74e7efd08c01908f3f5f0de7b55eab92b5f875190809c39d4162e1e6649618f854fd84aeab03970d16bb814e999852c06de38d82b95c0f32e2a7b5714021fe303389be9c0eac24c90a6b7210f929d390fabf903d44e04110bb7a7fd6c383c275804721efa6d7c93aa64c0bb2b18d97c5220a846c66a4895ae52adddbe2a9996825e013585adcec4b32ba61d782737bd343e5fabd68e8a95b8b1340318559860792dd70dffbe05a1052b54cbfb48cfa7bb3c19cea52076bddac5c25ee276f153a610f6d06ed696d192d8ae4507ffae4e5bdda10a625d6b67f32f7cffcd48dee2431fe66f6105f9d17e611cdcc674868e81692a360f4052", ++ "6c6f72656d20697073756d" ++ },{ ++ "6. positive test case with double null truncated ciphertext", ++ NULL, ++ "", ++ "f36da3b72d8ff6ded74e7efd08c01908f3f5f0de7b55eab92b5f875190809c39d4162e1e6649618f854fd84aeab03970d16bb814e999852c06de38d82b95c0f32e2a7b5714021fe303389be9c0eac24c90a6b7210f929d390fabf903d44e04110bb7a7fd6c383c275804721efa6d7c93aa64c0bb2b18d97c5220a846c66a4895ae52adddbe2a9996825e013585adcec4b32ba61d782737bd343e5fabd68e8a95b8b1340318559860792dd70dffbe05a1052b54cbfb48cfa7bb3c19cea52076bddac5c25ee276f153a610f6d06ed696d192d8ae4507ffae4e5bdda10a625d6b67f32f7cffcd48dee2431fe66f6105f9d17e611cdcc674868e81692a360f4052", ++ "6c6f72656d20697073756d" ++ },{ ++ "7. a random negative test case that generates an 11 byte long message", ++ NULL, ++ "", ++ "00f910200830fc8fff478e99e145f1474b312e2512d0f90b8cef77f8001d09861688c156d1cbaf8a8957f7ebf35f724466952d0524cad48aad4fba1e45ce8ea27e8f3ba44131b7831b62d60c0762661f4c1d1a88cd06263a259abf1ba9e6b0b172069afb86a7e88387726f8ab3adb30bfd6b3f6be6d85d5dfd044e7ef052395474a9cbb1c3667a92780b43a22693015af6c513041bdaf87d43b24ddd244e791eeaea1066e1f4917117b3a468e22e0f7358852bb981248de4d720add2d15dccba6280355935b67c96f9dcb6c419cc38ab9f6fba2d649ef2066e0c34c9f788ae49babd9025fa85b21113e56ce4f43aa134c512b030dd7ac7ce82e76f0be9ce09ebca", ++ "1189b6f5498fd6df532b00" ++ },{ ++ "8. otherwise correct plaintext, but with wrong first byte (0x01 instead of 0x00)", ++ NULL, ++ "", ++ "002c9ddc36ba4cf0038692b2d3a1c61a4bb3786a97ce2e46a3ba74d03158aeef456ce0f4db04dda3fe062268a1711250a18c69778a6280d88e133a16254e1f0e30ce8dac9b57d2e39a2f7d7be3ee4e08aec2fdbe8dadad7fdbf442a29a8fb40857407bf6be35596b8eefb5c2b3f58b894452c2dc54a6123a1a38d642e23751746597e08d71ac92704adc17803b19e131b4d1927881f43b0200e6f95658f559f912c889b4cd51862784364896cd6e8618f485a992f82997ad6a0917e32ae5872eaf850092b2d6c782ad35f487b79682333c1750c685d7d32ab3e1538f31dcaa5e7d5d2825875242c83947308dcf63ba4bfff20334c9c140c837dbdbae7a8dee72ff", ++ "f6d0f5b78082fe61c04674" ++ },{ ++ "9. otherwise correct plaintext, but with wrong second byte (0x01 instead of 0x02)", ++ NULL, ++ "", ++ "00c5d77826c1ab7a34d6390f9d342d5dbe848942e2618287952ba0350d7de6726112e9cebc391a0fae1839e2bf168229e3e0d71d4161801509f1f28f6e1487ca52df05c466b6b0a6fbbe57a3268a970610ec0beac39ec0fa67babce1ef2a86bf77466dc127d7d0d2962c20e66593126f276863cd38dc6351428f884c1384f67cad0a0ffdbc2af16711fb68dc559b96b37b4f04cd133ffc7d79c43c42ca4948fa895b9daeb853150c8a5169849b730cc77d68b0217d6c0e3dbf38d751a1998186633418367e7576530566c23d6d4e0da9b038d0bb5169ce40133ea076472d055001f0135645940fd08ea44269af2604c8b1ba225053d6db9ab43577689401bdc0f3", ++ "1ab287fcef3ff17067914d" ++ },{ ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ } ++ } ++ }, ++ { ++ "RSA decrypt with 3072 bit keys", ++ "afd71caad5e9f5b8c6c367070a47f19d7e66aede18a5b2741fb3c4d33434560692a2d909ef6888ec602ff6b93af258ee74303c301afcd4edbec43311ddc8ddbf00ddbbe386d33b8d0e22b1b44936dc489871b85237b34ce747ad8fdb0c4e4d1daa7aadf07385c5c8732ccb7d5a49e2e50c883c7d7ac10ed6a74d9ac90df9129905a17d4a087210fc78b6d04b1eb969482c11a6eeb79c50e5b16f3f254f7571528b2f1716ab816d6eca0727bdea98059329730eb8c33ce71d61dd4ac393b6256e07ac1d124f0200d1c3e05a4c1bc7f1ed2fc83e57199cfe5908b10087e27fbd97d2c24214619c7147c8fbefca39bc256762a6823531f7e234d68eae7a0d9faf10dd15e9523780c7d5ae58094ad525a9063b4c33f95e1006da2eb12d3743689495c1f2023e407353c5eb3e4ca1c48cff81a10900d14820eb801af4f1a596c4b9ce9a531fcf8a54d9ffd724258b6eec20108df6fdfd76d4ae03ba7ea598dcb0e4a280849587286f4d7f256ce85e5eb5679b1dacc1f9095649b72e5fa072aeb00379", ++ "010001", ++ "2517eacb3afef4bffae60398df9957a5d2a154a83368d8e15842b2f59ee09f79197bd2ef1e9addaf8786f6b4127447405e3042b21f2f50b7aa73771680c3bbcb6c225a5d5ff6b56c471c1882a0a33b0af165a3ed6c249dc7783e6bc758ac37e6572d33fe325078ed952650f2eb9604902ef99a511a1119d13c4fc9a43a175dcdfbfb1a1400fe17093b69cd3cdb895f65432ea2195f90511c7336b58a685dacff2daf4c5e92e565b1665ae60e512baa9965b808d5ff119ceb7cd692cbf920067afacd809f66be706da26810ac790db156c948e6fe581bc9849157f4da493f3a64b0c6e119e031b107f8436ce29160b458b8f9f1095fdeb19263384ff5387557bc4f10b4034de841703bad2c1e769c23853eb630a36d1061dd46e2a6bbaf743a97f0b32c36f50c1a3722def3a394d91c2e078bf09d795ecde5e56b8202f974026f75fc56e9a0dd6a88f2e7cb78ef1298cc6c65207ca45bd37188807b4fcdb1e60dd8e5b85648fb7efa8b6fdd448f39741a8d9809fe163af3de45bac24a5a841c81", ++ { ++ { ++ "1. a random invalid ciphertext that generates an empty synthethic one", ++ NULL, ++ "", ++ "5e956cd9652f4a2ece902931013e09662b6a9257ad1e987fb75f73a0606df2a4b04789770820c2e02322c4e826f767bd895734a01e20609c3be4517a7a2a589ea1cdc137beb73eb38dac781b52e863de9620f79f9b90fd5b953651fcbfef4a9f1cc07421d511a87dd6942caab6a5a0f4df473e62defb529a7de1509ab99c596e1dff1320402298d8be73a896cc86c38ae3f2f576e9ea70cc28ad575cb0f854f0be43186baa9c18e29c47c6ca77135db79c811231b7c1730955887d321fdc06568382b86643cf089b10e35ab23e827d2e5aa7b4e99ff2e914f302351819eb4d1693243b35f8bf1d42d08f8ec4acafa35f747a4a975a28643ec630d8e4fa5be59d81995660a14bb64c1fea5146d6b11f92da6a3956dd5cb5e0d747cf2ea23f81617769185336263d46ef4c144b754de62a6337342d6c85a95f19f015724546ee3fc4823eca603dbc1dc01c2d5ed50bd72d8e96df2dc048edde0081284068283fc5e73a6139851abf2f29977d0b3d160c883a42a37efba1be05c1a0b1741d7ddf59", ++ "" ++ },{ ++ "2. a random invalid that has PRF output with a length one byte too long", ++ NULL, ++ "", ++ "7db0390d75fcf9d4c59cf27b264190d856da9abd11e92334d0e5f71005cfed865a711dfa28b791188374b61916dbc11339bf14b06f5f3f68c206c5607380e13da3129bfb744157e1527dd6fdf6651248b028a496ae1b97702d44706043cdaa7a59c0f41367303f21f268968bf3bd2904db3ae5239b55f8b438d93d7db9d1666c071c0857e2ec37757463769c54e51f052b2a71b04c2869e9e7049a1037b8429206c99726f07289bac18363e7eb2a5b417f47c37a55090cda676517b3549c873f2fe95da9681752ec9864b069089a2ed2f340c8b04ee00079055a817a3355b46ac7dc00d17f4504ccfbcfcadb0c04cb6b22069e179385ae1eafabad5521bac2b8a8ee1dfff59a22eb3fdacfc87175d10d7894cfd869d056057dd9944b869c1784fcc27f731bc46171d39570fbffbadf082d33f6352ecf44aca8d9478e53f5a5b7c852b401e8f5f74da49da91e65bdc97765a9523b7a0885a6f8afe5759d58009fbfa837472a968e6ae92026a5e0202a395483095302d6c3985b5f5831c521a271", ++ "56a3bea054e01338be9b7d7957539c" ++ },{ ++ "3. a random invalid that generates a synthethic of maximum size", ++ NULL, ++ "", ++ "1715065322522dff85049800f6a29ab5f98c465020467414b2a44127fe9446da47fa18047900f99afe67c2df6f50160bb8e90bff296610fde632b3859d4d0d2e644f23835028c46cca01b84b88231d7e03154edec6627bcba23de76740d839851fa12d74c8f92e540c73fe837b91b7d699b311997d5f0f7864c486d499c3a79c111faaacbe4799597a25066c6200215c3d158f3817c1aa57f18bdaad0be1658da9da93f5cc6c3c4dd72788af57adbb6a0c26f42d32d95b8a4f95e8c6feb2f8a5d53b19a50a0b7cbc25e055ad03e5ace8f3f7db13e57759f67b65d143f08cca15992c6b2aae643390483de111c2988d4e76b42596266005103c8de6044fb7398eb3c28a864fa672de5fd8774510ff45e05969a11a4c7d3f343e331190d2dcf24fb9154ba904dc94af98afc5774a9617d0418fe6d13f8245c7d7626c176138dd698a23547c25f27c2b98ea4d8a45c7842b81888e4cc14e5b72e9cf91f56956c93dbf2e5f44a8282a7813157fc481ff1371a0f66b31797e81ebdb09a673d4db96d6", ++ "7b036fcd6243900e4236c894e2462c17738acc87e01a76f4d95cb9a328d9acde81650283b8e8f60a217e3bdee835c7b222ad4c85d0acdb9a309bd2a754609a65dec50f3aa04c6d5891034566b9563d42668ede1f8992b17753a2132e28970584e255efc8b45a41c5dbd7567f014acec5fe6fdb6d484790360a913ebb9defcd74ff377f2a8ba46d2ed85f733c9a3da08eb57ecedfafda806778f03c66b2c5d2874cec1c291b2d49eb194c7b5d0dd2908ae90f4843268a2c45563092ade08acb6ab481a08176102fc803fbb2f8ad11b0e1531bd37df543498daf180b12017f4d4d426ca29b4161075534bfb914968088a9d13785d0adc0e2580d3548494b2a9e91605f2b27e6cc701c796f0de7c6f471f6ab6cb9272a1ed637ca32a60d117505d82af3c1336104afb537d01a8f70b510e1eebf4869cb976c419473795a66c7f5e6e20a8094b1bb603a74330c537c5c0698c31538bd2e138c1275a1bdf24c5fa8ab3b7b526324e7918a382d1363b3d463764222150e04" ++ },{ ++ "4. a positive test case that decrypts to 9 byte long value", ++ NULL, ++ "", ++ "6c60845a854b4571f678941ae35a2ac03f67c21e21146f9db1f2306be9f136453b86ad55647d4f7b5c9e62197aaff0c0e40a3b54c4cde14e774b1c5959b6c2a2302896ffae1f73b00b862a20ff4304fe06cea7ff30ecb3773ca9af27a0b54547350d7c07dfb0a39629c7e71e83fc5af9b2adbaf898e037f1de696a3f328cf45af7ec9aff7173854087fb8fbf34be981efbd8493f9438d1b2ba2a86af082662aa46ae9adfbec51e5f3d9550a4dd1dcb7c8969c9587a6edc82a8cabbc785c40d9fbd12064559fb769450ac3e47e87bc046148130d7eaa843e4b3ccef3675d0630500803cb7ffee3882378c1a404e850c3e20707bb745e42b13c18786c4976076ed9fa8fd0ff15e571bef02cbbe2f90c908ac3734a433b73e778d4d17fcc28f49185ebc6e8536a06d293202d94496453bfdf1c2c7833a3f99fa38ca8a81f42eaa529d603b890308a319c0ab63a35ff8ebac965f6278f5a7e5d622be5d5fe55f0ca3ec993d55430d2bf59c5d3e860e90c16d91a04596f6fdf60d89ed95d88c036dde", ++ "666f7274792074776f" ++ },{ ++ "5. a positive test case with null padded ciphertext", ++ NULL, ++ "", ++ "00f4d565a3286784dbb85327db8807ae557ead229f92aba945cecda5225f606a7d6130edeeb6f26724d1eff1110f9eb18dc3248140ee3837e6688391e78796c526791384f045e21b6b853fb6342a11f309eb77962f37ce23925af600847fbd30e6e07e57de50b606e6b7f288cc777c1a6834f27e6edace508452128916eef7788c8bb227e3548c6a761cc4e9dd1a3584176dc053ba3500adb1d5e1611291654f12dfc5722832f635db3002d73f9defc310ace62c63868d341619c7ee15b20243b3371e05078e11219770c701d9f341af35df1bc729de294825ff2e416aa11526612852777eb131f9c45151eb144980d70608d2fc4043477368369aa0fe487a48bd57e66b00c3c58f941549f5ec050fca64449debe7a0c4ac51e55cb71620a70312aa4bd85fac1410c9c7f9d6ec610b7d11bf8faeffa20255d1a1bead9297d0aa8765cd2805847d639bc439f4a6c896e2008f746f9590ff4596de5ddde000ed666c452c978043ff4298461eb5a26d5e63d821438627f91201924bf7f2aeee1727", ++ "666f7274792074776f" ++ },{ ++ "6. a positive test case with double null padded ciphertext", ++ NULL, ++ "", ++ "00001ec97ac981dfd9dcc7a7389fdfa9d361141dac80c23a060410d472c16094e6cdffc0c3684d84aa402d7051dfccb2f6da33f66985d2a259f5b7fbf39ac537e95c5b7050eb18844a0513abef812cc8e74a3c5240009e6e805dcadf532bc1a2702d5acc9e585fad5b89d461fcc1397351cdce35171523758b171dc041f412e42966de7f94856477356d06f2a6b40e3ff0547562a4d91bbf1338e9e049facbee8b20171164505468cd308997447d3dc4b0acb49e7d368fedd8c734251f30a83491d2506f3f87318cc118823244a393dc7c5c739a2733d93e1b13db6840a9429947357f47b23fbe39b7d2d61e5ee26f9946c4632f6c4699e452f412a26641d4751135400713cd56ec66f0370423d55d2af70f5e7ad0adea8e4a0d904a01e4ac272eba4af1a029dd53eb71f115bf31f7a6c8b19a6523adeecc0d4c3c107575e38572a8f8474ccad163e46e2e8b08111132aa97a16fb588c9b7e37b3b3d7490381f3c55d1a9869a0fd42cd86fed59ecec78cb6b2dfd06a497f5afe3419691314ba0", ++ "666f7274792074776f" ++ },{ ++ "7. a positive test case with double null truncated ciphertext", ++ NULL, ++ "", ++ "1ec97ac981dfd9dcc7a7389fdfa9d361141dac80c23a060410d472c16094e6cdffc0c3684d84aa402d7051dfccb2f6da33f66985d2a259f5b7fbf39ac537e95c5b7050eb18844a0513abef812cc8e74a3c5240009e6e805dcadf532bc1a2702d5acc9e585fad5b89d461fcc1397351cdce35171523758b171dc041f412e42966de7f94856477356d06f2a6b40e3ff0547562a4d91bbf1338e9e049facbee8b20171164505468cd308997447d3dc4b0acb49e7d368fedd8c734251f30a83491d2506f3f87318cc118823244a393dc7c5c739a2733d93e1b13db6840a9429947357f47b23fbe39b7d2d61e5ee26f9946c4632f6c4699e452f412a26641d4751135400713cd56ec66f0370423d55d2af70f5e7ad0adea8e4a0d904a01e4ac272eba4af1a029dd53eb71f115bf31f7a6c8b19a6523adeecc0d4c3c107575e38572a8f8474ccad163e46e2e8b08111132aa97a16fb588c9b7e37b3b3d7490381f3c55d1a9869a0fd42cd86fed59ecec78cb6b2dfd06a497f5afe3419691314ba0", ++ "666f7274792074776f" ++ },{ ++ "8. a random negative test case that generates a 9 byte long message", ++ NULL, ++ "", ++ "5c8555f5cef627c15d37f85c7f5fd6e499264ea4b8e3f9112023aeb722eb38d8eac2be3751fd5a3785ab7f2d59fa3728e5be8c3de78a67464e30b21ee23b5484bb3cd06d0e1c6ad25649c8518165653eb80488bfb491b20c04897a6772f69292222fc5ef50b5cf9efc6d60426a449b6c489569d48c83488df629d695653d409ce49a795447fcec2c58a1a672e4a391401d428baaf781516e11e323d302fcf20f6eab2b2dbe53a48c987e407c4d7e1cb41131329138313d330204173a4f3ff06c6fadf970f0ed1005d0b27e35c3d11693e0429e272d583e57b2c58d24315c397856b34485dcb077665592b747f889d34febf2be8fce66c265fd9fc3575a6286a5ce88b4b413a08efc57a07a8f57a999605a837b0542695c0d189e678b53662ecf7c3d37d9dbeea585eebfaf79141118e06762c2381fe27ca6288edddc19fd67cd64f16b46e06d8a59ac530f22cd83cc0bc4e37feb52015cbb2283043ccf5e78a4eb7146827d7a466b66c8a4a4826c1bad68123a7f2d00fc1736525ff90c058f56", ++ "257906ca6de8307728" ++ },{ ++ "9. a random negative test case that generates a 9 byte long message based on second to last value from PRF", ++ NULL, ++ "", ++ "758c215aa6acd61248062b88284bf43c13cb3b3d02410be4238607442f1c0216706e21a03a2c10eb624a63322d854da195c017b76fea83e274fa371834dcd2f3b7accf433fc212ad76c0bac366e1ed32e25b279f94129be7c64d6e162adc08ccebc0cfe8e926f01c33ab9c065f0e0ac83ae5137a4cb66702615ad68a35707d8676d2740d7c1a954680c83980e19778ed11eed3a7c2dbdfc461a9bbef671c1bc00c882d361d29d5f80c42bdf5efec886c34138f83369c6933b2ac4e93e764265351b4a0083f040e14f511f09b22f96566138864e4e6ff24da4810095da98e0585410951538ced2f757a277ff8e17172f06572c9024eeae503f176fd46eb6c5cd9ba07af11cde31dccac12eb3a4249a7bfd3b19797ad1656984bfcbf6f74e8f99d8f1ac420811f3d166d87f935ef15ae858cf9e72c8e2b547bf16c3fb09a8c9bf88fd2e5d38bf24ed610896131a84df76b9f920fe76d71fff938e9199f3b8cd0c11fd0201f9139d7673a871a9e7d4adc3bbe360c8813617cd60a90128fbe34c9d5", ++ "043383c929060374ed" ++ },{ ++ "10. a random negative test that generates message based on 3rd last value from PRF", ++ NULL, ++ "", ++ "7b22d5e62d287968c6622171a1f75db4b0fd15cdf3134a1895d235d56f8d8fe619f2bf4868174a91d7601a82975d2255190d28b869141d7c395f0b8c4e2be2b2c1b4ffc12ce749a6f6803d4cfe7fba0a8d6949c04151f981c0d84592aa2ff25d1bd3ce5d10cb03daca6b496c6ad40d30bfa8acdfd02cdb9326c4bdd93b949c9dc46caa8f0e5f429785bce64136a429a3695ee674b647452bea1b0c6de9c5f1e8760d5ef6d5a9cfff40457b023d3c233c1dcb323e7808103e73963b2eafc928c9eeb0ee3294955415c1ddd9a1bb7e138fecd79a3cb89c57bd2305524624814aaf0fd1acbf379f7f5b39421f12f115ba488d380586095bb53f174fae424fa4c8e3b299709cd344b9f949b1ab57f1c645d7ed3c8f81d5594197355029fee8960970ff59710dc0e5eb50ea6f4c3938e3f89ed7933023a2c2ddffaba07be147f686828bd7d520f300507ed6e71bdaee05570b27bc92741108ac2eb433f028e138dd6d63067bc206ea2d826a7f41c0d613daed020f0f30f4e272e9618e0a8c39018a83", ++ "70263fa6050534b9e0" ++ },{ ++ "11. an otherwise valid plaintext, but with wrong first byte (0x01 instead of 0x00)", ++ NULL, ++ "", ++ "6db80adb5ff0a768caf1378ecc382a694e7d1bde2eff4ba12c48aaf794ded7a994a5b2b57acec20dbec4ae385c9dd531945c0f197a5496908725fc99d88601a17d3bb0b2d38d2c1c3100f39955a4cb3dbed5a38bf900f23d91e173640e4ec655c84fdfe71fcdb12a386108fcf718c9b7af37d39703e882436224c877a2235e8344fba6c951eb7e2a4d1d1de81fb463ac1b880f6cc0e59ade05c8ce35179ecd09546731fc07b141d3d6b342a97ae747e61a9130f72d37ac5a2c30215b6cbd66c7db893810df58b4c457b4b54f34428247d584e0fa71062446210db08254fb9ead1ba1a393c724bd291f0cf1a7143f32df849051dc896d7d176fef3b57ab6dffd626d0c3044e9edb2e3d012ace202d2581df01bec7e9aa0727a6650dd373d374f0bc0f4a611f8139dfe97d63e70c6188f4df5b672e47c51d8aa567097293fbff127c75ec690b43407578b73c85451710a0cece58fd497d7f7bd36a8a92783ef7dc6265dff52aac8b70340b996508d39217f2783ce6fc91a1cc94bb2ac487b84f62", ++ "6d8d3a094ff3afff4c" ++ },{ ++ "12. an otherwise valid plaintext, but with wrong second byte (0x01 instead of 0x02)", ++ NULL, ++ "", ++ "417328c034458563079a4024817d0150340c34e25ae16dcad690623f702e5c748a6ebb3419ff48f486f83ba9df35c05efbd7f40613f0fc996c53706c30df6bba6dcd4a40825f96133f3c21638a342bd4663dffbd0073980dac47f8c1dd8e97ce1412e4f91f2a8adb1ac2b1071066efe8d718bbb88ca4a59bd61500e826f2365255a409bece0f972df97c3a55e09289ef5fa815a2353ef393fd1aecfc888d611c16aec532e5148be15ef1bf2834b8f75bb26db08b66d2baad6464f8439d1986b533813321dbb180080910f233bcc4dd784fb21871aef41be08b7bfad4ecc3b68f228cb5317ac6ec1227bc7d0e452037ba918ee1da9fdb8393ae93b1e937a8d4691a17871d5092d2384b6190a53df888f65b951b05ed4ad57fe4b0c6a47b5b22f32a7f23c1a234c9feb5d8713d949686760680da4db454f4acad972470033472b9864d63e8d23eefc87ebcf464ecf33f67fbcdd48eab38c5292586b36aef5981ed2fa07b2f9e23fc57d9eb71bfff4111c857e9fff23ceb31e72592e70c874b4936", ++ "c6ae80ffa80bc184b0" ++ },{ ++ "13. an otherwise valid plaintext, but with zero byte in first byte of padding", ++ NULL, ++ "", ++ "8542c626fe533467acffcd4e617692244c9b5a3bf0a215c5d64891ced4bf4f9591b4b2aedff9843057986d81631b0acb3704ec2180e5696e8bd15b217a0ec36d2061b0e2182faa3d1c59bd3f9086a10077a3337a3f5da503ec3753535ffd25b837a12f2541afefd0cffb0224b8f874e4bed13949e105c075ed44e287c5ae03b155e06b90ed247d2c07f1ef3323e3508cce4e4074606c54172ad74d12f8c3a47f654ad671104bf7681e5b061862747d9afd37e07d8e0e2291e01f14a95a1bb4cbb47c304ef067595a3947ee2d722067e38a0f046f43ec29cac6a8801c6e3e9a2331b1d45a7aa2c6af3205be382dd026e389614ee095665a611ab2e8dced2ee1c9d08ac9de11aef5b3803fc9a9ce8231ec87b5fed386fb92ee3db995a89307bcba844bd0a691c29ae51216e949dfc813133cb06a07265fd807bcb3377f6adb0a481d9b7f442003115895939773e6b95371c4febef29edae946fa245e7c50729e2e558cfaad773d1fd5f67b457a6d9d17a847c6fcbdb103a86f35f228cefc06cea0", ++ "a8a9301daa01bb25c7" ++ },{ ++ "14. an otherwise valid plaintext, but with zero byte in eight byte of padding", ++ NULL, ++ "", ++ "449dfa237a70a99cb0351793ec8677882021c2aa743580bf6a0ea672055cffe8303ac42855b1d1f3373aae6af09cb9074180fc963e9d1478a4f98b3b4861d3e7f0aa8560cf603711f139db77667ca14ba3a1acdedfca9ef4603d6d7eb0645bfc805304f9ad9d77d34762ce5cd84bd3ec9d35c30e3be72a1e8d355d5674a141b5530659ad64ebb6082e6f73a80832ab6388912538914654d34602f4b3b1c78589b4a5d964b2efcca1dc7004c41f6cafcb5a7159a7fc7c0398604d0edbd4c8f4f04067da6a153a05e7cbeea13b5ee412400ef7d4f3106f4798da707ec37a11286df2b7a204856d5ff773613fd1e453a7114b78e347d3e8078e1cb3276b3562486ba630bf719697e0073a123c3e60ebb5c7a1ccff4279faffa2402bc1109f8d559d6766e73591943dfcf25ba10c3762f02af85187799b8b4b135c3990793a6fd32642f1557405ba55cc7cf7336a0e967073c5fa50743f9cc5e3017c172d9898d2af83345e71b3e0c22ab791eacb6484a32ec60ebc226ec9deaee91b1a0560c2b571", ++ "6c716fe01d44398018" ++ },{ ++ "15. an otherwise valid plaintext, but with null separator missing", ++ NULL, ++ "", ++ "a7a5c99e50da48769ecb779d9abe86ef9ec8c38c6f43f17c7f2d7af608a4a1bd6cf695b47e97c191c61fb5a27318d02f495a176b9fae5a55b5d3fabd1d8aae4957e3879cb0c60f037724e11be5f30f08fc51c033731f14b44b414d11278cd3dba7e1c8bfe208d2b2bb7ec36366dacb6c88b24cd79ab394adf19dbbc21dfa5788bacbadc6a62f79cf54fd8cf585c615b5c0eb94c35aa9de25321c8ffefb8916bbaa2697cb2dd82ee98939df9b6704cee77793edd2b4947d82e00e5749664970736c59a84197bd72b5c71e36aae29cd39af6ac73a368edbc1ca792e1309f442aafcd77c992c88f8e4863149f221695cb7b0236e75b2339a02c4ea114854372c306b9412d8eedb600a31532002f2cea07b4df963a093185e4607732e46d753b540974fb5a5c3f9432df22e85bb17611370966c5522fd23f2ad3484341ba7fd8885fc8e6d379a611d13a2aca784fba2073208faad2137bf1979a0fa146c1880d4337db3274269493bab44a1bcd0681f7227ffdf589c2e925ed9d36302509d1109ba4", ++ "aa2de6cde4e2442884" ++ },{ ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ } ++ } + } + }; +diff --git a/tests/pkcs1v2.c b/tests/pkcs1v2.c +index ab070a94..a2153728 100644 +--- a/tests/pkcs1v2.c ++++ b/tests/pkcs1v2.c +@@ -82,7 +82,7 @@ data_from_hex (const char *string, size_t *r_length) + + static int + extract_cmp_data (gcry_sexp_t sexp, const char *name, const char *expected, +- const char *description) ++ const char *description, int quiet) + { + gcry_sexp_t l1; + const void *a; +@@ -96,13 +96,15 @@ extract_cmp_data (gcry_sexp_t sexp, const char *name, const char *expected, + b = data_from_hex (expected, &blen); + if (!a) + { +- info ("%s: parameter \"%s\" missing in key\n", description, name); ++ if (!quiet) ++ info ("%s: parameter \"%s\" missing in key\n", description, name); + rc = 1; + } + else if ( alen != blen || memcmp (a, b, alen) ) + { +- info ("%s: parameter \"%s\" does not match expected value\n", +- description, name); ++ if (!quiet) ++ info ("%s: parameter \"%s\" does not match expected value\n", ++ description, name); + rc = 1; + } + gcry_free (b); +@@ -192,7 +194,7 @@ check_oaep (void) + else + { + if (extract_cmp_data (ciph, "a", tbl[tno].m[mno].encr, +- tbl[tno].m[mno].desc)) ++ tbl[tno].m[mno].desc, 0)) + { + show_sexp ("encrypt result:\n", ciph); + fail ("mismatch in gcry_pk_encrypt\n"); +@@ -223,7 +225,7 @@ check_oaep (void) + else + { + if (extract_cmp_data (plain, "value", tbl[tno].m[mno].mesg, +- tbl[tno].m[mno].desc)) ++ tbl[tno].m[mno].desc, 0)) + { + show_sexp ("decrypt result:\n", plain); + fail ("mismatch in gcry_pk_decrypt\n"); +@@ -326,7 +328,7 @@ check_pss (void) + else + { + if (extract_cmp_data (sig, "s", tbl[tno].m[mno].sign, +- tbl[tno].m[mno].desc)) ++ tbl[tno].m[mno].desc, 0)) + { + show_sexp ("sign result:\n", sig); + fail ("mismatch in gcry_pk_sign\n"); +@@ -429,44 +431,89 @@ check_v15crypt (void) + size_t mesg_len, seed_len, encr_len; + gcry_sexp_t plain, ciph; + ++ if (tbl[tno].m[mno].desc == NULL) ++ break; ++ + if (verbose) + info ("running test: %s\n", tbl[tno].m[mno].desc); + +- mesg = data_from_hex (tbl[tno].m[mno].mesg, &mesg_len); +- seed = data_from_hex (tbl[tno].m[mno].seed, &seed_len); ++ if (tbl[tno].m[mno].mesg) ++ { ++ mesg = data_from_hex (tbl[tno].m[mno].mesg, &mesg_len); ++ seed = data_from_hex (tbl[tno].m[mno].seed, &seed_len); ++ ++ err = gcry_sexp_build (&plain, NULL, ++ "(data (flags pkcs1)" ++ "(value %b)(random-override %b))", ++ (int)mesg_len, mesg, ++ (int)seed_len, seed); ++ if (err) ++ die ("constructing plain data failed: %s\n", gpg_strerror (err)); ++ gcry_free (mesg); ++ gcry_free (seed); ++ ++ err = gcry_pk_encrypt (&ciph, plain, pub_key); ++ if (err) ++ { ++ show_sexp ("plain:\n", ciph); ++ fail ("gcry_pk_encrypt failed: %s\n", gpg_strerror (err)); ++ } ++ else ++ { ++ if (extract_cmp_data (ciph, "a", tbl[tno].m[mno].encr, ++ tbl[tno].m[mno].desc, 0)) ++ { ++ show_sexp ("encrypt result:\n", ciph); ++ fail ("mismatch in gcry_pk_encrypt\n"); ++ } ++ gcry_sexp_release (ciph); ++ ciph = NULL; ++ } ++ gcry_sexp_release (plain); ++ plain = NULL; ++ } + +- err = gcry_sexp_build (&plain, NULL, +- "(data (flags pkcs1)" +- "(value %b)(random-override %b))", +- (int)mesg_len, mesg, +- (int)seed_len, seed); ++ /* Now test the decryption. */ ++ encr = data_from_hex (tbl[tno].m[mno].encr, &encr_len); ++ seed = data_from_hex (tbl[tno].m[mno].seed, &seed_len); ++ ++#ifdef WITH_MARVIN_WORKAROUND ++ /* First try without implicit rejection -- this should fail for invalid inputs */ ++ err = gcry_sexp_build (&ciph, NULL, ++ "(enc-val (flags pkcs1 no-implicit-rejection)" ++ "(rsa (a %b)))", ++ (int)encr_len, encr); + if (err) +- die ("constructing plain data failed: %s\n", gpg_strerror (err)); +- gcry_free (mesg); +- gcry_free (seed); ++ die ("constructing cipher data failed: %s\n", gpg_strerror (err)); + +- err = gcry_pk_encrypt (&ciph, plain, pub_key); ++ err = gcry_pk_decrypt (&plain, ciph, sec_key); + if (err) + { +- show_sexp ("plain:\n", ciph); +- fail ("gcry_pk_encrypt failed: %s\n", gpg_strerror (err)); ++ /* If the message is not set, the padding is invalid and failure is expected */ ++ if (tbl[tno].m[mno].mesg != NULL) ++ { ++ show_sexp ("ciph:\n", ciph); ++ fail ("gcry_pk_decrypt failed: %s\n", gpg_strerror (err)); ++ } + } + else + { +- if (extract_cmp_data (ciph, "a", tbl[tno].m[mno].encr, +- tbl[tno].m[mno].desc)) ++ /* If the message is not set, it is in the synthetic field */ ++ const char *msg = tbl[tno].m[mno].mesg; ++ if (msg == NULL) ++ msg = tbl[tno].m[mno].synt; ++ if (extract_cmp_data (plain, "value", msg, ++ tbl[tno].m[mno].desc, 0)) + { +- show_sexp ("encrypt result:\n", ciph); +- fail ("mismatch in gcry_pk_encrypt\n"); ++ show_sexp ("decrypt result:\n", plain); ++ fail ("mismatch in gcry_pk_decrypt.\n"); + } +- gcry_sexp_release (ciph); +- ciph = NULL; ++ gcry_sexp_release (plain); ++ plain = NULL; + } +- gcry_sexp_release (plain); +- plain = NULL; +- +- /* Now test the decryption. */ +- encr = data_from_hex (tbl[tno].m[mno].encr, &encr_len); ++ gcry_sexp_release (ciph); ++ ciph = NULL; ++#endif /* WITH_MARVIN_WORKAROUND */ + + err = gcry_sexp_build (&ciph, NULL, + "(enc-val (flags pkcs1)" +@@ -484,15 +530,19 @@ check_v15crypt (void) + } + else + { +- if (extract_cmp_data (plain, "value", tbl[tno].m[mno].mesg, +- tbl[tno].m[mno].desc)) ++ /* If the message is not set, expect the synthetic message here */ ++ const char *msg = tbl[tno].m[mno].mesg; ++ if (msg == NULL) ++ msg = tbl[tno].m[mno].synt; ++ if (extract_cmp_data (plain, "value", msg, ++ tbl[tno].m[mno].desc, 0)) + { + show_sexp ("decrypt result:\n", plain); +- fail ("mismatch in gcry_pk_decrypt\n"); ++ fail ("mismatch in gcry_pk_decrypt. Expected %s\n", msg); + } +- gcry_sexp_release (plain); +- plain = NULL; + } ++ gcry_sexp_release (plain); ++ plain = NULL; + gcry_sexp_release (ciph); + ciph = NULL; + } +@@ -584,7 +634,7 @@ check_v15sign (void) + else + { + if (extract_cmp_data (sig, "s", tbl[tno].m[mno].sign, +- tbl[tno].m[mno].desc)) ++ tbl[tno].m[mno].desc, 0)) + { + show_sexp ("sign result:\n", sig); + fail ("mismatch in gcry_pk_sign\n"); +-- +2.46.0 + diff --git a/libgcrypt-1.6.1-mpicoder-gccopt.patch b/libgcrypt-1.6.1-mpicoder-gccopt.patch deleted file mode 100644 index 12555d3..0000000 --- a/libgcrypt-1.6.1-mpicoder-gccopt.patch +++ /dev/null @@ -1,104 +0,0 @@ -diff -up libgcrypt-1.6.1/mpi/mpicoder.c.gccopt libgcrypt-1.6.1/mpi/mpicoder.c ---- libgcrypt-1.6.1/mpi/mpicoder.c.gccopt 2014-02-28 15:37:53.983139821 +0100 -+++ libgcrypt-1.6.1/mpi/mpicoder.c 2014-02-28 15:47:35.312576387 +0100 -@@ -627,16 +627,16 @@ _gcry_mpi_print (enum gcry_mpi_format fo - extra = 1; - } - -- if (buffer && n > len) -- { -- /* The provided buffer is too short. */ -- xfree (tmp); -- return GPG_ERR_TOO_SHORT; -- } - if (buffer) - { - unsigned char *s = buffer; - -+ if (n > len) -+ { -+ /* The provided buffer is too short. */ -+ xfree (tmp); -+ return GPG_ERR_TOO_SHORT; -+ } - if (extra == 1) - *s++ = 0; - else if (extra) -@@ -654,13 +654,12 @@ _gcry_mpi_print (enum gcry_mpi_format fo - /* Note: We ignore the sign for this format. */ - /* FIXME: for performance reasons we should put this into - mpi_aprint because we can then use the buffer directly. */ -- -- if (buffer && n > len) -- return GPG_ERR_TOO_SHORT; - if (buffer) - { - unsigned char *tmp; - -+ if (n > len) -+ return GPG_ERR_TOO_SHORT; - tmp = _gcry_mpi_get_buffer (a, 0, &n, NULL); - if (!tmp) - return gpg_err_code_from_syserror (); -@@ -678,14 +677,14 @@ _gcry_mpi_print (enum gcry_mpi_format fo - if (negative) - return GPG_ERR_INV_ARG; - -- if (buffer && n+2 > len) -- return GPG_ERR_TOO_SHORT; -- - if (buffer) - { - unsigned char *tmp; - unsigned char *s = buffer; - -+ if (n+2 > len) -+ return GPG_ERR_TOO_SHORT; -+ - s[0] = nbits >> 8; - s[1] = nbits; - -@@ -724,16 +723,16 @@ _gcry_mpi_print (enum gcry_mpi_format fo - extra=1; - } - -- if (buffer && n+4 > len) -- { -- xfree(tmp); -- return GPG_ERR_TOO_SHORT; -- } -- - if (buffer) - { - unsigned char *s = buffer; - -+ if (n+4 > len) -+ { -+ xfree(tmp); -+ return GPG_ERR_TOO_SHORT; -+ } -+ - *s++ = n >> 24; - *s++ = n >> 16; - *s++ = n >> 8; -@@ -761,15 +760,15 @@ _gcry_mpi_print (enum gcry_mpi_format fo - if (!n || (*tmp & 0x80)) - extra = 2; - -- if (buffer && 2*n + extra + negative + 1 > len) -- { -- xfree(tmp); -- return GPG_ERR_TOO_SHORT; -- } - if (buffer) - { - unsigned char *s = buffer; - -+ if (2*n + extra + negative + 1 > len) -+ { -+ xfree(tmp); -+ return GPG_ERR_TOO_SHORT; -+ } - if (negative) - *s++ = '-'; - if (extra) diff --git a/libgcrypt-1.7.3-fips-cavs.patch b/libgcrypt-1.7.3-fips-cavs.patch deleted file mode 100644 index 42d028e..0000000 --- a/libgcrypt-1.7.3-fips-cavs.patch +++ /dev/null @@ -1,1311 +0,0 @@ -diff -up libgcrypt-1.7.3/tests/cavs_driver.pl.cavs libgcrypt-1.7.3/tests/cavs_driver.pl ---- libgcrypt-1.7.3/tests/cavs_driver.pl.cavs 2013-03-15 20:25:38.000000000 +0100 -+++ libgcrypt-1.7.3/tests/cavs_driver.pl 2016-11-22 17:29:06.067553077 +0100 -@@ -1,9 +1,11 @@ - #!/usr/bin/env perl - # --# $Id: cavs_driver.pl 1497 2009-01-22 14:01:29Z smueller $ -+# $Id: cavs_driver.pl 2124 2010-12-20 07:56:30Z smueller $ - # - # CAVS test driver (based on the OpenSSL driver) - # Written by: Stephan Müller -+# Werner Koch (libgcrypt interface) -+# Tomas Mraz (addition of DSA2) - # Copyright (c) atsec information security corporation - # - # Permission is hereby granted, free of charge, to any person obtaining a copy -@@ -85,13 +87,16 @@ - # T[CBC|CFB??|ECB|OFB]varkey - # T[CBC|CFB??|ECB|OFB]invperm - # T[CBC|CFB??|ECB|OFB]vartext -+# WARNING: TDES in CFB and OFB mode problems see below - # - # ANSI X9.31 RNG - # ANSI931_AES128MCT - # ANSI931_AES128VST - # --# DSA -+# DSA2 - # PQGGen -+# PQGVer -+# KeyPair - # SigGen - # SigVer - # -@@ -101,6 +106,36 @@ - # RC4PltBD - # RC4REGT - # -+# -+# TDES MCT for CFB and OFB: -+# ------------------------- -+# The inner loop cannot be handled by this script. If you want to have tests -+# for these cipher types, implement your own inner loop and add it to -+# crypto_mct. -+# -+# the value $next_source in crypto_mct is NOT set by the standard implementation -+# of this script. It would need to be set as follows for these two (code take -+# from fipsdrv.c from libgcrypt - the value input at the end will contain the -+# the value for $next_source: -+# -+# ... inner loop ... -+# ... -+# get_current_iv (hd, last_iv, blocklen); -+# ... encrypt / decrypt (input is the data to be en/decrypted and output is the -+# result of operation) ... -+# if (encrypt_mode && (cipher_mode == GCRY_CIPHER_MODE_CFB)) -+# memcpy (input, last_iv, blocklen); -+# else if (cipher_mode == GCRY_CIPHER_MODE_OFB) -+# memcpy (input, last_iv, blocklen); -+# else if (!encrypt_mode && cipher_mode == GCRY_CIPHER_MODE_CFB) -+# { -+# /* Reconstruct the output vector. */ -+# int i; -+# for (i=0; i < blocklen; i++) -+# input[i] ^= output[i]; -+# } -+# ... inner loop ends ... -+# ==> now, the value of input is to be put into $next_source - - use strict; - use warnings; -@@ -226,6 +261,8 @@ my $hmac; - # Generate the P, Q, G, Seed, counter, h (value used to generate g) values - # for DSA - # $1: modulus size -+# $2: q size -+# $3: seed (might be empty string) - # return: string with the calculated values in hex format, where each value - # is separated from the previous with a \n in the following order: - # P\n -@@ -236,6 +273,19 @@ my $hmac; - # h - my $dsa_pqggen; - -+# Generate the G value from P and Q -+# for DSA -+# $1: modulus size -+# $2: q size -+# $3: P in hex form -+# $4: Q in hex form -+# return: string with the calculated values in hex format, where each value -+# is separated from the previous with a \n in the following order: -+# P\n -+# Q\n -+# G\n -+my $dsa_ggen; -+ - # - # Generate an DSA public key from the provided parameters: - # $1: Name of file to create -@@ -255,16 +305,30 @@ my $dsa_verify; - - # generate a new DSA key with the following properties: - # PEM format --# $1 keyfile name --# return: file created, hash with keys of P, Q, G in hex format -+# $1: modulus size -+# $2: q size -+# $3 keyfile name -+# return: file created with key, string with values of P, Q, G in hex format - my $gen_dsakey; - -+# generate a new DSA private key XY parameters in domain: -+# PEM format -+# $1: P in hex form -+# $2: Q in hex form -+# $3: G in hex form -+# return: string with values of X, Y in hex format -+my $gen_dsakey_domain; -+ - # Sign a message with DSA - # $1: data to be signed in hex form - # $2: Key file in PEM format with the private key - # return: hash of digest information in hex format with Y, R, S as keys - my $dsa_sign; - -+my $rsa_keygen; -+ -+my $rsa_keygen_kat; -+ - ################################################################ - ##### OpenSSL interface functions - ################################################################ -@@ -404,6 +468,35 @@ sub libgcrypt_rsa_derive($$$$$$$$) { - } - - -+sub libgcrypt_rsa_keygen($) { -+ my $n = shift; -+ my $sexp; -+ -+ $n = sprintf ("%u", $n); -+ $sexp = "(genkey(rsa(nbits " . sprintf ("%u:%s", length($n), $n) . ")))\n"; -+ -+ return pipe_through_program($sexp, "fipsdrv rsa-keygen"); -+} -+ -+ -+sub libgcrypt_rsa_keygen_kat($$$$) { -+ my $n = shift; -+ my $e = shift; -+ my $p = shift; -+ my $q = shift; -+ my $sexp; -+ -+ $n = sprintf ("%u", $n); -+ $sexp = "(genkey(rsa(nbits " . sprintf ("%u:%s", length($n), $n) . ")" -+ . "(test-parms" -+ . "(e #$e#)" -+ . "(p #$p#)" -+ . "(q #$q#))))\n"; -+ -+ return pipe_through_program($sexp, "fipsdrv rsa-keygen-kat"); -+} -+ -+ - sub libgcrypt_rsa_sign($$$) { - my $data = shift; - my $hashalgo = shift; -@@ -500,17 +593,32 @@ sub libgcrypt_hmac($$$$) { - return pipe_through_program($msg, $program); - } - --sub libgcrypt_dsa_pqggen($) { -+sub libgcrypt_dsa_pqggen($$$) { -+ my $mod = shift; -+ my $qsize = shift; -+ my $seed = shift; -+ -+ my $program = "fipsdrv --keysize $mod --qsize $qsize dsa-pqg-gen"; -+ return pipe_through_program($seed, $program); -+} -+ -+sub libgcrypt_dsa_ggen($$$$) { - my $mod = shift; -+ my $qsize = shift; -+ my $p = shift; -+ my $q = shift; -+ my $domain = "(domain (p #$p#)(q #$q#))"; - -- my $program = "fipsdrv --keysize $mod dsa-pqg-gen"; -+ my $program = "fipsdrv --keysize $mod --qsize $qsize --key \'$domain\' dsa-g-gen"; - return pipe_through_program("", $program); - } - --sub libgcrypt_gen_dsakey($) { -+sub libgcrypt_gen_dsakey($$$) { -+ my $mod = shift; -+ my $qsize = shift; - my $file = shift; - -- my $program = "fipsdrv --keysize 1024 --key $file dsa-gen"; -+ my $program = "fipsdrv --keysize $mod --qsize $qsize --key $file dsa-gen"; - my $tmp; - my %ret; - -@@ -519,10 +627,21 @@ sub libgcrypt_gen_dsakey($) { - $tmp = pipe_through_program("", $program); - die "dsa key gen failed: file $file not created" if (! -f $file); - -- @ret{'P', 'Q', 'G', 'Seed', 'c', 'H'} = split(/\n/, $tmp); -+ @ret{'P', 'Q', 'G'} = split(/\n/, $tmp); - return %ret; - } - -+sub libgcrypt_gen_dsakey_domain($$$) { -+ my $p = shift; -+ my $q = shift; -+ my $g = shift; -+ my $domain = "(domain (p #$p#)(q #$q#)(g #$g#))"; -+ -+ my $program = "fipsdrv --key '$domain' dsa-gen-key"; -+ -+ return pipe_through_program("", $program); -+} -+ - sub libgcrypt_dsa_genpubkey($$$$$) { - my $filename = shift; - my $p = shift; -@@ -1139,7 +1258,7 @@ sub hmac_kat($$$$) { - $out .= "Tlen = $tlen\n"; - $out .= "Key = $key\n"; - $out .= "Msg = $msg\n"; -- $out .= "Mac = " . &$hmac($key, $tlen, $msg, $hashtype{$tlen}) . "\n"; -+ $out .= "Mac = " . lc(&$hmac($key, $tlen, $msg, $hashtype{$tlen})) . "\n"; - - return $out; - } -@@ -1205,7 +1324,7 @@ sub crypto_mct($$$$$$$$) { - } - my ($CO, $CI); - my $cipher_imp = &$state_cipher($cipher, $enc, $bufsize, $key1, $iv); -- $cipher_imp = &$state_cipher_des($cipher, $enc, $bufsize, $key1, $iv) if($cipher =~ /des/); -+ $cipher_imp = &$state_cipher_des($cipher, $enc, $bufsize, $key1, $iv) if($cipher =~ /des/ && defined($state_cipher_des)); - my $pid = open2($CO, $CI, $cipher_imp); - - my $calc_data = $iv; # CT[j] -@@ -1213,8 +1332,8 @@ sub crypto_mct($$$$$$$$) { - my $old_old_calc_data; # CT[j-2] - my $next_source; - -- # TDES inner loop implements logic within driver -- if ($cipher =~ /des/) { -+ # TDES inner loop implements logic within driver of libgcrypt -+ if ($cipher =~ /des/ && $opt{'I'} && $opt{'I'} eq 'libgcrypt' ) { - # Need to provide a dummy IV in case of ECB mode. - my $iv_arg = (defined($iv) && $iv ne "") - ? bin2hex($iv) -@@ -1238,6 +1357,10 @@ sub crypto_mct($$$$$$$$) { - $line = <$CO>; - } else { - for (my $j = 0; $j < $iloop; ++$j) { -+ if ($cipher =~ /des-ede3-ofb/ || -+ (!$enc && $cipher =~ /des-ede3-cfb/)) { -+ die "Implementation lacks support for TDES OFB and TDES CFB in encryption mode - the problem is that we would need to extract the IV of the last round of encryption which would be the input for the next round - see comments in this script for implementation requirements"; -+ } - $old_old_calc_data = $old_calc_data; - $old_calc_data = $calc_data; - -@@ -1429,7 +1552,7 @@ sub rsa_sigver($$$$$) { - # $7 xq2 - # $8 Xq - # return: string formatted as expected by CAVS --sub rsa_keygen($$$$$$$$) { -+sub rsa_keygen_x931($$$$$$$$) { - my $modulus = shift; - my $e = shift; - my $xp1 = shift; -@@ -1503,21 +1626,23 @@ sub rngx931($$$$) { - return $out; - } - --# DSA PQGGen test -+# DSA PQGen test - # $1 modulus size --# $2 number of rounds to perform the test -+# $2 q size -+# $3 number of rounds to perform the test - # return: string formatted as expected by CAVS --sub dsa_pqggen_driver($$) { -+sub dsa_pqgen_driver($$$) { - my $mod = shift; -+ my $qsize = shift; - my $rounds = shift; - - my $out = ""; - for(my $i=0; $i<$rounds; $i++) { -- my $ret = &$dsa_pqggen($mod); -+ my $ret = &$dsa_pqggen($mod, $qsize, ""); - my ($P, $Q, $G, $Seed, $c, $H) = split(/\n/, $ret); -- die "Return value does not contain all expected values of P, Q, G, Seed, c, H for dsa_pqggen" -- if (!defined($P) || !defined($Q) || !defined($G) || -- !defined($Seed) || !defined($c) || !defined($H)); -+ die "Return value does not contain all expected values of P, Q, Seed, c for dsa_pqggen" -+ if (!defined($P) || !defined($Q) || -+ !defined($Seed) || !defined($c)); - - # now change the counter to decimal as CAVS wants decimal - # counter value although all other is HEX -@@ -1525,15 +1650,166 @@ sub dsa_pqggen_driver($$) { - - $out .= "P = $P\n"; - $out .= "Q = $Q\n"; -- $out .= "G = $G\n"; -- $out .= "Seed = $Seed\n"; -- $out .= "c = $c\n"; -- $out .= "H = $H\n\n"; -+ $out .= "domain_parameter_seed = $Seed\n"; -+ $out .= "counter = $c\n\n"; - } - - return $out; - } - -+# DSA GGen test -+# $1 modulus size -+# $2 q size -+# $3 p in hex form -+# $4 q in hex form -+# return: string formatted as expected by CAVS -+sub dsa_ggen_driver($$$$) { -+ my $mod = shift; -+ my $qsize = shift; -+ my $p = shift; -+ my $q = shift; -+ -+ my $out = ""; -+ my $ret = &$dsa_ggen($mod, $qsize, $p, $q); -+ my ($P, $Q, $G) = split(/\n/, $ret); -+ die "Return value does not contain all expected values of P, Q, G for dsa_ggen" -+ if (!defined($P) || !defined($Q) || !defined($G)); -+ -+ $out .= "G = $G\n\n"; -+ -+ return $out; -+} -+ -+sub hexcomp($$) { -+ my $a = lc shift; -+ my $b = lc shift; -+ -+ if (length $a < length $b) { -+ my $c = $a; -+ $a = $b; -+ $b = $a; -+ } -+ -+ while (length $b < length $a) { -+ $b = "00$b"; -+ } -+ -+ return $a eq $b; -+} -+ -+# DSA PQVer test -+# $1 modulus size -+# $2 q size -+# $3 p in hex form -+# $4 q in hex form -+# $5 seed in hex form -+# $6 c decimal counter -+# return: string formatted as expected by CAVS -+sub dsa_pqver_driver($$$$$$) { -+ my $mod = shift; -+ my $qsize = shift; -+ my $p = shift; -+ my $q = shift; -+ my $seed = shift; -+ my $c = shift; -+ -+ my $out = ""; -+ my $ret = &$dsa_pqggen($mod, $qsize, $seed); -+ my ($P, $Q, $G, $seed2, $c2, $h2) = split(/\n/, $ret); -+ die "Return value does not contain all expected values of P, Q, G, seed, c for dsa_pqggen" -+ if (!defined($P) || !defined($Q) || !defined($G) || -+ !defined($seed2) || !defined($c2)); -+ -+ $c2 = hex($c2); -+ -+ $out .= "Seed = $seed\n"; -+ $out .= "c = $c\n"; -+ -+ if (hexcomp($P, $p) && hexcomp($Q, $q) && hexcomp($seed, $seed2) && $c == $c2) { -+ $out .= "Result = P\n\n"; -+ } -+ else { -+ $out .= "Result = F\n\n"; -+ } -+ return $out; -+} -+ -+# DSA PQGVer test -+# $1 modulus size -+# $2 q size -+# $3 p in hex form -+# $4 q in hex form -+# $5 g in hex form -+# $6 seed in hex form -+# $7 c decimal counter -+# $8 h in hex form -+# return: string formatted as expected by CAVS -+sub dsa_pqgver_driver($$$$$$$$) { -+ my $mod = shift; -+ my $qsize = shift; -+ my $p = shift; -+ my $q = shift; -+ my $g = shift; -+ my $seed = shift; -+ my $c = shift; -+ my $h = shift; -+ -+ my $out = ""; -+ my $ret = &$dsa_pqggen($mod, $qsize, $seed); -+ my ($P, $Q, $G, $seed2, $c2, $h2) = split(/\n/, $ret); -+ die "Return value does not contain all expected values of P, Q, G, seed, c, H for dsa_pqggen" -+ if (!defined($P) || !defined($Q) || !defined($G) || -+ !defined($seed2) || !defined($c2) || !defined($h2)); -+ -+ -+ -+ $out .= "Seed = $seed\n"; -+ $out .= "c = $c\n"; -+ $out .= "H = $h\n"; -+ -+ $c2 = hex($c2); -+ -+ if (hexcomp($P, $p) && hexcomp($Q, $q) && hexcomp($G, $g) && hexcomp($seed, $seed2) && -+ $c == $c2 && hex($h) == hex($h2)) { -+ $out .= "Result = P\n\n"; -+ } -+ else { -+ $out .= "Result = F\n\n"; -+ } -+ -+ return $out; -+} -+ -+# DSA Keypair test -+# $1 modulus size -+# $2 q size -+# $3 number of rounds to perform the test -+# return: string formatted as expected by CAVS -+sub dsa_keypair_driver($$$) { -+ my $mod = shift; -+ my $qsize = shift; -+ my $rounds = shift; -+ -+ my $out = ""; -+ my $tmpkeyfile = "dsa_siggen.tmp.$$"; -+ my %pqg = &$gen_dsakey($mod, $qsize, $tmpkeyfile); -+ $out .= "P = " . $pqg{'P'} . "\n"; -+ $out .= "Q = " . $pqg{'Q'} . "\n"; -+ $out .= "G = " . $pqg{'G'} . "\n\n"; -+ unlink($tmpkeyfile); -+ -+ for(my $i=0; $i<$rounds; $i++) { -+ my $ret = &$gen_dsakey_domain($pqg{'P'}, $pqg{'Q'}, $pqg{'G'}); -+ my ($X, $Y) = split(/\n/, $ret); -+ die "Return value does not contain all expected values of X, Y for gen_dsakey_domain" -+ if (!defined($X) || !defined($Y)); -+ -+ $out .= "X = $X\n"; -+ $out .= "Y = $Y\n\n"; -+ } -+ -+ return $out; -+} - - # DSA SigGen test - # $1: Message to be signed in hex form -@@ -1598,6 +1874,53 @@ sub dsa_sigver($$$$$$$$) { - return $out; - } - -+# RSA Keygen RPP test -+# $1 modulus size -+# $2 number of rounds to perform the test -+# return: string formatted as expected by CAVS -+sub rsa_keygen_driver($$) { -+ my $mod = shift; -+ my $rounds = shift; -+ -+ my $out = ""; -+ -+ for(my $i=0; $i<$rounds; $i++) { -+ my $ret = &$rsa_keygen($mod); -+ my ($e, $p, $q, $n, $d) = split(/\n/, $ret); -+ die "Return value does not contain all expected values of e, p, q, n, d for rsa_keygen" -+ if (!defined($e) || !defined($p) || !defined($q) || !defined($n) || !defined($d)); -+ -+ $out .= "e = $e\n"; -+ $out .= "p = $p\n"; -+ $out .= "q = $q\n"; -+ $out .= "n = $n\n"; -+ $out .= "d = $d\n\n"; -+ } -+ -+ return $out; -+} -+ -+# RSA RPP Keygen KAT test -+# $1 modulus size -+# $2 p in hex form -+# $3 q in hex form -+# return: string formatted as expected by CAVS -+sub rsa_keygen_kat_driver($$$) { -+ my $mod = shift; -+ my $p = shift; -+ my $q = shift; -+ -+ my $out = ""; -+ my $ret = &$rsa_keygen_kat($mod, $p, $q); -+ my ($Result) = split(/\n/, $ret); -+ die "Return value does not contain all expected values of Result for rsa_keygen_kat" -+ if (!defined($Result)); -+ -+ $out .= "Result = $Result\n\n"; -+ return $out; -+} -+ -+ - ############################################################## - # Parser of input file and generator of result file - # -@@ -1658,12 +1981,18 @@ sub parse($$) { - my $klen = ""; - my $tlen = ""; - my $modulus = ""; -+ my $qsize = ""; - my $capital_n = 0; -+ my $num = 0; - my $capital_p = ""; - my $capital_q = ""; - my $capital_g = ""; - my $capital_y = ""; - my $capital_r = ""; -+ my $capital_h = ""; -+ my $c = ""; -+ my $prandom = ""; -+ my $qrandom = ""; - my $xp1 = ""; - my $xp2 = ""; - my $Xp = ""; -@@ -1700,7 +2029,7 @@ sub parse($$) { - - ##### Extract cipher - # XXX there may be more - to be added -- if ($tmpline =~ /^#.*(CBC|ECB|OFB|CFB|SHA-|SigGen|SigVer|RC4VS|ANSI X9\.31|Hash sizes tested|PQGGen|KeyGen RSA)/) { -+ if ($tmpline =~ /^#.*(CBC|ECB|OFB|CFB|SHA-|SigGen|SigVer|RC4VS|ANSI X9\.31|Hash sizes tested|PQGGen|KeyGen RSA|KeyGen - Random Probably Prime|KeyPair|PQGVer)/) { - if ($tmpline =~ /CBC/) { $mode="cbc"; } - elsif ($tmpline =~ /ECB/) { $mode="ecb"; } - elsif ($tmpline =~ /OFB/) { $mode="ofb"; } -@@ -1749,7 +2078,23 @@ sub parse($$) { - - if ($tt == 0) { - ##### Identify the test type -- if ($tmpline =~ /KeyGen RSA \(X9\.31\)/) { -+ if ($tmpline =~ /KeyGen - Random Probably Prime Known Answer Test/) { -+ $tt = 19; -+ die "Interface function rsa_keygen_kat for RSA key generation KAT not defined for tested library" -+ if (!defined($rsa_keygen_kat)); -+ } elsif ($tmpline =~ /KeyGen - Random Probably Prime Test/) { -+ $tt = 18; -+ die "Interface function rsa_keygen for RSA key generation not defined for tested library" -+ if (!defined($rsa_keygen)); -+ } elsif ($tmpline =~ /PQGVer/) { -+ $tt = 16; -+ die "Interface function for DSA PQGVer testing not defined for tested library" -+ if (!defined($dsa_pqggen)); -+ } elsif ($tmpline =~ /KeyPair/) { -+ $tt = 14; -+ die "Interface function dsa_keygen for DSA key generation not defined for tested library" -+ if (!defined($gen_dsakey_domain)); -+ } elsif ($tmpline =~ /KeyGen RSA \(X9\.31\)/) { - $tt = 13; - die "Interface function rsa_derive for RSA key generation not defined for tested library" - if (!defined($rsa_derive)); -@@ -1760,11 +2105,11 @@ sub parse($$) { - } elsif ($tmpline =~ /SigGen/ && $opt{'D'}) { - $tt = 11; - die "Interface function dsa_sign or gen_dsakey for DSA sign not defined for tested library" -- if (!defined($dsa_sign) || !defined($gen_rsakey)); -+ if (!defined($dsa_sign) || !defined($gen_dsakey)); - } elsif ($tmpline =~ /PQGGen/) { - $tt = 10; - die "Interface function for DSA PQGGen testing not defined for tested library" -- if (!defined($dsa_pqggen)); -+ if (!defined($dsa_pqggen) || !defined($dsa_ggen)); - } elsif ($tmpline =~ /Hash sizes tested/) { - $tt = 9; - die "Interface function hmac for HMAC testing not defined for tested library" -@@ -1792,7 +2137,7 @@ sub parse($$) { - } elsif ($tmpline =~ /Monte|MCT|Carlo/) { - $tt = 2; - die "Interface function state_cipher for Stateful Cipher operation defined for tested library" -- if (!defined($state_cipher) || !defined($state_cipher_des)); -+ if (!defined($state_cipher) && !defined($state_cipher_des)); - } elsif ($cipher =~ /^sha/) { - $tt = 3; - die "Interface function hash for Hashing not defined for tested library" -@@ -1875,18 +2220,44 @@ sub parse($$) { - die "Msg/Seed seen twice - input file crap" if ($pt ne ""); - $pt=$2; - } -- elsif ($line =~ /^\[mod\s*=\s*(.*)\]$/) { # found in RSA requests -+ elsif ($line =~ /^\[A.2.1\s.*\]$/) { # found in DSA2 PQGGen request -+ $out .= $line . "\n"; # print it -+ if ($tt == 10) { -+ # now generate G from PQ -+ $tt = 15; -+ } -+ } -+ elsif ($line =~ /^\[A.2.2\s.*\]$/) { # found in DSA2 PQGVer request -+ $out .= $line . "\n"; # print it -+ if ($tt == 16) { -+ # now verify PQG -+ $tt = 17; -+ } -+ } -+ elsif ($line =~ /^\[mod\s*=\s*L=([0-9]*),\s*N=([0-9]*).*\]$/) { # found in DSA2 requests - $modulus = $1; -+ $qsize = $2; - $out .= $line . "\n\n"; # print it -+ # clear eventual PQG -+ $capital_p = ""; -+ $capital_q = ""; -+ $capital_g = ""; - # generate the private key with given bit length now - # as we have the required key length in bit - if ($tt == 11) { - $dsa_keyfile = "dsa_siggen.tmp.$$"; -- my %pqg = &$gen_dsakey($dsa_keyfile); -+ my %pqg = &$gen_dsakey($modulus, $qsize, $dsa_keyfile); - $out .= "P = " . $pqg{'P'} . "\n"; - $out .= "Q = " . $pqg{'Q'} . "\n"; -- $out .= "G = " . $pqg{'G'} . "\n"; -- } elsif ( $tt == 5 ) { -+ $out .= "G = " . $pqg{'G'} . "\n\n"; -+ } -+ } -+ elsif ($line =~ /^\[mod\s*=\s*(.*)\]$/) { # found in RSA requests -+ $modulus = $1; -+ $out .= $line . "\n\n"; # print it -+ # generate the private key with given bit length now -+ # as we have the required key length in bit -+ if ( $tt == 5 ) { - # XXX maybe a secure temp file name is better here - # but since it is not run on a security sensitive - # system, I hope that this is fine -@@ -1907,6 +2278,9 @@ sub parse($$) { - } - elsif ($line =~ /^e\s*=\s*(.*)/) { # found in RSA requests - $e=$1; -+ if ($tt == 19) { -+ $out .= $line . "\n"; # print it -+ } - } - elsif ($line =~ /^S\s*=\s*(.*)/) { # found in RSA requests - die "S seen twice - input file crap" if ($signature ne ""); -@@ -1932,11 +2306,16 @@ sub parse($$) { - if ($tlen ne ""); - $tlen=$1; - } -- elsif ($line =~ /^N\s*=\s*(.*)/) { #DSA PQGGen -+ elsif ($line =~ /^N\s*=\s*(.*)/) { #DSA KeyPair - die "N seen twice - check input file" - if ($capital_n); - $capital_n = $1; - } -+ elsif ($line =~ /^Num\s*=\s*(.*)/) { #DSA PQGGen -+ die "Num seen twice - check input file" -+ if ($num); -+ $num = $1; -+ } - elsif ($line =~ /^P\s*=\s*(.*)/) { #DSA SigVer - die "P seen twice - check input file" - if ($capital_p); -@@ -1965,6 +2344,16 @@ sub parse($$) { - if ($capital_r); - $capital_r = $1; - } -+ elsif ($line =~ /^H\s*=\s*(.*)/) { #DSA PQGVer -+ die "H seen twice - check input file" -+ if ($capital_h); -+ $capital_h = $1; -+ } -+ elsif ($line =~ /^c\s*=\s*(.*)/) { #DSA PQGVer -+ die "c seen twice - check input file" -+ if ($c); -+ $c = $1; -+ } - elsif ($line =~ /^xp1\s*=\s*(.*)/) { #RSA key gen - die "xp1 seen twice - check input file" - if ($xp1); -@@ -1995,6 +2384,22 @@ sub parse($$) { - if ($Xq); - $Xq = $1; - } -+ elsif ($line =~ /^prandom\s*=\s*(.*)/) { #RSA key gen KAT -+ die "prandom seen twice - check input file" -+ if ($prandom); -+ $prandom = $1; -+ $out .= $line . "\n"; # print it -+ } -+ elsif ($line =~ /^qrandom\s*=\s*(.*)/) { #RSA key gen KAT -+ die "qrandom seen twice - check input file" -+ if ($qrandom); -+ $qrandom = $1; -+ $out .= $line . "\n"; # print it -+ } -+ elsif ($tt == 19 && $line =~ /^ / && $qrandom eq "") { #RSA key gen KAT -+ $qrandom = "00"; -+ $out .= $line . "\n"; # print it -+ } - else { - $out .= $line . "\n"; - } -@@ -2074,11 +2479,10 @@ sub parse($$) { - } - } - elsif ($tt == 10) { -- if ($modulus ne "" && $capital_n > 0) { -- $out .= dsa_pqggen_driver($modulus, $capital_n); -- #$mod is not resetted -- $capital_n = 0; -- } -+ if ($modulus ne "" && $qsize ne "" && $num > 0) { -+ $out .= dsa_pqgen_driver($modulus, $qsize, $num); -+ $num = 0; -+ } - } - elsif ($tt == 11) { - if ($pt ne "" && $dsa_keyfile ne "") { -@@ -2124,7 +2528,7 @@ sub parse($$) { - $xq1 ne "" && - $xq2 ne "" && - $Xq ne "") { -- $out .= rsa_keygen($modulus, -+ $out .= rsa_keygen_x931($modulus, - $e, - $xp1, - $xp2, -@@ -2141,6 +2545,96 @@ sub parse($$) { - $Xq = ""; - } - } -+ elsif ($tt == 14) { -+ if ($modulus ne "" && -+ $qsize ne "" && -+ $capital_n > 0) { -+ $out .= dsa_keypair_driver($modulus, -+ $qsize, -+ $capital_n); -+ $capital_n = 0; -+ } -+ } -+ elsif ($tt == 15) { -+ if ($modulus ne "" && -+ $qsize ne "" && -+ $capital_p ne "" && -+ $capital_q ne "") { -+ $out .= dsa_ggen_driver($modulus, -+ $qsize, -+ $capital_p, -+ $capital_q); -+ $capital_p = ""; -+ $capital_q = ""; -+ $num--; -+ } -+ } -+ elsif ($tt == 16) { -+ if ($modulus ne "" && -+ $qsize ne "" && -+ $capital_p ne "" && -+ $capital_q ne "" && -+ $pt ne "" && -+ $c ne "") { -+ $out .= dsa_pqver_driver($modulus, -+ $qsize, -+ $capital_p, -+ $capital_q, -+ $pt, -+ $c); -+ $capital_p = ""; -+ $capital_q = ""; -+ $pt = ""; -+ $c = ""; -+ } -+ } -+ elsif ($tt == 17) { -+ if ($modulus ne "" && -+ $qsize ne "" && -+ $capital_p ne "" && -+ $capital_q ne "" && -+ $capital_g ne "" && -+ $pt ne "" && -+ $c ne "" && -+ $capital_h ne "") { -+ $out .= dsa_pqgver_driver($modulus, -+ $qsize, -+ $capital_p, -+ $capital_q, -+ $capital_g, -+ $pt, -+ $c, -+ $capital_h); -+ $capital_p = ""; -+ $capital_q = ""; -+ $capital_g = ""; -+ $pt = ""; -+ $c = ""; -+ $capital_h = ""; -+ } -+ } -+ elsif ($tt == 18) { -+ if ($modulus ne "" && -+ $capital_n > 0) { -+ $out .= rsa_keygen_driver($modulus, -+ $capital_n); -+ $capital_n = 0; -+ } -+ } -+ elsif ($tt == 19) { -+ if ($modulus ne "" && -+ $e ne "" && -+ $prandom ne "" && -+ $qrandom ne "") { -+ $out .= rsa_keygen_kat_driver($modulus, -+ $e, -+ $prandom, -+ $qrandom); -+ $prandom = ""; -+ $qrandom = ""; -+ $e = ""; -+ } -+ } - elsif ($tt > 0) { - die "Test case $tt not defined"; - } -@@ -2199,10 +2693,14 @@ sub main() { - $state_rng = \&libgcrypt_state_rng; - $hmac = \&libgcrypt_hmac; - $dsa_pqggen = \&libgcrypt_dsa_pqggen; -+ $dsa_ggen = \&libgcrypt_dsa_ggen; - $gen_dsakey = \&libgcrypt_gen_dsakey; -+ $gen_dsakey_domain = \&libgcrypt_gen_dsakey_domain; - $dsa_sign = \&libgcrypt_dsa_sign; - $dsa_verify = \&libgcrypt_dsa_verify; - $dsa_genpubkey = \&libgcrypt_dsa_genpubkey; -+ $rsa_keygen = \&libgcrypt_rsa_keygen; -+ $rsa_keygen_kat = \&libgcrypt_rsa_keygen_kat; - } else { - die "Invalid interface option given"; - } -diff -up libgcrypt-1.7.3/tests/cavs_tests.sh.cavs libgcrypt-1.7.3/tests/cavs_tests.sh ---- libgcrypt-1.7.3/tests/cavs_tests.sh.cavs 2013-03-15 20:25:38.000000000 +0100 -+++ libgcrypt-1.7.3/tests/cavs_tests.sh 2016-11-22 17:29:06.067553077 +0100 -@@ -55,7 +55,7 @@ function run_one_test () { - [ -d "$respdir" ] || mkdir "$respdir" - [ -f "$rspfile" ] && rm "$rspfile" - -- if echo "$reqfile" | grep '/DSA/req/' >/dev/null 2>/dev/null; then -+ if echo "$reqfile" | grep '/DSA.\?/req/' >/dev/null 2>/dev/null; then - dflag="-D" - fi - -diff -up libgcrypt-1.7.3/tests/fipsdrv.c.cavs libgcrypt-1.7.3/tests/fipsdrv.c ---- libgcrypt-1.7.3/tests/fipsdrv.c.cavs 2016-07-14 11:19:17.000000000 +0200 -+++ libgcrypt-1.7.3/tests/fipsdrv.c 2016-11-22 17:33:15.468330859 +0100 -@@ -892,6 +892,9 @@ print_mpi_line (gcry_mpi_t a, int no_lz) - die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err)); - - p = buf; -+ while (*p) -+ *p++ = tolower(*p); -+ p = buf; - if (no_lz && p[0] == '0' && p[1] == '0' && p[2]) - p += 2; - -@@ -1765,14 +1768,14 @@ run_rsa_verify (const void *data, size_t - /* Generate a DSA key of size KEYSIZE and return the complete - S-expression. */ - static gcry_sexp_t --dsa_gen (int keysize) -+dsa_gen (int keysize, int qsize) - { - gpg_error_t err; - gcry_sexp_t keyspec, key; - - err = gcry_sexp_build (&keyspec, NULL, -- "(genkey (dsa (nbits %d)(use-fips186-2)))", -- keysize); -+ "(genkey (dsa (nbits %d)(qbits %d)(use-fips186)))", -+ keysize, qsize); - if (err) - die ("gcry_sexp_build failed for DSA key generation: %s\n", - gpg_strerror (err)); -@@ -1790,7 +1793,7 @@ dsa_gen (int keysize) - /* Generate a DSA key of size KEYSIZE and return the complete - S-expression. */ - static gcry_sexp_t --dsa_gen_with_seed (int keysize, const void *seed, size_t seedlen) -+dsa_gen_with_seed (int keysize, int qsize, const void *seed, size_t seedlen) - { - gpg_error_t err; - gcry_sexp_t keyspec, key; -@@ -1799,10 +1802,11 @@ dsa_gen_with_seed (int keysize, const vo - "(genkey" - " (dsa" - " (nbits %d)" -- " (use-fips186-2)" -+ " (qbits %d)" -+ " (use-fips186)" - " (derive-parms" - " (seed %b))))", -- keysize, (int)seedlen, seed); -+ keysize, qsize, (int)seedlen, seed); - if (err) - die ("gcry_sexp_build failed for DSA key generation: %s\n", - gpg_strerror (err)); -@@ -1810,6 +1814,37 @@ dsa_gen_with_seed (int keysize, const vo - err = gcry_pk_genkey (&key, keyspec); - if (err) - die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err)); -+ -+ gcry_sexp_release (keyspec); -+ -+ return key; -+} -+ -+/* Generate a DSA key with specified domain parameters and return the complete -+ S-expression. */ -+static gcry_sexp_t -+dsa_gen_key (const char *domain) -+{ -+ gpg_error_t err; -+ gcry_sexp_t keyspec, key, domspec; -+ -+ err = gcry_sexp_new (&domspec, domain, strlen(domain), 0); -+ if (err) -+ die ("gcry_sexp_build failed for domain spec: %s\n", -+ gpg_strerror (err)); -+ -+ err = gcry_sexp_build (&keyspec, NULL, -+ "(genkey" -+ " (dsa" -+ " (use-fips186)" -+ " %S))", -+ domspec); -+ if (err) -+ die ("gcry_sexp_build failed for DSA key generation: %s\n", -+ gpg_strerror (err)); -+ err = gcry_pk_genkey (&key, keyspec); -+ if (err) -+ die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err)); - - gcry_sexp_release (keyspec); - -@@ -1849,7 +1884,7 @@ ecdsa_gen_key (const char *curve) - with one parameter per line in hex format using this order: p, q, - g, seed, counter, h. */ - static void --print_dsa_domain_parameters (gcry_sexp_t key) -+print_dsa_domain_parameters (gcry_sexp_t key, int print_misc) - { - gcry_sexp_t l1, l2; - gcry_mpi_t mpi; -@@ -1885,6 +1920,9 @@ print_dsa_domain_parameters (gcry_sexp_t - } - gcry_sexp_release (l1); - -+ if (!print_misc) -+ return; -+ - /* Extract the seed values. */ - l1 = gcry_sexp_find_token (key, "misc-key-info", 0); - if (!l1) -@@ -1976,38 +2014,106 @@ print_ecdsa_dq (gcry_sexp_t key) - } - - --/* Generate DSA domain parameters for a modulus size of KEYSIZE. The -+/* Print just the XY private key parameters. KEY -+ is the complete key as returned by dsa_gen. We print to stdout -+ with one parameter per line in hex format using this order: x, y. */ -+static void -+print_dsa_xy (gcry_sexp_t key) -+{ -+ gcry_sexp_t l1, l2; -+ gcry_mpi_t mpi; -+ int idx; -+ -+ l1 = gcry_sexp_find_token (key, "private-key", 0); -+ if (!l1) -+ die ("private key not found in genkey result\n"); -+ -+ l2 = gcry_sexp_find_token (l1, "dsa", 0); -+ if (!l2) -+ die ("returned private key not formed as expected\n"); -+ gcry_sexp_release (l1); -+ l1 = l2; -+ -+ /* Extract the parameters from the S-expression and print them to stdout. */ -+ for (idx=0; "xy"[idx]; idx++) -+ { -+ l2 = gcry_sexp_find_token (l1, "xy"+idx, 1); -+ if (!l2) -+ die ("no %c parameter in returned public key\n", "xy"[idx]); -+ mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); -+ if (!mpi) -+ die ("no value for %c parameter in returned private key\n","xy"[idx]); -+ gcry_sexp_release (l2); -+ if (standalone_mode) -+ printf ("%c = ", "XY"[idx]); -+ print_mpi_line (mpi, 1); -+ gcry_mpi_release (mpi); -+ } -+ -+ gcry_sexp_release (l1); -+} -+ -+ -+/* Generate DSA pq domain parameters for a modulus size of KEYSIZE. The - result is printed to stdout with one parameter per line in hex -- format and in this order: p, q, g, seed, counter, h. If SEED is -+ format and in this order: p, q, seed, counter. If SEED is - not NULL this seed value will be used for the generation. */ - static void --run_dsa_pqg_gen (int keysize, const void *seed, size_t seedlen) -+run_dsa_pqg_gen (int keysize, int qsize, const void *seed, size_t seedlen) - { - gcry_sexp_t key; - - if (seed) -- key = dsa_gen_with_seed (keysize, seed, seedlen); -+ key = dsa_gen_with_seed (keysize, qsize, seed, seedlen); - else -- key = dsa_gen (keysize); -- print_dsa_domain_parameters (key); -+ key = dsa_gen (keysize, qsize); -+ print_dsa_domain_parameters (key, 1); -+ gcry_sexp_release (key); -+} -+ -+ -+/* Generate DSA domain parameters for a modulus size of KEYSIZE. The -+ result is printed to stdout with one parameter per line in hex -+ format and in this order: p, q, g, seed, counter, h. If SEED is -+ not NULL this seed value will be used for the generation. */ -+static void -+run_dsa_g_gen (int keysize, int qsize, const char *domain) -+{ -+ gcry_sexp_t key; -+ -+ key = dsa_gen_key (domain); -+ print_dsa_domain_parameters (key, 0); -+ gcry_sexp_release (key); -+} -+ -+/* Generate a DSA key with specified domain parameters -+ and print the XY values. */ -+static void -+run_dsa_gen_key (const char *domain) -+{ -+ gcry_sexp_t key; -+ -+ key = dsa_gen_key (domain); -+ print_dsa_xy (key); -+ - gcry_sexp_release (key); - } - - - /* Generate a DSA key of size of KEYSIZE and write the private key to - FILENAME. Also write the parameters to stdout in the same way as -- run_dsa_pqg_gen. */ -+ run_dsa_g_gen. */ - static void --run_dsa_gen (int keysize, const char *filename) -+run_dsa_gen (int keysize, int qsize, const char *filename) - { - gcry_sexp_t key, private_key; - FILE *fp; - -- key = dsa_gen (keysize); -+ key = dsa_gen (keysize, qsize); - private_key = gcry_sexp_find_token (key, "private-key", 0); - if (!private_key) - die ("private key not found in genkey result\n"); -- print_dsa_domain_parameters (key); -+ print_dsa_domain_parameters (key, 1); - - fp = fopen (filename, "wb"); - if (!fp) -@@ -2020,6 +2126,53 @@ run_dsa_gen (int keysize, const char *fi - } - - -+static int -+dsa_hash_from_key(gcry_sexp_t s_key) -+{ -+ gcry_sexp_t l1, l2; -+ gcry_mpi_t q; -+ unsigned int qbits; -+ -+ l1 = gcry_sexp_find_token (s_key, "public-key", 0); -+ if (!l1) -+ { -+ l1 = gcry_sexp_find_token (s_key, "private-key", 0); -+ if (!l1) -+ die ("neither private nor public key found in the loaded key\n"); -+ } -+ -+ l2 = gcry_sexp_find_token (l1, "dsa", 0); -+ if (!l2) -+ die ("public key not formed as expected - no dsa\n"); -+ gcry_sexp_release (l1); -+ l1 = l2; -+ -+ l2 = gcry_sexp_find_token (l1, "q", 0); -+ if (!l2) -+ die ("public key not formed as expected - no q\n"); -+ gcry_sexp_release (l1); -+ l1 = l2; -+ -+ q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); -+ if (!q) -+ die ("public key not formed as expected - no mpi in q\n"); -+ qbits = gcry_mpi_get_nbits(q); -+ gcry_sexp_release(l1); -+ gcry_mpi_release(q); -+ switch(qbits) -+ { -+ case 160: -+ return GCRY_MD_SHA1; -+ case 224: -+ return GCRY_MD_SHA224; -+ case 256: -+ return GCRY_MD_SHA256; -+ default: -+ die("bad number bits (%d) of q in key\n", qbits); -+ } -+ return GCRY_MD_NONE; -+} -+ - - /* Sign DATA of length DATALEN using the key taken from the S-expression - encoded KEYFILE. */ -@@ -2029,11 +2182,16 @@ run_dsa_sign (const void *data, size_t d - { - gpg_error_t err; - gcry_sexp_t s_data, s_key, s_sig, s_tmp, s_tmp2; -- char hash[20]; -+ char hash[128]; - gcry_mpi_t tmpmpi; -+ int algo; -+ -+ s_key = read_sexp_from_file (keyfile); -+ algo = dsa_hash_from_key(s_key); - -- gcry_md_hash_buffer (GCRY_MD_SHA1, hash, data, datalen); -- err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, 20, NULL); -+ gcry_md_hash_buffer (algo, hash, data, datalen); -+ err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, -+ gcry_md_get_algo_dlen(algo), NULL); - if (!err) - { - err = gcry_sexp_build (&s_data, NULL, -@@ -2044,8 +2202,6 @@ run_dsa_sign (const void *data, size_t d - die ("gcry_sexp_build failed for DSA data input: %s\n", - gpg_strerror (err)); - -- s_key = read_sexp_from_file (keyfile); -- - err = gcry_pk_sign (&s_sig, s_data, s_key); - if (err) - { -@@ -2121,13 +2277,18 @@ run_dsa_verify (const void *data, size_t - { - gpg_error_t err; - gcry_sexp_t s_data, s_key, s_sig; -- char hash[20]; -+ char hash[128]; - gcry_mpi_t tmpmpi; -+ int algo; - -- gcry_md_hash_buffer (GCRY_MD_SHA1, hash, data, datalen); -+ s_key = read_sexp_from_file (keyfile); -+ algo = dsa_hash_from_key(s_key); -+ -+ gcry_md_hash_buffer (algo, hash, data, datalen); - /* Note that we can't simply use %b with HASH to build the - S-expression, because that might yield a negative value. */ -- err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, 20, NULL); -+ err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, -+ gcry_md_get_algo_dlen(algo), NULL); - if (!err) - { - err = gcry_sexp_build (&s_data, NULL, -@@ -2138,7 +2299,6 @@ run_dsa_verify (const void *data, size_t - die ("gcry_sexp_build failed for DSA data input: %s\n", - gpg_strerror (err)); - -- s_key = read_sexp_from_file (keyfile); - s_sig = read_sexp_from_file (sigfile); - - err = gcry_pk_verify (s_sig, s_data, s_key); -@@ -2304,7 +2464,7 @@ usage (int show_help) - "MODE:\n" - " encrypt, decrypt, digest, random, hmac-sha,\n" - " rsa-{derive,gen,sign,verify},\n" -- " dsa-{pqg-gen,gen,sign,verify}, ecdsa-{gen-key,sign,verify}\n" -+ " dsa-{pq-gen,g-gen,gen,sign,verify}, ecdsa-{gen-key,sign,verify}\n" - "OPTIONS:\n" - " --verbose Print additional information\n" - " --binary Input and output is in binary form\n" -@@ -2315,6 +2475,7 @@ usage (int show_help) - " --algo NAME Use algorithm NAME\n" - " --curve NAME Select ECC curve spec NAME\n" - " --keysize N Use a keysize of N bits\n" -+ " --qize N Use a DSA q parameter size of N bits\n" - " --signature NAME Take signature from file NAME\n" - " --chunk N Read in chunks of N bytes (implies --binary)\n" - " --pkcs1 Use PKCS#1 encoding\n" -@@ -2344,6 +2505,7 @@ main (int argc, char **argv) - const char *dt_string = NULL; - const char *algo_string = NULL; - const char *keysize_string = NULL; -+ const char *qsize_string = NULL; - const char *signature_string = NULL; - FILE *input; - void *data; -@@ -2437,6 +2599,14 @@ main (int argc, char **argv) - keysize_string = *argv; - argc--; argv++; - } -+ else if (!strcmp (*argv, "--qsize")) -+ { -+ argc--; argv++; -+ if (!argc) -+ usage (0); -+ qsize_string = *argv; -+ argc--; argv++; -+ } - else if (!strcmp (*argv, "--signature")) - { - argc--; argv++; -@@ -2792,23 +2962,49 @@ main (int argc, char **argv) - } - else if (!strcmp (mode_string, "dsa-pqg-gen")) - { -- int keysize; -+ int keysize, qsize; -+ -+ keysize = keysize_string? atoi (keysize_string) : 0; -+ if (keysize < 1024 || keysize > 3072) -+ die ("invalid keysize specified; needs to be 1024 .. 3072\n"); -+ qsize = qsize_string? atoi (qsize_string) : 0; -+ if (qsize < 160 || qsize > 256) -+ die ("invalid qsize specified; needs to be 160 .. 256\n"); -+ run_dsa_pqg_gen (keysize, qsize, datalen? data:NULL, datalen); -+ } -+ else if (!strcmp (mode_string, "dsa-g-gen")) -+ { -+ int keysize, qsize; - - keysize = keysize_string? atoi (keysize_string) : 0; - if (keysize < 1024 || keysize > 3072) - die ("invalid keysize specified; needs to be 1024 .. 3072\n"); -- run_dsa_pqg_gen (keysize, datalen? data:NULL, datalen); -+ qsize = qsize_string? atoi (qsize_string) : 0; -+ if (qsize < 160 || qsize > 256) -+ die ("invalid qsize specified; needs to be 160 .. 256\n"); -+ if (!key_string) -+ die ("option --key containing pq domain parameters is required in this mode\n"); -+ run_dsa_g_gen (keysize, qsize, key_string); -+ } -+ else if (!strcmp (mode_string, "dsa-gen-key")) -+ { -+ if (!key_string) -+ die ("option --key containing pqg domain parameters is required in this mode\n"); -+ run_dsa_gen_key (key_string); - } - else if (!strcmp (mode_string, "dsa-gen")) - { -- int keysize; -+ int keysize, qsize; - - keysize = keysize_string? atoi (keysize_string) : 0; - if (keysize < 1024 || keysize > 3072) - die ("invalid keysize specified; needs to be 1024 .. 3072\n"); -+ qsize = qsize_string? atoi (qsize_string) : 0; -+ if (qsize < 160 || qsize > 256) -+ die ("invalid qsize specified; needs to be 160 .. 256\n"); - if (!key_string) - die ("option --key is required in this mode\n"); -- run_dsa_gen (keysize, key_string); -+ run_dsa_gen (keysize, qsize, key_string); - } - else if (!strcmp (mode_string, "dsa-sign")) - { diff --git a/libgcrypt-1.8.3-fips-ctor.patch b/libgcrypt-1.8.3-fips-ctor.patch deleted file mode 100644 index a5d3467..0000000 --- a/libgcrypt-1.8.3-fips-ctor.patch +++ /dev/null @@ -1,73 +0,0 @@ -diff -up libgcrypt-1.8.3/src/global.c.fips-ctor libgcrypt-1.8.3/src/global.c ---- libgcrypt-1.8.3/src/global.c.fips-ctor 2017-11-23 19:25:58.000000000 +0100 -+++ libgcrypt-1.8.3/src/global.c 2020-04-17 16:29:59.258218015 +0200 -@@ -141,6 +141,34 @@ global_init (void) - } - - -+#ifndef FIPS_MODULE_PATH -+#define FIPS_MODULE_PATH "/etc/system-fips" -+#endif -+ -+void __attribute__ ((constructor)) _gcry_global_constructor (void) -+{ -+ int rv; -+ -+ rv = access (FIPS_MODULE_PATH, F_OK); -+ if (rv < 0 && errno != ENOENT) -+ rv = 0; -+ -+ if (!rv) -+ { -+ int no_secmem_save; -+ -+ /* it should be always 0 at this point but let's keep on the safe side */ -+ no_secmem_save = no_secure_memory; -+ no_secure_memory = 1; -+ /* force selftests */ -+ global_init(); -+ _gcry_fips_run_selftests (0); -+ if (!fips_mode()) -+ _gcry_random_close_fds (); -+ no_secure_memory = no_secmem_save; -+ } -+} -+ - /* This function is called by the macro fips_is_operational and makes - sure that the minimal initialization has been done. This is far - from a perfect solution and hides problems with an improper -@@ -671,8 +699,7 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, - - case GCRYCTL_FIPS_MODE_P: - if (fips_mode () -- && !_gcry_is_fips_mode_inactive () -- && !no_secure_memory) -+ && !_gcry_is_fips_mode_inactive ()) - rc = GPG_ERR_GENERAL; /* Used as TRUE value */ - break; - -@@ -749,9 +776,9 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, - break; - - case GCRYCTL_SET_ENFORCED_FIPS_FLAG: -- if (!_gcry_global_any_init_done) -+ if (fips_mode ()) - { -- /* Not yet initialized at all. Set the enforced fips mode flag */ -+ /* We are in FIPS mode, we can set the enforced fips mode flag. */ - _gcry_set_preferred_rng_type (0); - _gcry_set_enforced_fips_mode (); - } -diff --git a/tests/t-secmem.c b/tests/t-secmem.c -index 2b769134..1d33bbfd 100644 ---- a/tests/t-secmem.c -+++ b/tests/t-secmem.c -@@ -54,7 +54,7 @@ test_secmem (void) - - /* Allocating another 2k should fail for the default 16k pool. */ - b = gcry_malloc_secure (chunk_size*4); -- if (b) -+ if (b && !gcry_fips_mode_active ()) - fail ("allocation did not fail as expected\n"); - - for (i=0; i < DIM(a); i++) diff --git a/libgcrypt-1.8.3-fips-enttest.patch b/libgcrypt-1.8.3-fips-enttest.patch deleted file mode 100644 index 34e54bf..0000000 --- a/libgcrypt-1.8.3-fips-enttest.patch +++ /dev/null @@ -1,113 +0,0 @@ -diff -up libgcrypt-1.8.3/random/random-drbg.c.fips-enttest libgcrypt-1.8.3/random/random-drbg.c ---- libgcrypt-1.8.3/random/random-drbg.c.fips-enttest 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.3/random/random-drbg.c 2019-06-24 10:04:23.219547141 +0200 -@@ -317,6 +317,7 @@ struct drbg_state_s - gcry_cipher_hd_t ctr_handle; /* CTR mode cipher handle */ - int seeded:1; /* DRBG fully seeded? */ - int pr:1; /* Prediction resistance enabled? */ -+ int ent_primed:1; /* Previous entropy data primed? */ - /* Taken from libgcrypt ANSI X9.31 DRNG: We need to keep track of the - * process which did the initialization so that we can detect a fork. - * The volatile modifier is required so that the compiler does not -@@ -324,6 +325,7 @@ struct drbg_state_s - pid_t seed_init_pid; - const struct drbg_state_ops_s *d_ops; - const struct drbg_core_s *core; -+ unsigned char ent_hash[64]; /* Hash of previous entropy data */ - struct drbg_test_data_s *test_data; - }; - -@@ -610,11 +612,13 @@ drbg_get_entropy (drbg_state_t drbg, uns - size_t len) - { - int rc = 0; -+ unsigned char newhash[64]; - - /* Perform testing as defined in 11.3.2 */ - if (drbg->test_data && drbg->test_data->fail_seed_source) - return -1; - -+redo: - read_cb_buffer = buffer; - read_cb_size = len; - read_cb_len = 0; -@@ -634,6 +638,27 @@ drbg_get_entropy (drbg_state_t drbg, uns - #else - rc = -1; - #endif -+ -+ /* to avoid storing the actual entropy obtained for indefinite -+ time, we just store the SHA-512 hash of the entropy gathered -+ */ -+ _gcry_md_hash_buffer (GCRY_MD_SHA512, newhash, buffer, len); -+ -+ if (!drbg->ent_primed) -+ { -+ memcpy (drbg->ent_hash, newhash, sizeof (drbg->ent_hash)); -+ drbg->ent_primed = 1; -+ goto redo; -+ } -+ -+ if (memcmp (newhash, drbg->ent_hash, sizeof (drbg->ent_hash)) == 0) -+ { -+ fips_signal_error ("Entropy source failed the continuous test"); -+ return -1; /* continuous entropy test failed */ -+ } -+ -+ memcpy (drbg->ent_hash, newhash, sizeof (drbg->ent_hash)); -+ - return rc; - } - -@@ -1341,26 +1366,38 @@ drbg_seed (drbg_state_t drbg, drbg_strin - } - else - { -+ int nonce = 0; - /* Gather entropy equal to the security strength of the DRBG. - * With a derivation function, a nonce is required in addition - * to the entropy. A nonce must be at least 1/2 of the security - * strength of the DRBG in size. Thus, entropy * nonce is 3/2 - * of the strength. The consideration of a nonce is only -- * applicable during initial seeding. */ -+ * applicable during initial seeding. -+ * To avoid pulling different length of data from entropy -+ * source, we use 2 * strength for initial seeding. */ - entropylen = drbg_sec_strength (drbg->core->flags); - if (!entropylen) - return GPG_ERR_GENERAL; - if (0 == reseed) -- /* make sure we round up strength/2 in -- * case it is not divisible by 2 */ -- entropylen = ((entropylen + 1) / 2) * 3; -+ { -+ nonce = 1; -+ } - dbg (("DRBG: (re)seeding with %lu bytes of entropy\n", entropylen)); -- entropy = xcalloc_secure (1, entropylen); -+ entropy = xcalloc_secure (nonce + 1, entropylen); - if (!entropy) - return GPG_ERR_ENOMEM; - ret = drbg_get_entropy (drbg, entropy, entropylen); - if (ret) - goto out; -+ if (nonce) -+ { -+ ret = drbg_get_entropy (drbg, entropy + entropylen, entropylen); -+ if (ret) -+ goto out; -+ /* make sure we round up strength/2 in -+ * case it is not divisible by 2 */ -+ entropylen = 2 * entropylen; -+ } - drbg_string_fill (&data1, entropy, entropylen); - } - -@@ -1597,6 +1634,7 @@ drbg_instantiate (drbg_state_t drbg, - drbg->core = &drbg_cores[coreref]; - drbg->pr = pr; - drbg->seeded = 0; -+ drbg->ent_primed = 0; - if (drbg->core->flags & DRBG_HMAC) - drbg->d_ops = &drbg_hmac_ops; - else if (drbg->core->flags & DRBG_HASH_MASK) diff --git a/libgcrypt-1.8.3-md-fips-enforce.patch b/libgcrypt-1.8.3-md-fips-enforce.patch deleted file mode 100644 index 3b730a1..0000000 --- a/libgcrypt-1.8.3-md-fips-enforce.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff -up libgcrypt-1.8.3/cipher/md.c.fips-enforce libgcrypt-1.8.3/cipher/md.c ---- libgcrypt-1.8.3/cipher/md.c.fips-enforce 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.3/cipher/md.c 2020-04-17 15:07:31.364945130 +0200 -@@ -409,13 +409,10 @@ md_enable (gcry_md_hd_t hd, int algorith - } - - -- if (!err && algorithm == GCRY_MD_MD5 && fips_mode ()) -+ if (!err && !spec->flags.fips && fips_mode ()) - { -- _gcry_inactivate_fips_mode ("MD5 used"); - if (_gcry_enforced_fips_mode () ) - { -- /* We should never get to here because we do not register -- MD5 in enforced fips mode. But better throw an error. */ - err = GPG_ERR_DIGEST_ALGO; - } - } -diff --git a/tests/t-kdf.c b/tests/t-kdf.c -index 7a48e98a..48309b9a 100644 ---- a/tests/t-kdf.c -+++ b/tests/t-kdf.c -@@ -1104,6 +1104,13 @@ check_pbkdf2 (void) - GCRY_KDF_PBKDF2, tv[tvidx].hashalgo, - tv[tvidx].salt, tv[tvidx].saltlen, - tv[tvidx].c, tv[tvidx].dklen, outbuf); -+ if (gcry_fips_mode_active() && tvidx > 6) -+ { -+ if (!err) -+ fail ("pbkdf2 test %d unexpectedly passed in FIPS mode: %s\n", -+ tvidx, gpg_strerror (err)); -+ continue; -+ } - if (err) - fail ("pbkdf2 test %d failed: %s\n", tvidx, gpg_strerror (err)); - else if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) - diff --git a/libgcrypt-1.8.4-fips-keygen.patch b/libgcrypt-1.8.4-fips-keygen.patch deleted file mode 100644 index a3e241f..0000000 --- a/libgcrypt-1.8.4-fips-keygen.patch +++ /dev/null @@ -1,64 +0,0 @@ -diff -up libgcrypt-1.8.4/cipher/dsa.c.fips-keygen libgcrypt-1.8.4/cipher/dsa.c ---- libgcrypt-1.8.4/cipher/dsa.c.fips-keygen 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.4/cipher/dsa.c 2019-02-12 14:29:25.629513989 +0100 -@@ -457,13 +457,22 @@ generate_fips186 (DSA_secret_key *sk, un - &prime_q, &prime_p, - r_counter, - r_seed, r_seedlen); -- else -+ else if (!domain->p || !domain->q) - ec = _gcry_generate_fips186_3_prime (nbits, qbits, - initial_seed.seed, - initial_seed.seedlen, - &prime_q, &prime_p, - r_counter, - r_seed, r_seedlen, NULL); -+ else -+ { -+ /* Domain parameters p and q are given; use them. */ -+ prime_p = mpi_copy (domain->p); -+ prime_q = mpi_copy (domain->q); -+ gcry_assert (mpi_get_nbits (prime_p) == nbits); -+ gcry_assert (mpi_get_nbits (prime_q) == qbits); -+ ec = 0; -+ } - sexp_release (initial_seed.sexp); - if (ec) - goto leave; -@@ -855,13 +866,12 @@ dsa_generate (const gcry_sexp_t genparms - sexp_release (l1); - sexp_release (domainsexp); - -- /* Check that all domain parameters are available. */ -- if (!domain.p || !domain.q || !domain.g) -+ /* Check that p and q domain parameters are available. */ -+ if (!domain.p || !domain.q || (!domain.g && !(flags & PUBKEY_FLAG_USE_FIPS186))) - { - _gcry_mpi_release (domain.p); - _gcry_mpi_release (domain.q); - _gcry_mpi_release (domain.g); -- sexp_release (deriveparms); - return GPG_ERR_MISSING_VALUE; - } - -diff -up libgcrypt-1.8.4/cipher/rsa.c.fips-keygen libgcrypt-1.8.4/cipher/rsa.c ---- libgcrypt-1.8.4/cipher/rsa.c.fips-keygen 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.4/cipher/rsa.c 2019-02-12 14:29:25.630513971 +0100 -@@ -389,7 +389,7 @@ generate_fips (RSA_secret_key *sk, unsig - - if (nbits < 1024 || (nbits & 0x1FF)) - return GPG_ERR_INV_VALUE; -- if (_gcry_enforced_fips_mode() && nbits != 2048 && nbits != 3072) -+ if (fips_mode() && nbits < 2048) - return GPG_ERR_INV_VALUE; - - /* The random quality depends on the transient_key flag. */ -@@ -696,7 +696,7 @@ generate_x931 (RSA_secret_key *sk, unsig - - *swapped = 0; - -- if (e_value == 1) /* Alias for a secure value. */ -+ if (e_value == 1 || e_value == 0) /* Alias for a secure value. */ - e_value = 65537; - - /* Point 1 of section 4.1: k = 1024 + 256s with S >= 0 */ diff --git a/libgcrypt-1.8.4-use-poll.patch b/libgcrypt-1.8.4-use-poll.patch deleted file mode 100644 index 8032992..0000000 --- a/libgcrypt-1.8.4-use-poll.patch +++ /dev/null @@ -1,77 +0,0 @@ -diff -up libgcrypt-1.8.4/random/rndlinux.c.use-poll libgcrypt-1.8.4/random/rndlinux.c ---- libgcrypt-1.8.4/random/rndlinux.c.use-poll 2018-10-26 13:50:20.000000000 +0200 -+++ libgcrypt-1.8.4/random/rndlinux.c 2018-11-20 15:51:56.760669058 +0100 -@@ -32,6 +32,7 @@ - #include - #include - #include -+#include - #if defined(__APPLE__) && defined(__MACH__) - #include - #ifdef __MAC_10_11 -@@ -241,9 +242,8 @@ _gcry_rndlinux_gather_random (void (*add - return with something we will actually use 100ms. */ - while (length) - { -- fd_set rfds; -- struct timeval tv; - int rc; -+ struct pollfd pfd; - - /* If we have a modern operating system, we first try to use the new - * getentropy function. That call guarantees that the kernel's -@@ -300,36 +300,25 @@ _gcry_rndlinux_gather_random (void (*add - any_need_entropy = 1; - } - -- /* If the system has no limit on the number of file descriptors -- and we encounter an fd which is larger than the fd_set size, -- we don't use the select at all. The select code is only used -- to emit progress messages. A better solution would be to -- fall back to poll() if available. */ --#ifdef FD_SETSIZE -- if (fd < FD_SETSIZE) --#endif -+ pfd.fd = fd; -+ pfd.events = POLLIN; -+ -+ _gcry_pre_syscall (); -+ rc = poll(&pfd, 1, delay); -+ _gcry_post_syscall (); -+ if (!rc) - { -- FD_ZERO(&rfds); -- FD_SET(fd, &rfds); -- tv.tv_sec = delay; -- tv.tv_usec = delay? 0 : 100000; -- _gcry_pre_syscall (); -- rc = select (fd+1, &rfds, NULL, NULL, &tv); -- _gcry_post_syscall (); -- if (!rc) -- { -- any_need_entropy = 1; -- delay = 3; /* Use 3 seconds henceforth. */ -- continue; -- } -- else if( rc == -1 ) -- { -- log_error ("select() error: %s\n", strerror(errno)); -- if (!delay) -- delay = 1; /* Use 1 second if we encounter an error before -- we have ever blocked. */ -- continue; -- } -+ any_need_entropy = 1; -+ delay = 3000; /* Use 3 seconds henceforth. */ -+ continue; -+ } -+ else if( rc == -1 ) -+ { -+ log_error ("poll() error: %s\n", strerror(errno)); -+ if (!delay) -+ delay = 1000; /* Use 1 second if we encounter an error before -+ we have ever blocked. */ -+ continue; - } - - do diff --git a/libgcrypt-1.8.5-fips-module.patch b/libgcrypt-1.8.5-fips-module.patch deleted file mode 100644 index 8ffe465..0000000 --- a/libgcrypt-1.8.5-fips-module.patch +++ /dev/null @@ -1,139 +0,0 @@ -diff -up libgcrypt-1.8.5/src/fips.c.fips-module libgcrypt-1.8.5/src/fips.c ---- libgcrypt-1.8.5/src/fips.c.fips-module 2020-04-20 19:07:45.924919645 +0200 -+++ libgcrypt-1.8.5/src/fips.c 2020-04-20 19:10:33.690722470 +0200 -@@ -35,10 +35,6 @@ - #include "hmac256.h" - - --/* The name of the file used to force libgcrypt into fips mode. */ --#define FIPS_FORCE_FILE "/etc/gcrypt/fips_enabled" -- -- - /* The states of the finite state machine used in fips mode. */ - enum module_states - { -@@ -122,54 +118,6 @@ _gcry_initialize_fips_mode (int force) - goto leave; - } - -- /* For testing the system it is useful to override the system -- provided detection of the FIPS mode and force FIPS mode using a -- file. The filename is hardwired so that there won't be any -- confusion on whether /etc/gcrypt/ or /usr/local/etc/gcrypt/ is -- actually used. The file itself may be empty. */ -- if ( !access (FIPS_FORCE_FILE, F_OK) ) -- { -- gcry_assert (!_gcry_no_fips_mode_required); -- goto leave; -- } -- -- /* Checking based on /proc file properties. */ -- { -- static const char procfname[] = "/proc/sys/crypto/fips_enabled"; -- FILE *fp; -- int saved_errno; -- -- fp = fopen (procfname, "r"); -- if (fp) -- { -- char line[256]; -- -- if (fgets (line, sizeof line, fp) && atoi (line)) -- { -- /* System is in fips mode. */ -- fclose (fp); -- gcry_assert (!_gcry_no_fips_mode_required); -- goto leave; -- } -- fclose (fp); -- } -- else if ((saved_errno = errno) != ENOENT -- && saved_errno != EACCES -- && !access ("/proc/version", F_OK) ) -- { -- /* Problem reading the fips file despite that we have the proc -- file system. We better stop right away. */ -- log_info ("FATAL: error reading `%s' in libgcrypt: %s\n", -- procfname, strerror (saved_errno)); --#ifdef HAVE_SYSLOG -- syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " -- "reading `%s' failed: %s - abort", -- procfname, strerror (saved_errno)); --#endif /*HAVE_SYSLOG*/ -- abort (); -- } -- } -- - /* Fips not not requested, set flag. */ - _gcry_no_fips_mode_required = 1; - -diff -up libgcrypt-1.8.5/src/g10lib.h.fips-module libgcrypt-1.8.5/src/g10lib.h ---- libgcrypt-1.8.5/src/g10lib.h.fips-module 2020-04-20 19:07:45.918919759 +0200 -+++ libgcrypt-1.8.5/src/g10lib.h 2020-04-20 19:11:05.003125740 +0200 -@@ -422,6 +422,9 @@ gpg_err_code_t _gcry_sexp_vextract_param - - /*-- fips.c --*/ - -+/* The name of the file used to force libgcrypt into fips mode. */ -+#define FIPS_FORCE_FILE "/etc/gcrypt/fips_enabled" -+ - extern int _gcry_no_fips_mode_required; - - void _gcry_initialize_fips_mode (int force); -diff -up libgcrypt-1.8.5/src/global.c.fips-module libgcrypt-1.8.5/src/global.c ---- libgcrypt-1.8.5/src/global.c.fips-module 2020-04-20 19:07:45.919919741 +0200 -+++ libgcrypt-1.8.5/src/global.c 2020-04-20 19:07:45.950919149 +0200 -@@ -160,6 +160,53 @@ void __attribute__ ((constructor)) _gcry - rv = access (FIPS_MODULE_PATH, F_OK); - if (rv < 0 && errno != ENOENT) - rv = 0; -+ -+ /* For testing the system it is useful to override the system -+ provided detection of the FIPS mode and force FIPS mode using a -+ file. The filename is hardwired so that there won't be any -+ confusion on whether /etc/gcrypt/ or /usr/local/etc/gcrypt/ is -+ actually used. The file itself may be empty. */ -+ if ( !access (FIPS_FORCE_FILE, F_OK) ) -+ { -+ rv = 0; -+ force_fips_mode = 1; -+ } -+ -+ /* Checking based on /proc file properties. */ -+ { -+ static const char procfname[] = "/proc/sys/crypto/fips_enabled"; -+ FILE *fp; -+ int saved_errno; -+ -+ fp = fopen (procfname, "r"); -+ if (fp) -+ { -+ char line[256]; -+ -+ if (fgets (line, sizeof line, fp) && atoi (line)) -+ { -+ /* System is in fips mode. */ -+ rv = 0; -+ force_fips_mode = 1; -+ } -+ fclose (fp); -+ } -+ else if ((saved_errno = errno) != ENOENT -+ && saved_errno != EACCES -+ && !access ("/proc/version", F_OK) ) -+ { -+ /* Problem reading the fips file despite that we have the proc -+ file system. We better stop right away. */ -+ log_info ("FATAL: error reading `%s' in libgcrypt: %s\n", -+ procfname, strerror (saved_errno)); -+#ifdef HAVE_SYSLOG -+ syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " -+ "reading `%s' failed: %s - abort", -+ procfname, strerror (saved_errno)); -+#endif /*HAVE_SYSLOG*/ -+ abort (); -+ } -+ } - - if (!rv) - { diff --git a/libgcrypt-1.8.5-getrandom.patch b/libgcrypt-1.8.5-getrandom.patch deleted file mode 100644 index 1779dc1..0000000 --- a/libgcrypt-1.8.5-getrandom.patch +++ /dev/null @@ -1,277 +0,0 @@ -diff -up libgcrypt-1.8.5/random/rand-internal.h.getrandom libgcrypt-1.8.5/random/rand-internal.h ---- libgcrypt-1.8.5/random/rand-internal.h.getrandom 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.5/random/rand-internal.h 2020-04-20 14:55:34.875949624 +0200 -@@ -47,6 +47,7 @@ void _gcry_random_progress (const char * - - /*-- random-csprng.c --*/ - void _gcry_rngcsprng_initialize (int full); -+void _gcry_rngcsprng_deinit (void); - void _gcry_rngcsprng_close_fds (void); - void _gcry_rngcsprng_dump_stats (void); - void _gcry_rngcsprng_secure_alloc (void); -@@ -68,6 +69,7 @@ void _gcry_rngcsprng_fast_poll (void); - - /*-- random-drbg.c --*/ - void _gcry_rngdrbg_inititialize (int full); -+void _gcry_rngdrbg_deinit (void); - void _gcry_rngdrbg_close_fds (void); - void _gcry_rngdrbg_dump_stats (void); - int _gcry_rngdrbg_is_faked (void); -diff -up libgcrypt-1.8.5/random/random.c.getrandom libgcrypt-1.8.5/random/random.c ---- libgcrypt-1.8.5/random/random.c.getrandom 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.5/random/random.c 2020-04-20 14:55:34.876949605 +0200 -@@ -110,8 +110,8 @@ _gcry_random_read_conf (void) - unsigned int result = 0; - - fp = fopen (fname, "r"); -- if (!fp) -- return result; -+ if (!fp) /* We make only_urandom the default. */ -+ return RANDOM_CONF_ONLY_URANDOM; - - for (;;) - { -@@ -228,6 +228,22 @@ _gcry_random_initialize (int full) - } - - -+/* Deinitialize this random subsystem. */ -+void -+_gcry_random_deinit (void) -+{ -+ if (fips_mode ()) -+ _gcry_rngdrbg_deinit (); -+ else if (rng_types.standard) -+ _gcry_rngcsprng_deinit (); -+ else if (rng_types.fips) -+ _gcry_rngdrbg_deinit (); -+ else -+ _gcry_rngcsprng_deinit (); -+ /* not needed for system */ -+} -+ -+ - /* If possible close file descriptors used by the RNG. */ - void - _gcry_random_close_fds (void) -diff -up libgcrypt-1.8.5/random/random-csprng.c.getrandom libgcrypt-1.8.5/random/random-csprng.c ---- libgcrypt-1.8.5/random/random-csprng.c.getrandom 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.5/random/random-csprng.c 2020-04-20 15:04:27.182877975 +0200 -@@ -55,6 +55,10 @@ - #ifdef __MINGW32__ - #include - #endif -+#if defined(__linux__) && defined(HAVE_SYSCALL) -+# include -+# include -+#endif - #include "g10lib.h" - #include "random.h" - #include "rand-internal.h" -@@ -343,6 +347,21 @@ _gcry_rngcsprng_initialize (int full) - } - - -+void -+_gcry_rngcsprng_deinit (void) -+{ -+ lock_pool(); -+ pool_writepos = 0; -+ pool_readpos = 0; -+ pool_filled = 0; -+ pool_filled_counter = 0; -+ did_initial_extra_seeding = 0; -+ pool_balance = 0; -+ just_mixed = 0; -+ unlock_pool(); -+} -+ -+ - /* Try to close the FDs of the random gather module. This is - currently only implemented for rndlinux. */ - void -@@ -1116,6 +1135,22 @@ getfnc_gather_random (void))(void (*)(co - enum random_origins, size_t, int); - - #if USE_RNDLINUX -+#if defined(__linux__) && defined(HAVE_SYSCALL) && defined(__NR_getrandom) -+ long ret; -+ char buffer[1]; -+ -+ _gcry_pre_syscall (); -+ ret = syscall (__NR_getrandom, -+ (void*)buffer, (size_t)1, (unsigned int)GRND_NONBLOCK); -+ _gcry_post_syscall (); -+ if (ret != -1 || errno != ENOSYS) -+ { -+ fnc = _gcry_rndlinux_gather_random; -+ return fnc; -+ } -+ else -+ /* The syscall is not supported - fallback to /dev/urandom. */ -+#endif - if ( !access (NAME_OF_DEV_RANDOM, R_OK) - && !access (NAME_OF_DEV_URANDOM, R_OK)) - { -diff -up libgcrypt-1.8.5/random/random-drbg.c.getrandom libgcrypt-1.8.5/random/random-drbg.c ---- libgcrypt-1.8.5/random/random-drbg.c.getrandom 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.5/random/random-drbg.c 2020-04-20 15:02:37.782947902 +0200 -@@ -1811,6 +1811,22 @@ _gcry_rngdrbg_inititialize (int full) - } - - /* -+ * Deinitialize the DRBG invoked by the libgcrypt API -+ * It will be automatically re-initialized on next call -+ */ -+void -+_gcry_rngdrbg_deinit (void) -+{ -+ drbg_lock (); -+ if (drbg_state) -+ { -+ drbg_uninstantiate (drbg_state); -+ drbg_state = NULL; -+ } -+ drbg_unlock (); -+} -+ -+/* - * Backend handler function for GCRYCTL_DRBG_REINIT - * - * Select a different DRBG type and initialize it. -diff -up libgcrypt-1.8.5/random/random.h.getrandom libgcrypt-1.8.5/random/random.h ---- libgcrypt-1.8.5/random/random.h.getrandom 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.5/random/random.h 2020-04-20 14:55:34.877949586 +0200 -@@ -29,6 +29,7 @@ void _gcry_register_random_progress (voi - - void _gcry_set_preferred_rng_type (int type); - void _gcry_random_initialize (int full); -+void _gcry_random_deinit (void); - void _gcry_random_close_fds (void); - int _gcry_get_rng_type (int ignore_fips_mode); - void _gcry_random_dump_stats(void); -diff -up libgcrypt-1.8.5/random/rndlinux.c.getrandom libgcrypt-1.8.5/random/rndlinux.c ---- libgcrypt-1.8.5/random/rndlinux.c.getrandom 2020-04-20 15:01:50.159848963 +0200 -+++ libgcrypt-1.8.5/random/rndlinux.c 2020-04-20 16:14:21.901610921 +0200 -@@ -35,6 +35,7 @@ - #if defined(__linux__) || !defined(HAVE_GETENTROPY) - #ifdef HAVE_SYSCALL - # include -+# include - # ifdef __NR_getrandom - # define getentropy(buf,buflen) syscall (__NR_getrandom, buf, buflen, 0) - # endif -@@ -147,12 +148,12 @@ _gcry_rndlinux_gather_random (void (*add - if (!add) - { - /* Special mode to close the descriptors. */ -- if (fd_random != -1) -+ if (fd_random >= 0) - { - close (fd_random); - fd_random = -1; - } -- if (fd_urandom != -1) -+ if (fd_urandom >= 0) - { - close (fd_urandom); - fd_urandom = -1; -@@ -166,12 +167,12 @@ _gcry_rndlinux_gather_random (void (*add - apid = getpid (); - if (my_pid != apid) - { -- if (fd_random != -1) -+ if (fd_random >= 0) - { - close (fd_random); - fd_random = -1; - } -- if (fd_urandom != -1) -+ if (fd_urandom >= 0) - { - close (fd_urandom); - fd_urandom = -1; -@@ -216,7 +217,23 @@ _gcry_rndlinux_gather_random (void (*add - that we always require the device to be existent but want a more - graceful behaviour if the rarely needed close operation has been - used and the device needs to be re-opened later. */ -- if (level >= GCRY_VERY_STRONG_RANDOM && !only_urandom) -+#if defined(__linux__) && defined(HAVE_SYSCALL) && defined(__NR_getrandom) -+ if (fd_urandom != -2 && !_gcry_in_constructor ()) -+ { -+ long ret; -+ -+ _gcry_pre_syscall (); -+ ret = syscall (__NR_getrandom, -+ (void*)buffer, (size_t)1, (unsigned int)GRND_NONBLOCK); -+ _gcry_post_syscall (); -+ if (ret > -1 || errno == EAGAIN || errno == EINTR) -+ { -+ fd_urandom = -2; -+ fd_random = -2; -+ } -+ } -+#endif -+ if (level >= GCRY_VERY_STRONG_RANDOM && !only_urandom && !_gcry_in_constructor ()) - { - if (fd_random == -1) - { -@@ -270,9 +288,7 @@ _gcry_rndlinux_gather_random (void (*add - _gcry_post_syscall (); - } - while (ret == -1 && errno == EINTR); -- if (ret == -1 && errno == ENOSYS) -- ; /* getentropy is not supported - fallback to pulling from fd. */ -- else -+ if (1) - { /* getentropy is supported. Some sanity checks. */ - if (ret == -1) - log_fatal ("unexpected error from getentropy: %s\n", -diff -up libgcrypt-1.8.5/src/g10lib.h.getrandom libgcrypt-1.8.5/src/g10lib.h ---- libgcrypt-1.8.5/src/g10lib.h.getrandom 2020-04-20 15:08:16.528538580 +0200 -+++ libgcrypt-1.8.5/src/g10lib.h 2020-04-20 15:08:28.641309399 +0200 -@@ -464,6 +464,6 @@ gpg_err_code_t _gcry_fips_run_selftests - void _gcry_fips_noreturn (void); - #define fips_noreturn() (_gcry_fips_noreturn ()) - -- -+int _gcry_in_constructor (void); - - #endif /* G10LIB_H */ -diff -up libgcrypt-1.8.5/src/global.c.getrandom libgcrypt-1.8.5/src/global.c ---- libgcrypt-1.8.5/src/global.c.getrandom 2020-04-20 15:06:21.891707597 +0200 -+++ libgcrypt-1.8.5/src/global.c 2020-04-20 15:07:29.018437509 +0200 -@@ -145,10 +145,18 @@ global_init (void) - #define FIPS_MODULE_PATH "/etc/system-fips" - #endif - -+static int in_constructor = 0; -+ -+int _gcry_in_constructor(void) -+{ -+ return in_constructor; -+} -+ - void __attribute__ ((constructor)) _gcry_global_constructor (void) - { - int rv; - -+ in_constructor = 1; - rv = access (FIPS_MODULE_PATH, F_OK); - if (rv < 0 && errno != ENOENT) - rv = 0; -@@ -163,10 +171,12 @@ void __attribute__ ((constructor)) _gcry - /* force selftests */ - global_init(); - _gcry_fips_run_selftests (0); -- if (!fips_mode()) -- _gcry_random_close_fds (); -+ _gcry_random_close_fds (); -+ _gcry_random_deinit (); - no_secure_memory = no_secmem_save; - } -+ -+ in_constructor = 0; - } - - /* This function is called by the macro fips_is_operational and makes diff --git a/libgcrypt-1.8.5-intel-cet.patch b/libgcrypt-1.8.5-intel-cet.patch deleted file mode 100644 index a19d2f1..0000000 --- a/libgcrypt-1.8.5-intel-cet.patch +++ /dev/null @@ -1,34 +0,0 @@ -From b04c0a86b19856071c29d2a6285f3240c606ee7a Mon Sep 17 00:00:00 2001 -From: "H.J. Lu" -Date: Tue, 27 Apr 2021 09:08:41 -0700 -Subject: [PATCH] Always include in cipher assembly codes - -* cipher/poly1305-s390x.S: Always include . - -When Intel CET is enabled, we need to include in assembly codes -to mark Intel CET support even if it is empty. We should always include - in cipher assembly codes so that they will be marked for -Intel CET support when compiling for x86-64 and i686. - -Signed-off-by: H.J. Lu ---- - cipher/poly1305-s390x.S | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/cipher/poly1305-s390x.S b/cipher/poly1305-s390x.S -index 844245f6..28bed560 100644 ---- a/cipher/poly1305-s390x.S -+++ b/cipher/poly1305-s390x.S -@@ -18,8 +18,8 @@ - * License along with this program; if not, see . - */ - --#if defined (__s390x__) && __GNUC__ >= 4 && __ARCH__ >= 9 - #include -+#if defined (__s390x__) && __GNUC__ >= 4 && __ARCH__ >= 9 - #if defined(HAVE_GCC_INLINE_ASM_S390X) - - #include "asm-poly1305-s390x.h" --- -GitLab - diff --git a/libgcrypt-1.8.5-use-fipscheck.patch b/libgcrypt-1.8.5-use-fipscheck.patch deleted file mode 100644 index 46145d8..0000000 --- a/libgcrypt-1.8.5-use-fipscheck.patch +++ /dev/null @@ -1,77 +0,0 @@ -diff -up libgcrypt-1.8.5/src/fips.c.use-fipscheck libgcrypt-1.8.5/src/fips.c ---- libgcrypt-1.8.5/src/fips.c.use-fipscheck 2017-11-23 19:16:58.000000000 +0100 -+++ libgcrypt-1.8.5/src/fips.c 2020-04-23 10:18:36.235764741 +0200 -@@ -581,23 +581,50 @@ run_random_selftests (void) - return !!err; - } - -+#ifdef ENABLE_HMAC_BINARY_CHECK -+static int -+get_library_path(const char *libname, const char *symbolname, char *path, size_t pathlen) -+{ -+ Dl_info info; -+ void *dl, *sym; -+ int rv = -1; -+ -+ dl = dlopen(libname, RTLD_LAZY); -+ if (dl == NULL) { -+ return -1; -+ } -+ -+ sym = dlsym(dl, symbolname); -+ -+ if (sym != NULL && dladdr(sym, &info)) { -+ strncpy(path, info.dli_fname, pathlen-1); -+ path[pathlen-1] = '\0'; -+ rv = 0; -+ } -+ -+ dlclose(dl); -+ -+ return rv; -+} -+#endif -+ - /* Run an integrity check on the binary. Returns 0 on success. */ - static int - check_binary_integrity (void) - { - #ifdef ENABLE_HMAC_BINARY_CHECK - gpg_error_t err; -- Dl_info info; -+ char libpath[4096]; - unsigned char digest[32]; - int dlen; - char *fname = NULL; -- const char key[] = "What am I, a doctor or a moonshuttle conductor?"; -- -- if (!dladdr ("gcry_check_version", &info)) -+ const char key[] = "orboDeJITITejsirpADONivirpUkvarP"; -+ -+ if (get_library_path ("libgcrypt.so.20", "gcry_check_version", libpath, sizeof(libpath))) - err = gpg_error_from_syserror (); - else - { -- dlen = _gcry_hmac256_file (digest, sizeof digest, info.dli_fname, -+ dlen = _gcry_hmac256_file (digest, sizeof digest, libpath, - key, strlen (key)); - if (dlen < 0) - err = gpg_error_from_syserror (); -@@ -605,7 +632,7 @@ check_binary_integrity (void) - err = gpg_error (GPG_ERR_INTERNAL); - else - { -- fname = xtrymalloc (strlen (info.dli_fname) + 1 + 5 + 1 ); -+ fname = xtrymalloc (strlen (libpath) + 1 + 5 + 1 ); - if (!fname) - err = gpg_error_from_syserror (); - else -@@ -614,7 +641,7 @@ check_binary_integrity (void) - char *p; - - /* Prefix the basename with a dot. */ -- strcpy (fname, info.dli_fname); -+ strcpy (fname, libpath); - p = strrchr (fname, '/'); - if (p) - p++; diff --git a/libgcrypt-1.9.3-fips-tests.patch b/libgcrypt-1.9.3-fips-tests.patch deleted file mode 100644 index 1ddc64f..0000000 --- a/libgcrypt-1.9.3-fips-tests.patch +++ /dev/null @@ -1,1804 +0,0 @@ -commit 57cf83834bc00a7431378aabda692bf700e4876c -Author: Jussi Kivilinna -Date: Sat Apr 17 17:06:40 2021 +0300 - - tests/bench-slope: add ECC benchmarking - - * tests/bench-slope.c (bench_print_result_nsec_per_iteration): New. - (bench_print_header_nsec_per_iteration): New. - (kdf_bench_one, kdf_bench): Use new print helper functions. - [USE_ECC]: New ECC benchmarks. - (ecc_bench): New. - (print_help): Add 'ecc' option. - (main): Add ECC benchmarks. - -- - - Signed-off-by: Jussi Kivilinna - -diff --git a/tests/bench-slope.c b/tests/bench-slope.c -index c8647b6b..9b4a139a 100644 ---- a/tests/bench-slope.c -+++ b/tests/bench-slope.c -@@ -783,6 +783,54 @@ bench_print_result (double nsecs_per_byte) - bench_print_result_std (nsecs_per_byte); - } - -+static void -+bench_print_result_nsec_per_iteration (double nsecs_per_iteration) -+{ -+ double cycles_per_iteration; -+ char nsecpiter_buf[16]; -+ char cpiter_buf[16]; -+ char mhz_buf[16]; -+ -+ strcpy(cpiter_buf, csv_mode ? "" : "-"); -+ strcpy(mhz_buf, csv_mode ? "" : "-"); -+ -+ double_to_str (nsecpiter_buf, sizeof (nsecpiter_buf), nsecs_per_iteration); -+ -+ /* If user didn't provide CPU speed, we cannot show cycles/iter results. */ -+ if (bench_ghz > 0.0) -+ { -+ cycles_per_iteration = nsecs_per_iteration * bench_ghz; -+ double_to_str (cpiter_buf, sizeof (cpiter_buf), cycles_per_iteration); -+ double_to_str (mhz_buf, sizeof (mhz_buf), bench_ghz * 1000); -+ } -+ -+ if (csv_mode) -+ { -+ if (auto_ghz) -+ printf ("%s,%s,%s,,,,,,,,,%s,ns/iter,%s,c/iter,%s,Mhz\n", -+ current_section_name, -+ current_algo_name ? current_algo_name : "", -+ current_mode_name ? current_mode_name : "", -+ nsecpiter_buf, -+ cpiter_buf, -+ mhz_buf); -+ else -+ printf ("%s,%s,%s,,,,,,,,,%s,ns/iter,%s,c/iter\n", -+ current_section_name, -+ current_algo_name ? current_algo_name : "", -+ current_mode_name ? current_mode_name : "", -+ nsecpiter_buf, -+ cpiter_buf); -+ } -+ else -+ { -+ if (auto_ghz) -+ printf ("%14s %13s %9s\n", nsecpiter_buf, cpiter_buf, mhz_buf); -+ else -+ printf ("%14s %13s\n", nsecpiter_buf, cpiter_buf); -+ } -+} -+ - static void - bench_print_section (const char *section_name, const char *print_name) - { -@@ -819,6 +867,28 @@ bench_print_header (int algo_width, const char *algo_name) - } - } - -+static void -+bench_print_header_nsec_per_iteration (int algo_width, const char *algo_name) -+{ -+ if (csv_mode) -+ { -+ gcry_free (current_algo_name); -+ current_algo_name = gcry_xstrdup (algo_name); -+ } -+ else -+ { -+ if (algo_width < 0) -+ printf (" %-*s | ", -algo_width, algo_name); -+ else -+ printf (" %-*s | ", algo_width, algo_name); -+ -+ if (auto_ghz) -+ printf ("%14s %13s %9s\n", "nanosecs/iter", "cycles/iter", "auto Mhz"); -+ else -+ printf ("%14s %13s\n", "nanosecs/iter", "cycles/iter"); -+ } -+} -+ - static void - bench_print_algo (int algo_width, const char *algo_name) - { -@@ -1991,11 +2061,7 @@ kdf_bench_one (int algo, int subalgo) - struct bench_kdf_mode mode = { &kdf_ops }; - struct bench_obj obj = { 0 }; - double nsecs_per_iteration; -- double cycles_per_iteration; - char algo_name[32]; -- char nsecpiter_buf[16]; -- char cpiter_buf[16]; -- char mhz_buf[16]; - - mode.algo = algo; - mode.subalgo = subalgo; -@@ -2030,45 +2096,7 @@ kdf_bench_one (int algo, int subalgo) - obj.priv = &mode; - - nsecs_per_iteration = do_slope_benchmark (&obj); -- -- strcpy(cpiter_buf, csv_mode ? "" : "-"); -- strcpy(mhz_buf, csv_mode ? "" : "-"); -- -- double_to_str (nsecpiter_buf, sizeof (nsecpiter_buf), nsecs_per_iteration); -- -- /* If user didn't provide CPU speed, we cannot show cycles/iter results. */ -- if (bench_ghz > 0.0) -- { -- cycles_per_iteration = nsecs_per_iteration * bench_ghz; -- double_to_str (cpiter_buf, sizeof (cpiter_buf), cycles_per_iteration); -- double_to_str (mhz_buf, sizeof (mhz_buf), bench_ghz * 1000); -- } -- -- if (csv_mode) -- { -- if (auto_ghz) -- printf ("%s,%s,%s,,,,,,,,,%s,ns/iter,%s,c/iter,%s,Mhz\n", -- current_section_name, -- current_algo_name ? current_algo_name : "", -- current_mode_name ? current_mode_name : "", -- nsecpiter_buf, -- cpiter_buf, -- mhz_buf); -- else -- printf ("%s,%s,%s,,,,,,,,,%s,ns/iter,%s,c/iter\n", -- current_section_name, -- current_algo_name ? current_algo_name : "", -- current_mode_name ? current_mode_name : "", -- nsecpiter_buf, -- cpiter_buf); -- } -- else -- { -- if (auto_ghz) -- printf ("%14s %13s %9s\n", nsecpiter_buf, cpiter_buf, mhz_buf); -- else -- printf ("%14s %13s\n", nsecpiter_buf, cpiter_buf); -- } -+ bench_print_result_nsec_per_iteration (nsecs_per_iteration); - } - - void -@@ -2079,14 +2107,7 @@ kdf_bench (char **argv, int argc) - - bench_print_section ("kdf", "KDF"); - -- if (!csv_mode) -- { -- printf (" %-*s | ", 24, ""); -- if (auto_ghz) -- printf ("%14s %13s %9s\n", "nanosecs/iter", "cycles/iter", "auto Mhz"); -- else -- printf ("%14s %13s\n", "nanosecs/iter", "cycles/iter"); -- } -+ bench_print_header_nsec_per_iteration (24, ""); - - if (argv && argc) - { -@@ -2116,13 +2137,571 @@ kdf_bench (char **argv, int argc) - } - - -+/************************************************************ ECC benchmarks. */ -+ -+#if USE_ECC -+enum bench_ecc_algo -+{ -+ ECC_ALGO_ED25519 = 0, -+ ECC_ALGO_ED448, -+ ECC_ALGO_NIST_P192, -+ ECC_ALGO_NIST_P224, -+ ECC_ALGO_NIST_P256, -+ ECC_ALGO_NIST_P384, -+ ECC_ALGO_NIST_P521, -+ ECC_ALGO_SECP256K1, -+ __MAX_ECC_ALGO -+}; -+ -+enum bench_ecc_operation -+{ -+ ECC_OPER_MULT = 0, -+ ECC_OPER_KEYGEN, -+ ECC_OPER_SIGN, -+ ECC_OPER_VERIFY, -+ __MAX_ECC_OPER -+}; -+ -+struct bench_ecc_oper -+{ -+ enum bench_ecc_operation oper; -+ const char *name; -+ struct bench_ops *ops; -+ -+ enum bench_ecc_algo algo; -+}; -+ -+struct bench_ecc_mult_hd -+{ -+ gcry_ctx_t ec; -+ gcry_mpi_t k, x, y; -+ gcry_mpi_point_t G, Q; -+}; -+ -+struct bench_ecc_hd -+{ -+ gcry_sexp_t key_spec; -+ gcry_sexp_t data; -+ gcry_sexp_t pub_key; -+ gcry_sexp_t sec_key; -+ gcry_sexp_t sig; -+}; -+ -+ -+static const char * -+ecc_algo_name (int algo) -+{ -+ switch (algo) -+ { -+ case ECC_ALGO_ED25519: -+ return "Ed25519"; -+ case ECC_ALGO_ED448: -+ return "Ed448"; -+ case ECC_ALGO_NIST_P192: -+ return "NIST-P192"; -+ case ECC_ALGO_NIST_P224: -+ return "NIST-P224"; -+ case ECC_ALGO_NIST_P256: -+ return "NIST-P256"; -+ case ECC_ALGO_NIST_P384: -+ return "NIST-P384"; -+ case ECC_ALGO_NIST_P521: -+ return "NIST-P521"; -+ case ECC_ALGO_SECP256K1: -+ return "secp256k1"; -+ default: -+ return NULL; -+ } -+} -+ -+static const char * -+ecc_algo_curve (int algo) -+{ -+ switch (algo) -+ { -+ case ECC_ALGO_ED25519: -+ return "Ed25519"; -+ case ECC_ALGO_ED448: -+ return "Ed448"; -+ case ECC_ALGO_NIST_P192: -+ return "NIST P-192"; -+ case ECC_ALGO_NIST_P224: -+ return "NIST P-224"; -+ case ECC_ALGO_NIST_P256: -+ return "NIST P-256"; -+ case ECC_ALGO_NIST_P384: -+ return "NIST P-384"; -+ case ECC_ALGO_NIST_P521: -+ return "NIST P-521"; -+ case ECC_ALGO_SECP256K1: -+ return "secp256k1"; -+ default: -+ return NULL; -+ } -+} -+ -+static int -+ecc_nbits (int algo) -+{ -+ switch (algo) -+ { -+ case ECC_ALGO_ED25519: -+ return 255; -+ case ECC_ALGO_ED448: -+ return 448; -+ case ECC_ALGO_NIST_P192: -+ return 192; -+ case ECC_ALGO_NIST_P224: -+ return 224; -+ case ECC_ALGO_NIST_P256: -+ return 256; -+ case ECC_ALGO_NIST_P384: -+ return 384; -+ case ECC_ALGO_NIST_P521: -+ return 521; -+ case ECC_ALGO_SECP256K1: -+ return 256; -+ default: -+ return 0; -+ } -+} -+ -+static int -+ecc_map_name (const char *name) -+{ -+ int i; -+ -+ for (i = 0; i < __MAX_ECC_ALGO; i++) -+ { -+ if (strcmp(ecc_algo_name(i), name) == 0) -+ { -+ return i; -+ } -+ } -+ -+ return -1; -+} -+ -+ -+static int -+bench_ecc_mult_init (struct bench_obj *obj) -+{ -+ struct bench_ecc_oper *oper = obj->priv; -+ struct bench_ecc_mult_hd *hd; -+ int p_size = ecc_nbits (oper->algo); -+ gpg_error_t err; -+ gcry_mpi_t p; -+ -+ obj->min_bufsize = 1; -+ obj->max_bufsize = 4; -+ obj->step_size = 1; -+ obj->num_measure_repetitions = -+ num_measurement_repetitions / obj->max_bufsize; -+ -+ while (obj->num_measure_repetitions == 0) -+ { -+ if (obj->max_bufsize == 2) -+ { -+ obj->num_measure_repetitions = 2; -+ } -+ else -+ { -+ obj->max_bufsize--; -+ obj->num_measure_repetitions = -+ num_measurement_repetitions / obj->max_bufsize; -+ } -+ } -+ -+ hd = calloc (1, sizeof(*hd)); -+ if (!hd) -+ return -1; -+ -+ err = gcry_mpi_ec_new (&hd->ec, NULL, ecc_algo_curve(oper->algo)); -+ if (err) -+ { -+ fprintf (stderr, PGM ": gcry_mpi_ec_new failed: %s\n", -+ gpg_strerror (err)); -+ exit (1); -+ } -+ hd->G = gcry_mpi_ec_get_point ("g", hd->ec, 1); -+ hd->Q = gcry_mpi_point_new (0); -+ hd->x = gcry_mpi_new (0); -+ hd->y = gcry_mpi_new (0); -+ hd->k = gcry_mpi_new (p_size); -+ gcry_mpi_randomize (hd->k, p_size, GCRY_WEAK_RANDOM); -+ p = gcry_mpi_ec_get_mpi ("p", hd->ec, 1); -+ gcry_mpi_mod (hd->k, hd->k, p); -+ gcry_mpi_release (p); -+ -+ obj->hd = hd; -+ return 0; -+} -+ -+static void -+bench_ecc_mult_free (struct bench_obj *obj) -+{ -+ struct bench_ecc_mult_hd *hd = obj->hd; -+ -+ gcry_mpi_release (hd->k); -+ gcry_mpi_release (hd->y); -+ gcry_mpi_release (hd->x); -+ gcry_mpi_point_release (hd->Q); -+ gcry_mpi_point_release (hd->G); -+ gcry_ctx_release (hd->ec); -+ free (hd); -+ obj->hd = NULL; -+} -+ -+static void -+bench_ecc_mult_do_bench (struct bench_obj *obj, void *buf, size_t num_iter) -+{ -+ struct bench_ecc_mult_hd *hd = obj->hd; -+ size_t i; -+ -+ (void)buf; -+ -+ for (i = 0; i < num_iter; i++) -+ { -+ gcry_mpi_ec_mul (hd->Q, hd->k, hd->G, hd->ec); -+ if (gcry_mpi_ec_get_affine (hd->x, hd->y, hd->Q, hd->ec)) -+ { -+ fprintf (stderr, PGM ": gcry_mpi_ec_get_affine failed\n"); -+ exit (1); -+ } -+ } -+} -+ -+ -+static int -+bench_ecc_init (struct bench_obj *obj) -+{ -+ struct bench_ecc_oper *oper = obj->priv; -+ struct bench_ecc_hd *hd; -+ int p_size = ecc_nbits (oper->algo); -+ gpg_error_t err; -+ gcry_mpi_t x; -+ -+ obj->min_bufsize = 1; -+ obj->max_bufsize = 4; -+ obj->step_size = 1; -+ obj->num_measure_repetitions = -+ num_measurement_repetitions / obj->max_bufsize; -+ -+ while (obj->num_measure_repetitions == 0) -+ { -+ if (obj->max_bufsize == 2) -+ { -+ obj->num_measure_repetitions = 2; -+ } -+ else -+ { -+ obj->max_bufsize--; -+ obj->num_measure_repetitions = -+ num_measurement_repetitions / obj->max_bufsize; -+ } -+ } -+ -+ hd = calloc (1, sizeof(*hd)); -+ if (!hd) -+ return -1; -+ -+ x = gcry_mpi_new (p_size); -+ gcry_mpi_randomize (x, p_size, GCRY_WEAK_RANDOM); -+ -+ switch (oper->algo) -+ { -+ default: -+ return -1; -+ -+ case ECC_ALGO_ED25519: -+ err = gcry_sexp_build (&hd->key_spec, NULL, -+ "(genkey (ecdsa (curve \"Ed25519\")" -+ "(flags eddsa)))"); -+ if (err) -+ break; -+ err = gcry_sexp_build (&hd->data, NULL, -+ "(data (flags eddsa)(hash-algo sha512)" -+ " (value %m))", x); -+ break; -+ -+ case ECC_ALGO_ED448: -+ err = gcry_sexp_build (&hd->key_spec, NULL, -+ "(genkey (ecdsa (curve \"Ed448\")" -+ "(flags eddsa)))"); -+ if (err) -+ break; -+ err = gcry_sexp_build (&hd->data, NULL, -+ "(data (flags eddsa)(hash-algo shake256)" -+ " (value %m))", x); -+ break; -+ -+ case ECC_ALGO_NIST_P192: -+ case ECC_ALGO_NIST_P224: -+ case ECC_ALGO_NIST_P256: -+ case ECC_ALGO_NIST_P384: -+ case ECC_ALGO_NIST_P521: -+ err = gcry_sexp_build (&hd->key_spec, NULL, -+ "(genkey (ECDSA (nbits %d)))", p_size); -+ if (err) -+ break; -+ err = gcry_sexp_build (&hd->data, NULL, -+ "(data (flags raw) (value %m))", x); -+ break; -+ } -+ -+ gcry_mpi_release (x); -+ -+ if (err) -+ { -+ fprintf (stderr, PGM ": gcry_sexp_build failed: %s\n", -+ gpg_strerror (err)); -+ exit (1); -+ } -+ -+ obj->hd = hd; -+ return 0; -+} -+ -+static void -+bench_ecc_free (struct bench_obj *obj) -+{ -+ struct bench_ecc_hd *hd = obj->hd; -+ -+ gcry_sexp_release (hd->sig); -+ gcry_sexp_release (hd->pub_key); -+ gcry_sexp_release (hd->sec_key); -+ gcry_sexp_release (hd->data); -+ gcry_sexp_release (hd->key_spec); -+ free (hd); -+ obj->hd = NULL; -+} -+ -+static void -+bench_ecc_keygen (struct bench_ecc_hd *hd) -+{ -+ gcry_sexp_t key_pair; -+ gpg_error_t err; -+ -+ err = gcry_pk_genkey (&key_pair, hd->key_spec); -+ if (err) -+ { -+ fprintf (stderr, PGM ": gcry_pk_genkey failed: %s\n", -+ gpg_strerror (err)); -+ exit (1); -+ } -+ -+ hd->pub_key = gcry_sexp_find_token (key_pair, "public-key", 0); -+ if (!hd->pub_key) -+ { -+ fprintf (stderr, PGM ": public part missing in key\n"); -+ exit (1); -+ } -+ hd->sec_key = gcry_sexp_find_token (key_pair, "private-key", 0); -+ if (!hd->sec_key) -+ { -+ fprintf (stderr, PGM ": private part missing in key\n"); -+ exit (1); -+ } -+ -+ gcry_sexp_release (key_pair); -+} -+ -+static void -+bench_ecc_keygen_do_bench (struct bench_obj *obj, void *buf, size_t num_iter) -+{ -+ struct bench_ecc_hd *hd = obj->hd; -+ size_t i; -+ -+ (void)buf; -+ -+ for (i = 0; i < num_iter; i++) -+ { -+ bench_ecc_keygen (hd); -+ gcry_sexp_release (hd->pub_key); -+ gcry_sexp_release (hd->sec_key); -+ } -+ -+ hd->pub_key = NULL; -+ hd->sec_key = NULL; -+} -+ -+static void -+bench_ecc_sign_do_bench (struct bench_obj *obj, void *buf, size_t num_iter) -+{ -+ struct bench_ecc_hd *hd = obj->hd; -+ gpg_error_t err; -+ size_t i; -+ -+ (void)buf; -+ -+ bench_ecc_keygen (hd); -+ -+ for (i = 0; i < num_iter; i++) -+ { -+ err = gcry_pk_sign (&hd->sig, hd->data, hd->sec_key); -+ if (err) -+ { -+ fprintf (stderr, PGM ": gcry_pk_sign failed: %s\n", -+ gpg_strerror (err)); -+ exit (1); -+ } -+ gcry_sexp_release (hd->sig); -+ } -+ -+ gcry_sexp_release (hd->pub_key); -+ gcry_sexp_release (hd->sec_key); -+ hd->sig = NULL; -+ hd->pub_key = NULL; -+ hd->sec_key = NULL; -+} -+ -+static void -+bench_ecc_verify_do_bench (struct bench_obj *obj, void *buf, size_t num_iter) -+{ -+ struct bench_ecc_hd *hd = obj->hd; -+ gpg_error_t err; -+ int i; -+ -+ (void)buf; -+ -+ bench_ecc_keygen (hd); -+ err = gcry_pk_sign (&hd->sig, hd->data, hd->sec_key); -+ if (err) -+ { -+ fprintf (stderr, PGM ": gcry_pk_sign failed: %s\n", -+ gpg_strerror (err)); -+ exit (1); -+ } -+ -+ for (i = 0; i < num_iter; i++) -+ { -+ err = gcry_pk_verify (hd->sig, hd->data, hd->pub_key); -+ if (err) -+ { -+ fprintf (stderr, PGM ": gcry_pk_verify failed: %s\n", -+ gpg_strerror (err)); -+ exit (1); -+ } -+ } -+ -+ gcry_sexp_release (hd->sig); -+ gcry_sexp_release (hd->pub_key); -+ gcry_sexp_release (hd->sec_key); -+ hd->sig = NULL; -+ hd->pub_key = NULL; -+ hd->sec_key = NULL; -+} -+ -+ -+static struct bench_ops ecc_mult_ops = { -+ &bench_ecc_mult_init, -+ &bench_ecc_mult_free, -+ &bench_ecc_mult_do_bench -+}; -+ -+static struct bench_ops ecc_keygen_ops = { -+ &bench_ecc_init, -+ &bench_ecc_free, -+ &bench_ecc_keygen_do_bench -+}; -+ -+static struct bench_ops ecc_sign_ops = { -+ &bench_ecc_init, -+ &bench_ecc_free, -+ &bench_ecc_sign_do_bench -+}; -+ -+static struct bench_ops ecc_verify_ops = { -+ &bench_ecc_init, -+ &bench_ecc_free, -+ &bench_ecc_verify_do_bench -+}; -+ -+ -+static struct bench_ecc_oper ecc_operations[] = { -+ { ECC_OPER_MULT, "mult", &ecc_mult_ops }, -+ { ECC_OPER_KEYGEN, "keygen", &ecc_keygen_ops }, -+ { ECC_OPER_SIGN, "sign", &ecc_sign_ops }, -+ { ECC_OPER_VERIFY, "verify", &ecc_verify_ops }, -+ { 0, NULL, NULL } -+}; -+ -+ -+static void -+cipher_ecc_one (enum bench_ecc_algo algo, struct bench_ecc_oper *poper) -+{ -+ struct bench_ecc_oper oper = *poper; -+ struct bench_obj obj = { 0 }; -+ double result; -+ -+ if (algo == ECC_ALGO_SECP256K1 && oper.oper != ECC_OPER_MULT) -+ return; -+ -+ oper.algo = algo; -+ -+ bench_print_mode (14, oper.name); -+ -+ obj.ops = oper.ops; -+ obj.priv = &oper; -+ -+ result = do_slope_benchmark (&obj); -+ bench_print_result_nsec_per_iteration (result); -+} -+ -+ -+static void -+_ecc_bench (int algo) -+{ -+ const char *algo_name; -+ int i; -+ -+ algo_name = ecc_algo_name (algo); -+ -+ bench_print_header_nsec_per_iteration (14, algo_name); -+ -+ for (i = 0; ecc_operations[i].name; i++) -+ cipher_ecc_one (algo, &ecc_operations[i]); -+ -+ bench_print_footer (14); -+} -+#endif -+ -+ -+void -+ecc_bench (char **argv, int argc) -+{ -+#if USE_ECC -+ int i, algo; -+ -+ bench_print_section ("ecc", "ECC"); -+ -+ if (argv && argc) -+ { -+ for (i = 0; i < argc; i++) -+ { -+ algo = ecc_map_name (argv[i]); -+ if (algo >= 0) -+ _ecc_bench (algo); -+ } -+ } -+ else -+ { -+ for (i = 0; i < __MAX_ECC_ALGO; i++) -+ _ecc_bench (i); -+ } -+#else -+ (void)argv; -+ (void)argc; -+#endif -+} -+ - /************************************************************** Main program. */ - - void - print_help (void) - { - static const char *help_lines[] = { -- "usage: bench-slope [options] [hash|mac|cipher|kdf [algonames]]", -+ "usage: bench-slope [options] [hash|mac|cipher|kdf|ecc [algonames]]", - "", - " options:", - " --cpu-mhz Set CPU speed for calculating cycles", -@@ -2304,6 +2883,7 @@ main (int argc, char **argv) - mac_bench (NULL, 0); - cipher_bench (NULL, 0); - kdf_bench (NULL, 0); -+ ecc_bench (NULL, 0); - } - else if (!strcmp (*argv, "hash")) - { -@@ -2337,6 +2917,14 @@ main (int argc, char **argv) - warm_up_cpu (); - kdf_bench ((argc == 0) ? NULL : argv, argc); - } -+ else if (!strcmp (*argv, "ecc")) -+ { -+ argc--; -+ argv++; -+ -+ warm_up_cpu (); -+ ecc_bench ((argc == 0) ? NULL : argv, argc); -+ } - else - { - fprintf (stderr, PGM ": unknown argument: %s\n", *argv); - -commit 9911069388e762d2a85bc2a11ca6cfcc4c895e4f -Author: Jussi Kivilinna -Date: Wed Jun 16 20:27:01 2021 +0300 - - bench-slope: add X25519 and X448 scalar multiplication - - * tests/bench-slope.c (ECC_ALGO_X25519, ECC_ALGO_X448): New. - (ecc_algo_name, ecc_algo_curve, ecc_nbits): Add X25519 and X448. - (bench_ecc_mult_do_bench): Pass Y as NULL to ec_get_affine with - X25519 and X448. - (cipher_ecc_one): Run only multiplication bench for X25519 and X448. - -- - - Signed-off-by: Jussi Kivilinna - -diff --git a/tests/bench-slope.c b/tests/bench-slope.c -index 9b4a139a..35272094 100644 ---- a/tests/bench-slope.c -+++ b/tests/bench-slope.c -@@ -2144,6 +2144,8 @@ enum bench_ecc_algo - { - ECC_ALGO_ED25519 = 0, - ECC_ALGO_ED448, -+ ECC_ALGO_X25519, -+ ECC_ALGO_X448, - ECC_ALGO_NIST_P192, - ECC_ALGO_NIST_P224, - ECC_ALGO_NIST_P256, -@@ -2197,6 +2199,10 @@ ecc_algo_name (int algo) - return "Ed25519"; - case ECC_ALGO_ED448: - return "Ed448"; -+ case ECC_ALGO_X25519: -+ return "X25519"; -+ case ECC_ALGO_X448: -+ return "X448"; - case ECC_ALGO_NIST_P192: - return "NIST-P192"; - case ECC_ALGO_NIST_P224: -@@ -2223,6 +2229,10 @@ ecc_algo_curve (int algo) - return "Ed25519"; - case ECC_ALGO_ED448: - return "Ed448"; -+ case ECC_ALGO_X25519: -+ return "Curve25519"; -+ case ECC_ALGO_X448: -+ return "X448"; - case ECC_ALGO_NIST_P192: - return "NIST P-192"; - case ECC_ALGO_NIST_P224: -@@ -2249,6 +2259,10 @@ ecc_nbits (int algo) - return 255; - case ECC_ALGO_ED448: - return 448; -+ case ECC_ALGO_X25519: -+ return 255; -+ case ECC_ALGO_X448: -+ return 448; - case ECC_ALGO_NIST_P192: - return 192; - case ECC_ALGO_NIST_P224: -@@ -2355,15 +2369,26 @@ bench_ecc_mult_free (struct bench_obj *obj) - static void - bench_ecc_mult_do_bench (struct bench_obj *obj, void *buf, size_t num_iter) - { -+ struct bench_ecc_oper *oper = obj->priv; - struct bench_ecc_mult_hd *hd = obj->hd; -+ gcry_mpi_t y; - size_t i; - - (void)buf; - -+ if (oper->algo == ECC_ALGO_X25519 || oper->algo == ECC_ALGO_X448) -+ { -+ y = NULL; -+ } -+ else -+ { -+ y = hd->y; -+ } -+ - for (i = 0; i < num_iter; i++) - { - gcry_mpi_ec_mul (hd->Q, hd->k, hd->G, hd->ec); -- if (gcry_mpi_ec_get_affine (hd->x, hd->y, hd->Q, hd->ec)) -+ if (gcry_mpi_ec_get_affine (hd->x, y, hd->Q, hd->ec)) - { - fprintf (stderr, PGM ": gcry_mpi_ec_get_affine failed\n"); - exit (1); -@@ -2634,7 +2659,8 @@ cipher_ecc_one (enum bench_ecc_algo algo, struct bench_ecc_oper *poper) - struct bench_obj obj = { 0 }; - double result; - -- if (algo == ECC_ALGO_SECP256K1 && oper.oper != ECC_OPER_MULT) -+ if ((algo == ECC_ALGO_X25519 || algo == ECC_ALGO_X448 || -+ algo == ECC_ALGO_SECP256K1) && oper.oper != ECC_OPER_MULT) - return; - - oper.algo = algo; - - -From bd49a4fa915b8a078c341b35f5a51fcb2a004c35 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Mon, 12 Jul 2021 21:35:18 +0200 -Subject: [PATCH 1/8] mac: Disable AES GMAC in FIPS mode - -* cipher/mac-gmac.c (_gcry_mac_type_spec_gmac_aes): Remove FIPS flag --- -The GMAC does not work in FIPS mode until libgcrypt will have internal -IV generator. - -Signed-off-by: Jakub Jelen ---- - cipher/mac-gmac.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/cipher/mac-gmac.c b/cipher/mac-gmac.c -index e04c6d1e..1d59d085 100644 ---- a/cipher/mac-gmac.c -+++ b/cipher/mac-gmac.c -@@ -157,7 +157,7 @@ static gcry_mac_spec_ops_t gmac_ops = { - - #if USE_AES - gcry_mac_spec_t _gcry_mac_type_spec_gmac_aes = { -- GCRY_MAC_GMAC_AES, {0, 1}, "GMAC_AES", -+ GCRY_MAC_GMAC_AES, {0, 0}, "GMAC_AES", - &gmac_ops - }; - #endif --- -2.31.1 - - -From fd0e1c3e7f6062579869d345875d6ef823706e56 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Mon, 12 Jul 2021 21:36:34 +0200 -Subject: [PATCH 2/8] tests: Do not expect GCM work in FIPS - -* tests/basic.c (check_one_cipher_core): Expect GCM ciphers to fail in - FIPS mode - (check_cipher_modes): Skip GCM ciphers tests as they fail quite late - in gcry_cipher_gettag(). --- - -Signed-off-by: Jakub Jelen ---- - tests/basic.c | 14 +++++++++++++- - 1 file changed, 13 insertions(+), 1 deletion(-) - -diff --git a/tests/basic.c b/tests/basic.c -index 0572c46e..d7b7f496 100644 ---- a/tests/basic.c -+++ b/tests/basic.c -@@ -9510,6 +9510,13 @@ check_one_cipher_core (int algo, int mode, int flags, - - clutter_vector_registers(); - err = gcry_cipher_encrypt (hd, out, nplain, plain, nplain); -+ if (in_fips_mode && mode == GCRY_CIPHER_MODE_GCM) -+ { -+ if (!err) -+ fail ("pass %d, algo %d, mode %d, gcry_cipher_encrypt is expected to " -+ "fail in FIPS mode: %s\n", pass, algo, mode, gpg_strerror (err)); -+ goto err_out_free; -+ } - if (err) - { - fail ("pass %d, algo %d, mode %d, gcry_cipher_encrypt failed: %s\n", -@@ -10257,7 +10264,12 @@ check_cipher_modes(void) - check_cfb_cipher (); - check_ofb_cipher (); - check_ccm_cipher (); -- check_gcm_cipher (); -+ if (!in_fips_mode) -+ { -+ /* This will have to go through modifications as the GCM fails in fips -+ * as late as in gcry_cipher_gettag, but we want to allow it in the end */ -+ check_gcm_cipher (); -+ } - check_poly1305_cipher (); - check_ocb_cipher (); - check_xts_cipher (); --- -2.31.1 - - -From ccbb1694693080822858e2065cf88ab43ec03fbc Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 13 Jul 2021 09:18:52 +0200 -Subject: [PATCH 3/8] tests: Skip unsupported mechanisms in FIPS mode - -* tests/bench-slope.c (cipher_bench_one): Skip GCM mode in FIPS mode - (ecc_algo_fips_allowed): New function - (_ecc_bench): Skip algorithms disabled in FIPS mode - (main): Check for FIPS mode -* tests/benchmark.c (cipher_bench): Skip GCM in FIPS mode --- -In FIPS mode, not all the curves are allowed. This is already checked in -other parts of the code base, but not in the benchmark test. - -Signed-off-by: Jakub Jelen ---- - tests/bench-slope.c | 33 +++++++++++++++++++++++++++++++-- - tests/benchmark.c | 4 ++++ - 2 files changed, 35 insertions(+), 2 deletions(-) - -diff --git a/tests/bench-slope.c b/tests/bench-slope.c -index 35272094..5bdb391e 100644 ---- a/tests/bench-slope.c -+++ b/tests/bench-slope.c -@@ -70,6 +70,7 @@ static double bench_ghz; - /* Current accuracy of auto-detected CPU Ghz. */ - static double bench_ghz_diff; - -+static int in_fips_mode = 0; - - /*************************************** Default parameters for measurements. */ - -@@ -1642,8 +1643,8 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode) - if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN) - return; - -- /* GCM has restrictions for block-size */ -- if (mode.mode == GCRY_CIPHER_MODE_GCM && blklen != GCRY_GCM_BLOCK_LEN) -+ /* GCM has restrictions for block-size; not allowed in FIPS mode */ -+ if (mode.mode == GCRY_CIPHER_MODE_GCM && (in_fips_mode || blklen != GCRY_GCM_BLOCK_LEN)) - return; - - /* XTS has restrictions for block-size */ -@@ -2190,6 +2191,27 @@ struct bench_ecc_hd - }; - - -+static int -+ecc_algo_fips_allowed (int algo) -+{ -+ switch (algo) -+ { -+ case ECC_ALGO_NIST_P224: -+ case ECC_ALGO_NIST_P256: -+ case ECC_ALGO_NIST_P384: -+ case ECC_ALGO_NIST_P521: -+ return 1; -+ case ECC_ALGO_SECP256K1: -+ case ECC_ALGO_ED25519: -+ case ECC_ALGO_ED448: -+ case ECC_ALGO_X25519: -+ case ECC_ALGO_X448: -+ case ECC_ALGO_NIST_P192: -+ default: -+ return 0; -+ } -+} -+ - static const char * - ecc_algo_name (int algo) - { -@@ -2681,6 +2703,10 @@ _ecc_bench (int algo) - const char *algo_name; - int i; - -+ /* Skip not allowed mechanisms */ -+ if (!ecc_algo_fips_allowed(algo)) -+ return; -+ - algo_name = ecc_algo_name (algo); - - bench_print_header_nsec_per_iteration (14, algo_name); -@@ -2899,6 +2925,9 @@ main (int argc, char **argv) - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); - xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); - -+ if (gcry_fips_mode_active ()) -+ in_fips_mode = 1; -+ - if (in_regression_test) - fputs ("Note: " PGM " running in quick regression test mode.\n", stdout); - -diff --git a/tests/benchmark.c b/tests/benchmark.c -index 605b50c6..24141371 100644 ---- a/tests/benchmark.c -+++ b/tests/benchmark.c -@@ -943,6 +943,10 @@ cipher_bench ( const char *algoname ) - && algo != GCRY_CIPHER_CHACHA20) - continue; - -+ /* GCM is not available in FIPS mode */ -+ if (in_fips_mode && modes[modeidx].mode == GCRY_CIPHER_MODE_GCM) -+ continue; -+ - if (modes[modeidx].req_blocksize > 0 - && blklen != modes[modeidx].req_blocksize) - { --- -2.31.1 - - -From 744baa85722da10d7374c6eca6b08b8197819ab6 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 13 Jul 2021 09:20:18 +0200 -Subject: [PATCH 4/8] tests: Expect curves 25519/448 to fail in FIPS mode - -* tests/t-cv25519.c (test_cv_hl): Expect the operation to fail in FIPS - mode. - (test_cv_x25519, test_it): Ditto. - (main) Detect FIPS mode. -* tests/t-ed25519.c (one_test): Expect the operation to fail in FIPS - mode. - (main) Detect FIPS mode. -* tests/t-ed448.c (one_test): Expect the operation to fail in FIPS - mode. - (main) Detect FIPS mode. -* tests/t-x448.c (test_cv_hl): Expect the operation to fail in FIPS - mode. - (test_cv_x448, test_cv): Ditto. - (main) Detect FIPS mode. --- -The ed25519, ed448, cv25519 and cv448 curves are not available in FIPS -mode. Some of the tests already skipped these, but it is always better -to make sure thy are failing, rather than just skipping these. - -Signed-off-by: Jakub Jelen ---- - tests/t-cv25519.c | 37 +++++++++++++++++++++++++++++++++++-- - tests/t-ed25519.c | 18 ++++++++++++++---- - tests/t-ed448.c | 18 ++++++++++++++---- - tests/t-x448.c | 41 +++++++++++++++++++++++++++++++++++++---- - 4 files changed, 100 insertions(+), 14 deletions(-) - -diff --git a/tests/t-cv25519.c b/tests/t-cv25519.c -index 0de50a02..b4126f4c 100644 ---- a/tests/t-cv25519.c -+++ b/tests/t-cv25519.c -@@ -33,6 +33,7 @@ - #include "t-common.h" - #define N_TESTS 18 - -+static int in_fips_mode = 0; - - static void - print_mpi (const char *text, gcry_mpi_t a) -@@ -188,7 +189,17 @@ test_cv_hl (int testno, const char *k_str, const char *u_str, - xfree (buffer); - buffer = NULL; - -- if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk))) -+ err = gcry_pk_encrypt (&s_result, s_data, s_pk); -+ if (in_fips_mode) -+ { -+ if (!err) -+ fail ("gcry_pk_encrypt is not expected to work in FIPS mode for test %d", -+ testno); -+ if (verbose > 1) -+ info ("not executed in FIPS mode\n"); -+ goto leave; -+ } -+ if (err) - fail ("gcry_pk_encrypt failed for test %d: %s", testno, - gpg_strerror (err)); - -@@ -281,7 +292,17 @@ test_cv_x25519 (int testno, const char *k_str, const char *u_str, - goto leave; - } - -- if ((err = gcry_ecc_mul_point (algo, result, scalar, point))) -+ err = gcry_ecc_mul_point (algo, result, scalar, point); -+ if (in_fips_mode) -+ { -+ if (!err) -+ fail ("gcry_ecc_mul_point is not expected to work in FIPS mode for test %d", -+ testno); -+ if (verbose > 1) -+ info ("not executed in FIPS mode\n"); -+ goto leave; -+ } -+ if (err) - fail ("gcry_ecc_mul_point failed for test %d: %s", testno, - gpg_strerror (err)); - -@@ -335,6 +356,15 @@ test_it (int testno, const char *k_str, int iter, const char *result_str) - info ("Running test %d: iteration=%d\n", testno, iter); - - gcry_mpi_ec_new (&ctx, NULL, "Curve25519"); -+ if (in_fips_mode) -+ { -+ if (ctx) -+ fail ("gcry_mpi_ec_new should fail in FIPS mode for test %d", -+ testno); -+ if (verbose > 1) -+ info ("not executed in FIPS mode\n"); -+ return; -+ } - Q = gcry_mpi_point_new (0); - - if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 32) -@@ -640,6 +670,9 @@ main (int argc, char **argv) - xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); - -+ if (gcry_fips_mode_active ()) -+ in_fips_mode = 1; -+ - start_timer (); - check_cv25519 (); - stop_timer (); -diff --git a/tests/t-ed25519.c b/tests/t-ed25519.c -index a5271c25..567bc797 100644 ---- a/tests/t-ed25519.c -+++ b/tests/t-ed25519.c -@@ -36,6 +36,7 @@ - static int sign_with_pk; - static int no_verify; - static int custom_data_file; -+static int in_fips_mode = 0; - - - static void -@@ -271,7 +272,17 @@ one_test (int testno, const char *sk, const char *pk, - goto leave; - } - -- if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk))) -+ err = gcry_pk_sign (&s_sig, s_msg, s_sk); -+ if (in_fips_mode) -+ { -+ if (!err) -+ fail ("gcry_pk_sign is not expected to work in FIPS mode for test %d", -+ testno); -+ if (verbose > 1) -+ info ("not executed in FIPS mode\n"); -+ goto leave; -+ } -+ if (err) - fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err)); - if (debug) - show_sexp ("sig=", s_sig); -@@ -481,9 +492,8 @@ main (int argc, char **argv) - xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); - -- /* Ed25519 isn't supported in fips mode */ -- if (gcry_fips_mode_active()) -- return 77; -+ if (gcry_fips_mode_active ()) -+ in_fips_mode = 1; - - start_timer (); - check_ed25519 (fname); -diff --git a/tests/t-ed448.c b/tests/t-ed448.c -index 1f445ffc..f38cd10c 100644 ---- a/tests/t-ed448.c -+++ b/tests/t-ed448.c -@@ -36,6 +36,7 @@ - static int sign_with_pk; - static int no_verify; - static int custom_data_file; -+static int in_fips_mode = 0; - - - static void -@@ -302,7 +303,17 @@ one_test (int testno, int ph, const char *sk, const char *pk, - } - } - -- if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk))) -+ err = gcry_pk_sign (&s_sig, s_msg, s_sk); -+ if (in_fips_mode) -+ { -+ if (!err) -+ fail ("gcry_pk_sign is not expected to work in FIPS mode for test %d", -+ testno); -+ if (verbose > 1) -+ info ("not executed in FIPS mode\n"); -+ goto leave; -+ } -+ if (err) - fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err)); - if (debug) - show_sexp ("sig=", s_sig); -@@ -521,9 +532,8 @@ main (int argc, char **argv) - xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); - -- /* Ed448 isn't supported in fips mode */ -- if (gcry_fips_mode_active()) -- return 77; -+ if (gcry_fips_mode_active ()) -+ in_fips_mode = 1; - - start_timer (); - check_ed448 (fname); -diff --git a/tests/t-x448.c b/tests/t-x448.c -index 5c3cbeb9..cc4b10fc 100644 ---- a/tests/t-x448.c -+++ b/tests/t-x448.c -@@ -34,6 +34,7 @@ - #include "t-common.h" - #define N_TESTS 9 - -+static int in_fips_mode = 0; - - static void - print_mpi (const char *text, gcry_mpi_t a) -@@ -179,8 +180,18 @@ test_cv_hl (int testno, const char *k_str, const char *u_str, - xfree (buffer); - buffer = NULL; - -- if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk))) -- fail ("gcry_pk_encrypt failed for test %d: %s", testno, -+ err = gcry_pk_encrypt (&s_result, s_data, s_pk); -+ if (in_fips_mode) -+ { -+ if (!err) -+ fail ("gcry_pk_encrypt is not expected to work in FIPS mode for test %d", -+ testno); -+ if (verbose > 1) -+ info ("not executed in FIPS mode\n"); -+ goto leave; -+ } -+ if (err) -+ fail ("gcry_pk_encrypt goto leavefailed for test %d: %s", testno, - gpg_strerror (err)); - - s_tmp = gcry_sexp_find_token (s_result, "s", 0); -@@ -257,7 +268,17 @@ test_cv_x448 (int testno, const char *k_str, const char *u_str, - goto leave; - } - -- if ((err = gcry_ecc_mul_point (GCRY_ECC_CURVE448, result, scalar, point))) -+ err = gcry_ecc_mul_point (GCRY_ECC_CURVE448, result, scalar, point); -+ if (in_fips_mode) -+ { -+ if (err != GPG_ERR_NOT_SUPPORTED) -+ fail ("gcry_ecc_mul_point is not expected to work in FIPS mode for test %d: %s", -+ testno, gpg_strerror (err)); -+ if (verbose > 1) -+ info ("not executed in FIPS mode\n"); -+ goto leave; -+ } -+ if (err) - fail ("gcry_ecc_mul_point failed for test %d: %s", testno, - gpg_strerror (err)); - -@@ -296,7 +317,7 @@ test_cv (int testno, const char *k_str, const char *u_str, - static void - test_it (int testno, const char *k_str, int iter, const char *result_str) - { -- gcry_ctx_t ctx; -+ gcry_ctx_t ctx = NULL; - gpg_error_t err; - void *buffer = NULL; - size_t buflen; -@@ -311,6 +332,15 @@ test_it (int testno, const char *k_str, int iter, const char *result_str) - info ("Running test %d: iteration=%d\n", testno, iter); - - gcry_mpi_ec_new (&ctx, NULL, "X448"); -+ if (in_fips_mode) -+ { -+ if (ctx) -+ fail ("gcry_mpi_ec_new should fail in FIPS mode for test %d", -+ testno); -+ if (verbose > 1) -+ info ("not executed in FIPS mode\n"); -+ return; -+ } - Q = gcry_mpi_point_new (0); - - if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56) -@@ -583,6 +613,9 @@ main (int argc, char **argv) - xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); - -+ if (gcry_fips_mode_active ()) -+ in_fips_mode = 1; -+ - start_timer (); - check_x448 (); - stop_timer (); --- -2.31.1 - - -From d3a603cd4a0ff2a8df35f5bb39efd64c52b2b2c5 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 13 Jul 2021 10:11:42 +0200 -Subject: [PATCH 5/8] tests: Expect the 192b ECDSA tests to fail in fips mode - -* tests/dsa-rfc6979.c (check_dsa_rfc6979): Expect ECDSA 192b keys to - fail in FIPS mode. - (main): Detect FIPS mode. --- -The 192b ECDSA curve is not FIPS approved so it does not work. This adds -a flag to the list of the keys to mark if it is expected to work in FIPS -mode. - -Signed-off-by: Jakub Jelen ---- - tests/dsa-rfc6979.c | 24 +++++++++++++++++------- - 1 file changed, 17 insertions(+), 7 deletions(-) - -diff --git a/tests/dsa-rfc6979.c b/tests/dsa-rfc6979.c -index 7d3d2080..0f124575 100644 ---- a/tests/dsa-rfc6979.c -+++ b/tests/dsa-rfc6979.c -@@ -33,6 +33,7 @@ - #define PGM "dsa-rfc6979" - #include "t-common.h" - -+static int in_fips_mode = 0; - - static void - show_sexp (const char *prefix, gcry_sexp_t a) -@@ -111,6 +112,7 @@ check_dsa_rfc6979 (void) - static struct { - const char *name; - const char *key; -+ int fips; - } keys[] = { - { - "DSA, 1024 bits", -@@ -130,7 +132,7 @@ check_dsa_rfc6979 (void) - " 92195A38B90523E2542EE61871C0440CB87C322FC4B4D2EC5E1E7EC766E1BE8D" - " 4CE935437DC11C3C8FD426338933EBFE739CB3465F4D3668C5E473508253B1E6" - " 82F65CBDC4FAE93C2EA212390E54905A86E2223170B44EAA7DA5DD9FFCFB7F3B#)" -- " ))" -+ " ))", 1 - }, - { - "DSA, 2048 bits", -@@ -162,7 +164,7 @@ check_dsa_rfc6979 (void) - " 687972A2D382599C9BAC4E0ED7998193078913032558134976410B89D2C171D1" - " 23AC35FD977219597AA7D15C1A9A428E59194F75C721EBCBCFAE44696A499AFA" - " 74E04299F132026601638CB87AB79190D4A0986315DA8EEC6561C938996BEADF#)" -- " ))" -+ " ))", 1 - }, - { - "ECDSA, 192 bits (prime field)", -@@ -172,7 +174,7 @@ check_dsa_rfc6979 (void) - " (q #04AC2C77F529F91689FEA0EA5EFEC7F210D8EEA0B9E047ED56" - " 3BC723E57670BD4887EBC732C523063D0A7C957BC97C1C43#)" - " (d #6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4#)" -- " ))" -+ " ))", 0 - }, - { - "ECDSA, 224 bits (prime field)", -@@ -183,7 +185,7 @@ check_dsa_rfc6979 (void) - " 00CF08DA5AD719E42707FA431292DEA11244D64FC51610D94B130D6C" - " EEAB6F3DEBE455E3DBF85416F7030CBD94F34F2D6F232C69F3C1385A#)" - " (d #F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1#)" -- " ))" -+ " ))", 1 - }, - { - "ECDSA, 256 bits (prime field)", -@@ -194,7 +196,7 @@ check_dsa_rfc6979 (void) - " 60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6" - " 7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)" - " (d #C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721#)" -- " ))" -+ " ))", 1 - }, - { - "ECDSA, 384 bits (prime field)", -@@ -208,7 +210,7 @@ check_dsa_rfc6979 (void) - " 288B231C3AE0D4FE7344FD2533264720#)" - " (d #6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D8" - " 96D5724E4C70A825F872C9EA60D2EDF5#)" -- " ))" -+ " ))", 1 - }, - { - "ECDSA, 521 bits (prime field)", -@@ -225,7 +227,7 @@ check_dsa_rfc6979 (void) - " (d #FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75" - " CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B8" - " 3538#)" -- " ))" -+ " ))", 1 - }, - { NULL } - }; -@@ -937,6 +939,12 @@ check_dsa_rfc6979 (void) - die ("building data sexp failed: %s\n", gpg_strerror (err)); - - err = gcry_pk_sign (&sig, data, seckey); -+ if (in_fips_mode && !keys[i].fips) -+ { -+ if (!err) -+ fail ("signing should not work in FIPS mode: %s\n", gpg_strerror (err)); -+ continue; -+ } - if (err) - fail ("signing failed: %s\n", gpg_strerror (err)); - -@@ -972,6 +980,8 @@ main (int argc, char **argv) - die ("version mismatch; pgm=%s, library=%s\n", - GCRYPT_VERSION,gcry_check_version (NULL)); - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); -+ if (gcry_fips_mode_active ()) -+ in_fips_mode = 1; - if (debug) - xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u, 0)); - /* No valuable keys are create, so we can speed up our RNG. */ --- -2.31.1 - - -From d4a52861e7d0d1871f4c1d32bc0815f0824eb9ab Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 13 Jul 2021 10:32:33 +0200 -Subject: [PATCH 6/8] tests: Skip secmem overflow test in FIPS mode - -* tests/t-secmem.c (main): Skip overflow tests in FIPS mode because they - are fatal and there is no way to override the outofcore handler. --- - -Signed-off-by: Jakub Jelen ---- - tests/t-secmem.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/tests/t-secmem.c b/tests/t-secmem.c -index 2b769134..692c0f1a 100644 ---- a/tests/t-secmem.c -+++ b/tests/t-secmem.c -@@ -37,6 +37,7 @@ - #define MINIMUM_POOL_SIZE 16384 - static size_t pool_size; - static size_t chunk_size; -+static int in_fips_mode = 0; - - static void - test_secmem (void) -@@ -182,8 +183,11 @@ main (int argc, char **argv) - xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u , 0)); - xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); - xgcry_control ((GCRYCTL_INIT_SECMEM, pool_size, 0)); -+ /* This is ignored in FIPS Mode */ - gcry_set_outofcore_handler (outofcore_handler, NULL); - xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); -+ if (gcry_fips_mode_active ()) -+ in_fips_mode = 1; - - /* Libgcrypt prints a warning when the first overflow is allocated; - * we do not want to see that. */ -@@ -192,7 +196,8 @@ main (int argc, char **argv) - - - test_secmem (); -- test_secmem_overflow (); -+ if (!in_fips_mode) -+ test_secmem_overflow (); - /* FIXME: We need to improve the tests, for example by registering - * our own log handler and comparing the output of - * PRIV_CTL_DUMP_SECMEM_STATS to expected pattern. */ --- -2.31.1 - - -From fc6de9c7b48ab45deb8ea84d1c768e4f056edb08 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 13 Jul 2021 10:52:00 +0200 -Subject: [PATCH 7/8] tests: Transient DSA keys work in FIPS mode - -* tests/pubkey.c (check_run): Enable tests with DSA transient keys which - work in FIPS. --- - -Signed-off-by: Jakub Jelen ---- - tests/pubkey.c | 15 ++++++--------- - 1 file changed, 6 insertions(+), 9 deletions(-) - -diff --git a/tests/pubkey.c b/tests/pubkey.c -index 754952ee..cd51e0a2 100644 ---- a/tests/pubkey.c -+++ b/tests/pubkey.c -@@ -680,15 +680,12 @@ check_run (void) - gcry_sexp_release (pkey); - gcry_sexp_release (skey); - -- if (!gcry_fips_mode_active ()) -- { -- if (verbose) -- fprintf (stderr, "Generating transient DSA key.\n"); -- get_dsa_key_new (&pkey, &skey, 1); -- /* Fixme: Add a check function for DSA keys. */ -- gcry_sexp_release (pkey); -- gcry_sexp_release (skey); -- } -+ if (verbose) -+ fprintf (stderr, "Generating transient DSA key.\n"); -+ get_dsa_key_new (&pkey, &skey, 1); -+ /* Fixme: Add a check function for DSA keys. */ -+ gcry_sexp_release (pkey); -+ gcry_sexp_release (skey); - - if (verbose) - fprintf (stderr, "Generating DSA key (FIPS 186).\n"); --- -2.31.1 - - -From e68b538008063938b9b054f18e56e9f27bb3a720 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 13 Jul 2021 10:53:13 +0200 -Subject: [PATCH 8/8] tests: Generating DSA from domain should fail in FIPS - mode - -* tests/pubkey.c (get_dsa_key_with_domain_new): Expect failure in FIPS - mode - (check_x931_derived_key): Simplify testing for FIPS mode. - (main): Check for fips mode. --- - -Signed-off-by: Jakub Jelen ---- - tests/pubkey.c | 16 ++++++++++++++-- - 1 file changed, 14 insertions(+), 2 deletions(-) - -diff --git a/tests/pubkey.c b/tests/pubkey.c -index cd51e0a2..7209f44e 100644 ---- a/tests/pubkey.c -+++ b/tests/pubkey.c -@@ -29,6 +29,7 @@ - #define PGM "pubkey" - #include "t-common.h" - -+static int in_fips_mode; - - /* Sample RSA keys, taken from basic.c. */ - -@@ -518,6 +519,14 @@ get_dsa_key_with_domain_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) - die ("error creating S-expression: %s\n", gcry_strerror (rc)); - rc = gcry_pk_genkey (&key, key_spec); - gcry_sexp_release (key_spec); -+ if (in_fips_mode) -+ { -+ if (!rc) -+ die ("generating DSA key should fail in FIPS mode: %s\n", gcry_strerror (rc)); -+ *pkey = NULL; -+ *skey = NULL; -+ return; -+ } - if (rc) - die ("error generating DSA key: %s\n", gcry_strerror (rc)); - -@@ -899,7 +908,7 @@ check_x931_derived_key (int what) - err = _gcry_pk_util_get_nbits(key_spec, &nbits); - if (err) - die ("nbits not found\n"); -- if (gcry_fips_mode_active() && nbits < 2048) -+ if (in_fips_mode && nbits < 2048) - { - info("RSA key test with %d bits skipped in fips mode\n", nbits); - goto leave; -@@ -1186,6 +1195,9 @@ main (int argc, char **argv) - /* No valuable keys are create, so we can speed up our RNG. */ - xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); - -+ if (gcry_fips_mode_active ()) -+ in_fips_mode = 1; -+ - for (i=0; i < 2; i++) - check_run (); - -@@ -1193,7 +1205,7 @@ main (int argc, char **argv) - check_x931_derived_key (i); - - check_ecc_sample_key (); -- if (!gcry_fips_mode_active ()) -+ if (!in_fips_mode) - check_ed25519ecdsa_sample_key (); - - return !!error_count; --- -2.31.1 - -commit 61c87070abc4baeb9d847db015e6a3031f349483 -Author: NIIBE Yutaka -Date: Wed Jul 7 15:52:06 2021 +0900 - - tests: Skip unavailable ciphers in FIPS mode. - - * tests/basic.c (_check_poly1305_cipher): - (check_ocb_cipher_largebuf_split): Skip unavailable ciphers when - IN_FIPS_MODE. - (check_ocb_cipher_checksum, check_gost28147_cipher_basic): Likewise. - - -- - - GnuPG-bug-id: 5520 - Signed-off-by: NIIBE Yutaka - -diff --git a/tests/basic.c b/tests/basic.c -index 0a1e8293..43ef122e 100644 ---- a/tests/basic.c -+++ b/tests/basic.c -@@ -5026,6 +5026,14 @@ _check_poly1305_cipher (unsigned int step) - - for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) - { -+ if (gcry_cipher_test_algo (tv[i].algo) && in_fips_mode) -+ { -+ if (verbose) -+ fprintf (stderr, " algorithm %d not available in fips mode\n", -+ tv[i].algo); -+ continue; -+ } -+ - if (verbose) - fprintf (stderr, " checking POLY1305 mode for %s [%i]\n", - gcry_cipher_algo_name (tv[i].algo), -@@ -6643,6 +6651,14 @@ check_ocb_cipher_largebuf_split (int algo, int keylen, const char *tagexpect, - memcpy(inbuf + i, hash, 16); - } - -+ if (gcry_cipher_test_algo (algo) && in_fips_mode) -+ { -+ if (verbose) -+ fprintf (stderr, " algorithm %d not available in fips mode\n", -+ algo); -+ goto out_free; -+ } -+ - err = gcry_cipher_open (&hde, algo, GCRY_CIPHER_MODE_OCB, 0); - if (!err) - err = gcry_cipher_open (&hdd, algo, GCRY_CIPHER_MODE_OCB, 0); -@@ -6841,6 +6857,14 @@ check_ocb_cipher_checksum (int algo, int keylen) - blk[byteidx] |= 1 << bitpos; - } - -+ if (gcry_cipher_test_algo (algo) && in_fips_mode) -+ { -+ if (verbose) -+ fprintf (stderr, " algorithm %d not available in fips mode\n", -+ algo); -+ goto out_free; -+ } -+ - err = gcry_cipher_open (&hde, algo, GCRY_CIPHER_MODE_OCB, 0); - if (!err) - err = gcry_cipher_open (&hde2, algo, GCRY_CIPHER_MODE_OCB, 0); -@@ -7651,6 +7675,15 @@ check_gost28147_cipher_basic (enum gcry_cipher_algos algo) - - if (verbose) - fprintf (stderr, " Starting GOST28147 cipher checks.\n"); -+ -+ if (gcry_cipher_test_algo (algo) && in_fips_mode) -+ { -+ if (verbose) -+ fprintf (stderr, " algorithm %d not available in fips mode\n", -+ algo); -+ return; -+ } -+ - keylen = gcry_cipher_get_algo_keylen(algo); - if (!keylen) - { -commit b98ca3f798abd17696f52163665204c4e83d9092 -Author: Jussi Kivilinna -Date: Wed Jul 7 18:21:27 2021 +0300 - - tests/basic: use SHA256 instead of RMD160 for SHAKE extract testing - - * tests/basic.c (check_one_md): Use GCRY_MD_SHA256 as 'crcalgo' for - SHAKE128/SHAKE256 testing. - -- - - RMD160 was used to calculate output checksum from two SHAKE extract - streams, which are run with different extract buffer size but for - same total length and with same input. Patch switches to use SHA256 - for checksumming for FIPS support. - - GnuPG-bug-id: 5520 - Signed-off-by: Jussi Kivilinna - -diff --git a/tests/basic.c b/tests/basic.c -index bc34bb99..eb6d1514 100644 ---- a/tests/basic.c -+++ b/tests/basic.c -@@ -10395,7 +10395,7 @@ check_one_md (int algo, const char *data, int len, const char *expect, int elen, - - if (*data == '!' && !data[1]) - { -- int crcalgo = GCRY_MD_RMD160; -+ int crcalgo = GCRY_MD_SHA256; - gcry_md_hd_t crc1, crc2; - size_t startlen; - size_t piecelen; -commit ccb076e8aabb9c49e7026258b3a36a8422f6bbe4 -Author: NIIBE Yutaka -Date: Wed Jul 7 16:29:48 2021 +0900 - - tests: Fix tests/basic.c for FIPS mode. - - * tests/basic.c (main): Use bare gcry_control for - GCRYCTL_FORCE_FIPS_MODE. - - -- - - GnuPG-bug-id: 5520 - Signed-off-by: NIIBE Yutaka - -diff --git a/tests/basic.c b/tests/basic.c -index 43ef122e..bc34bb99 100644 ---- a/tests/basic.c -+++ b/tests/basic.c -@@ -14610,7 +14610,7 @@ main (int argc, char **argv) - gcry_md_hd_t md; - - /* First trigger a self-test. */ -- xgcry_control ((GCRYCTL_FORCE_FIPS_MODE, 0)); -+ gcry_control (GCRYCTL_FORCE_FIPS_MODE, 0); - if (!gcry_control (GCRYCTL_OPERATIONAL_P, 0)) - fail ("not in operational state after self-test\n"); - -@@ -14639,7 +14639,7 @@ main (int argc, char **argv) - { - /* Now run a self-test and to get back into - operational state. */ -- xgcry_control ((GCRYCTL_FORCE_FIPS_MODE, 0)); -+ gcry_control (GCRYCTL_FORCE_FIPS_MODE, 0); - if (!gcry_control (GCRYCTL_OPERATIONAL_P, 0)) - fail ("did not reach operational after error " - "and self-test\n"); diff --git a/libgcrypt.spec b/libgcrypt.spec index a90a8ba..23ec779 100644 --- a/libgcrypt.spec +++ b/libgcrypt.spec @@ -1,59 +1,35 @@ +# This is taken from gnutls.spec +%define srpmhash() %{lua: +local files = rpm.expand("%_specdir/libgcrypt.spec") +for i, p in ipairs(patches) do + files = files.." "..p +end +for i, p in ipairs(sources) do + files = files.." "..p +end +local sha256sum = assert(io.popen("cat "..files.."| sha256sum")) +local hash = sha256sum:read("*a") +sha256sum:close() +print(string.sub(hash, 0, 16)) +} + Name: libgcrypt -Version: 1.9.4 +Version: 1.11.2 Release: 1%{?dist} URL: https://www.gnupg.org/ -Source0: libgcrypt-%{version}-hobbled.tar.xz -# The original libgcrypt sources now contain potentially patented ECC -# cipher support. We have to remove it in the tarball we ship with -# the hobble-libgcrypt script. -# (We replace it with RH approved ECC in Source4-5) -# rm -rf libgcrypt-x.y.z # make sure there are no leftover files -# tar -xf libgcrypt-x.y.z.tar.bz2 -# pushd libgcrypt-x.y.z && ../hobble-libgcrypt && popd -# tar -cvJf libgcrypt-x.y.z-hobbled.tar.xz libgcrypt-x.y.z -#Source0: https://www.gnupg.org/ftp/gcrypt/libgcrypt/libgcrypt-{version}.tar.bz2 -#Source1: https://www.gnupg.org/ftp/gcrypt/libgcrypt/libgcrypt-{version}.tar.bz2.sig -Source2: wk@g10code.com -Source3: hobble-libgcrypt -# Approved ECC support -Source4: ecc-curves.c -Source5: curves.c -Source7: random.conf -Source8: keygrip.c -# make FIPS hmac compatible with fipscheck - non upstreamable -# update on soname bump -Patch2: libgcrypt-1.8.5-use-fipscheck.patch -# modify FIPS RSA and DSA keygen to comply with requirements -Patch5: libgcrypt-1.8.4-fips-keygen.patch -# fix the tests to work correctly in the FIPS mode -Patch6: libgcrypt-1.9.3-fips-tests.patch -# update the CAVS tests -Patch7: libgcrypt-1.7.3-fips-cavs.patch -# use poll instead of select when gathering randomness -Patch11: libgcrypt-1.8.4-use-poll.patch -# slight optimalization of mpicoder.c to silence Valgrind (#968288) -Patch13: libgcrypt-1.6.1-mpicoder-gccopt.patch -# Run the FIPS mode initialization in the shared library constructor -Patch18: libgcrypt-1.8.3-fips-ctor.patch -# Do not try to open /dev/urandom if getrandom() works -Patch24: libgcrypt-1.8.5-getrandom.patch -# Continuous FIPS entropy test -Patch26: libgcrypt-1.8.3-fips-enttest.patch -# Disable non-approved FIPS hashes in the enforced FIPS mode -Patch27: libgcrypt-1.8.3-md-fips-enforce.patch -# Missing Intel CET support in the library (#1954049) -Patch28: libgcrypt-1.8.5-intel-cet.patch -# FIPS module is redefined a little bit (implicit by kernel FIPS mode) -Patch30: libgcrypt-1.8.5-fips-module.patch +Source0: https://www.gnupg.org/ftp/gcrypt/libgcrypt/libgcrypt-%{version}.tar.bz2 +Source1: https://www.gnupg.org/ftp/gcrypt/libgcrypt/libgcrypt-%{version}.tar.bz2.sig +Source2: https://gnupg.org/signature_key.asc +# Pass the annobin flags to the libgcrypt.so (#2016349) +Patch1: libgcrypt-1.10.1-annobin.patch +# https://gitlab.com/redhat-crypto/libgcrypt/libgcrypt-mirror/-/merge_requests/19/ +Patch5: libgcrypt-1.11.0-marvin.patch %global gcrylibdir %{_libdir} %global gcrysoname libgcrypt.so.20 %global hmackey orboDeJITITejsirpADONivirpUkvarP -# Technically LGPLv2.1+, but Fedora's table doesn't draw a distinction. -# Documentation and some utilities are GPLv2+ licensed. These files -# are in the devel subpackage. -License: LGPLv2+ +License: BSD-3-Clause AND (BSD-3-Clause OR GPL-2.0-only) AND GPL-2.0-or-later AND LGPL-2.1-or-later AND LGPL-2.0-or-later AND MIT-Modern-Variant Summary: A general-purpose cryptography library BuildRequires: gcc BuildRequires: gawk, libgpg-error-devel >= 1.11, pkgconfig @@ -61,10 +37,11 @@ BuildRequires: gawk, libgpg-error-devel >= 1.11, pkgconfig BuildRequires: texinfo BuildRequires: autoconf, automake, libtool BuildRequires: make +BuildRequires: gnupg2 +BuildRequires: annobin-annocheck binutils %package devel Summary: Development files for the %{name} package -License: LGPLv2+ and GPLv2+ Requires: libgpg-error-devel Requires: %{name}%{?_isa} = %{version}-%{release} Requires: pkgconfig @@ -79,33 +56,18 @@ in GNU Privacy Guard. This package contains files needed to develop applications using libgcrypt. %prep +%{gpgverify} --keyring='%{SOURCE2}' --signature='%{SOURCE1}' --data='%{SOURCE0}' %setup -q -%{SOURCE3} -%patch2 -p1 -b .use-fipscheck -%patch5 -p1 -b .fips-keygen -%patch6 -p1 -b .tests-fipsmode -%patch7 -p1 -b .cavs -%patch11 -p1 -b .use-poll -%patch13 -p1 -b .gccopt -%patch18 -p1 -b .fips-ctor -%patch24 -p1 -b .getrandom -%patch26 -p1 -b .fips-enttest -%patch27 -p1 -b .fips-enforce -%patch28 -p1 -b .intel-cet -%patch30 -p1 -b .fips-module - -cp %{SOURCE4} cipher/ -cp %{SOURCE5} %{SOURCE8} tests/ +%patch 1 -p1 +%patch 5 -p1 %build -# This package has a configure test which uses ASMs, but does not link the -# resultant .o files. As such the ASM test is always successful, even on -# architectures were the ASM is not valid when compiling with LTO. -# -# -ffat-lto-objects is sufficient to address this issue. It is the default -# for F33, but is expected to only be enabled for packages that need it in -# F34, so we use it here explicitly -%define _lto_cflags -flto=auto -ffat-lto-objects +# should be all algorithms except SM3 and SM4, aria +export DIGESTS='crc gostr3411-94 md4 md5 rmd160 sha1 sha256 sha512 sha3 tiger whirlpool stribog blake2' +export CIPHERS='arcfour blowfish cast5 des aes twofish serpent rfc2268 seed camellia idea salsa20 gost28147 chacha20' + +eval $(sed -n 's/^\(\(NAME\|VERSION_ID\)=.*\)/OS_\1/p' /etc/os-release) +export FIPS_MODULE_NAME="$OS_NAME ${OS_VERSION_ID%%.*} %name" autoreconf -f %configure --disable-static \ @@ -113,23 +75,36 @@ autoreconf -f --disable-asm \ %endif --enable-noexecstack \ - --enable-hmac-binary-check \ - --enable-pubkey-ciphers='dsa elgamal rsa ecc' \ - --disable-O-flag-munging + --enable-hmac-binary-check=%{hmackey} \ + --disable-jent-support \ + --disable-O-flag-munging \ + --enable-digests="$DIGESTS" \ + --enable-ciphers="$CIPHERS" \ + --enable-marvin-workaround \ + --with-fips-module-version="$FIPS_MODULE_NAME %{version}-%{srpmhash}" sed -i -e '/^sys_lib_dlsearch_path_spec/s,/lib /usr/lib,/usr/lib /lib64 /usr/lib64 /lib,g' libtool %make_build %check -src/hmac256 %{hmackey} src/.libs/%{gcrysoname} | cut -f1 -d ' ' >src/.libs/.%{gcrysoname}.hmac - make check +# try in faked FIPS mode too +LIBGCRYPT_FORCE_FIPS_MODE=1 make check + +%define libpath $RPM_BUILD_ROOT%{gcrylibdir}/%{gcrysoname}.?.? + +PROFILE=%{?dist} annocheck --ignore-unknown --verbose --profile=${PROFILE:1} %{libpath} # Add generation of HMAC checksums of the final stripped binaries %define __spec_install_post \ %{?__debug_package:%{__debug_install_post}} \ %{__arch_install_post} \ %{__os_install_post} \ - src/hmac256 %{hmackey} $RPM_BUILD_ROOT%{gcrylibdir}/%{gcrysoname} | cut -f1 -d ' ' >$RPM_BUILD_ROOT%{gcrylibdir}/.%{gcrysoname}.hmac \ + cd src \ + sed -i -e 's|FILE=.*|FILE=\\\$1|' gen-note-integrity.sh \ + READELF=readelf AWK=awk ECHO_N="-n" bash gen-note-integrity.sh %{libpath} > %{libpath}.hmac \ + objcopy --update-section .note.fdo.integrity=%{libpath}.hmac %{libpath} %{libpath}.new \ + mv -f %{libpath}.new %{libpath} \ + rm -f %{libpath}.hmac %{nil} %install @@ -174,16 +149,13 @@ popd # Create /etc/gcrypt (hardwired, not dependent on the configure invocation) so # that _someone_ owns it. mkdir -p -m 755 $RPM_BUILD_ROOT/etc/gcrypt -install -m644 %{SOURCE7} $RPM_BUILD_ROOT/etc/gcrypt/random.conf %ldconfig_scriptlets %files %dir /etc/gcrypt -%config(noreplace) /etc/gcrypt/random.conf %{gcrylibdir}/libgcrypt.so.*.* %{gcrylibdir}/%{gcrysoname} -%{gcrylibdir}/.%{gcrysoname}.hmac %license COPYING.LIB %doc AUTHORS NEWS THANKS @@ -202,6 +174,80 @@ install -m644 %{SOURCE7} $RPM_BUILD_ROOT/etc/gcrypt/random.conf %license COPYING %changelog +* Mon Nov 03 2025 Jakub Jelen - 1.11.2-1 +- New upstream release (#2406804) + +* Thu Jul 24 2025 Fedora Release Engineering - 1.11.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + +* Tue May 13 2025 Jakub Jelen - 1.11.1-1 +- New upstream release (#2364878) + +* Fri Jan 17 2025 Fedora Release Engineering - 1.11.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + +* Tue Oct 29 2024 Jakub Jelen - 1.11.0-4 +- Run annocheck as part of the build +- Use upstream CF protection patches +- Fix PAC/BTI protection for Aarch64 +- Fix CVE-2024-2236 (#2274128) + +* Thu Jul 25 2024 Jakub Jelen - 1.11.0-3 +- Disable -O flag munging and remove needles modification of LTO flags. + +* Thu Jul 18 2024 Fedora Release Engineering - 1.11.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + +* Wed Jun 19 2024 Jakub Jelen - 1.11.0-1 +- New upstream release (#2293064) + +* Tue Mar 26 2024 Jakub Jelen - 1.10.3-4 +- Replace deprecated fgrep (#2271503) + +* Thu Jan 25 2024 Fedora Release Engineering - 1.10.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Sun Jan 21 2024 Fedora Release Engineering - 1.10.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Tue Nov 14 2023 Jakub Jelen - 1.10.3-1 +- New upstream release (#2249639) + +* Thu Jul 20 2023 Fedora Release Engineering - 1.10.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Tue Apr 11 2023 Jakub Jelen - 1.10.2-1 +- New upstream release (#2185084) + +* Thu Jan 19 2023 Fedora Release Engineering - 1.10.1-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Sat Dec 3 2022 Florian Weimer - 1.10.1-6 +- Port configure script to C99 + +* Tue Nov 08 2022 Todd Zullinger - 1.10.1-5 +- enable brainpool by default (#1413618) +- fix sporadic failures generating RSA keys in FIPS mode + +* Thu Jul 21 2022 Fedora Release Engineering - 1.10.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Sun May 29 2022 Todd Zullinger - 1.10.1-3 +- improve --disable-brainpool configure output +- use %%bcond_with to disable brainpool curves + +* Fri Apr 08 2022 Jakub Jelen - 1.10.1-2 +- Adjust integrity check creation to match upstream (#2073018) + +* Tue Mar 29 2022 Jakub Jelen - 1.10.1-1 +- Final release (#2069263) + +* Wed Feb 02 2022 Jakub Jelen - 1.10.0-1 +- New upstream release (#2049322) + +* Thu Jan 20 2022 Fedora Release Engineering - 1.9.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + * Mon Aug 23 2021 Jakub Jelen - 1.9.4-1 - New upstream release (#1996467) diff --git a/random.conf b/random.conf deleted file mode 100644 index 6041bcd..0000000 --- a/random.conf +++ /dev/null @@ -1,4 +0,0 @@ -# use only /dev/urandom - see https://www.2uo.de/myths-about-urandom/ -only-urandom -# Keep jitter entropy generator enabled (should do no harm) -#disable-jent diff --git a/signature_key.asc b/signature_key.asc new file mode 100644 index 0000000..151e8fa --- /dev/null +++ b/signature_key.asc @@ -0,0 +1,86 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +eiInsGZ/+h5XYrpXTgA= +=4+Sn +-----END PGP PUBLIC KEY BLOCK----- diff --git a/sources b/sources index 8d0f1cb..90c992c 100644 --- a/sources +++ b/sources @@ -1 +1,2 @@ -SHA512 (libgcrypt-1.9.4-hobbled.tar.xz) = 80b1b2159ec3c7795450037d76d81b737f2734f1cff72c8aa6ac8c1a5654ece4fce0aabfbed0cce99d123747831db135df0d040f67081052cb2363bf52f5d4e6 +SHA512 (libgcrypt-1.11.2.tar.bz2) = b706cea602cc8f0896e57ce979643bf78974b05faec27c1b053b773c57d8b04250e30e95a4ef5899e1df981d01d8d08f0a36e10b5820a5ec4183e74c02e5f1f0 +SHA512 (libgcrypt-1.11.2.tar.bz2.sig) = e63d240a9b97aa218c98c5baa72a30321ae85412d4a8748e36578bd9498fe8c8ee2ba84835d1783cdfc1e5dfb66b0a89409d6263e9c06f4cd11e919d4bce0cd2 diff --git a/tests/smoke-test/Makefile b/tests/smoke-test/Makefile index 0e0dda3..0682152 100644 --- a/tests/smoke-test/Makefile +++ b/tests/smoke-test/Makefile @@ -57,10 +57,14 @@ $(METADATA): Makefile @echo "Requires: libgcrypt" >> $(METADATA) @echo "Requires: nfs-utils" >> $(METADATA) @echo "Requires: rpm-build" >> $(METADATA) + @echo "Requires: autoconf automake libtool" >> $(METADATA) @echo "Requires: gawk" >> $(METADATA) - @echo "Requires: gcc-c++ fipscheck" >> $(METADATA) - @echo "Requires: libgpg-error-devel wget" >> $(METADATA) - @echo "Requires: pkgconfig texinfo libselinux-utils" >> $(METADATA) + @echo "Requires: gcc-c++" >> $(METADATA) + @echo "Requires: libgpg-error-devel" >> $(METADATA) + @echo "Requires: pkgconfig texinfo fipscheck libkcapi-fipscheck" >> $(METADATA) + @echo "Requires: crypto-policies-scripts" >> $(METADATA) + @echo "Requires: bc" >> $(METADATA) + @echo "RhtsRequires: library(distribution/fips)" >> $(METADATA) @echo "Releases: -RHEL3 -RHEL4" >> $(METADATA) @echo "Priority: Normal" >> $(METADATA) @echo "Architectures: i386 x86_64 s390x ia64" >> $(METADATA) diff --git a/tests/smoke-test/main.fmf b/tests/smoke-test/main.fmf new file mode 100644 index 0000000..70bd1ba --- /dev/null +++ b/tests/smoke-test/main.fmf @@ -0,0 +1,25 @@ +summary: Test calls upstream test suite. +test: ./runtest.sh +enabled: true +framework: beakerlib +require: + - library(distribution/fips) +recommend: + - libgcrypt + - nfs-utils + - rpm-build + - autoconf + - automake + - libtool + - gawk + - gcc-c++ + - libgpg-error-devel + - pkgconfig + - texinfo + - fipscheck + - libkcapi-fipscheck + - crypto-policies-scripts + - bc + - annobin-annocheck + - gnupg2 +duration: 45m diff --git a/tests/smoke-test/runtest.sh b/tests/smoke-test/runtest.sh index dd1803d..0f5bc82 100755 --- a/tests/smoke-test/runtest.sh +++ b/tests/smoke-test/runtest.sh @@ -26,8 +26,6 @@ # # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Include rhts environment -#. /usr/bin/rhts-environment.sh . /usr/share/beakerlib/beakerlib.sh PACKAGE="libgcrypt" @@ -35,36 +33,112 @@ PACKAGE="libgcrypt" rlJournalStart rlPhaseStartSetup + rlImport distribution/fips || rlFail "FIPS library needed for some phases" TmpDir=`mktemp -d` rlAssertRpm $PACKAGE - rlFileBackup --clean "/etc/gcrypt/fips_enabled" + rlRun "rlFileBackup --clean /etc/gcrypt/fips_enabled" + if [[ -r /etc/gcrypt/hwf.deny ]]; then + rlRun "rlFileBackup /etc/gcrypt/hwf.deny" + rlRun "rm -f /etc/gcrypt/hwf.deny" + fi rlRun "pushd $TmpDir" 0 - rlFetchSrcForInstalled $PACKAGE + rlRun "rlFetchSrcForInstalled $PACKAGE" rlRun "rpm -ihv `ls *.rpm`" 0 if grep '1' /proc/sys/crypto/fips_enabled; then rlRun "echo '1' > /etc/gcrypt/fips_enabled" 0 fi rlPhaseEnd - rlPhaseStartTest + rlPhaseStartTest "Package build" TOPDIR=`rpm --eval %_topdir` rlRun "pushd $TOPDIR" 0 rlRun "rm -rf BUILD/libgcrypt-*" 0-255 rlRun "rpmbuild -vv -bc SPECS/libgcrypt.spec" 0 - rlRun "pushd BUILD/libgcrypt-*" 0 + libgcrypt_dir="libgcrypt-`rpm -q --qf "%{VERSION}\n" libgcrypt | head -1`" + if [ -d "BUILD/$libgcrypt_dir-build" ]; then + BUILDDIR="BUILD/$libgcrypt_dir-build/$libgcrypt_dir" + else + BUILDDIR="BUILD/$libgcrypt_dir" + fi + rlRun "pushd $BUILDDIR" 0 rlRun "fipshmac src/.libs/libgcrypt.so.??" 0 - rlRun "make check > $TmpDir/make_check.out" 0 - rlRun "popd" 0 - rlRun "popd" 0 - rlRun "grep \"All [0-9]\+ tests passed\" $TmpDir/make_check.out" 0 \ - "All tests passed" - rlRun "cat $TmpDir/make_check.out" 0 + rlRun "popd" + rlRun "popd" rlPhaseEnd + rlPhaseStartTest "Upstream testsuite" + rlRun "pushd $TOPDIR/$BUILDDIR" 0 + exp_fails=() + + rlRun "echo 'Expecting ${#exp_fails[@]} fails'" + exp_exitcode=0 + if [[ ${#exp_fails[@]} -gt 0 ]]; then exp_exitcode=2; fi + rlRun "make check &> $TmpDir/make_check.out" $exp_exitcode + rlRun "grep ^FAIL: $TmpDir/make_check.out" 0,1 "Print fails" + if [[ ${#exp_fails[@]} -gt 0 ]]; then + for f in $(grep '^FAIL:' $TmpDir/make_check.out | awk -F ': ' '{print $2}'); do + if [[ "${exp_fails[@]}" =~ "$f" ]]; then + rlLog "$f failure is expected" + else + rlFail "$f failure is not expected" + fi + done + else + rlRun "grep \"All [0-9]\+ tests passed\" $TmpDir/make_check.out" 0 \ + "All tests passed" + fi + rlRun "cat $TmpDir/make_check.out" + rlRun "popd" + rlPhaseEnd + + if ! (rlIsRHEL '<=8.4' || ([[ $fipsMode = "enabled" ]] && rlIsFedora '<37')); then + # (we are ~expecting to fix HW optimization problem around Fedora-37) + rlPhaseStartTest "Performance with HW optimizations disabled - bz1990059" + # we are gathering more samples, so that this test is robust in shared environments + N_SAMPLES=5 + N_MEDIAN=$(echo "$N_SAMPLES/2+1" |bc) + HWF_DENY_FILE="/etc/gcrypt/hwf.deny" + # Disabling via cmdline arguments would need either handpicking + # all the algorithms (variant with ":"), or making "all" work. + # "--disable-hwf all" does not work: https://dev.gnupg.org/T5636 + #dis_arg="--disable-hwf all" + dis_arg="" + + for x in "cipher:aes256:CBC enc" "hash:sha256:SHA256" "mac:hmac_sha256:HMAC_SHA256"; do + alg_type=$(echo $x |cut -d: -f1) + algorithm=$(echo $x |cut -d: -f2) + alg_line=$(echo $x |cut -d: -f3) + rlRun "rm -f $algorithm.ena $algorithm.dis" + rlLogInfo "Performance measurements started ..." + for i in `seq 1 $N_SAMPLES`; do + # run with HW optimizations ENAbled + rm -f $HWF_DENY_FILE + $TOPDIR/$BUILDDIR/tests/bench-slope $alg_type $algorithm |grep "$alg_line" >>$algorithm.ena + + # run with HW optimizations DISabled + # this looks idiotic, but I wasn't able to make --disable-hwf work + echo "all" >$HWF_DENY_FILE + $TOPDIR/$BUILDDIR/tests/bench-slope $dis_arg $alg_type $algorithm |grep "$alg_line" >>$algorithm.dis + done + rlLogInfo "Performance measurements finished" + rlRun "cat $algorithm.ena" + rlRun "cat $algorithm.dis" + dis=$(cat $algorithm.dis |cut -d'|' -f2 |awk '{ print $1 }' |sort -n |sed -n "$N_MEDIAN p") + ena=$(cat $algorithm.ena |cut -d'|' -f2 |awk '{ print $1 }' |sort -n |sed -n "$N_MEDIAN p") + rlRun "echo '$algorithm Time: disabled $dis enabled $ena'" + if (( $(echo "$dis > $ena" |bc -l) )); then + rlPass "HW optimizations work for $algorithm" + else + rlFail "HW optimizations DO NOT work for $algorithm" + fi + done + rlPhaseEnd + fi + rlPhaseStartCleanup - rlRun "popd" 0 + rlRun "popd" rlRun "rm -r $TmpDir" 0 - rlFileRestore + rlRun "rlFileRestore" rlPhaseEnd rlJournalPrintText diff --git a/tests/tests.yml b/tests/tests.yml deleted file mode 100644 index dd115e9..0000000 --- a/tests/tests.yml +++ /dev/null @@ -1,20 +0,0 @@ -- hosts: localhost - tags: - - classic - - container - roles: - - role: standard-test-beakerlib - tests: - - smoke-test - required_packages: - - fipscheck - - gawk - - gcc-c++ - - libgcrypt - - libgpg-error-devel - - libselinux-utils - - nfs-utils - - pkgconfig - - rpm-build - - texinfo - - wget diff --git a/wk@g10code.com b/wk@g10code.com deleted file mode 100644 index 5e193e3..0000000 --- a/wk@g10code.com +++ /dev/null @@ -1,2007 +0,0 @@ -Login: wk Name: Werner Koch -No project. -No plan. -Public key: - pub 1024D/5B0358A2 1999-03-15 [expires: 2011-07-11] - uid Werner Koch - uid Werner Koch - uid Werner Koch - uid Werner Koch - sub 1024D/010A57ED 2004-03-21 [expires: 2007-12-31] - sub 2048R/C3680A6E 2006-01-01 [expires: 2007-12-31] - - pub 1024R/1CE0C630 2006-01-01 [expires: 2008-12-31] - uid Werner Koch (dist sig) - - pub 1024R/37D92FFB 2003-11-17 [expires: 2009-12-31] - uid g10 Code (code signing key 1) - - pub 1024D/57548DCD 1998-07-07 [expired: 2005-12-31] - uid Werner Koch (gnupg sig) - - -5B0358A2 is my main key. -1CE0C630 is used to sign software distributions. -37D92FFB is used to sign certain source files. -57548DCD was used to sign GNU crypto software; - it has been superseded by 1CE0C630. - -Please note that I use a subkey for signing messages; some old OpenPGP -implementations may not be able to check such a signature. The primary -key is stored at a more or less secure place and only used on a spare -laptop which is not connected to any network. If you find a key -certified by this one, you can be sure that I personally met this -person and checked the name part of the user ID against an official -looking passport or another suitable photo id. My signature does not -say anything about the email address (I merely check that the address -looks plausible). - - ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.4.7 (GNU/Linux) - -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 -Ooh6BBMRAgA6BQJEXmVkMxpodHRwOi8vd3d3LnNjLWRlbHBoaW4tZXNjaHdlaWxl -ci5kZS9wZ3AvaW5kZXguaHRtbAAKCRC0deIHurWCKcwdAJ4hvG79xMFKtYL0GGP8 -WuvvxnMnzQCfZUjHFhMNMGgrCPthD6oeNaw2FxCIRgQTEQIABgUCRFz9JwAKCRCK -o2Kv6XIyzVjaAJ9MSW3iP1fUZqr7xdbj/PLkHLzJeACfZNe6IgDrJk9E3a/8IKZi -E4iYtqWIRgQTEQIABgUCRFz9LwAKCRCn31hycNRMI+EXAKDRHf6ZRUGa6T7Uosgx -huo5I3sK6gCfRxthF9FN1SXK11O+tjRS1mwN0JaIYwQTEQIAGwUCNxrPkAUJDMl8 -gAMLCgMDFQMCAxYCAQIXgAASCRBd4kmWWwNYogdlR1BHAAEBXcIAnjv7ON5AiwzC -LBwm9h9ywufXJQuVAJ9RMq6lpPqnDly6UCKz+kGt0EplyIhmBBMRAgAeAwsKAwMV -AwIDFgIBAheAAhkBBQJAXdnoBQkTa4v/ABIHZUdQRwABAQkQXeJJllsDWKLs0gCg -y5RdOqhFvwUFYWj+dHb4LGt7xi0AoKduFxGMuM/loPShQnjvk/VVFesAiF4EExEC -AB4DCwoDAxUDAgMWAgECF4ACGQEFAkBd2egFCRNri/8ACgkQXeJJllsDCRDs0gCg -y5RdOqhFvwUFYWj+dHb4LGt7xi0AoKduFxGMuM/loPShQnjvk/VVFesAiQEVAwUQ -O0HCUNImKUTOasbBAQFLZwgAkgMC/ximskOjL/CxghgdkSWkDFdpEr3XYhzUdLes -WgN4AM28mGZZKA9la7dXXRrKYkxhX8mpL4C3Q9LnrafP+Zn1c8mTuNIxX86j7iZA -IksoZ4D2csN8NSMYT9pKK6jZP1IOckCFBBI0W/yMGUGulDitWj4TwIArf2xQkV73 -//////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////// -/////////////////////////////////////////////4hGBBMRAgAGBQJDRBuk -AAoJEOrUtZD2iZvAafoAoKReCbMFRrpwzb2tS7M6iHDbN9E+AJoDy8nLf8A4f/EV -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 -wZyP4MwTIOb4NIhGBBARAgAGBQJEKFdaAAoJEBH0xB6z+64zBhUAoJ6kWu5gDqHB -Z8Wan0YynEZi6tKyAJsHBacxx6UNYPENlVXlb0xl+kssa4h6BBMRAgA6BQJEXmVA -MxpodHRwOi8vd3d3LnNjLWRlbHBoaW4tZXNjaHdlaWxlci5kZS9wZ3AvaW5kZXgu -aHRtbAAKCRCUj9ag4Q9QLulZAJ992LLlu7wU5sJddqnffDzPoKh69QCgoX8QzBEi -SZjLKSrALwLCTYI/akSIegQTEQIAOgUCRF5lZDMaaHR0cDovL3d3dy5zYy1kZWxw -aGluLWVzY2h3ZWlsZXIuZGUvcGdwL2luZGV4Lmh0bWwACgkQtHXiB7q1gikU0QCf -ZM81Hr4p7ZPwxMZW+TVcRdaSIjoAoMq7mN4xSLtRsB/lGlFLhc1O4soLiEYEExEC -AAYFAkRc/SoACgkQiqNir+lyMs3HBACfb7fa3KzNGB9Oma3KDWeC42feCzAAoIfF -w3OXxtigxx8+AowiXGsZmqI+iEYEExECAAYFAkRc/TEACgkQp99YcnDUTCOrZQCg -gV7PoPMl1d+gvPTiuUAZm8UG/dYAn3PlOcTP4vxayC7OGSkoDA8Dut+KiEYEExEC -AAYFAkNEG6QACgkQ6tS1kPaJm8DI0ACfRfci8SHm+sQpjzKqr0zxfJT5yF0AnA37 -mn7nQyY28SJ2sddCo/f1JhvAiEYEEBECAAYFAkQFglgACgkQb11ZaBzVLeiG9wCf -aX6+h8bpMOraCn/sH1yQb6aI34EAoLzx4H6lGdyqnQTYiLXSrBTrto/niEYEEBEC -AAYFAkQGCjQACgkQmsnSLGNcY0vZ4wCgvBjcB5ABcNYRMEFSWsClxhaRAqUAmwUv -MA9NWdLk0pcUndbE3w70B5hUiEwEEhECAAwFAkQGKycFgwZSqcEACgkQ3+ZXkjKV -xc2qMwCfdew+RX+7ultYmO0Y8gRoLJzjBfgAn1UkZILe3+NKZBJTd1ZowJQiljHI -iEwEEhECAAwFAkQGK2cFgwZSqYEACgkQoOUVvzmICVQuFwCgqbjde48qPvUqLEip -he/zFq6pqgAAn1dXi6z+1s+bHavwCQK/q+3K5gdHiJwEEwECAAYFAkQNdesACgkQ -/dJ0ek5GOmrWRgQAoFF9IdTs5vX5OIDKIHW+d4aJfPg6w91SKkfpElDxNqZuk3rA -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 -nj+zW9f9Z2M0K2r+gldEC2Ew2yTl -=I1gH ------END PGP PUBLIC KEY BLOCK-----