<<< Date Index >>>     <<< Thread Index >>>

Breaking RSA: Totient indirect factorization



Breaking RSA: Totient indirect factorization
=============================================

Author: Alex Bassas Serramia

(I'm very sorry for my poor english but it's not my first language).



Introduction
------------

This document tries to expose an algorithm that allows the RSA
modulus' totien factorization and then
breaking RSA.


RSA algorithm
-------------

RSA algorithm is generated the following way:

                    1) m = p*q            -> RSA modulus
                    2) t = (p-1)*(q-1)    -> totien(m)

                    3) (e*d) mod t = 1 mod t

                    4) a^e mod m = b
                    5) b^d mod m = a

e = public key
d = private key



RSA strength
------------

Equation (3) shows that is possible to recover private key "d" knowing
public key "e" and totient "t".


"a^n mod m" sequence
--------------------

To know about totien we have to examine the sequence "a^n mod m", one
sample is "2^n mod 11" (n from 1 to 11)
with totien 10:

                  2, 4, 8, 5, 10, 9, 7, 3, 6,*1, 2

At "n=10" we have one "1" because "a^totien(m) mod m" is always one
(Euler's theorem).

The sequence "3^n mod 11" has the same totien 10:

                  3, 9, 5, 4,*1, 3, 9, 5, 4,*1, 3

but we have two "1", "n=5" y "n=10" (totien), in this case we can
observe the cyclic nature of the "a^n mod m"
because we always have the same list of numbers before each "1".



"a^n mod m = 1" equation
------------------------

The cyclic nature of the "a^n mod m" sequence take us to the first statement:


       1) - The exponent's values of the "a^n mod m = 1" solutions are always
            totien's divisors.



The sequence "3^n mod 11" has "5" and "10" as solutions, they are
totien's divisors (totien(11) = 10).

                  3, 9, 5, 4,*1, 3, 9, 5, 4,*1, 3


Maximazing "a^n mod m = 1" solutions
------------------------------------

The second statement is:

      2) - If "x" is a totien's divisor  then "a^x^n mod m = 1" will
multiply the
           "a^n mod m = 1" solutions by "x".


Ex.: The "2^n mod 11" sequence has one "1"

                  2, 4, 8, 5, 10, 9, 7, 3, 6,*1, 2

"2^5^n=32^n", "32^n mod 11" produces 5 ones:

                  10,*1, 10,*1, 10,*1, 10,*1, 10,*1, 10



"a^n mod m = 1" limit
---------------------

The third statement is:

      3) - If x is not yet a totien's divisor then "a^x^n mod m = 1"
will have the same
           solutions that "a^n mod m = 1" but with the values permuted.


Ex.: The "2^n mod 11" sequence has one "1"

                  2, 4, 8, 5, 10, 9, 7, 3, 6,*1, 2

"2^2^n= 4^n", "4^n mod 11" has two "1"

                  4, 5, 9, 3,*1, 4, 5, 9, 3,*1, 4

"4^2^n= 16^n", "16^n mod 11" is still having two "1" but with the
values permuted.

                  5, 3, 4, 9,*1, 5, 3, 4, 9,*1, 5



Ending the sequence
--------------------

The last statement is:

      4) - If "a" contains by power all the totien's divisors then
"a^n mod m" will
           always be "1".


Ex.: "2^2^5^n= 1024^n mod 11

                  *1,*1,*1,*1,*1,*1,*1,*1,*1,*1,*1


Euler's extension
-----------------

This statement is a consequence of the statement number 3 but I don't
use it in the algorithm.

      5) - If "n" is greater than the biggest number of the
coincidents totient's
           divisors then:

           a^((n-1)(t*(t+1)/2)) mod m = 1 mod m    (t = totien(m))


Algorithm
---------

- Repeat "a = a^n mod m" with n from 2 to m, saving all the results in
a table until a == 1 (Statement 4).
- Examine the table from end to begining printing "n" if the number of
"ones" is divided by "n" (Statements 1,2,3),



Impact
------

PKI vendors must change modulus generator algorithms to discard totients with
lower factors. Current certificates can be factorized in lower time
than expected
and compromised, vendors must review each one separately.



Credits
--------

Alex Bassas serramia
Barcelon (SPAIN)



Sample
------

/*

       (c) Alex Bassas Serramia,
           Barcelona,
           SPAIN.

*/

#ifdef WIN32
#include <windows.h>
#include <io.h>
#else
typedef long long ULONG64;
#define TRUE  (-1)
#define FALSE (0)
#endif
#include <stdio.h>
#include <time.h>

ULONG64 getrand (void) {
ULONG64 n,num;

 for (n=0;n<8;n++) {
   num = (num << 8) | (rand()%256);
 }
 return (num);
}

ULONG64 expmod (ULONG64 x,ULONG64 n,ULONG64 m) {
ULONG64 r = 1;

 while (n) {
   if (n&1) {
     r = (r*x)%m;
     n = n - 1;
   }
   x = (x*x)%m;
   n = n / 2;
 }
 return (r);
}

int isprime (ULONG64 p) {
ULONG64 k,a;

 for (k=0;k<8;k++) {
   a = getrand() % p;
   if (expmod(a,p-1,p) != 1) {
     return (FALSE);
   }
 }
 return (TRUE);
}

ULONG64 value (ULONG64 bits) {
ULONG64 n;

 n = 1 << bits;
 return (n);
}

ULONG64 getprime (ULONG64 bits,ULONG64 mbits) {
ULONG64 num,m;

 m = value(mbits);
 do {
   num = getrand();
   if (bits < 64) {
     num %= value(bits);
   }
 }
 while ((num<m) || (num<=1) || !isprime(num));
 return (num);
}

struct s_reg {
 ULONG64 base;
 ULONG64 exp;
 ULONG64 res;
};

int num_reg (FILE *f) {
int l;

 fseek (f,0,SEEK_END);
 l = ftell(f);
 return (l/sizeof(struct s_reg));
}

int go_reg (FILE *f,int num) {
 fseek(f,num*sizeof(struct s_reg),SEEK_SET);
 return (TRUE);
}

int read_reg (FILE *f,int num,struct s_reg *r) {
 if (go_reg(f,num)) {
   fread (r,sizeof(struct s_reg),1,f);
 }
 return (TRUE);
}

int write_reg (FILE *f,int num,struct s_reg *r) {
 if (go_reg(f,num)) {
   fwrite (r,sizeof(struct s_reg),1,f);
 }
 return (TRUE);
}

void delete_table (char *name) {
FILE *f;

 f = fopen (name,"wb");
 if (f != NULL) {
   fclose(f);
 }
}

void expand_table (char *name,ULONG64 m) {
FILE *f;
int l;
struct s_reg r;

 f = fopen (name,"a+b");
 if (f != NULL) {
   l = num_reg (f);
   if (!l) {
     r.base = 2;
     r.exp = 2;
     r.res = expmod (r.base,r.exp,m);
     write_reg (f,0,&r);
     l = 1;
   }
   read_reg (f,l-1,&r);
   while (r.res != 1) {
     r.base = r.res;
     r.exp++;
     r.res = expmod (r.base,r.exp,m);
     write_reg (f,l++,&r);
   }
   fclose (f);
 }
}

void reverse_table (char *name,ULONG64 m) {
FILE *f;
struct s_reg a,b;
int l,n;
ULONG64 r,e;

 f = fopen (name,"rb");
 if (f != NULL) {
   l = num_reg (f);
   e = 1;
   for (n=l-1;n>=0;n--) {
     read_reg (f,n,&a);
     read_reg (f,n+1,&b);
     r = expmod (a.base,e,m);
     if (r != 1) {
       printf ("reverse\texp = %I64i\r\n",a.exp);
       e *= a.exp;
     }
   }
   fclose (f);
 }
}

#define TABLE   "test.dat"
#define P_BITS  32

int main (int argc,char *argv[],char *envp[]) {
ULONG64 p,q,m,t;

 srand((unsigned)time(NULL));
 p = getprime(P_BITS/2,0);
 printf ("p = %I64i\r\n",p);
 q = getprime(P_BITS/2,0);
 printf ("q = %I64i\r\n",q);
 m = p*q;
 printf ("m = %I64i\r\n",m);
 t = (p-1)*(q-1);
 printf ("t = %I64i\r\n",t);
 delete_table (TABLE);
 expand_table (TABLE,m);
 reverse_table (TABLE,m);
 return (0);
}