In Go, bits.Add64 takes as input and returns the carry.
In C, there are some constructs that modern compilers will recognize as patterns people use to extract the carry, but it is a hit-and-miss.
Even compiler-specific intrinsics are a few and far between, inefficient or even broken.
Edit: OK, I have much worse time generating "adc" instructions, and only gcc sees the opportunity to emit "setc" where appropriate. I see the issue now.
With respect to efficiency and particularly timing, Go and C seems to be in the same boat here in that depending on your compiler and platform it may either compile to a constant time chain of add/adc equivalent instructions or something sub-optimal and branchy.
static uint64_t
add64(uint64_t x, uint64_t y, uint64_t *carry)
{
uint64_t yc, sum;
yc = y + *carry;
sum = x + yc;
*carry = (sum < x || yc < y);
return sum;
}
But again, some other compiler on some other platform may have another idea of what to output.Anyway I agree to be careful. On platforms that have to use the fallback implementations, if they aren't optimized to constant time operations, this will lose one of the important properties of Poly1305.
The best you can do is to write statements that you think map to certain instructions, and to check every release build to see what instructions the compiler chose.
You also may have to make sure that the buffers you use don’t cross page boundaries.
What you really want is https://github.com/agl/ctgrind or something similar.
And it’s worse: that your code is constant-time in the absence of an adversary doesn’t guarantee that it is so in the presence of one. You will have to stress test your code with code running in other CPU threads that flush various caches, switch CPU modes, do DMA, etc.
Having said that, running with random inputs isn’t a bad idea, if only because you have to get confidence that the CPU manufacturer’s timing data is correct.
Or perhaps it's about avoiding allocations?