Open yanesca opened 1 year ago
I am leaning towards Option 1:
Can anyone think of an issue I am missing here?
the need for conversion is not obvious from the type
This makes me dislike option 2.
The temporary buffer is relatively small and could be allocated on the stack (suitable to handle the maximum curve size)
For ECC-specific code I don't see anything wrong with allocating a bignum on the stack, or even a small number. It's only with RSA-sized numbers that a bignum on the stack is not acceptable.
Option 3 (mbedtls_mpi_scalar):
(…)
need to convert for ECDSA (as opposed to just reading it in)
Why convert? Can't mbedtls_mpi_scalar
just be a struct containing a byte array (so yes, it's a conversion, but a trivial one that doesn't affect CPU or memory usage)?
Why convert? Can't mbedtls_mpi_scalar just be a struct containing a byte array (so yes, it's a conversion, but a trivial one that doesn't affect CPU or memory usage)?
When I wrote Option 3, I had an mbedtls_mpi_uint*
based struct in mind. But you are right, we have an Option 4 where we wrap an unsigned char*
. That would make the type distinct (in the sense that passing the struct to a function taking unsigned char*
wouldn't compile) and could provide static inline functions in the API for convenience and as you say wouldn't affect CPU or memory usage.
Yes, that sounds like a good idea, thanks!
By the way, option 3/4 could potentially be a union {char[]; mpi_uint[];}
, allowing modifications in place, if you have sufficient control about ownership.
Note that there is a downside of wrapping a pointer in a struct: that tends to rule out some optimizations, like keeping the pointer in a register. So it might not be transparent to code size. In my earlier message I was thinking of wrapping an array in a struct which doesn't tend to suffer from such problems.
Using a direct typedef would not rule out any optimisations, but the downside is that it would compile when a type of that variable is passed to a function expecting unsigned char*
.
In theory we could make it with an array, but that would mean that we are wasting some ROM with some scalars as it has to be maximum sized. So the choice seems to be an optimisation question, with what I am assuming would be relatively low impact.
@gilles-peskine-arm are you happy with moving forward with Option 4 as you suggested (wrapped array variant)?
I'm happiest with a wrapped array (option 4) if it's doable, or a wrapped pointer (option 3) if it doesn't hurt efficiency.
Context
Scalars in EC modules are both used in point operations as scalars and modular operations modulo the group order. The original approach was to represent them as an
mpi_mod_residue
. The representation ofmpi_mod_residue
is opaque and direct bit manipulation operations might not yield correct results. Alternatively bit manipulations could automatically convert, but that is prohibitively expensive. This means that the conversion would need to be done manually inside the ECP module. So far so good.The problem is, that Montgomery keys don't fit into a
mpi_mod_residue
modulo the group order. They are multiplied by 2^k, where k is the cofactor of the curve (k=2 or 3). In theory these could be reduced modulo the group order and restored when needed, but that would be non-trivial, increase the attack surface and code size. Also, it would make harder to argue about the correctness of the code.This means that we can't use
mpi_mod_residue
for representing scalars. (Scalars should be the same C type - at least outside of the ECP module - whether they belong to Montgomery or Weierstrass curves.)Operations
Inside the ECP module:
Outside the ECP module:
Options
In either case, bit manipulation is only needed for scalars and the type would need to be aligned to the chosen one. Also, since negation is not exactly the modular negation a dedicated function would be needed whatever the type of choice is.
*Option 1 (`char`):**
mbedtls_mpi_uint*
) and would need to allocate a temporary buffer*Option 2 (`mbedtls_mpi_uint`):**
Option 3 (
mbedtls_mpi_scalar
):mbedtls_mpi_residue
(can't think of a use case for conversion the other way)