mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-04 20:38:50 +01:00
93a96e3c0c
* cipher/random.c (randomize_mpi): New. * g10/gpgv.c (randomize_mpi): New stub. * cipher/rsa.c (USE_BLINDING): Define macro. (secret): Implement blinding. -- GPG 1.x has never used any protection against timing attacks on the RSA secret operation. The rationale for this has been that there was no way to mount a remote timing attack on GnuPG. With the turning up of Acoustic Cryptanalysis (http://cs.tau.ac.il/~tromer/acoustic) this assumption no longer holds true and thus we need to do do something about it. Blinding seems to be a suitable mitigation to the threat of key extraction. It does not help against distinguishing used keys, though. Note that GPG 2.x uses Libgcrypt which does blinding by default. The performance penalty is negligible: Modifying the core pubkey_sign or pubkey_decrypt function to run 100 times in a loop, the entire execution times for signing or decrypting a small message using a 4K RSA key on a Thinkpad X220 are Without blinding: 5.2s (8.9s) With blinding: 5.6s (9.3s) The numbers in parentheses give the values without the recently implemented k-ary exponentiation code. Thus for the next release the user will actually experience faster signing and decryption. A drawback of blinding is that we need random numbers even for decryption (albeit at low quality). Signed-off-by: Werner Koch <wk@gnupg.org> CVE-id: CVE-2013-4576
510 lines
13 KiB
C
510 lines
13 KiB
C
/* rsa.c - RSA function
|
|
* Copyright (C) 1997, 1998, 1999, 2013 by Werner Koch (dd9jn)
|
|
* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuPG is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* This code uses an algorithm protected by U.S. Patent #4,405,829
|
|
which expires on September 20, 2000. The patent holder placed that
|
|
patent into the public domain on Sep 6th, 2000.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "util.h"
|
|
#include "mpi.h"
|
|
#include "cipher.h"
|
|
#include "rsa.h"
|
|
|
|
/* Blinding is used to mitigate side-channel attacks. You may undef
|
|
this to speed up the operation in case the system is secured
|
|
against physical and network mounted side-channel attacks. */
|
|
#define USE_BLINDING 1
|
|
|
|
typedef struct {
|
|
MPI n; /* modulus */
|
|
MPI e; /* exponent */
|
|
} RSA_public_key;
|
|
|
|
|
|
typedef struct {
|
|
MPI n; /* public modulus */
|
|
MPI e; /* public exponent */
|
|
MPI d; /* exponent */
|
|
MPI p; /* prime p. */
|
|
MPI q; /* prime q. */
|
|
MPI u; /* inverse of p mod q. */
|
|
} RSA_secret_key;
|
|
|
|
|
|
static void test_keys( RSA_secret_key *sk, unsigned nbits );
|
|
static void generate( RSA_secret_key *sk, unsigned nbits );
|
|
static int check_secret_key( RSA_secret_key *sk );
|
|
static void public(MPI output, MPI input, RSA_public_key *skey );
|
|
static void secret(MPI output, MPI input, RSA_secret_key *skey );
|
|
|
|
|
|
static void
|
|
test_keys( RSA_secret_key *sk, unsigned nbits )
|
|
{
|
|
RSA_public_key pk;
|
|
MPI test = mpi_alloc ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
MPI out1 = mpi_alloc ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
MPI out2 = mpi_alloc ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
|
|
pk.n = sk->n;
|
|
pk.e = sk->e;
|
|
{ char *p = get_random_bits( nbits, 0, 0 );
|
|
mpi_set_buffer( test, p, (nbits+7)/8, 0 );
|
|
xfree(p);
|
|
}
|
|
|
|
public( out1, test, &pk );
|
|
secret( out2, out1, sk );
|
|
if( mpi_cmp( test, out2 ) )
|
|
log_fatal("RSA operation: public, secret failed\n");
|
|
secret( out1, test, sk );
|
|
public( out2, out1, &pk );
|
|
if( mpi_cmp( test, out2 ) )
|
|
log_fatal("RSA operation: secret, public failed\n");
|
|
mpi_free( test );
|
|
mpi_free( out1 );
|
|
mpi_free( out2 );
|
|
}
|
|
|
|
/****************
|
|
* Generate a key pair with a key of size NBITS
|
|
* Returns: 2 structures filled with all needed values
|
|
*/
|
|
static void
|
|
generate( RSA_secret_key *sk, unsigned nbits )
|
|
{
|
|
MPI p, q; /* the two primes */
|
|
MPI d; /* the private key */
|
|
MPI u;
|
|
MPI t1, t2;
|
|
MPI n; /* the public key */
|
|
MPI e; /* the exponent */
|
|
MPI phi; /* helper: (p-1)(q-1) */
|
|
MPI g;
|
|
MPI f;
|
|
|
|
/* make sure that nbits is even so that we generate p, q of equal size */
|
|
if ( (nbits&1) )
|
|
nbits++;
|
|
|
|
n = mpi_alloc ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
|
|
p = q = NULL;
|
|
do {
|
|
/* select two (very secret) primes */
|
|
if (p)
|
|
mpi_free (p);
|
|
if (q)
|
|
mpi_free (q);
|
|
p = generate_secret_prime( nbits / 2 );
|
|
q = generate_secret_prime( nbits / 2 );
|
|
if( mpi_cmp( p, q ) > 0 ) /* p shall be smaller than q (for calc of u)*/
|
|
mpi_swap(p,q);
|
|
/* calculate the modulus */
|
|
mpi_mul( n, p, q );
|
|
} while ( mpi_get_nbits(n) != nbits );
|
|
|
|
/* calculate Euler totient: phi = (p-1)(q-1) */
|
|
t1 = mpi_alloc_secure( mpi_get_nlimbs(p) );
|
|
t2 = mpi_alloc_secure( mpi_get_nlimbs(p) );
|
|
phi = mpi_alloc_secure ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
g = mpi_alloc_secure ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
f = mpi_alloc_secure ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
mpi_sub_ui( t1, p, 1 );
|
|
mpi_sub_ui( t2, q, 1 );
|
|
mpi_mul( phi, t1, t2 );
|
|
mpi_gcd(g, t1, t2);
|
|
mpi_fdiv_q(f, phi, g);
|
|
|
|
/* Find an public exponent.
|
|
Benchmarking the RSA verify function with a 1024 bit key yields
|
|
(2001-11-08):
|
|
e=17 0.54 ms
|
|
e=41 0.75 ms
|
|
e=257 0.95 ms
|
|
e=65537 1.80 ms
|
|
|
|
This code used 41 until 2006-06-28 when it was changed to use
|
|
65537 as the new best practice. See FIPS-186-3.
|
|
*/
|
|
e = mpi_alloc ( mpi_nlimb_hint_from_nbits (32) );
|
|
mpi_set_ui( e, 65537);
|
|
while( !mpi_gcd(t1, e, phi) ) /* (while gcd is not 1) */
|
|
mpi_add_ui( e, e, 2);
|
|
|
|
/* calculate the secret key d = e^1 mod phi */
|
|
d = mpi_alloc ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
mpi_invm(d, e, f );
|
|
/* calculate the inverse of p and q (used for chinese remainder theorem)*/
|
|
u = mpi_alloc ( mpi_nlimb_hint_from_nbits (nbits) );
|
|
mpi_invm(u, p, q );
|
|
|
|
if( DBG_CIPHER ) {
|
|
log_mpidump(" p= ", p );
|
|
log_mpidump(" q= ", q );
|
|
log_mpidump("phi= ", phi );
|
|
log_mpidump(" g= ", g );
|
|
log_mpidump(" f= ", f );
|
|
log_mpidump(" n= ", n );
|
|
log_mpidump(" e= ", e );
|
|
log_mpidump(" d= ", d );
|
|
log_mpidump(" u= ", u );
|
|
}
|
|
|
|
mpi_free(t1);
|
|
mpi_free(t2);
|
|
mpi_free(phi);
|
|
mpi_free(f);
|
|
mpi_free(g);
|
|
|
|
sk->n = n;
|
|
sk->e = e;
|
|
sk->p = p;
|
|
sk->q = q;
|
|
sk->d = d;
|
|
sk->u = u;
|
|
|
|
/* now we can test our keys (this should never fail!) */
|
|
test_keys( sk, nbits - 64 );
|
|
}
|
|
|
|
|
|
/****************
|
|
* Test wether the secret key is valid.
|
|
* Returns: true if this is a valid key.
|
|
*/
|
|
static int
|
|
check_secret_key( RSA_secret_key *sk )
|
|
{
|
|
int rc;
|
|
MPI temp = mpi_alloc( mpi_get_nlimbs(sk->p)*2 );
|
|
|
|
mpi_mul(temp, sk->p, sk->q );
|
|
rc = mpi_cmp( temp, sk->n );
|
|
mpi_free(temp);
|
|
return !rc;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* Public key operation. Encrypt INPUT with PKEY and put result into OUTPUT.
|
|
*
|
|
* c = m^e mod n
|
|
*
|
|
* Where c is OUTPUT, m is INPUT and e,n are elements of PKEY.
|
|
*/
|
|
static void
|
|
public(MPI output, MPI input, RSA_public_key *pkey )
|
|
{
|
|
if( output == input ) { /* powm doesn't like output and input the same */
|
|
MPI 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 );
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
stronger_key_check ( RSA_secret_key *skey )
|
|
{
|
|
MPI t = mpi_alloc_secure ( 0 );
|
|
MPI t1 = mpi_alloc_secure ( 0 );
|
|
MPI t2 = mpi_alloc_secure ( 0 );
|
|
MPI 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\n");
|
|
|
|
|
|
/* 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 );
|
|
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\n");
|
|
|
|
/* check for crrectness of u */
|
|
mpi_invm(t, skey->p, skey->q );
|
|
if ( mpi_cmp(t, skey->u ) )
|
|
log_info ( "RSA Oops: u is wrong\n");
|
|
|
|
log_info ( "RSA secret key check finished\n");
|
|
|
|
mpi_free (t);
|
|
mpi_free (t1);
|
|
mpi_free (t2);
|
|
mpi_free (phi);
|
|
}
|
|
#endif
|
|
|
|
|
|
/****************
|
|
* Secret key operation. Encrypt INPUT with SKEY and put result into OUTPUT.
|
|
*
|
|
* m = c^d mod n
|
|
*
|
|
* Or faster:
|
|
*
|
|
* m1 = c ^ (d mod (p-1)) mod p
|
|
* m2 = c ^ (d mod (q-1)) mod q
|
|
* h = u * (m2 - m1) mod q
|
|
* m = m1 + h * p
|
|
*
|
|
* Where m is OUTPUT, c is INPUT and d,n,p,q,u are elements of SKEY.
|
|
*/
|
|
static void
|
|
secret(MPI output, MPI input, RSA_secret_key *skey )
|
|
{
|
|
#if 0
|
|
mpi_powm( output, input, skey->d, skey->n );
|
|
#else
|
|
int nlimbs = mpi_get_nlimbs (skey->n)+1;
|
|
MPI m1 = mpi_alloc_secure (nlimbs);
|
|
MPI m2 = mpi_alloc_secure (nlimbs);
|
|
MPI h = mpi_alloc_secure (nlimbs);
|
|
# ifdef USE_BLINDING
|
|
MPI r = mpi_alloc_secure (nlimbs);
|
|
MPI bdata= mpi_alloc_secure (nlimbs);
|
|
|
|
/* Blind: bdata = (data * r^e) mod n */
|
|
randomize_mpi (r, mpi_get_nbits (skey->n), 0);
|
|
mpi_fdiv_r (r, r, skey->n);
|
|
mpi_powm (bdata, r, skey->e, skey->n);
|
|
mpi_mulm (bdata, bdata, input, skey->n);
|
|
input = bdata;
|
|
# endif /* USE_BLINDING */
|
|
|
|
/* RSA secret operation: */
|
|
/* m1 = c ^ (d mod (p-1)) mod p */
|
|
mpi_sub_ui( h, skey->p, 1 );
|
|
mpi_fdiv_r( h, skey->d, h );
|
|
mpi_powm( m1, input, h, skey->p );
|
|
/* m2 = c ^ (d mod (q-1)) mod q */
|
|
mpi_sub_ui( h, skey->q, 1 );
|
|
mpi_fdiv_r( h, skey->d, h );
|
|
mpi_powm( m2, input, h, skey->q );
|
|
/* h = u * ( m2 - m1 ) mod q */
|
|
mpi_sub( h, m2, m1 );
|
|
if ( mpi_is_neg( h ) )
|
|
mpi_add ( h, h, skey->q );
|
|
mpi_mulm( h, skey->u, h, skey->q );
|
|
/* m = m2 + h * p */
|
|
mpi_mul ( h, h, skey->p );
|
|
mpi_add ( output, m1, h );
|
|
|
|
# ifdef USE_BLINDING
|
|
/* Unblind: output = (output * r^(-1)) mod n */
|
|
mpi_free (bdata);
|
|
mpi_invm (r, r, skey->n);
|
|
mpi_mulm (output, output, r, skey->n);
|
|
mpi_free (r);
|
|
# endif /* USE_BLINDING */
|
|
|
|
mpi_free ( h );
|
|
mpi_free ( m1 );
|
|
mpi_free ( m2 );
|
|
#endif
|
|
}
|
|
|
|
|
|
/*********************************************
|
|
************** interface ******************
|
|
*********************************************/
|
|
|
|
int
|
|
rsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors )
|
|
{
|
|
RSA_secret_key sk;
|
|
|
|
if( !is_RSA(algo) )
|
|
return G10ERR_PUBKEY_ALGO;
|
|
|
|
generate( &sk, nbits );
|
|
skey[0] = sk.n;
|
|
skey[1] = sk.e;
|
|
skey[2] = sk.d;
|
|
skey[3] = sk.p;
|
|
skey[4] = sk.q;
|
|
skey[5] = sk.u;
|
|
/* make an empty list of factors */
|
|
if (retfactors)
|
|
*retfactors = xmalloc_clear( 1 * sizeof **retfactors );
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
rsa_check_secret_key( int algo, MPI *skey )
|
|
{
|
|
RSA_secret_key sk;
|
|
|
|
if( !is_RSA(algo) )
|
|
return G10ERR_PUBKEY_ALGO;
|
|
|
|
sk.n = skey[0];
|
|
sk.e = skey[1];
|
|
sk.d = skey[2];
|
|
sk.p = skey[3];
|
|
sk.q = skey[4];
|
|
sk.u = skey[5];
|
|
if( !check_secret_key( &sk ) )
|
|
return G10ERR_BAD_SECKEY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rsa_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey )
|
|
{
|
|
RSA_public_key pk;
|
|
|
|
if( algo != 1 && algo != 2 )
|
|
return G10ERR_PUBKEY_ALGO;
|
|
|
|
pk.n = pkey[0];
|
|
pk.e = pkey[1];
|
|
resarr[0] = mpi_alloc( mpi_get_nlimbs( pk.n ) );
|
|
public( resarr[0], data, &pk );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rsa_decrypt( int algo, MPI *result, MPI *data, MPI *skey )
|
|
{
|
|
RSA_secret_key sk;
|
|
|
|
if( algo != 1 && algo != 2 )
|
|
return G10ERR_PUBKEY_ALGO;
|
|
|
|
sk.n = skey[0];
|
|
sk.e = skey[1];
|
|
sk.d = skey[2];
|
|
sk.p = skey[3];
|
|
sk.q = skey[4];
|
|
sk.u = skey[5];
|
|
*result = mpi_alloc_secure( mpi_get_nlimbs( sk.n ) );
|
|
secret( *result, data[0], &sk );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rsa_sign( int algo, MPI *resarr, MPI data, MPI *skey )
|
|
{
|
|
RSA_secret_key sk;
|
|
|
|
if( algo != 1 && algo != 3 )
|
|
return G10ERR_PUBKEY_ALGO;
|
|
|
|
sk.n = skey[0];
|
|
sk.e = skey[1];
|
|
sk.d = skey[2];
|
|
sk.p = skey[3];
|
|
sk.q = skey[4];
|
|
sk.u = skey[5];
|
|
resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.n ) );
|
|
secret( resarr[0], data, &sk );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rsa_verify( int algo, MPI hash, MPI *data, MPI *pkey )
|
|
{
|
|
RSA_public_key pk;
|
|
MPI result;
|
|
int rc;
|
|
|
|
if( algo != 1 && algo != 3 )
|
|
return G10ERR_PUBKEY_ALGO;
|
|
pk.n = pkey[0];
|
|
pk.e = pkey[1];
|
|
result = mpi_alloc ( mpi_nlimb_hint_from_nbits (160) );
|
|
public( result, data[0], &pk );
|
|
rc = mpi_cmp( result, hash )? G10ERR_BAD_SIGN:0;
|
|
mpi_free(result);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
unsigned int
|
|
rsa_get_nbits( int algo, MPI *pkey )
|
|
{
|
|
if( !is_RSA(algo) )
|
|
return 0;
|
|
return mpi_get_nbits( pkey[0] );
|
|
}
|
|
|
|
|
|
/****************
|
|
* Return some information about the algorithm. We need algo here to
|
|
* distinguish different flavors of the algorithm.
|
|
* Returns: A pointer to string describing the algorithm or NULL if
|
|
* the ALGO is invalid.
|
|
* Usage: Bit 0 set : allows signing
|
|
* 1 set : allows encryption
|
|
*/
|
|
const char *
|
|
rsa_get_info( int algo,
|
|
int *npkey, int *nskey, int *nenc, int *nsig, int *r_usage )
|
|
{
|
|
*npkey = 2;
|
|
*nskey = 6;
|
|
*nenc = 1;
|
|
*nsig = 1;
|
|
|
|
switch( algo ) {
|
|
case 1: *r_usage = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; return "RSA";
|
|
case 2: *r_usage = PUBKEY_USAGE_ENC; return "RSA-E";
|
|
case 3: *r_usage = PUBKEY_USAGE_SIG; return "RSA-S";
|
|
default:*r_usage = 0; return NULL;
|
|
}
|
|
}
|