FaCT: Constant Time Programming Language
github.com
github.com
fn compare_hashes(a: &[u8], b: &[u8]) -> bool {
let size = a.len();
fact!{
for (uint64 i from 0 to size) {
if (x[i] != y[i]) { return -1; }
}
return 0;
}
}
It would get compiled into asm and inlined.I'm also wondering how this interacts with bounds checking, which would introduce branches. I tried searching the paper (sorry, no time to read the whole thing today) for 'bounds' but did not get a result.
Semi-related. One (insufficient) option for dealing with timing leaks is to add a random delay. ie: `sleep_ms(rand(1,1000))`. The problem is that given N leaks an attacker can remove that noise. I have two questions here:
1. What is "N"?
2. Does that sleeping introduce any additional vulnerabilities? Basically, is this a "if you can afford that latency it won't hurt".
For your sleeping proposal, it sounds a little like differential privacy [1] where you can add some randomness to gain some privacy but spend some of the predetermined privacy budget. In that case, `N` depends on the (roughly) variance of the computation time, the noise amount, and your privacy budget. If you get it right, it has provable security properties. However, that doesn't work as well when the adversary has access to the machine and can observe the intermediate state (or side channel leaks thereof).
[0]: https://github.com/dalek-cryptography/subtle
[1]: https://blog.openmined.org/privacy-series-basics-definition/
That said, it's fairly easy to tell if a CPU spends time sleeping or doing actual work, so it depends on who is in control of the CPU.
But there’s still a high risk of some side channel letting the attacker tell when the program is working and when it’s sleeping.
Regardless, you probably don't want to do your fast-path cryptography on the slowest coprocessors imaginable.
I think there are more 8-bit μCs around than ever before, even though it's harder and harder to justify using one instead of a 32-bit μC. But lots of 32-bit μCs don't have caching either, or they have it to the extent of "you can run code from internal RAM, or more slowly from internal Flash, or much more slowly from QSPI-connected memory". But that doesn't introduce any nondeterminism into the timing, because your code has control over where it's running from.
The way old processors used to do XIP with constant random access latency is pretty much dead at this point for new designs. Semiengineering did a good article on this awhile back [1]. Even if there weren't usually a lot of invisible caching in the middle, flash cells usually don't have identical access latencies (worse, they change with age/write cycles!), and the AVR conditonal branch has data-dependent timings besides. At the design side, there's also a lot of decisions like burst modes that can introduce timing variations that can change with minor design revisions. Most of these aren't documented or even well understood by manufacturers. Truly eliminating architectural timing side channels is very, very difficult today.
[1] https://semiengineering.com/what-happened-to-execute-in-plac...
This is a good article! I guess you're right that when you're running code loaded over a QSPI bus it's probably going to be cached unless you're using an old chip. So data processed by code loaded that way is subject to timing-channel leaks.
However, I think it's very unusual for embedded NOR memory in a μC to have detectable variable access latency. As the article says:
> As long as the operating frequency is slow enough that the flash access times can keep up, instructions can be fetched and executed directly out of the flash, otherwise known as executed in place. The original idea was that you didn’t need to buffer the instructions somewhere else before executing them. ¶ Embedded NOR memory is still alive and well, although today’s higher volumes are on nodes far from what would be considered leading-edge for logic.
So, on-chip Flash is probably still fine.
You probably don't want to try to make an AVR8 into an SSL accelerator, but there are plenty of applications where the ability to do crypto securely is valuable, even if it's very slow.
Bitbanging protocols with tight timing requirements using cycle-accurate code is still a thing people routinely do on small microcontrollers, and it wouldn't work if instruction timing was as unpredictable as you're saying.
Nevertheless, I agree that removing data-dependent branches is a better way to close timing leaks.
If you bubble sort a list of bytes, say your algorithm runs in 10000+n^2 clock cycles. You have at most an exabyte of storage, so your code will run in at most 10^37 clock cycles! :p
As storage technology improves, of course the coefficient will increase, but at that point is should be considered a different algorithm. This one has undefined behaviour when you have more than an exabyte of storage.
In essence, this is about a programming language that can ensure that execution time always equals the worst case; if there's branching, taking both branches and ignoring one of the results; if there's iteration, doing a constant maximum count of iterations no matter what the input data is, etc.
Would you have any links to more reading on this and when it happens? It's the first time I've heard it.
But yes, that may mean the language can’t be implemented well on lots of modern hardware.
A lot probably can be done by avoiding some instructions, flushing caches very often, clearing branch prediction history (if possible), etc.
That would not make the language win performance shootouts, though.
There also is the problem that not all cores may be equal and that quite a few modern CPUs drop speed ‘at will’, depending on system load, core temperature, etc.
[1]: https://developer.arm.com/documentation/ddi0601/2020-12/AArc...
[2]: https://www.intel.com/content/www/us/en/developer/articles/t...
https://github.com/PLSysSec/FaCT/blob/master/example/example...
https://www.thestrangeloop.com/2018/fact-a-new-language-for-...
>> ...cryptographic programming language...
> side channel attacks
I know what I’m about, son.
You know the rudeness is against guidelines, as well.
Tribal knowledge is always a danger. Which part did you guys think I lost the plot? Why code that runs slower is a feature for cryptography? Assuming people know what a side channel attack is? Why the co-creator of one of the longest surviving cipher systems who spent his career breaking other ciphers is relevant background information? Something else?
The summary for the linked package is:
> This is the compiler for the Flexible and Constant Time cryptographic programming language. FaCT is a domain-specific language that aids you in writing constant-time code for cryptographic routines that need to be free from timing side channels.
I would also be inclined to ask if you meant to reply to another comment. Let me explain. To me the other option is to say that what you wrote makes no sense and I cannot understand why you wrote it. Asking if you meant to reply to another comment is my way of showing that I don't think you're a crank, but instead you just made a silly mistake any one of us could've. But if I wrote it the other way you might think that I think you're a crank. Y'know?
Anyway my understanding of the original comment is that it was meant as a clarification to the title. Hence why your first reply which starts explaining who Avi Shamir is confused me too.
A constant time programming language sounds like a ridiculous notion to most problem domains. It is a ridiculous notion. You only want code that always runs in exactly half a millisecond when you’re solving a very, very specific set of problems, like protecting secrets or opening airbags, and then it’s one of the most important things.
Hence our confusion of you mentioning Shamir. I went back to the site thinking he may have been a contributor.
If two documents have the same length, but differ in the time it takes to encrypt them, then their encryption times leak information about their contents. In cryptography, this is an example of a "side channel" - and side channels are considered bad. A "constant time" language helps minimise the presence of such side channels, making it useful in cryptography.
But, sadly, there are applications where the secrets can't just be limited to keys: For example in an anonymous messaging system essentially all the data is secret, as they're usually constructed someone who could perform a timing/cache sidechannel attack against you could potentially determine which messages you were reading (or writing) and discover your identity.
So security with those anonymity tools critically depends on the participants being protected against observation-- which would be considered a fairly reckless security assumption if we were talking about the security of your AES disk encryption or ECDSA message signing.
I wouldn't go so far as to say that constant time programming is a solved problem for basic crypto-- the fact that languages and instruction sets make few guarantees remains an issue-- but the situation there isn't particularly dire. There are few enough basic primitives and the techniques needed to get protect keys from sidechannels in C are well known, so in practice things are usually pretty good (unless you get into people making crypto code in JS...).
But without better tools we can't even start to thinking about extending sidechannel security more elaborate cases. Domain specific languages might help.
transformed code, but insecure
secret mut uint32 rval = 0;
secret mut bool notRet = true;
if (sec) { rval = 1; notRet = false; }
if (notRet) {
// long-running computation ...
}
return rval;
the long running block depends on the secret, thus leaks it.https://news.ycombinator.com/item?id=33150179 Should Linux set the new constant-time mode CPU flags?
More generally, complex memory access patterns like reading/writing an array at a secret index are forbidden, you have to transform them into looping over the whole array and testing if the current index is equal to the secret index. It seems doing such transformations automatically is possible but FaCT just errors because the resulting code is slow.
https://github.com/xoreaxeaxeax/movfuscator
Since this is effectively branchless and every instruction would take the same number of micro ops, wouldn't this be a very safe way of writing cryptographic code free of side channels?
https://youtu.be/kbn9UCRK2Qg?t=1228
Judging by the image of generated assembly code in movfuscator repo, I don't think the mov-only solution would be efficient.
1. Leakage via cache. If our memory access patterns are influenced by secret data, then we can detect variance in execution time as a result of cache hits/misses. Movfuscator generates code that does lots of loads and stores using application data as addresses so my guess is that even if your source program didn’t depend on secrets in this way, the output code probably still would.
2. Termination rules. Movfuscator programs run inside a giant loop. Every execution of that loop drives execution forward, and every instruction is executed on every loop. Even if the body of this loop is constant-time (see above for why it’s probably not), we need to consider how the program actually terminates. For example if I write the following C code:
for (int i = 0; password[i] != 0 && entry[i] != 0; i++) {
if (password[i] != entry[i]) return false;
}
return true;
We can see that it takes fewer iterations to check entries which are incorrect earlier. For example, if the password is “foo” and the entry is “bar”, then we return in the first iteration, as opposed to the entry “fob” which returns on the third. Thus, if the programs termination time is affected by the secret value, could still detect timing variance because the full program would terminate faster even if each loop took the same amount of time.