#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define KEY	    907
#define PRIME_NUM   9973
#define MAX_DIV_TIME 8


#define RANDOME        118956
#define KEY_OFFSET   89048231
#define MAX_NUM     100000000
#define UINT8 unsigned char
#define INT32 long
#define INT16 long
#define PASSWORD_OK 1
#define PASSWORD_NG 0

UINT8 ALCheckSimLockPasswd( INT32 imeiKey, INT32 unlockKey );

void  setOddEvenDigits( INT32 d8digits, INT16 *pwEvenDigits, INT16 *pwOddDigits );

UINT8 checkEachPassWord( INT16 wEachPassWord, INT16 wEachMobileId );

INT32 getPassWord( INT32 dMobileId );

INT16 getEachPassWord( INT16 wEachMobileId );

INT32 builtLong( INT16 wShort1, INT16 wShort2 );

void  splitShort( INT32 dLong, INT16 *pwShort1, INT16 *pwShort2 );

INT16 getInverse( INT16 iInput );



/*--------------------------- Local statics ----------------------------------*/

/*--------------------------- Local functions --------------------------------*/


/*----------------------------------------------------------------------------*/
/*
** This function performs whether the input password is valid against a ID
*/
UINT8	ALCheckSimLockPasswd( INT32 imeiKey, INT32 unlockKey )
{
	UINT8 wResult;
	INT16 wEvenPassWord, wOddPassWord;
	INT16 wEvenMobileId, wOddMobileId;
	INT32  dx;

   imeiKey   %= MAX_NUM;
   unlockKey %= MAX_NUM;

	dx = (imeiKey + RANDOME) % MAX_NUM ;

	setOddEvenDigits( dx, &wEvenMobileId, &wOddMobileId );

	unlockKey -= KEY_OFFSET;

	if( unlockKey < 0 ){
		unlockKey += MAX_NUM;
	}

	splitShort( unlockKey, &wOddPassWord, &wEvenPassWord );

	wResult = checkEachPassWord( wEvenPassWord, wEvenMobileId );

	if( wResult == PASSWORD_NG ){
		return( PASSWORD_NG );
	}

	wResult = checkEachPassWord( wOddPassWord, wOddMobileId );

	return( wResult );
}

/*----------------------------------------------------------------------------*/
/*
** This function makes a 2N-digits integer split a pair of N-digits integers.
** One N-digits integer is composed of even digits of 2N-digits integer..
** Another N-digits interger is composed of odd digits 2N-digits interger.
** The digit order is same(keep the order)
**
** 2N-digits interger   : 76543210
** Odd N-digits intrger :     7531
** Even N-digits integer:     6420
*/
void setOddEvenDigits( INT32 d8digits, INT16 *pwEvenDigits, INT16 *pwOddDigits )
{
	INT16 wDigit;
	INT32  dRest, dDiv;

	dRest = d8digits;
	*pwEvenDigits = 0;
	*pwOddDigits  = 0;

	dDiv = MAX_NUM;

	while( dDiv > 1 ){
		dDiv /= 10;
		wDigit = dRest / dDiv;
		dRest = dRest % dDiv;

		*pwOddDigits *= 10;
		*pwOddDigits += wDigit;

		dDiv /= 10;
		wDigit = dRest / dDiv;
		dRest = dRest % dDiv;

		*pwEvenDigits *= 10;
		*pwEvenDigits += wDigit;
	}
}

/*----------------------------------------------------------------------------*/

UINT8 checkEachPassWord( INT16 wEachPassWord, INT16 wEachMobileId )
{
	INT16 dResult;

	if( wEachPassWord > PRIME_NUM ){
		return( PASSWORD_NG);
	}
	if( (wEachPassWord == PRIME_NUM) || (wEachPassWord == 0) ){
		if( (wEachPassWord + wEachMobileId) == PRIME_NUM ){
			return( PASSWORD_OK );
		}else{
			return( PASSWORD_NG );
		}
	}

	dResult = ( wEachPassWord * wEachMobileId ) % PRIME_NUM;

	if( dResult == KEY ){
		return( PASSWORD_OK );     
	}else{
		return( PASSWORD_NG );
	}
}

/*----------------------------------------------------------------------------*/
/*
** provide one password from one ID
*/
INT32 getPassWord( INT32 dMobileId )
{
	INT16 wEvenMobileId, wOddMobileId;
	INT16 wEvenPassWord, wOddPassWord;
	INT32 dx, dPassWord;

	dx = ( dMobileId + RANDOME ) % MAX_NUM ;

	setOddEvenDigits( dx, &wEvenMobileId, &wOddMobileId );

	wEvenPassWord = getEachPassWord( wEvenMobileId );

	wOddPassWord = getEachPassWord( wOddMobileId );

	dPassWord = builtLong( wEvenPassWord, wOddPassWord );

	dPassWord += KEY_OFFSET;

	dPassWord = dPassWord % MAX_NUM;

	return( dPassWord );
}

/*----------------------------------------------------------------------------*/

INT16 getEachPassWord( INT16 wEachMobileId )
{
	INT16 wInv, wEachPassWord;

	if( wEachMobileId == 0 ){
		return( PRIME_NUM );
	}

	if( wEachMobileId == PRIME_NUM ){
		return( 0 );
	}

	wInv = getInverse( wEachMobileId );

	wEachPassWord = ( KEY * wInv ) % PRIME_NUM ;

	return( (INT16) wEachPassWord );
}

/*----------------------------------------------------------------------------*/
/*
** This function makes a 2N-digits integer using two N-digits integeres.
** The digti order is reverse.
**
** N-digits intrger1 :     7531
** N-digits integer2 :     6420
** 2N-digits integer : 01234567
*/
INT32 builtLong( INT16 wShort1, INT16 wShort2 )
{
	INT16 wDigit;
	INT32 dCt;
	INT32 dLong;

	dLong = 0;

	dCt = 1;

	while( dCt < MAX_NUM ){

		wDigit = wShort1 % 10 ;
		wShort1 /= 10;

		dLong *= 10;
		dLong += wDigit;
		dCt *= 10;

		wDigit = wShort2 % 10 ;
		wShort2 /= 10;

		dLong *= 10;
		dLong += wDigit;
		dCt *= 10;
	}

	return( dLong );

}

/*----------------------------------------------------------------------------*/
/*
** This function makes two N-digits integers using a 2N-digits integer..
** The digti order is reverse.
**
** 2N-digits integer : 76543210
** N-digits intrger1 :     1357
** N-digits integer2 :     0246
*/
void splitShort( INT32 dLong, INT16 *pwShort1, INT16 *pwShort2 )
{
	INT16 wDigit;
	INT32 dCt;

	dCt = 1;

	*pwShort1 = 0;
	*pwShort2 = 0;

	while( dCt < MAX_NUM ){
		wDigit = dLong % 10;
		dLong /= 10;

		*pwShort1 *= 10;
		*pwShort1 += wDigit;

		dCt *= 10;

		wDigit = dLong % 10;
		dLong /= 10;
		
		*pwShort2 *= 10;
		*pwShort2 += wDigit;

		dCt *= 10;

	}
}

/*----------------------------------------------------------------------------*/
/*
** This function returns the reverse Y such as XY = 1 mod P where P is a prime n
umber.
** This is based on Euclid's algorithm.
*/
INT16 getInverse( INT16 iInput )
{
	/*
	** Return x such as that ax = 1 mod N 
	** where 0 < a < N 
	*/

	INT16 g_0, u_0, v_0 ;
	INT16 g_1, u_1, v_1 ;
	INT16 g_2, u_2, v_2 ;
	INT16 x, y;

	/*
	** Initialization 
	**
	** g(0) = u(0)*N + v(0)*a = n
	** g(1) = u(1)*N + v(1)*a = a
	**      :
	** g(n) = u(n)*N + v(n)*a 
	*/

	g_0 = PRIME_NUM ;
	u_0 = 1;
	v_0 = 0;

	g_1 = iInput ;
	u_1 = 0;
	v_1 = 1;

	/*
	** g(n) = g(n-2) mod g(n-1)
	**      = g(n-2) - y(n-1)*g(n-1)
	**      = {u(n-2) - y(n-1)*u(n-2)}*N + {v(n-2) - y(n-1)*v(n-1)}*a
	**
	** u(n) = u(n-2) - y(n-1)*u(n-1)
	** v(n) = v(n-2) - y(n-1)*v(n-1)
	**
	** if g(n) = 1,
	**
	**    u(n)*N + v(n)*a = 1 mod N
	**             v(n)*a = 1 mod N
	**             x = v(n)
	*/
	while ( g_1 != 1 ){
		y   = g_0 / g_1 ;
		g_2 = g_0 - y * g_1 ;
		u_2 = u_0 - y * u_1 ;
		v_2 = v_0 - y * v_1 ;

		g_0 = g_1;
		u_0 = u_1;
		v_0 = v_1;

		g_1 = g_2;
		u_1 = u_2;
		v_1 = v_2;

	}
	
	x = v_1;

	if( x < 0 ){
		return( x + PRIME_NUM );
	}else{
		return( x );
	}
}
void main (int argc, char *argv[])
{
    char tmp1[81];
    char CurrentIMEI[16];
    long imei= 0;
    long pass;
    printf("\n******************************\n");
    printf("*** SIMUNLOCK by S.S. v1.0 ***\n");
    printf("******************************\n");
    if (argc == 1)
    {
      printf("ParamŠtre IMEI manquant\n");
      printf("Tapez les 8 derniers chiffres du nø IMEI exception faite du check digit\n\n");
      printf("IMEI:> ");
      fflush(stdin);
      scanf("%ld",&imei);
      pass = getPassWord(imei);
      printf("\nClef simlock = %ld\n\n",pass);
      fflush(stdin);
	}
    else
    {
      imei = atol(argv[1]);
      pass = getPassWord(imei);
      printf("\nClef simlock = %ld\n\n",pass);
    }
    printf("Usage  *#7465625*AB*%ld#		AB=11  Network Lock ON\n",pass);
    printf("                                    AB=12  Network Lock Off\n\n");
    exit(0);

}