Commit message: rsa: Add exponent blinding.

* cipher/rsa.c (secret_core_crt): Blind secret D with randomized
nonce R for mpi_powm computation.

--

Co-authored-by: Werner Koch <wk@gnupg.org>
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>

The paper describing attack: https://eprint.iacr.org/2017/627

Sliding right into disaster: Left-to-right sliding windows leak
by Daniel J. Bernstein and Joachim Breitner and Daniel Genkin and
Leon Groot Bruinderink and Nadia Heninger and Tanja Lange and
Christine van Vredendaal and Yuval Yarom

  It is well known that constant-time implementations of modular
  exponentiation cannot use sliding windows. However, software
  libraries such as Libgcrypt, used by GnuPG, continue to use sliding
  windows. It is widely believed that, even if the complete pattern of
  squarings and multiplications is observed through a side-channel
  attack, the number of exponent bits leaked is not sufficient to
  carry out a full key-recovery attack against RSA. Specifically,
  4-bit sliding windows leak only 40% of the bits, and 5-bit sliding
  windows leak only 33% of the bits.

  In this paper we demonstrate a complete break of RSA-1024 as
  implemented in Libgcrypt. Our attack makes essential use of the fact
  that Libgcrypt uses the left-to-right method for computing the
  sliding-window expansion. We show for the first time that the
  direction of the encoding matters: the pattern of squarings and
  multiplications in left-to-right sliding windows leaks significantly
  more information about exponent bits than for right-to-left. We show
  how to incorporate this additional information into the
  Heninger-Shacham algorithm for partial key reconstruction, and use
  it to obtain very efficient full key recovery for RSA-1024. We also
  provide strong evidence that the same attack works for RSA-2048 with
  only moderately more computation.

Exponent blinding is a kind of workaround to add noise.  Signal (leak)
is still there for non-constant-time implementation.
CVE: CVE-2017-7526
CWE: CWE-310
Vulnerability description: libgcrypt before version 1.7.8 is vulnerable to a cache side-channel attack resulting into a complete break of RSA-1024 while using the left-to-right method for computing the sliding-window expansion. The same attack is believed to work on RSA-2048 with moderately more computation. This side-channel requires that attacker can run arbitrary software on the hardware where the private RSA key is used.

Project: libgcrypt
File: rsa.c

stronger_key_check ( RSA_secret_key *skey )
{
  gcry_mpi_t t = mpi_alloc_secure ( 0 );
  gcry_mpi_t t1 = mpi_alloc_secure ( 0 );
  gcry_mpi_t t2 = mpi_alloc_secure ( 0 );
  gcry_mpi_t phi = mpi_alloc_secure ( 0 );

  /* check that n == p * q */
  mpi_mul( t, skey->p, skey->q);
  if (mpi_cmp( t, skey->n) )
    log_info ( "RSA Oops: n != p * q\n" );

  /* check that p is less than q */
  if( mpi_cmp( skey->p, skey->q ) > 0 )
    {
      log_info ("RSA Oops: p >= q - fixed\n");
      _gcry_mpi_swap ( skey->p, skey->q);
    }

    /* check that e divides neither p-1 nor q-1 */
    mpi_sub_ui(t, skey->p, 1 );
    mpi_fdiv_r(t, t, skey->e );
    if ( !mpi_cmp_ui( t, 0) )
        log_info ( "RSA Oops: e divides p-1\n" );
    mpi_sub_ui(t, skey->q, 1 );
    mpi_fdiv_r(t, t, skey->e );
    if ( !mpi_cmp_ui( t, 0) )
        log_info ( "RSA Oops: e divides q-1\n" );

    /* check that d is correct */
    mpi_sub_ui( t1, skey->p, 1 );
    mpi_sub_ui( t2, skey->q, 1 );
    mpi_mul( phi, t1, t2 );
    gcry_mpi_gcd(t, t1, t2);
    mpi_fdiv_q(t, phi, t);
    mpi_invm(t, skey->e, t );
    if ( mpi_cmp(t, skey->d ) )
      {
        log_info ( "RSA Oops: d is wrong - fixed\n");
        mpi_set (skey->d, t);
        log_printmpi ("  fixed d", skey->d);
      }

    /* check for correctness of u */
    mpi_invm(t, skey->p, skey->q );
    if ( mpi_cmp(t, skey->u ) )
      {
        log_info ( "RSA Oops: u is wrong - fixed\n");
        mpi_set (skey->u, t);
        log_printmpi ("  fixed u", skey->u);
      }

    log_info ( "RSA secret key check finished\n");

    mpi_free (t);
    mpi_free (t1);
    mpi_free (t2);
    mpi_free (phi);
}

Project: libgcrypt
File: rsa.c

secret (gcry_mpi_t output, gcry_mpi_t input, RSA_secret_key *skey )
{
  /* Remove superfluous leading zeroes from INPUT.  */
  mpi_normalize (input);

  if (!skey->p || !skey->q || !skey->u)
    {
      secret_core_std (output, input, skey->d, skey->n);
    }
  else
    {
      secret_core_crt (output, input, skey->d, mpi_get_nlimbs (skey->n),
                       skey->p, skey->q, skey->u);
    }
}

Project: libgcrypt
File: rsa.c

secret_core_crt (gcry_mpi_t M, gcry_mpi_t C,
                 gcry_mpi_t D, unsigned int Nlimbs,
                 gcry_mpi_t P, gcry_mpi_t Q, gcry_mpi_t U)
{
  gcry_mpi_t m1 = mpi_alloc_secure ( Nlimbs + 1 );
  gcry_mpi_t m2 = mpi_alloc_secure ( Nlimbs + 1 );
  gcry_mpi_t h  = mpi_alloc_secure ( Nlimbs + 1 );
  gcry_mpi_t D_blind = mpi_alloc_secure ( Nlimbs + 1 );
  gcry_mpi_t r;
  unsigned int r_nbits;

  r_nbits = mpi_get_nbits (P) / 4;
  if (r_nbits < 96)
    r_nbits = 96;
  r = mpi_alloc_secure ( (r_nbits + BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB );

  /* d_blind = (d mod (p-1)) + (p-1) * r            */
  /* m1 = c ^ d_blind mod p */
  _gcry_mpi_randomize (r, r_nbits, GCRY_WEAK_RANDOM);
  mpi_set_highbit (r, r_nbits - 1);
  mpi_sub_ui ( h, P, 1 );
  mpi_mul ( D_blind, h, r );
  mpi_fdiv_r ( h, D, h );
  mpi_add ( D_blind, D_blind, h );
  mpi_powm ( m1, C, D_blind, P );

  /* d_blind = (d mod (q-1)) + (q-1) * r            */
  /* m2 = c ^ d_blind mod q */
  _gcry_mpi_randomize (r, r_nbits, GCRY_WEAK_RANDOM);
  mpi_set_highbit (r, r_nbits - 1);
  mpi_sub_ui ( h, Q, 1  );
  mpi_mul ( D_blind, h, r );
  mpi_fdiv_r ( h, D, h );
  mpi_add ( D_blind, D_blind, h );
  mpi_powm ( m2, C, D_blind, Q );

  mpi_free ( r );
  mpi_free ( D_blind );

  /* h = u * ( m2 - m1 ) mod q */
  mpi_sub ( h, m2, m1 );
  if ( mpi_has_sign ( h ) )
    mpi_add ( h, h, Q );
  mpi_mulm ( h, U, h, Q );

  /* m = m1 + h * p */
  mpi_mul ( h, h, P );
  mpi_add ( M, m1, h );

  mpi_free ( h );
  mpi_free ( m1 );
  mpi_free ( m2 );
}

Project: libgcrypt
File: rsa.c

public(gcry_mpi_t output, gcry_mpi_t input, RSA_public_key *pkey )
{
  if( output == input )  /* powm doesn't like output and input the same */
    {
      gcry_mpi_t x = mpi_alloc( mpi_get_nlimbs(input)*2 );
      mpi_powm( x, input, pkey->e, pkey->n );
      mpi_set(output, x);
      mpi_free(x);
    }
  else
    mpi_powm( output, input, pkey->e, pkey->n );
}