Square and multiply algorithm in GoThe square and multiply method is used in RSA calculations: |
Outline
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 \pmod 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^4\) (where 4 is the exponent) becomes:
5² = 25 25²= 625
If we can to multiply \(5^8\) that is \(5^2\) squared to give \(5^4\), and then if we square again we get \(5^8\). It has thus taken us three operations to find a power of 8. For \(5^{64}\), we will need six operations:
\(5^2 → 5^4 → 5^8 → 5^{16} → 5^{32} → 5^{64}\)
But lets say we want \(5^9\). For this we square as we did before to give us \(5^8\), and then just multiply by 5 to give \(5^9\).
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 Python 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 %d (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 }
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^{12}\), we square(\(5^2\)) and multiply (\(5^3\)), next square (\(5^6\)) , next square (\(5^{12}\)):
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^{128}\) we get:
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\n",x,y) res:=exp_func(x,y) fmt.Printf("\n%d to the power of %d is %s",x,y,res) }
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]: