[PATCH] MPI helper of comparison, Least Leak Intended

Jacob Bachmeyer jcb62281 at gmail.com
Mon Feb 10 05:02:12 CET 2025


On 2/9/25 08:06, Jussi Kivilinna wrote:
> Hello,
>
> On 9.2.2025 0.50, Jacob Bachmeyer via Gcrypt-devel wrote:
>> On 2/7/25 20:05, NIIBE Yutaka via Gcrypt-devel wrote:
>>> NIIBE Yutaka<gniibe at fsij.org> wrote:
>>>> I think that this implementation could be improved.
>>> I should use ct_limb_gen_inv_mask function instead of directly use 
>>> unary
>>> minus operator.
>>
>> Could it make more sense to write:
>>
>> result &= ct_limb_gen_inv_mask (gt) & ct_limb_gen_inv_mask (lt);
>> result |= gt | -lt;
>>
>> Assuming that ct_limb_gen_inv_mask returns all-bits-set if its 
>> argument is zero and all-bits-clear otherwise, the first line clears 
>> result if a previous value is to be overwritten and the second sets 
>> the new value.
>>
>> I also still suggest considering an alternate encoding for the 
>> comparison result.  The Hamming distance between 0 and 1 is 1, but 
>> the Hamming distance between 0 and -1 is the maximum on a 2's 
>> complement machine, which means that any information leakage on the 
>> power rail will be at its strongest when the comparison result is 
>> "less than".
>
> I'd move final result generation outside from the loop and instead 
> generate separate result_lt and result_gt values in loop. These would 
> then be combined at the end of function to form final result code. 
> That should mostly mitigate the 0/1/-1 hamming distance EM leakage 
> from inside the loop.
>
> [...]

I had not thought of that.  Thank you.

>>
>> A one-hot encoding would have a constant Hamming distance (of 2) 
>> between any pair of valid values.
>
> If returned value (0 vs 1 vs -1) could cause EM leakage, last line of 
> function could be changed to something like:
>
>   return (int)(res_gt | (res_lt << 1)); /* return 0 if U==V, 1 if U>V, 
> 2 if U<V */
>
> Or if having sign-bit set is important but we want to avoid "set all 
> bits to ones" case, then only set sign-bit for "U<V":
>
>   return (int)(res_gt | (res_lt << (sizeof(int) * CHAR_BIT - 1))); /* 
> return 0 if U==V, 1 if U>V, INT_MIN if U<V */

This still leaves a small difference between "equal" (Hamming weight of 
zero) and "not equal" (both with Hamming weight of one).  I was 
originally thinking of using 1, 2, and 4 as result codes to avoid that 
difference.

However, using 0 vs 1 vs INT_MIN certainly greatly reduces the potential 
leak.  Would it be expected to weaken the signal enough for it to be 
lost in the other noise in a practical system?

For example, the Hamming weights of the MPI limbs are definitely 
variable, and I am unsure if there is anything software can reasonably 
do to mitigate that.  Storing both true and complement might help, but 
would be a different in-memory representation and computing with both 
might even make a stronger signal.

I think using 0/1/INT_MIN as results is probably the best we can do if 
we want to keep to the C convention of using comparison to zero as a 
proxy for the result of a complex comparison.  There are still several 0 
vs 1 values used in the loop, but changing that would likely cascade 
throughout the MPI implementation.

Overall, I suggest using Jussi Kivilinna's last suggestion and leaving 
"fully charge balanced" for another day.


-- Jacob





More information about the Gcrypt-devel mailing list