Depending on your use-case and threat model, you may well need to consider side-channel attacks. Given your edit suggesting that you've never heard of this, consider the following situation. You have a 4-digit PIN, and you have the following algorithm for checking said PIN when it's entered
// Probably not valid C, but the language is irrelevant here.
bool check_pin(char[] guess, char[] actual_pin){
for(int i=0; i<4; i++){
sleep(1);
if(guess[i] != actual_pin[i]){
return False;
}
}
return True;
}
I would like to attack that PIN using this algorithm. Though my search space is 10,000 possible PINs, it's fairly clear that I can guess any value of actual_pin within only 40 attempts by using a stopwatch. I guess 0000
, 1000
, 2000
, ..., 9000
until I find that one of my guesses takes slightly longer to return than the others. Clearly, that guess has the first digit correct because it hits the sleep
twice. I then attack the second digit by a similar method, then the third, then the fourth.
If the sleep were to be removed, it's still possible to use large numbers of guesses and nanosecond timing mechanisms to attack this implementation.
So, OK, you refactor the above algorithm into:
// Probably not valid C, but the language is irrelevant here.
bool check_pin(char[] guess, char[] actual_pin){
ret = True
for(int i=0; i<4; i++){
if(guess[i] != actual_pin[i]){
ret = False;
} else {
// Do something that takes just as long as assigning ret to False
}
}
return ret;
}
And maybe this algorithm now always takes the same amount of time, meaning the previous attack is completely invalid. Great. Now suppose I were to get an electromagnetic probe and point it at some resistor long the power line to your chip. As the power consumed by your device during this algorithm fluctuates, the amount of electromagnetic radiation emitted by this resistor will also fluctuate. If the true branch of that if statement goes out into the main memory and fiddles a value over there, whilst the false branch just sits and does nothing for a bit, those consumptions will differ, and I'll be able to tell which branch of this algorithm you're dropping down on every iteration.
This all sounds rather far-fetched, but I'm afraid that I've actually done this, against a home-brewed ECC implementation.
As it turns out, ECC is usually based off group exponentiation, which does the bulk of its work in something like this:
// Definitely not valid C, but the language is irrelevant here.
bool scalar_multiply(curve_point base, int[] exponent /*as an array of bits, highest-order bit first*/){
ret = point_at_infinity
for(bit in exponent){
if(bit == 1){
ret = mult(ret, base)
}
base = square(base)
}
return ret;
}
Unfortunately, in ECC, the square and multiply operations are drastically different and will produce completely different power signatures, making it really obvious which of the branches you've just hopped down, leaking your secret exponent without much difficulty. Timing attacks are also possible against these loops, but they usually involve loads of statistical tests, making them a bit harder to explain. Feel free to ask a follow up on how to use timing information to SCA a naive square-and-multiply loop.
Attacks like this are seriously hard, and usually involve a lab with an expensive oscilloscope in them (the one at my university cost tens-of-thousands of pounds), but they work. Timing attacks are much cheaper, but still possible.
This, of course, all depends on your threat model and where your chips are going to get used. If you're planning to distribute these across a large number of people and you're using them to protect millions of pounds in people's bank accounts, then you should probably take threats like this seriously. If you're securing a TV remote or something, it may not be worth bothering.
The take-home point is that, if you work naively, a skilled and well-funded attacker could conceivably leak all the private exponents used by a device that they have physical access to.