📄 rsa.cpp
字号:
/*
* The RSA PK cryptosystem
*
* Copyright (C) 2006 Christophe Devine
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License, version 2.1 as published by the Free Software Foundation.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
/*
* RSA was designed by Ron Rivest, Adi Shamir and Len Adleman.
*
* http://theory.lcs.mit.edu/~rivest/rsapaper.pdf
* http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf
*/
#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE 1
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "rsa.h"
/*
* Generate a RSA keypair of nbits in size according
* to the specified public exponent.
*
* Function "rng_func" takes one argument (rng_state)
* and should return a random unsigned long.
*
* Returns 0 if successful, or ERR_RSA_KEYGEN_FAILED.
*/
int rsa_gen_key( rsa_context *ctx, uint nbits, uint exponent,
ulong (*rng_func)(void *), void *rng_state )
{
int ret;
mpi P1, Q1, H, G;
mpi_init( &P1, &Q1, &H, &G, NULL );
memset( ctx, 0, sizeof( rsa_context ) );
/*
* find primes P and Q with Q < P so that
* GCD( E, (P-1)*(Q-1) ) == 1
*/
CHK( mpi_lset( &ctx->E, exponent ) );
do
{
CHK( mpi_gen_prime( &ctx->P, nbits / 2, 0,
rng_func, rng_state ) );
CHK( mpi_gen_prime( &ctx->Q, nbits / 2, 0,
rng_func, rng_state ) );
if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 )
mpi_swap( &ctx->P, &ctx->Q );
CHK( mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) );
CHK( mpi_sub_int( &P1, &ctx->P, 1 ) );
CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) );
CHK( mpi_mul_mpi( &H, &P1, &Q1 ) );
CHK( mpi_gcd( &G, &ctx->E, &H ) );
}
while( mpi_cmp_int( &G, 1 ) != 0 );
/*
* D = E^-1 mod ((P-1)*(Q-1))
* DP = D mod (P - 1)
* DQ = D mod (Q - 1)
* QP = Q^-1 mod P
*/
CHK( mpi_inv_mod( &ctx->D , &ctx->E, &H ) );
CHK( mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ) );
CHK( mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ) );
CHK( mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ) );
ctx->len = ( mpi_size( &ctx->N ) + 7 ) >> 3;
cleanup:
debug_print(&P1);
debug_print(&Q1);
debug_print(&H);
debug_print(&G);
mpi_free( &P1, &Q1, &H, &G, NULL );
if( ret != 0 )
{
rsa_free( ctx );
return( ERR_RSA_KEYGEN_FAILED | ret );
}
return( 0 );
}
/*
* Perform an RSA public key operation. This function
* does not take care of message padding: both ilen and
* olen must be equal to the modulus size (ctx->len).
*
* Returns 0 if successful, or ERR_RSA_PUBLIC_FAILED.
*/
int rsa_public( rsa_context *ctx, uchar *input, uint ilen,
uchar *output, uint olen )
{
int ret;
mpi T;
if( ilen != ctx->len || olen != ctx->len )
return( ERR_RSA_PUBLIC_FAILED );
mpi_init( &T, NULL );
CHK( mpi_import( &T, input, ilen ) );
if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 )
{
debug_print(&T);
mpi_free( &T, NULL );
return( ERR_RSA_PUBLIC_FAILED );
}
CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N ) );
CHK( mpi_export( &T, output, &olen ) );
cleanup:
debug_print(&T); //32
mpi_free( &T, NULL );
if( ret != 0 )
return( ERR_RSA_PUBLIC_FAILED | ret );
return( 0 );
}
/*
* Perform an RSA private key operation. This function
* does not take care of message padding: both ilen an
* olen must be equal to the modulus size (ctx->len).
*
* Returns 0 if successful, or ERR_RSA_PRIVATE_FAILED.
*/
int rsa_private( rsa_context *ctx, uchar *input, uint ilen,
uchar *output, uint olen )
{
int ret;
mpi T, T1, T2;
if( ilen != ctx->len || olen != ctx->len )
return( ERR_RSA_PRIVATE_FAILED );
mpi_init( &T, &T1, &T2, NULL );
CHK( mpi_import( &T, input, ilen ) );
if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 )
{
debug_print(&T);
mpi_free( &T, NULL );
return( ERR_RSA_PRIVATE_FAILED );
}
#if 0
CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N ) );
#else
/*
* faster decryption using the CRT
*
* T1 = input ^ dP mod P
* T2 = input ^ dQ mod Q
*/
CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P ) );
CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q ) );
/*
* T = (T1 - T2) * (Q^-1 mod P) mod P
*/
CHK( mpi_sub_mpi( &T, &T1, &T2 ) );
CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) );
CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) );
/*
* output = T2 + T * Q
*/
CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) );
CHK( mpi_add_mpi( &T, &T2, &T1 ) );
#endif
CHK( mpi_export( &T, output, &olen ) );
cleanup:
debug_print(&T); //32
debug_print(&T1); //32
debug_print(&T2); //16
mpi_free( &T, &T1, &T2, NULL );
if( ret != 0 )
return( ERR_RSA_PRIVATE_FAILED | ret );
return( 0 );
}
/*
* Returns 0 if the public key is valid,
* or ERR_RSA_KEY_CHECK_FAILED.
*/
int rsa_check_pubkey( rsa_context *ctx )
{
if( ( ctx->N.p[0] & 1 ) == 0 ||
( ctx->E.p[0] & 1 ) == 0 )
return( ERR_RSA_KEY_CHECK_FAILED );
if( mpi_size( &ctx->N ) < 128 ||
mpi_size( &ctx->N ) > 4096 )
return( ERR_RSA_KEY_CHECK_FAILED );
if( mpi_size( &ctx->E ) < 2 ||
mpi_size( &ctx->E ) > 64 )
return( ERR_RSA_KEY_CHECK_FAILED );
return( 0 );
}
/*
* Returns 0 if the private key is valid,
* or ERR_RSA_KEY_CHECK_FAILED.
*/
int rsa_check_privkey( rsa_context *ctx )
{
int ret = 0;
mpi TN, P1, Q1, H, G;
mpi_init( &TN, &P1, &Q1, &H, &G, NULL );
CHK( mpi_mul_mpi( &TN, &ctx->P, &ctx->Q ) );
CHK( mpi_sub_int( &P1, &ctx->P, 1 ) );
CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) );
CHK( mpi_mul_mpi( &H, &P1, &Q1 ) );
CHK( mpi_gcd( &G, &ctx->E, &H ) );
if( mpi_cmp_mpi( &TN, &ctx->N ) == 0 &&
mpi_cmp_int( &G, 1 ) == 0 )
{
debug_print(&TN); //32
debug_print(&P1); //16
debug_print(&Q1); //16
debug_print(&H); //32
debug_print(&G); //2
mpi_free( &TN, &P1, &Q1, &H, &G, NULL );
return( 0 );
}
cleanup:
debug_print(&TN);
debug_print(&P1);
debug_print(&Q1);
debug_print(&H);
debug_print(&G);
mpi_free( &TN, &P1, &Q1, &H, &G, NULL );
return( ERR_RSA_KEY_CHECK_FAILED | ret );
}
/*
* Add the PKCS1 v1.5 padding and perform a public RSA.
*
* ctx points to an RSA public key
* input buffer holding the data to be encrypted
* ilen length of the plaintext; cannot be longer
* than the modulus, minus 3+8 for padding
* output buffer that will hold the ciphertext
* olen must be the same as the modulus size
* (for example, 128 if RSA-1024 is used)
*
* Returns 0 if successful, or ERR_RSA_ENCRYPT_FAILED
*/
int rsa_pkcs1_encrypt( rsa_context *ctx,
uchar *input, uint ilen,
uchar *output, uint olen )
{
int nb_pad;
uchar *p = output;
if( olen != ctx->len || olen < ilen + 11 )
return( ERR_RSA_ENCRYPT_FAILED );
nb_pad = olen - 3 - ilen;
*p++ = 0;
*p++ = RSA_CRYPT;
while( nb_pad-- > 0 )
{
do { *p = (uchar) rand(); } while( *p == 0 );
p++;
}
*p++ = 0;
memcpy( p, input, ilen );
if( rsa_public( ctx, output, olen, output, olen ) != 0 )
return( ERR_RSA_ENCRYPT_FAILED );
return( 0 );
}
/*
* Perform a private RSA and remove the PKCS1 v1.5 padding.
*
* ctx points to an RSA private key
* input buffer holding the encrypted data
* ilen must be the same as the modulus size
* output buffer that will hold the plaintext
* olen size of output buffer, will be updated
* to contain the length of the plaintext
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -