📜 ⬆️ ⬇️

Patch gnupg or a pair of RSA-32768 in 106 minutes

security

In fact, patch gnupg and libgcrypt ...

Once upon a time, in order to use 8192 and 16384 RSA keys, I relied on the size in keygen.c and the size of the SECMEM buffer next door. Cases of bygone days, now SECMEM is rendered in config.h and is called SECMEM_BUFFER_SIZE .
')
As a result, after downloading Veria 2.0.29 under the fresh debian 8.3, for the place of the killed 12th update on Ubuntu on the 14th, I quickly screwed up the key size and buffer size and joyfully generated a key for 1800 minutes on the 5200U for 18 (or 19) minutes, which used to take 45 50 minutes on the P6200.

But 32kbit gave me a bunch of errors. Free time is - we understand.

The first thing that catches your eye is the hard-coded size when selecting SECMEMs. Here are a few:

agent / gpg-agent.c: gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0); - 4 kilobytes (since this implementation uses the value in bits)
scd / scdaemon.c: gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
tools / gpg-check-pattern.c: gcry_control (GCRYCTL_INIT_SECMEM, 4096, 0);

In principle, saving memory is a good thing ... on the other hand, if there is enough memory, why not allocate a full volume equal to the constant SECMEM_BUFFER_SIZE .

Further, the program is full of places where 4kb keys appear or associated with them (allocating a buffer for reading, or creating a “packet” for writing), here are a few:

agent / command-ssh.c: log_error (_("ssh keys greater than %d bits are not supported\n"), 4096);
g10 / card-util.c: n = get_data_from_file (args, 16384, &data);
g10 / plaintext.c: byte *buffer = xmalloc( 32768 );
common / dns-cert.c: rc=get_dns_cert (argv[1],16384,&iobuf,&fpr,&fpr_len,&url);

The very size of the key that the program eats is given in g10 / keygen.c:

const unsigned maxsize = (opt.flags.large_rsa ? 8192 : 4096);
unsigned int nbits, min, def = DEFAULT_STD_KEYSIZE, max=4096;

But, by correcting the gnupg source when generating the RSA-32768 key, we get:
gpg: checking the trustdb
gpg: keyring_get_keyblock: read error: Invalid packet
gpg: keyring_get_keyblock failed: Invalid keyring
gpg: failed to rebuild keyring cache: Invalid keyring
gpg: keydb_search failed: Invalid packet
gpg: FB2E6BDF not found public key
gpg: keyring_get_keyblock: read error: Invalid packet
gpg: keydb_get_keyblock failed: Invalid keyring
gpg: keydb_search failed: Invalid keyring
gpg: public key of ultimately trusted key 6146D68D not found
gpg: 3 marginal (s) needed, 1 complete (s) needed, PGP trust model
gpg: keyring_get_keyblock: read error: Invalid packet
gpg: keydb_get_keyblock failed: Invalid keyring
gpg: validate_key_list failed

The devil is covered in details in libgcrypt:

mpi / mpicoder.c: #define MAX_EXTERN_MPI_BITS 16384 - packet size in bits.

And in some way:
src / secmem.c: #define MINIMUM_POOL_SIZE 16384
src / secmem.c: #define STANDARD_POOL_SIZE 32768
In a certain way, because, as I understood from the code, this is the size of the pool for SECMEM in bytes, in which gnupg initializes its securememory via gcry_control (GCRYCTL_INIT_SECMEM, __, 0);

In the end, the modification resulted in such a diff for GnuPG-2.0.29

gnupg-2.0.29-RSA32k.patch
 diff -uraN a/agent/command-ssh.cb/agent/command-ssh.c --- a/agent/command-ssh.c 2015-09-08 14:39:24.000000000 +0200 +++ b/agent/command-ssh.c 2016-02-26 21:46:47.000000000 +0100 @@ -592,7 +592,7 @@ not too large. */ if (mpi_data_size > 520) { - log_error (_("ssh keys greater than %d bits are not supported\n"), 4096); + log_error (_("ssh keys greater than %d bits are not supported\n"), KEY_MAX_SIZE_LOOKSLIKE); err = GPG_ERR_TOO_LARGE; goto out; } diff -uraN a/agent/gpg-agent.cb/agent/gpg-agent.c --- a/agent/gpg-agent.c 2015-09-08 14:39:24.000000000 +0200 +++ b/agent/gpg-agent.c 2016-02-26 21:46:47.000000000 +0100 @@ -233,7 +233,7 @@ /* To avoid surprises we limit the size of the mapped IPC file to this value. Putty currently (0.62) uses 8k, thus 16k should be enough for the foreseeable future. */ -#define PUTTY_IPC_MAXLEN 16384 +#define PUTTY_IPC_MAXLEN KEY_MAX_SIZE_LOOKSLIKE #endif /*HAVE_W32_SYSTEM*/ /* The list of open file descriptors at startup. Note that this list @@ -743,7 +743,7 @@ } /* Initialize the secure memory. */ - gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0); + gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0); maybe_setuid = 0; /* diff -uraN a/agent/protect-tool.cb/agent/protect-tool.c --- a/agent/protect-tool.c 2015-09-08 14:39:24.000000000 +0200 +++ b/agent/protect-tool.c 2016-02-26 21:46:47.000000000 +0100 @@ -1036,7 +1036,7 @@ } setup_libgcrypt_logging (); - gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0); opt_homedir = default_homedir (); diff -uraN a/common/dns-cert.cb/common/dns-cert.c --- a/common/dns-cert.c 2015-09-08 14:39:24.000000000 +0200 +++ b/common/dns-cert.c 2016-02-26 21:46:47.000000000 +0100 @@ -305,7 +305,7 @@ printf("CERT lookup on %s\n",argv[1]); - rc=get_dns_cert (argv[1],16384,&iobuf,&fpr,&fpr_len,&url); + rc=get_dns_cert (argv[1],KEY_MAX_SIZE_LOOKSLIKE,&iobuf,&fpr,&fpr_len,&url); if(rc==-1) printf("error\n"); else if(rc==0) diff -uraN a/config.h.in b/config.h.in --- a/config.h.in 2015-09-08 15:30:25.000000000 +0200 +++ b/config.h.in 2016-02-26 21:46:47.000000000 +0100 @@ -608,6 +608,9 @@ /* Size of secure memory buffer */ #undef SECMEM_BUFFER_SIZE +/* Maximum key size or lookslike */ +#undef KEY_MAX_SIZE_LOOKSLIKE + /* defines the filename of the shred program */ #undef SHRED diff -uraN a/configure b/configure --- a/configure 2015-09-08 16:12:06.000000000 +0200 +++ b/configure 2016-02-26 21:46:47.000000000 +0100 @@ -5307,13 +5307,13 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $large_secmem" >&5 $as_echo "$large_secmem" >&6; } if test "$large_secmem" = yes ; then - SECMEM_BUFFER_SIZE=65536 + SECMEM_BUFFER_SIZE=262144 else - SECMEM_BUFFER_SIZE=32768 + SECMEM_BUFFER_SIZE=262144 fi - cat >>confdefs.h <<_ACEOF #define SECMEM_BUFFER_SIZE $SECMEM_BUFFER_SIZE +#define KEY_MAX_SIZE_LOOKSLIKE 32768 _ACEOF diff -uraN a/configure.ac b/configure.ac --- a/configure.ac 2015-09-08 14:39:24.000000000 +0200 +++ b/configure.ac 2016-02-26 21:46:47.000000000 +0100 @@ -183,12 +183,15 @@ large_secmem=$enableval, large_secmem=no) AC_MSG_RESULT($large_secmem) if test "$large_secmem" = yes ; then - SECMEM_BUFFER_SIZE=65536 + SECMEM_BUFFER_SIZE=262144 else - SECMEM_BUFFER_SIZE=32768 + SECMEM_BUFFER_SIZE=262144 fi AC_DEFINE_UNQUOTED(SECMEM_BUFFER_SIZE,$SECMEM_BUFFER_SIZE, [Size of secure memory buffer]) + +AC_DEFINE_UNQUOTED(KEY_MAX_SIZE_LOOKSLIKE,32768, + [Maximum key size or lookslike]) # Allow disabling of bzib2 support. diff -uraN a/doc/gnupg.info-1 b/doc/gnupg.info-1 --- a/doc/gnupg.info-1 2015-09-08 16:15:29.000000000 +0200 +++ b/doc/gnupg.info-1 2016-02-26 21:46:47.000000000 +0100 @@ -2552,7 +2552,7 @@ max-cert-size When retrieving a key via DNS CERT, only accept keys up to - this size. Defaults to 16384 bytes. + this size. Defaults to 32768 bytes. debug Turn on debug output in the keyserver helper program. Note diff -uraN a/doc/gpg.texi b/doc/gpg.texi --- a/doc/gpg.texi 2015-09-08 14:39:24.000000000 +0200 +++ b/doc/gpg.texi 2016-02-26 21:46:47.000000000 +0100 @@ -1645,7 +1645,7 @@ @ifclear gpgtwoone @item max-cert-size When retrieving a key via DNS CERT, only accept keys up to this size. - Defaults to 16384 bytes. + Defaults to 32768 bytes. @end ifclear @item debug diff -uraN a/g10/card-util.cb/g10/card-util.c --- a/g10/card-util.c 2015-09-08 14:39:24.000000000 +0200 +++ b/g10/card-util.c 2016-02-26 21:46:47.000000000 +0100 @@ -946,7 +946,7 @@ { for (args++; spacep (args); args++) ; - n = get_data_from_file (args, 16384, &data); + n = get_data_from_file (args, KEY_MAX_SIZE_LOOKSLIKE, &data); if (n < 0) return -1; } @@ -1285,7 +1285,7 @@ ask_card_keysize (int keyno, unsigned int nbits) { unsigned int min_nbits = 1024; - unsigned int max_nbits = 4096; + unsigned int max_nbits = KEY_MAX_SIZE_LOOKSLIKE; char *prompt, *answer; unsigned int req_nbits; diff -uraN a/g10/gpg.hb/g10/gpg.h --- a/g10/gpg.h 2015-09-08 14:39:24.000000000 +0200 +++ b/g10/gpg.h 2016-02-26 21:46:47.000000000 +0100 @@ -35,7 +35,7 @@ /* Number of bits we accept when reading or writing MPIs. */ -#define MAX_EXTERN_MPI_BITS 16384 +#define MAX_EXTERN_MPI_BITS KEY_MAX_SIZE_LOOKSLIKE /* The maximum length of a binary fingerprints. */ #define MAX_FINGERPRINT_LEN 20 diff -uraN a/g10/keygen.cb/g10/keygen.c --- a/g10/keygen.c 2015-09-08 14:39:24.000000000 +0200 +++ b/g10/keygen.c 2016-02-26 21:46:47.000000000 +0100 @@ -1429,7 +1429,7 @@ PKT_secret_key *sk; PKT_public_key *pk; gcry_sexp_t s_parms, s_key; - const unsigned maxsize = (opt.flags.large_rsa ? 8192 : 4096); + const unsigned maxsize = KEY_MAX_SIZE_LOOKSLIKE; assert (is_RSA(algo)); @@ -1798,7 +1798,7 @@ static unsigned ask_keysize (int algo, unsigned int primary_keysize) { - unsigned int nbits, min, def = DEFAULT_STD_KEYSIZE, max=4096; + unsigned int nbits, min, def = DEFAULT_STD_KEYSIZE, max=KEY_MAX_SIZE_LOOKSLIKE; int for_subkey = !!primary_keysize; int autocomp = 0; diff -uraN a/g10/keyserver.cb/g10/keyserver.c --- a/g10/keyserver.c 2015-09-08 14:39:24.000000000 +0200 +++ b/g10/keyserver.c 2016-02-26 21:46:47.000000000 +0100 @@ -94,7 +94,7 @@ struct keyserver_spec *keyserver); /* Reasonable guess */ -#define DEFAULT_MAX_CERT_SIZE 16384 +#define DEFAULT_MAX_CERT_SIZE KEY_MAX_SIZE_LOOKSLIKE static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE; diff -uraN a/g10/parse-packet.cb/g10/parse-packet.c --- a/g10/parse-packet.c 2015-09-08 14:39:24.000000000 +0200 +++ b/g10/parse-packet.c 2016-02-26 21:46:47.000000000 +0100 @@ -1681,7 +1681,7 @@ --*length; nbits |= c; - if (nbits > 16384) + if (nbits > KEY_MAX_SIZE_LOOKSLIKE) { log_error ("mpi too large (%u bits)\n", nbits); return NULL; diff -uraN a/g10/plaintext.cb/g10/plaintext.c --- a/g10/plaintext.c 2015-09-08 14:39:24.000000000 +0200 +++ b/g10/plaintext.c 2016-02-26 21:46:47.000000000 +0100 @@ -225,9 +225,9 @@ } } else { /* binary mode */ - byte *buffer = xmalloc( 32768 ); + byte *buffer = xmalloc( KEY_MAX_SIZE_LOOKSLIKE ); while( pt->len ) { - int len = pt->len > 32768 ? 32768 : pt->len; + int len = pt->len > KEY_MAX_SIZE_LOOKSLIKE ? KEY_MAX_SIZE_LOOKSLIKE : pt->len; len = iobuf_read( pt->buf, buffer, len ); if( len == -1 ) { rc = gpg_error_from_syserror (); @@ -294,7 +294,7 @@ } } else { /* binary mode */ - byte *buffer = xmalloc( 32768 ); + byte *buffer = xmalloc( KEY_MAX_SIZE_LOOKSLIKE ); int eof_seen = 0; while ( !eof_seen ) { @@ -304,10 +304,10 @@ * off and therefore we don't catch the boundary. * So, always assume EOF if iobuf_read returns less bytes * then requested */ - int len = iobuf_read( pt->buf, buffer, 32768 ); + int len = iobuf_read( pt->buf, buffer, KEY_MAX_SIZE_LOOKSLIKE ); if( len == -1 ) break; - if( len < 32768 ) + if( len < KEY_MAX_SIZE_LOOKSLIKE ) eof_seen = 1; if( mfx->md ) gcry_md_write ( mfx->md, buffer, len ); diff -uraN a/scd/apdu.cb/scd/apdu.c --- a/scd/apdu.c 2015-09-08 14:39:24.000000000 +0200 +++ b/scd/apdu.c 2016-02-26 21:46:47.000000000 +0100 @@ -2964,7 +2964,7 @@ if (opt.ctapi_driver && *opt.ctapi_driver) { - int port = portstr? atoi (portstr) : 32768; + int port = portstr? atoi (portstr) : KEY_MAX_SIZE_LOOKSLIKE; if (!ct_api_loaded) { @@ -3612,7 +3612,7 @@ else if (extended_mode < 0) { /* Send APDU using chaining mode. */ - if (lc > 16384) + if (lc > KEY_MAX_SIZE_LOOKSLIKE) return SW_WRONG_LENGTH; /* Sanity check. */ if ((class&0xf0) != 0) return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0. */ diff -uraN a/scd/command.cb/scd/command.c --- a/scd/command.c 2015-09-08 14:39:24.000000000 +0200 +++ b/scd/command.c 2016-02-26 21:46:47.000000000 +0100 @@ -45,13 +45,13 @@ #define MAXLEN_PIN 100 /* Maximum allowed size of key data as used in inquiries. */ -#define MAXLEN_KEYDATA 4096 +#define MAXLEN_KEYDATA KEY_MAX_SIZE_LOOKSLIKE /* Maximum allowed total data size for SETDATA. */ -#define MAXLEN_SETDATA 4096 +#define MAXLEN_SETDATA KEY_MAX_SIZE_LOOKSLIKE /* Maximum allowed size of certificate data as used in inquiries. */ -#define MAXLEN_CERTDATA 16384 +#define MAXLEN_CERTDATA KEY_MAX_SIZE_LOOKSLIKE #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) diff -uraN a/scd/scdaemon.cb/scd/scdaemon.c --- a/scd/scdaemon.c 2015-09-08 14:39:24.000000000 +0200 +++ b/scd/scdaemon.c 2016-02-26 21:46:47.000000000 +0100 @@ -497,7 +497,7 @@ } /* initialize the secure memory. */ - gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0); maybe_setuid = 0; /* diff -uraN a/sm/gpgsm.cb/sm/gpgsm.c --- a/sm/gpgsm.c 2015-09-08 14:39:24.000000000 +0200 +++ b/sm/gpgsm.c 2016-02-26 21:46:47.000000000 +0100 @@ -965,7 +965,7 @@ /* Initialize the secure memory. */ - gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0); maybe_setuid = 0; /* diff -uraN a/tools/gpg-check-pattern.cb/tools/gpg-check-pattern.c --- a/tools/gpg-check-pattern.c 2015-09-08 14:39:24.000000000 +0200 +++ b/tools/gpg-check-pattern.c 2016-02-26 21:46:47.000000000 +0100 @@ -179,7 +179,7 @@ } setup_libgcrypt_logging (); - gcry_control (GCRYCTL_INIT_SECMEM, 4096, 0); + gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0); opt.homedir = default_homedir (); diff -uraN a/tools/make-dns-cert.cb/tools/make-dns-cert.c --- a/tools/make-dns-cert.c 2015-09-01 08:52:21.000000000 +0200 +++ b/tools/make-dns-cert.c 2016-02-26 21:46:47.000000000 +0100 @@ -64,7 +64,7 @@ goto fail; } - if(statbuf.st_size>16384) + if(statbuf.st_size>KEY_MAX_SIZE_LOOKSLIKE) fprintf(stderr,"Warning: key file %s is larger than the default" " GnuPG max-cert-size\n",keyfile); diff -uraN a/tools/symcryptrun.cb/tools/symcryptrun.c --- a/tools/symcryptrun.c 2015-09-08 14:39:24.000000000 +0200 +++ b/tools/symcryptrun.c 2016-02-26 21:46:47.000000000 +0100 @@ -999,7 +999,7 @@ NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); } setup_libgcrypt_logging (); - gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0); /* Tell simple-pwquery about the the standard socket name. */ { 



The maximum certificate and key size for ssh has been increased to the heap.

And such a diff for libgcrypt-1.6.5

libgcrypt-1.6.5-RSA32k.patch
 diff -uraN a/mpi/mpicoder.cb/mpi/mpicoder.c --- a/mpi/mpicoder.c 2015-02-23 11:55:58.000000000 +0100 +++ b/mpi/mpicoder.c 2016-02-25 17:45:29.000000000 +0100 @@ -27,7 +27,7 @@ #include "mpi-internal.h" #include "g10lib.h" -#define MAX_EXTERN_MPI_BITS 16384 +#define MAX_EXTERN_MPI_BITS 32768 /* Helper used to scan PGP style MPIs. Returns NULL on failure. */ static gcry_mpi_t diff -uraN a/src/secmem.cb/src/secmem.c --- a/src/secmem.c 2016-02-09 10:10:38.000000000 +0100 +++ b/src/secmem.c 2016-02-25 17:45:29.000000000 +0100 @@ -45,8 +45,8 @@ #define MAP_ANONYMOUS MAP_ANON #endif -#define MINIMUM_POOL_SIZE 16384 -#define STANDARD_POOL_SIZE 32768 +#define MINIMUM_POOL_SIZE 32768 +#define STANDARD_POOL_SIZE 65536 #define DEFAULT_PAGE_SIZE 4096 typedef struct memblock 



Pool size has been doubled. For almost bezlukova 16384 keys, the size of the pool was 32768 (with a key rate of 4096 and 8192).

Does this work?

Yes:
sec 32768R / 18B54A62 2016-02-24

uid test32768key

ssb 32768R / 1507CD2A 2016-02-24

Productivity on i5-5200 under Debian 8.3 (4.3.0-0.bpo.1-amd64 # 1 SMP Debian 4.3.3-7 ~ bpo8 + 1 (2016-01-19) x86_64 GNU / Linux):
RSA 16384 - 19 minutes (pair generation time)

RSA 32768 - 106 minutes (pair generation time)

Encrypting a file of 12MB in size (another RSA-32768 key, during the tests and writing, I downloaded 10 pieces of them):
time gpg2 --out file.gz.enc --recipient "test32768pair" --encrypt file.gz

real 0m0.079s

user 0m0.072s

sys 0m0.004s

Decryption:
time gpg2 --out file.gz.gz --decrypt file.gz.enc

real 0m7.610s

user 0m5.624s

sys 0m0.024s

And check:
7ab98fd4a154fad5f5bbe0d698178783cd2ac994 file.gz

9773bb1b9d7f75f408f562d476e8936aafa0f3b9 file.gz.enc

7ab98fd4a154fad5f5bbe0d698178783cd2ac994 file.gz.gz

Subtleties: if you add such a key to the keyring, then the vanilla version will not read, as described above (mpi, etc.). So when using this version (modified) and using vanilla software (which works with classic keys), you need to create a separate keyring.

Well, as usual: LOSS OF DATA, Crash Systems and Red Eyes are POSSIBLE.

Links to the project and patches (= diff) on github:

Good luck and in the presence of questions I am ready to answer them, with my two years of experience C which was 15 years ago!

Just listen to comments on improving the modification.

Question : why RSA-32768, you that RSA- (2048 | 4096 | 8192 | 16384) is not enough?

Answer : because * you can !

* Modern hardware allows itself to work with large keys without tangible problems. Even notebook. I will not invoke RSA-32768, but it’s a good idea to have such a big key and a pack of sub-keys, as well as to encrypt especially critical data, until the great Quantum Computer has conquered the world of prime numbers!

Source: https://habr.com/ru/post/278051/


All Articles