Should Linux set the new constant-time mode CPU flags?
lore.kernel.org
lore.kernel.org
One thing I've always wanted but never seen is a way to be explicit to the compiler what time complexity I require for a specific piece of code. At the moment, we have in some cases library guarantees about how slow an algorithm is allowed to be (in C++ STL, I don't think I've seen guarantees anywhere else?) and compilers will try to manipulate code to make it faster if they can prove the result is the same. But that leaves cryptography developers trying to ensure that the optimiser doesn't do as good a job as it is trying to do.
What we've learned is that telling the compiler what you're trying to achieve is much more straightforward than only giving the compiler part of the information you have then relying on it happening to do the right thing. Being able to set a constant time annotation on source code and have it propagate all the way to the hardware would be really neat.
(Among other things, "Note that Intel's documentation says that CPUs before Ice Lake behave as if DOITM is always set")
Also interesting was Catalin’s comment on ARM: https://lore.kernel.org/lkml/YyNmQ2bPvf3B3Xvo@arm.com/
All the proposals for more fine-grained ways to set the flag for code that needs it (opposed to having it always enabled) come with some overhead, and it's hard to weigh that overhead against an unknown upside of leaving the flag off whenever possible.
https://travisdowns.github.io/blog/2020/05/18/icelake-zero-o...
The list Intel provides is here:
https://www.intel.com/content/www/us/en/developer/articles/t...
Most of the integer instructions you'd expect to be constant-time are in fact constant-time; it's a slice of vector integer multiply (and integer multiply-adds) that are inadvertently not constant-time.
All the instructions in the list are no longer (guaranteed to be?) constant-time unless the DOITM bit is set.
> Specifically, when data operand independent timing mode [DOITM] is enabled, instructions within the data operand independent timing subset execute with timing (in terms of processor cycles) that is independent of the data values in the sources of the instruction
* Vector integer multiply instructions may be data-dependent in some circumstances.
* Intel appears to be willing to commit to making most integer instructions constant-time.
* Some of the vector integer multiply instructions appear to be largely reusing the floating-point FMA units (VPMADD52* is a pretty strong clue there--52 bits covers the size of the mantissa in double-precision), and those units appear to have some non-constant-time behavior.
My interpretation is that all of the instructions in that list require DOIT set for guarantees of data independent timing
> a new model specific register (MSR) control enables data operand independent timing for the listed data operand independent timing instructions.
https://www.intel.com/content/www/us/en/developer/articles/t...
As a simple example, consider a naive password check:
char* realPass;
char* guess;
for(int i=0; i<len; i++){
if(realPass[i] != guess[i]) return false
}
return true.
You might think that this function only tells you if the guess was correct or not. But it actually can tell you which character is the first incorrect one. With enough tries and some statistical analysis, this sort of attack had been demonstrated even over the internet.The most obvious example is if you compare a user-supplied secret with a stored secret, the naive `for i in 0..min(len(a), len(b)) { if a[i] != b[i] return false } return true` will take longer the more of the first letters you guessed correctly. By averaging lots of tries, even tiny differences can be measured even over the internet.
Real crypto code takes some pains to ensure that you can't observe secrets from timing information, but it can only ensure that on the level of assembly instructions. If the CPU messes with timing on the level beyond that, that could open up vulnerabilities.
- easily identifiable and more easily attackable
- because of the extra prefix byte, less likely to fit in cache and more likely to suffer performance
and also I can't make existing, well tested crypto code in binary form on critical systems I don't want to update yet run in constant time.