Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Cracking KidRSA

The security of KidRSA relies on that it is difficult for an attacker to determine dd if they know ee and nn. For small values of ee, dd, and nn, this is certainly not true since checking all possible values of dd to see if 1ed(modn)1 \equiv e \cdot d \pmod{n}.

For example, using the keys in the first KidRSA example in the previous section, the Python module timeit() and a single for loop in the function bruteForceKidRSA() can determine through the brute force method that d=73d=73 one hundred times in 0.0006409000000076048 seconds using only the information e=103e = 103 and n=537n = 537 on a non-specialized laptop computer in 2019.

import timeit

def bruteForceKidRSA( e, n):
    for d in range(0, e):
        if (d * e) % n == 1:
            return (d)

print(timeit.timeit( 'bruteForceKidRSA(103, 537)', number=100, setup="from __main__ import bruteForceKidRSA"))
print('d =', bruteForceKidRSA(103, 537))
0.0006409000000076048
d = 73

However, when keys get larger it does take quite a bit longer to brute force the value of dd.

public keysize of nnSeconds to brute force
(55601,3727687)\left( 55601, 3727687 \right)22 bits0.0069977
(2279259,316834949)\left( 2279259, 316834949 \right)29 bits0.3364858
(46508795,17347912289)\left( 46508795, 17347912289 \right)35 bits9.2515968
(171534261,97603308101)\left( 171534261, 97603308101 \right)37 bits30.9215976
(412356815,312154666949)\left( 412356815, 312154666949 \right)39 bits96.3439985
(1072651357,1108049904433)\left( 1072651357, 1108049904433 \right)41 bits239.9175115
(2722214743,3873713550481)\left( 2722214743, 3873713550481 \right)42 bits1008.8244588

We can see from the table that for every additional few bits needed to represent the modulus nn, it takes much longer to brute force an inverse key. It wouldn’t take much to create a public key with very large integer values that would take your computer days, weeks, months, or years to brute force. It seems like a large key should be sufficient for this system to be secure (enough) for use with information that is time-sensitive. It wouldn’t matter if someone was able to find out your encrypted credit card information 10 years from now, because you’ll have already obtained a new card number by then!

However, while this system seems secure there’s a mathematical method that can determine multiplicative inverses, and thus private key values, very quickly and efficiently. This method is called the Extended Euclidean Algorithm.

The Extended Euclidean Algorithm

The Extended Euclidean Algorithm is a method that can be used to quickly determine multiplicative inverses of each other. It’s a process that can be done almost entirely by hand, although we’ll see a Python function that can perform it much quicker.

The Algorithm

Create a table with two rows and two columns. The first row and column should be labeled with the value of nn and the second labeled with the value of ee. Fill in the field of the table as follows:

nen10e01\begin{array}{c|cc} & n & e \\ \hline n & 1 & 0 \\ e & 0 & 1 \end{array}

These rows are meant to represent that n=1(n)+0(e)n = 1(n) + 0(e) and e=0(n)+1(e)e = 0(n) + 1(e). We’ll use that information to generate other equivalent equations that may be more helpful than this starting information.

Much like when solving systems of linear equations, we can subtract off multiples of the bottom row from the previous row to generate additional rows in the table. We’ll use this step to generate additional rows, such that the values that the multiples of nn and ee sum to decrease. The goal is to continue generating new rows until eventually the last row starts with the value of 1. The number found in the second column, the column labeled ee, is the multiplicative inverse of ee in modulus nn.

Working through the following example indicates why this must be true.

Example

Let a kidRSA public key be (707,979)\left( 707, 979 \right), meaning e=707e = 707 and n=979n = 979. Start by setting up the table.

9797079791070701\begin{array}{c|rr} & 979 & 707 \\ \hline 979 & 1 & 0 \\ 707 & 0 & 1 \end{array}

Which represents that 979=1(979)+0(707)979 = 1(979) + 0(707) and 707=0(979)+1(707)707 = 0(979) + 1(707). We can subtract off the second row from the first row to obtain a new row in the table:

979707979107070127211\begin{array}{c|rr} & 979 & 707 \\ \hline 979 & 1 & 0 \\ 707 & 0 & 1 \\ 272 & 1 & -1 \end{array}

Which represents 272=1(979)1(707)272 = 1(979) - 1 (707). We can then subtract off the third row twice from the second row to obtain a new row:

97970797910707012721116323\begin{array}{c|rr} & 979 & 707 \\ \hline 979 & 1 & 0 \\ 707 & 0 & 1 \\ 272 & 1 & -1 \\ 163 & -2 & 3 \end{array}

Which represents that 163=2(979)+3(707)163 = -2(979) + 3(707). Continuing to generate additional rows in this manner yields:

9797079791070701272111632310934545711318\begin{array}{c|rr} & 979 & 707 \\ \hline 979 & 1 & 0 \\ 707 & 0 & 1 \\ 272 & 1 & -1 \\ 163 & -2 & 3 \\ 109 & 3 & -4 \\ 54 & -5 & 7 \\ 1 & 13 & -18 \end{array}

So we now know that 1=13(979)18(707)1 = 13(979) - 18(707). We can rearrange this equation to obtain:

18(707)=113(979)-18(707) = 1 - 13(979)

This implies that 18(707)-18(707) is equal to 1 plus some multiple of 979 (which is our value of nn). Next we can reduce the equation by modulus 979, to find an equivalent multiple between 0 and 979 to obtain the equation:

18(707)1(mod979)-18(707) \equiv 1 \pmod{979}

Which implies that -18 is the multiplicative inverse of 707 in modulus 979. We typically write our keys as positive integers, so instead of -18 we would write 961.

We know that that 1961707(mod979)1 \equiv 961 \cdot 707 \pmod{979} making the private key to this system (961,979)\left( 961, 979 \right).

Using Python

This algorithm can reasonably be completed by hand, but with large key values it may take several hundred calculations. The mathematics behind the algorithm guarantees that it will never take more than 2log2n2\log_2{n} rows to complete the algorithm. For example, even if n10200n \approx 10^{200}, it won’t take more than 1329 rows to determine the inverse, which is very reasonable for a computer to calculate quickly. We’ll see how with the Python functions below.

The function egcd() runs the Extended Euclidean Algorithm and returns the last row of the table.

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

print( egcd(707,979) )
(1, -18, 13)

The function modinv() takes the output of egcd() and returns only the multiplicative inverse.

def modinv(e, n):
    g, x, y = egcd(e, n)
    if g != 1:
        return ''
    else:
        return x % n

print( modinv( 707, 979) )
961

This relatively simple looking algorithm is rooted in some very important Number Theory concepts that prove that this method will always work. We won’t cover this theory, but if interested, you should pursue further study in Number Theory.

Efficiency of Extended Euclidean Algorithm

The hope of this algorithm is to speed up the process of finding private keys from public keys. Let’s see how well this works compared to the brute force method already discussed.

public keysize of nnSeconds to compute w/ EEA
(55601,3727687)\left( 55601, 3727687 \right)22 bits0.000001543
(2279259,316834949)\left( 2279259, 316834949 \right)29 bits0.000001562
(46508795,17347912289)\left( 46508795, 17347912289 \right)35 bits0.000001735
(171534261,97603308101)\left( 171534261, 97603308101 \right)37 bits0.000001739
(412356815,312154666949)\left( 412356815, 312154666949 \right)39 bits0.000001745
(1072651357,1108049904433)\left( 1072651357, 1108049904433 \right)41 bits0.000001775
(2722214743,3873713550481)\left( 2722214743, 3873713550481 \right)42 bits0.000001932

The Extended Euclidean Algorithm is incredibly efficient at determining private keys, taking fractions of a second regardless of how large the values are in the public key. This one piece of mathematics has made KidRSA very insecure for use. We’ll see that the real RSA algorithm is similar to KidRSA, but does not have a known mathematical algorithm that provides this type of shortcut for determining the private key from a public one.