pornin
(Note that -O3 is usually not that good an idea: aggressive loop unrolling can sometimes decrease performance because of cache issues, for instance. To optimize code properly, you have to make benchmarks and adjust both the code and compiler flags accordingly, in a well-thought feedback cycle. The use of aggressive optimizations in the blog post is to make the examples trip UB more clearly.)
Also, when a thread is sleeping, the OS will schedule other threads, which opens many additional ways for attackers to notice that sleeping started.
On the other hand, branches that do not depend on secret data are OK in constant-time code. Typically, when you process a chunk of data, the chunk length is not secret, and there will be a loop whose exit condition really is a conditional jump that depends on the length.
This kind of leak still counts as a "timing attack" because the test for cache eviction is based on the time it takes to next access the relevant element (and this occurs _after_ the cryptographic computation, from other code). Notably, this can still be done remotely, possibly from a large distance, thanks to the efficiency of modern networking (this is what makes timing attacks special among side-channel leaks: power analysis, electro-magnetic emissions, sound... require the attacker to be in the physical vicinity of the target system, while timing attacks may be performed from hundreds of miles away). Demonstrations have been made with network access (over ethernet or optic fibre), and, in another kind of setup, when the attacker can run his own code on the same hardware, even with logical isolation (another process, or even in another VM that happens to run on some other cores of the same machine).
Thus, "true" constant-time code will make no memory access at an address that depends on secret data (but the data contents, of course, may be secret). Similarly, it won't make conditional jumps that depend on secret data (because of the cache accesses for loading the code, and also because of the jump prediction cache, both having been successfully exploited in lab demonstrations).
However, doing implementations is not at all the same thing as publishing implementations! The first one or two attempts are always flawed in some way; only the third one can hope to be reasonably good. I took care to properly kill and dispose of the corpses of all my learning code.
The trick (and it's a difficult one) is to decide in advance that the code you write to learn will have to be deleted -- and stick to it. Developers have trouble letting go of their creations, in general. If you can maintain that discipline, then there is no problem in "writing your own crypto". But that is a big "if".
Despite the horrors implied by a Forth syntax, this actually made writing the code easier. As a bonus, it turns out that T0-generated code is very compact, so I could pack more behaviour into less bytes.
I intend to write (in many details) how the whole thing is designed. Give me a couple of months.
Also, when I say that BearSSL is written in C, it is partly a lie: some of it (especially X.509 certificate decoding, and handling of handshake messages) is done in T0, a new Forth-like language that I invented for the task (compiler is provided, and produces C code), specifically to have coroutines (incidentally, it means that most of that code read things byte by byte, with relatively few accesses to buffers, thus less potential buffer overflows).