Mbed-TLS / mbedtls

An open source, portable, easy to use, readable and flexible TLS library, and reference implementation of the PSA Cryptography API. Releases are on a varying cadence, typically around 3 - 6 months between releases.
https://www.trustedfirmware.org/projects/mbed-tls/
Other
5.56k stars 2.61k forks source link

PK: refactor SpecifiedECDomain support #7779

Open mpg opened 1 year ago

mpg commented 1 year ago

Context: ECC keys need to specify the domain parameters, aka curve, aka group, they use. This is most often done by means of a predefined identifier for well-known curves. However, standards also allow an alternative where all the elements that define the curve (type of field, modulus for prime field, coefficients of the curve's equation, base point) are explicitly encoded. This is known as SpecifiedECDomain and we have opt-in support for it with the option MBEDTLS_PK_PARSE_EC_EXTENDED. However we do not accept arbitrary parameters, but only explicit encoding of one of the curves we support. (Also, this is only possible for Short Weierstrass curves, newer curves can only be referred to by a predefined identifier.)

Currently, our implementation goes as follows:

  1. Fully parse the SpecifiedECDomain structure into an mbedtls_ecp_group structure - this is pk_group_from_specified().
  2. Compare this custom mbedtls_ecp_group to those we know, and output the ID if found or an error - this is pk_group_id_from_group().

These two functions are combined in pk_group_id_from_specified() which is the only one called by the rest of the code. This task is to refactor pk_group_id_from_specified() in order to pave the way for supporting MBEDTLS_PK_PARSE_EC_EXTENDED without ECP_LIGHT. (Secondary goals: (1) reduce the amount of parsing by preferring to match inputs against expected values, (2) reduce RAM usage.)

The new strategy will be as follows:

  1. Start parsing the SpecifiedECDomain structure up to the prime P that defines the group. Identify the group ID from that (turns out P is enough to uniquely identify it).
  2. Continue parsing, but instead of loading A, B, N, G, just verify that they have the expected value based on the group ID. (We need to check those in case someone tries using a custom group with the same prime as one of the groups we know - we want to reject it if this happens.)

When ECP_LIGHT is enabled, this will be done by making use of the group data available; when it isn't, group data will be encoded in tables in pkparse.c - but that will be the next task. This task is only about changing the code structure, still relying on ECP_LIGHT, but keeping the next step in mind while designing the new internal functions.

Suggested internal functions:

Note: the next task will provide implementations of these internal functions that don't depend on ECP_LIGHT but use hard-coded tables instead. The top-level function pk_group_id_from_specified() will be common to the ECP_LIGHT and !ECP_LIGHT case: it will do the parsing, in a way similar to the current pk_group_from_specified() except instead of populating a temporary grp it will call the above functions.

Note: this task is purely a refactoring, so it is complete when the existing tests pass and the code structure is as we want it.

Follow-up: #7789 Previous discussion: #7628, https://github.com/Mbed-TLS/mbedtls/issues/6792#issuecomment-1555065647

mpg commented 1 year ago

For reference, we have a single test for SpecifiedECDomain, in test_suite_pkparse, using tests/data_files/ec_prv.specdom.der.

The specification for the format can be found in SEC 1, C.2, and is largely quoted in our current source. This spec also defines compressed and uncompressed point formats (2.3.3 and 2.3.4). (Note: we only support prime fields, hence the name of the module ecp: Elliptic Curves over Prime fields.)

General information about ASN.1 and pointers to tools can be found in our documentation.

Examined with openssl asn1parse, our test file tests/data_files/ec_prv.specdom.der looks like:

    0:d=0  hl=3 l= 211 cons: SEQUENCE          
    3:d=1  hl=2 l=   1 prim:  INTEGER           :01
    6:d=1  hl=2 l=  32 prim:  OCTET STRING      [HEX DUMP]:8EA106A78201D7BFC11AF5C9230803DB1EB104E0A4F47FA3AF4A23A785584206
   40:d=1  hl=3 l= 133 cons:  cont [ 0 ]        
   43:d=2  hl=3 l= 130 cons:   SEQUENCE          
   46:d=3  hl=2 l=   1 prim:    INTEGER           :01
   49:d=3  hl=2 l=  44 cons:    SEQUENCE          
   51:d=4  hl=2 l=   7 prim:     OBJECT            :prime-field
   60:d=4  hl=2 l=  33 prim:     INTEGER           :FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
   95:d=3  hl=2 l=   6 cons:    SEQUENCE          
   97:d=4  hl=2 l=   1 prim:     OCTET STRING      [HEX DUMP]:00
  100:d=4  hl=2 l=   1 prim:     OCTET STRING      [HEX DUMP]:07
  103:d=3  hl=2 l=  33 prim:    OCTET STRING      [HEX DUMP]:0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
  138:d=3  hl=2 l=  33 prim:    INTEGER           :FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
  173:d=3  hl=2 l=   1 prim:    INTEGER           :01
  176:d=1  hl=2 l=  36 cons:  cont [ 1 ]        
  178:d=2  hl=2 l=  34 prim:   BIT STRING 

Unless I'm mistaken, the SpecifiedECDomain structure is the SEQUENCE starting at offset 43. We can see the version identifier :01 then the field specification, with a value matching secp256k1 (warning: different endianness). Then we have the equation constants A == 0 and B == 7 as expected, then the base point G (here in compressed form, as indicated both by the length and the leading byte 0x02), then the order N. again with the expected value, and we can ignore the rest.

mpg commented 1 year ago

@tom-daubney-arm I'm thinking a good starting point here would to:

Suggested prototype:

/* [in] p: pointer to the value of the prime
 * [in] len: length of the prime in bytes
 * return: the corresponding group ID, or MBEDTLS_ECP_DP_NONE
 */
mbedtls_ecp_group_id mbedtls_pk_group_id_from_p(const unsigned char *p, size_t len);

(Really just a suggestion to clarify my thinking, but feel free to change if it's not convenient or I forgot things.)

mpg commented 1 year ago

Note: I'd suggest starting on top of https://github.com/Mbed-TLS/mbedtls/pull/7861 which will hopefully be merged soon. Otherwise you risk getting stupid conflicts: file contents re-order, internal functions renamed...