RSA Encryption Schemes

From Crypto++ Wiki
Jump to: navigation, search

For the main RSA page, visit RSA Cryptography. For signature schemes, visit RSA Signature Schemes. For a detailed treatment of key generation, loading, saving, validation, and formats, see Keys and Formats.

Remarks

Plaintext Length

The maximum length of a plaintext string that can be encrypted with RSA can be determined by FixedMaxPlaintextLength. On error, FixedMaxPlaintextLength returns 0. The value returned by FixedMaxPlaintextLength is directly related to the modulus size n.

Ciphertext Length

The length of the resulting ciphertext can be determined by FixedCiphertextLength. On error, FixedCiphertextLength returns 0. The value returned by FixedCiphertextLength is directly related to the modulus size n.

Buffers

When encrypting and decrypting using Crypto++, the plaintext and ciphertext buffers can be the same. This effectively means in-place encryption and decryption can be performed. However, always make sure the buffer is large enough to hold the encrypted data (which will be larger than the plaintext). If the buffers are not the same, the buffers must not overlap. Finally, both CiphertextLength and MaxPlaintextLength return 0 on error.

// Encrypt
size_t cipherTextSize = publicKey.CiphertextLength( plainTextSize );
assert( 0 != cipherTextSize );
assert( plainTextSize >= cipherTextSize );

publicKey.Encrypt(rnd, (byte*)plainText, plainTextSize, (byte*)plainText);

...

// Decrypt
size_t plainTextSize = privateKey.MaxPlaintextLength( cipherTextSize );
assert( 0 != plainTextSize );
assert( cipherTextSize >= plainTextSize );

DecodingResult result = privateKey.Decrypt(rnd, (byte*)plainText,
    cipherTextSize, (byte*)plainText);
assert( plainTextSize == result.messageLength );

Sample Programs

Profiling Key Generation

The following demonstrates profiling key generation times using Crypto++'s ThreadUserTimer.

try
{  
  AutoSeededRandomPool prng;
  RSA::PrivateKey rsa;
  ThreadUserTimer timer(TimerBase::MILLISECONDS);

  timer.StartTimer();

  const int bits = 2048;
  rsa.GenerateRandomWithKeySize(prng, bits);

  ///////////////////////////////////////

  unsigned long elapsed = timer.GetCurrentTimerValue();
  unsigned long ticks = timer.TicksPerSecond();
  unsigned long seconds = elapsed / ticks;

  // days, hours, minutes, seconds, 100th seconds
  unsigned int d=0, h=0, m=0, s=0, p=0;

  p = ((elapsed * 100) / ticks) % 100;
  s = seconds % 60;
  m = (seconds / 60) % 60;
  h = (seconds / 60 / 60) % 60;
  d = (seconds / 60 / 60 / 24) % 24;

  float fs = (seconds + ((float)p/100));

  ///////////////////////////////////////

  stringstream ss;

  if(d) {
    ss << d << ((d == 1) ? " day, " : " days, ");
    goto print_hours;
  }

  if(h) {
    print_hours:
      ss << h << ((h == 1) ? " hour, " : " hours, ");
      goto print_minutes;
  }

  if(m) {
    print_minutes:
      ss << m << ((m == 1) ? " minute, " : " minutes, ");
  }

  ss << s << ((s == 1) ? " second" : " seconds");        

  cout << "Elapsed time for " << bits << " RSA key: ";
  cout << fixed << setprecision(2) << fs << "s";
  if(seconds)
    cout << " (" << ss.str() << ")";
  cout << endl;
}

catch(CryptoPP::Exception& e)
{
  ...
}
...

Typical runs will look similar to below.

cryptopp$ rsa_kgen.exe 61440
Elapsed time for 61140 RSA key: 25654.01s (7 hours, 7 minutes, 34 seconds)
cryptopp$ rsa_kgen.exe 30720
Elapsed time for 30720 RSA key: 2255.30s (37 minutes, 35 seconds)
cryptopp$ rsa_kgen.exe 15360
Elapsed time for 15360 RSA key: 285.05s (4 minutes, 45 seconds)
cryptopp$ rsa_kgen.exe 11776
Elapsed time for 11776 RSA key: 142.52s (2 minutes, 22 seconds)
cryptopp$ rsa_kgen.exe 8192
Elapsed time for 8192 RSA key: 43.08s (43 seconds)
cryptopp$ rsa_kgen.exe 4096
Elapsed time for 4096 RSA key: 0.70s
cryptopp$ rsa_kgen.exe 2048
Elapsed time for 2048 RSA key: 0.09s
cryptopp$ rsa_kgen.exe 1024
Elapsed time for 1024 RSA key: 0.01s
cryptopp$ rsa_kgen.exe 512
Elapsed time for 512 RSA key: 0.00s
cryptopp$ rsa_kgen.exe 256
Elapsed time for 256 RSA key: 0.00s

RSA Encryption Scheme (OAEP and SHA)

The following code demonstrates RSA encryption using OAEP. SHA is used to generate the padding bits and mask the input (see OAEP_Base::Pad).

////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;

InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize( rng, 1536 );

RSA::PrivateKey privateKey( parameters );
RSA::PublicKey publicKey( parameters );

////////////////////////////////////////////////
// Secret to protect
static const int SECRET_SIZE = 16;
SecByteBlock plaintext( SECRET_SIZE );
memset( plaintext, 'A', SECRET_SIZE );

////////////////////////////////////////////////
// Encrypt
RSAES_OAEP_SHA_Encryptor encryptor( publicKey );

// Now that there is a concrete object, we can validate
assert( 0 != encryptor.FixedMaxPlaintextLength() );
assert( plaintext.size() <= encryptor.FixedMaxPlaintextLength() );

// Create cipher text space
size_t ecl = encryptor.CiphertextLength( plaintext.size() );
assert( 0 != ecl );
SecByteBlock ciphertext( ecl );

encryptor.Encrypt( rng, plaintext, plaintext.size(), ciphertext );

////////////////////////////////////////////////
// Decrypt
RSAES_OAEP_SHA_Decryptor decryptor( privateKey );

// Now that there is a concrete object, we can check sizes
assert( 0 != decryptor.FixedCiphertextLength() );
assert( ciphertext.size() <= decryptor.FixedCiphertextLength() );

// Create recovered text space
size_t dpl = decryptor.MaxPlaintextLength( ciphertext.size() );
assert( 0 != dpl );
SecByteBlock recovered( dpl );

DecodingResult result = decryptor.Decrypt( rng,
    ciphertext, ciphertext.size(), recovered );

// More sanity checks
assert( result.isValidCoding );        
assert( result.messageLength <= decryptor.MaxPlaintextLength( ciphertext.size() ) );

// At this point, we can set the size of the recovered
//  data. Until decryption occurs (successfully), we
//  only know its maximum size
recovered.resize( result.messageLength );

// SecByteBlock is overloaded for proper results below
assert( plaintext == recovered );

cout << "Recovered plain text" << endl;

RSA Encryption Scheme (OAEP and SHA) using Filters

Using Crypto++ pipelining, the previous program can be reduced to the following.

////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;

InvertibleRSAFunction params;
params.GenerateRandomWithKeySize( rng, 1536 );

RSA::PrivateKey privateKey( params );
RSA::PublicKey publicKey( params );

string plain="RSA Encryption", cipher, recovered;

////////////////////////////////////////////////
// Encryption
RSAES_OAEP_SHA_Encryptor e( publicKey );

StringSource ss1( plain, true,
    new PK_EncryptorFilter( rng, e,
        new StringSink( cipher )
    ) // PK_EncryptorFilter
 ); // StringSource

////////////////////////////////////////////////
// Decryption
RSAES_OAEP_SHA_Decryptor d( privateKey );

StringSource ss2( cipher, true,
    new PK_DecryptorFilter( rng, d,
        new StringSink( recovered )
    ) // PK_DecryptorFilter
 ); // StringSource

assert( plain == recovered );

Downloads

RSA-ES-OAEP-SHA-Test.zip - Demonstrates RSA-ES (OAEP/SHA) - 5KB

RSA-ES-OAEP-SHA-Filter-Test.zip - Demonstrates RSA-ES (OAEP/SHA) using Filters - 5KB