How Do I Calculate 5¹²⁸ in eight easy steps? Ask the Gopher!

The answer is:

How Do I Calculate 5¹²⁸ in eight easy steps? Ask the Gopher!

The answer is:

293,873,587,705,571,876,992,184,134,305,561,419,454,666,389,193,021,880,377,187,926,569,604,314,863,681,793,212,890,625 [here], and I did it in eight mathematical operations. Here are some more:

5¹² Try! 7¹³ Try! 9²¹ Try! 103⁴¹ Try!

A recent paper showed that it is possible to determine the private key of RSA by simply listening to the radio waves emitted from a mobile phone. This is because the RSA method uses multiplication and square operations, and which can be observed as the processor consumes different amounts of electrical power as it performs the calculation.

In RSA we decrypt by taking the cipher, and then raising it to the power of d:

Message=Cipher^d (mod N)

and where N is the multiplication of two prime numbers. Our decryption key is (d,N). Thus if we find d, we crack RSA. We know N, as the public key is (e,N).

In order to perform the exponent operation (Cipher^d), we normally use the square and multiply method. So 5⁴ (where 4 is the exponent) becomes:

5² = 25
25²= 625

If we want 5⁸ we have 5² squared to give 5⁴, and then if we square again we get 5⁸. It has thus taken us three operations to find a power of 8. For 5⁶⁴, we will need six operations:

5²->5⁴ ->5¹⁶->5³²->5⁶⁴

But lets say we want 5⁹. For this we square as we did before to give us 5⁸, and then just multiply by 5 to give 5⁹.

The basic method involves converting the exponent into bits, and then multiplying and squaring if the bit is a ‘1’ (or a power of two), or square if it is a ‘0’. In Go this becomes:

func exp_func(x int, y int64) (value *big.Int) {
 exp:=strconv.FormatInt(y, 2)
fmt.Printf("Binary value of b is: %s\n",exp)
 fmt.Printf("Bit Result\n===========\n")
 v := big.NewInt(int64(x))

for i := 1; i < len(exp); i++ {
   v.Mul(v, v)
   fmt.Printf("%d %s (multiply)\n",i+1,v)
   if(exp[i]=='1') {
v.Mul(v, big.NewInt(int64(x)))
fmt.Printf("%d %s (square)\n",i+1,v)
}
}
return v   
}

We use Big Integers, as our values may be greater than 64-bit integrations. So, with an exponent is 12 we have a binary value of 1100. We ignore the first bit, and start on the ‘1’ (1100) , where we multiply and square. Next we have a ‘0’ (1100), so we just square, and finally a ‘0’ (1100), so we again just square. If we want to raise 5¹², we square (5²) and multiply (5³), next square (5⁶), next square (5¹²):

Binary value of b is: 0b1100
Bit Result
2 : 25 (square)
2 : 125 (multiply)
3 : 15625 (square)
4 : 244140625 (square)
Result: 244140625

If we try 5¹²⁸ we get [here]:

We will calculate a^b
a= 5
b= 128
==== Calculation ====
Binary value of b is: 0b10000000
Bit Result
2 : 25 (square)
3 : 625 (square)
4 : 390625 (square)
5 : 152587890625 (square)
6 : 23283064365386962890625 (square)
7 : 542101086242752217003726400434970855712890625 (square)
8 : 293873587705571876992184134305561419454666389193021880377187926569604314863681793212890625 (square)
Result: 293873587705571876992184134305561419454666389193021880377187926569604314863681793212890625
===========

And now we can look at a power trace from a device. In this case we see that the SM (Square and Multiply) method takes longer than the S (Square method):

Power trace [1]

And so we just read of the bits:

0 (S), 1(SM), 1(SM), 0(S), 1SM , 0(S), 0(S), 1(SM), 1(SM), 1(SM), 0(S), 1(SM). We have now revealed virtually all of the bits in the key:

1 011010011101

You must now be worried if you have an embedded device, that you will reveal the decryption key. The safe guard is to put in dummy multiplication operations at random, so that they processor performs operations which do not actually affect the calculation.

If you are interested, here is the code to perform the operation:

package main
import (
"fmt"
"os"
"strconv"
"math/big"
)
func exp_func(x int, y int64) (value *big.Int) {
 exp:=strconv.FormatInt(y, 2)
fmt.Printf("Binary value of b is: %s\n",exp)
 fmt.Printf("Bit Result\n===========\n")
 v := big.NewInt(int64(x))

for i := 1; i < len(exp); i++ {
    v.Mul(v, v)
    fmt.Printf("%d %s (multiply)\n",i+1,v)
    if(exp[i]=='1') {
v.Mul(v, big.NewInt(int64(x)))
fmt.Printf("%d %s (square)\n",i+1,v)
}
}
 return v

}
func main() {
 x:=3
y:=int64(12)
 argCount := len(os.Args[1:])
 if (argCount>0) {x,_= strconv.Atoi(os.Args[1]) }        
if (argCount>1) {y,_= strconv.ParseInt(os.Args[2],10,64) }
 fmt.Printf("a=%d\nb=%d\n",x,y)
fmt.Printf("%d to the power of %d is %s",x,y,exp_func(x,y))
}

Cracking with radio waves

The operations on the process will also emit radio waves. The faster we run the process, the strong the current in the wires and on the chip, and the more electromagnetic waves that a device will emit.

If you want to read how we can detect the RSA decryption key with radio wave, read this:

Presentation

A presentation is here [slides]:

References

[1] Understanding Cryptography: A textbook for students, Page 197.