ECC-step2: curves y^2 = x^3 + ax + b #236
No reviewers
Labels
No labels
bug
duplicate
enhancement
feature
help wanted
improve API
invalid
needs documentation
not-planned
rtfm
stale
support
wishlist
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-libtom/libtomcrypt!236
Loading…
Reference in a new issue
No description provided.
Delete branch "pr/ecc-non-asn1-part"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
While hacking on
ecc_sign+verify_hash_rfc7518earlier today I realized that it would be possible to extract from my branchmiko-ecc-enhancementsthe crucial part implementing generalized elliptic curvesy^2 = x^3 + ax + b(currentdevelopbranch supports onlyy^2 = x^3 - 3x + b).The API changes comprise of the following new functions:
These are missing due to #187
ecc_export_full- export EC key in OpenSSL compatible formatecc_import_full- import EC key in OpenSSL compatible formatecc_import_pkcs8- import EC key in PKCS#8 formatA note (https://github.com/libtom/libtomcrypt/pull/65#issuecomment-308957259) related to this PR:
Another note related to this PR:
We will need
sqrtmod_prime()in tfm and gmp before this can be merged.First part of a review... To Be Continued
c.f. my comment to
ecc_export_raw()@ -290,1 +299,4 @@/** ECC key's domain parameters */typedef struct {/** The size of the curve in octets */especially since this has the same signature as
ecc_export(), how about re-using the same pattern that introducedPK_STD?@ -315,0 +345,4 @@int ecc_generate_key(prng_state *prng, int wprng, ecc_key *key);int ecc_set_key(const unsigned char *in, unsigned long inlen, int type, ecc_key *key);int ecc_get_key(unsigned char *out, unsigned long *outlen, int type, const ecc_key *key);int ecc_get_oid_str(char *out, unsigned long *outlen, const ecc_key *key);👍
why is this cast required?
same
same
looks like we'll loop here forever as well and that the standard says to do so...
If you mean
ecc_dp_find_by_nameit is for convenience like:this can be moved to ECC part2, where it is required as some DER formats do not contain all curve parameters but only OID of the curve
I'll will also move this to ECC part 2 (this one might end up removed completely)
OK, I'll apply PK_MAX_RETRIES here
@ -290,1 +299,4 @@/** ECC key's domain parameters */typedef struct {/** The size of the curve in octets */With EC things are a bit tricky as we have couple more scenarios:
we have our old DER format (basically compatible just with LTC) where the curve is identified just by its size
I am not yet sure what would be the best approach here. But yes we can handle all of them in one single
ecc_(import|export).@ -290,1 +299,4 @@/** ECC key's domain parameters */typedef struct {/** The size of the curve in octets */On the other hand in ECC part 2 we call
ecc_(import|export)_rawduring importing/exporting OpenSSL keys asecc_(import|export)_rawis kind of a lower level action.what I meant was "why is this
return(ltc_ecc_set_type*)<c_ecc_sets[i];cast required?"and I just got the answer... because the API signature is lying and you're returning a const object as non-const
three possibilities to solve this in order of preference:
const ltc_ecc_set_type*ltc_ecc_setsnot constThis stuff gets even more complicated in ECC part 2 where we are importing keys in OpenSSL compatible DER format:
ecc_dp_find_by_oid(..)which finds correspondingltc_ecc_sets[i]item (const)ltc_ecc_sets- this is the idea behindecc_dp_find_by_params(..)- otherwise we have to allocate and properly fillltc_ecc_set_typestructure and free it during the key destruction inecc_free(here it is tricky asecc_freehas no idea whether theltc_ecc_set_typestructure was dynamically allocated or not)and suddenly possibility 2 seems to become the preferred one, simply always allocate a copy so we can always free
Yes, and it should be perhaps better named
ecc_dp_new_by_nameI have created some draft related to "possibility 2"
But it turned into a question: Why do we make copies of hexadecimal strings? It would make IMO more sense to convert those hex string into bignums during making a "copy":
done
Some thoughts about applying similar API ideas as we did in RSA, DSA, DH.
This is what we have in 1.18-rc1 (and probably want to keep it for backwards compatibility):
I propose the following new additions to public API in the "next" (UPDATED):
This PR was re-factored as proposed here: https://github.com/libtom/libtomcrypt/pull/236#issuecomment-314246411
Currently it is ready only for API review (I did my best to keep the API as much backwards compatible as possible).
Definitely needs at least one more pass to check/add more LTC_ARGCHK's + check whether we correctly free what has to be freed etc.
Just for record - the idea:
UPDATE: the latest version of this PR adds the following to the public API
closing this one? AFAICT it is still unmerged.
sorry, already reopened
@sjaeckel could you please have a look at API additions proposed in https://github.com/libtom/libtomcrypt/pull/236#issuecomment-331476974 ? Just the API, no need to go through the implementation at the moment.
first a question, after an answer more comments :)
@ -274,7 +277,13 @@ typedef struct {is
ltc_ecc_set_typethe right name for this struct? shouldn't it better be calledltc_ecc_curve@ -288,18 +297,36 @@ typedef struct {void *z;} ecc_point;this will prevent compilation of a user-space app without defining
LTC_SOURCE@ -274,7 +277,13 @@ typedef struct {Yes, it would. However, I have not introduced this struct (only added A, cofactor, OID) it already exists and is part of the API. So for backwards compatibility we have to stay with
ltc_ecc_set_type.@ -274,7 +277,13 @@ typedef struct {we're going to change the API's so why shouldn't we fix the names in the same step?
@ -288,18 +297,36 @@ typedef struct {void *z;} ecc_point;The newly introduced
ltc_ecc_dpwas intended to be "invisible" from public API. User-space apps should neither see nor manipulate this struct directly but only viaecc_set_dp(..).But I have to check whether this intention really happened.
@ -288,18 +297,36 @@ typedef struct {void *z;} ecc_point;this won't be possible as
ecc_keycontains an instance ofltc_ecc_dp@ -274,7 +277,13 @@ typedef struct {I wanted to keep the changes as small as possible. In fact we are "only" adding to the public API:
ltc_ecc_set_typestruct@ -288,18 +297,36 @@ typedef struct {void *z;} ecc_point;You are right. So either moving
oid_stto public API or switching tounsigned long oid[16]; unsigned long oidlen;.@ -274,7 +277,13 @@ typedef struct {I'd say with 1.18 out of the door "keeping changes small" is finished. We're not going to release a 1.18.1 with these ECC changes, even if the changes don't look that huge we're going to have to bump the ABI again... in the light of the late ABI bump for 1.18 we could (should!?) have included this in 1.18 :/ sorry for that
@ -274,7 +277,13 @@ typedef struct {No need to be sorry. Ad ABI versioning I think we can keep it independent on lib versioning and simply bump it when necessary.
@karel-m What is the reason for removing this?
To me it looks like it's not needed as a user will normally not identify the curve by its size but by its name.
Also the size got ambiguous now that there are multiple standard-curves with the same size.
But that's just a wild guess :)
@karel-m ?
Sent fm iTouch
Definitely agree!
Hmm, I think that keeping the size or rather, changing the size to refer to the curve size in bits would be better. Many crypto libraries allow one to just specify a requested curve size in bits and requested field type and then provide a default named curve if one is present. (For example Java Cryptographic Architecture requires that)
Removing the size or storing the byte size can also pose problems for curves of bit size
% 8 != 0.There's still
ecc_make_key()which takes the size of the desired key and then chooses one for you :)@ -309,3 +336,3 @@/** the ECC params provided */extern const ltc_ecc_set_type ltc_ecc_sets[];extern const ltc_ecc_curve ltc_ecc_curves[];that one's redundant in the public API with
ecc_make_key_ex()why can't we just set this to NULL and check before the invocation?
Just an idea...
AFAIU there's no functional difference of compressed and uncompressed points, right?
so we could disable compression at runtime in case tfm is used... (in fact that would be the same behavior at runtime than what is currently done in
ecc_encrypt_key()at compile-time)@ -309,3 +336,3 @@/** the ECC params provided */extern const ltc_ecc_set_type ltc_ecc_sets[];extern const ltc_ecc_curve ltc_ecc_curves[];ah, or is there a use-case where you want to do the following?
ecc_make_key()currently works by looking at thesizeattribute of curves.This PR removes that attribute and does the lookup at ecc_set_dp_size by hardcoding the values.
@ -309,3 +336,3 @@/** the ECC params provided */extern const ltc_ecc_set_type ltc_ecc_sets[];extern const ltc_ecc_curve ltc_ecc_curves[];Well, for example when manually importing the key from other/sources formats. This should provide the convenience of having function calls instead of manipulating the ecc_key struct directly.
I've stopped to type
{curve,dp}and only used XX in the end... probably also not in every place I could/should have.I don't know which one is better, curve or dp, we talked about using curve in a comment before, that's why I mention it here. I think dp (domain parameters) sounds a bit more appropriate, but I'm not sure.
In general I can say that this feels like a too big PR to review properly...
@ -28,7 +23,12 @@void ecc_free(ecc_key *key)this could be one call
or can't it because this is also called in cases where either of one (dp, key) is set and the other isn't?
ecc_get_curve_by_name?ecc_get_dp_by_name?yes I know that this has been the way like that before, but it somehow feels wrong that someone calls a function with a partially initialized key and on error this function free's the stuff it didn't allocate...
this comment applies to several places where this pattern is used...
@ -0,0 +1,90 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denisecc_set_curve_by_size()?ecc_set_dp_by_size()?@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denisecc_set_XX_by_oidecc_copy_XXecc_set_XX_from_mpis@ -21,68 +16,12 @@at least return
CRYPT_NOP:)@ -0,0 +1,62 @@/* LibTomCrypt, modular cryptographic library -- Tom St DenisIIUC this is a critical test, so I don't think the logic that goes "if everything went fine we should trigger the error" applies here, but we should go for "if something fails we should trigger the error".
so we should set
retval = 1before themp_init_multiand set it to zero in inverted logic here.most likely even better would be to return
errand add anotherint*-argument for the currentretval.@ -45,14 +41,32 @@ int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, voidreturn err;I've seen this code now for at least the 3rd time, can you please add a
ltc_ecc_set_to_point_at_infinity()@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denissrccan also beconst@ -309,3 +336,3 @@/** the ECC params provided */extern const ltc_ecc_set_type ltc_ecc_sets[];extern const ltc_ecc_curve ltc_ecc_curves[];thou shalt never manipulate the
ecc_keystruct directly!yeah, it's truly not elegant at all... :)
I think it'd make sense to add a
ecc_get_dp_by_bits(or sth like that) which finally uses bits instead of octets, then iterate there over theltc_ecc_setsarray, and compare themp_count_bits(key->dp.prime).ecc_make_key()would then call that one withkeysize*8The
int sizeis simply redundant (it is the byte size ofprime). It used to be a unique identifier of elliptic curve but it is not true any more. For some sizes we several curves so it does not make sense to ask for a curve just by giving a size (no matter whether byte size or bit size). You should use curve name or curve OID.The
ecc_set_dp_sizewas implemented just for compatibility reasons. It is not nice but it is a backward compatible mapping between old and new API.@ -309,3 +336,3 @@/** the ECC params provided */extern const ltc_ecc_set_type ltc_ecc_sets[];extern const ltc_ecc_curve ltc_ecc_curves[];The sequence:
ecc_get_set_by_name+ecc_set_dp+ecc_set_keyis IMO pretty handy. Considerecc_set_keyas a kind of "import key" function that is capable of loading raw private/public keys (e.g. bitcoin ECDSA keys uses this raw format, the curve is fixed/well-known "secp256k1").Yes it is big but the nature of this change is simply quite complex.
I suggest firstly focus on reviewing newly added functions (like proper naming, calling conventions ...) + changes to the publicly "visible" structs.
After that I can write the updated documentation (which can reveal couple of API gotchas).
And in the end we can polish implementation details.
sounds like a plan, we should probably now first finish #309 , I'll add documentation soon, but I think from a coding PoV it's fine. Then we go on with this one.
I've added some more comments, as all the old ones still need to be addressed.
Thanks!
a. we should accept the official&historic names (e.g. "Curve P-256", "secp256r1", "NIST P-256" and ltc's old "ECC-256" for "NISTP256")
b. we should agree on which standard to refer to for the output and then use that (I'd say we go with NIST names "Curve P-256" etc.), this one should then also be used in
ltc_ecc_setsc. we should use something like
strcasecmp()instead ofstrcmp()... I say "something like" because that compare function should also ignore at least spaces and dashes as "NISTP256" is called e.g. "NIST P-256" on djb's safecurves but "nistp256" in some SSH RFC's ... or we simply add both "NISTP256" and "NIST P-256" to thealt_namestable and usestrcasecmp();)@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denishow about converting the
keyelements once to strings andstrcmp()then the elements of the sets?alternatively add a function like
@ -274,7 +277,13 @@ typedef struct {done (renamed to ltc_ecc_curve)
done
@ -0,0 +1,90 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denisdone (ecc_set_dp_by_size)
renamed to
ecc_get_curve_by_namehere some background when I usedpand whencurvecurveis always a pointer toltc_ecc_curvestruct containing curve definition as hex stringsdpis part of the key, I meanecc_keywhich looks like:dpis a structure containing also curve parameters but not as hexadecimal strings, but loaded as mpi numbersSo
ecc_get_curve_by_..means: findltc_ecc_curvestruct which fits given criteria. On the other handecc_set_dp_..means setupdppart ofecc_key@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denisdone
@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denisdone
@ -21,68 +16,12 @@done
@ -0,0 +1,62 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denisdone ( return err and add another int*-argument)
Looks good!
@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St Deniscopied from https://github.com/libtom/libtomcrypt/pull/236#discussion_r176715691
how about converting the
keyelements once to strings andstrcmp()then the elements of the sets?alternatively add a function like
@ -45,14 +41,32 @@ int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, voidreturn err;as there's also a case above that does similar stuff how about adding
@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St DenisWell, I did basically the opposite thing, when loading the key I do convert all curve params (hex strings) into mpis. It saved couple of hex2mpi operations (grep for
mp_read_radixin the old ecc implementation).The function
_ecc_oid_lookupis not heavily used, therefore I think an extra cost is acceptable here.@ -45,14 +41,32 @@ int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, voidreturn err;The
ltc_ecc_set_to_point_at_infinitylooks reasonable, I do not see many uses for more generalltc_ecc_set_coordinates. But it may be worth consideringltc_ecc_copy_point.TODO: I agree, a support for alternative names would be nice
Ad alternative names - what about to change the curve struct like this:
and later use this:
BTW this PR has broken LTC_MECC_FP support 😞
@ -28,7 +23,12 @@void ecc_free(ecc_key *key)done (cleanup in one call)
You are right but this pattern already exist e.g. in
dsa_set_keyunfixable?
we are missing
orderinltc_ecc_fp_mul2addparams as "standard" mul2add does not need itI'm not 100% sure if I really like this
please use
'\0'to check for the terminating NUL bytehow about adding it as an argument to
ltc_ecc_fp_add_point()and call that one (from one of our API's) before any offp_mul2add()orfp_mulmod()are called?BUT I've no clue how widely
LTC_MECC_FPis even used and if we should bother investing a lot of time...Can you please use the official spelling? (as proposed in https://github.com/libtom/libtomcrypt/pull/236#discussion_r176721275)
then we also need a
ecc_curve_get_name()which simply returns the first element of the names@ -0,0 +34,4 @@if ((err = mp_read_radix(key->dp.base.y, curve->Gy, 16)) != CRYPT_OK) { goto error; }if ((err = mp_set(key->dp.base.z, 1)) != CRYPT_OK) { goto error; }/* cofactor & size */key->dp.cofactor = curve->cofactor;how about adding
str_get_oid(const char*, oid_st*)similar topk_get_oid()?@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St DenisOkay, perfectly fine. But please still add
_ecc_cmp_hex_bn()@ -0,0 +34,4 @@if ((err = mp_read_radix(key->dp.base.y, curve->Gy, 16)) != CRYPT_OK) { goto error; }if ((err = mp_set(key->dp.base.z, 1)) != CRYPT_OK) { goto error; }/* cofactor & size */key->dp.cofactor = curve->cofactor;...which converts the input, not a LUT as the latter... :) and
str_to_oid(const char*, unsigned char*, unsigned long*)would also be perfectly fine!@ -0,0 +34,4 @@if ((err = mp_read_radix(key->dp.base.y, curve->Gy, 16)) != CRYPT_OK) { goto error; }if ((err = mp_set(key->dp.base.z, 1)) != CRYPT_OK) { goto error; }/* cofactor & size */key->dp.cofactor = curve->cofactor;done, see pk_oid_str_to_num + pk_oid_num_to_str
I am not sure, I do not a use case for
ecc_curve_get_nameDo you mean:
?
I also want to point out that apart from official names there is something like "industry standard" how these curves are named in other libraries - e.g. openssl:
We should be IMO able to understand these "common" names.
@ -0,0 +1,82 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denis👍
*cough* der_test.c:313 *cough*
:)
sounds good
btw regarding the names
... and this comment is only relevant if we add
ecc_curve_get_name()... I don't like the inconsistency in the openssl naming...I'd propose to use the names if available in this order of the standards bodies: NIST-SECG-ANSI-BRAINPOOL-OTHERS
We have ecc_get_curve_by_name() but ought we also have ecc_get_curve_by_oid() using the dotted string form of OID?
int ecc_get_curve_by_oid(const char* oid, const ltc_ecc_curve** cu);
Ought we have a mechanical process to confirm that a name only appears once in a list of all names that appear in all curves?
I like the idea of supporting synonymous names for the same curve but am bothered by including the potentially ambiguous names like P-256 and ECC-256, past history notwithstanding. I'd much prefer developers change over to the standard-based names like secp256r1 or prime256v1. Failing that, the doc should be crystal clear that P-XXX and ECC-XXX are only supported for reasons of backward compatibility.
Ad P-256 & co. - these names are also used in JSON Web Signature (JWS) related RFCs 7515, 7518. It is perhaps based on curve names from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
ad
int ecc_get_curve_by_oid(const char* oid, const ltc_ecc_curve** cu);with current implementation you can call:
I know that the name is a bit misleading but we can explain it in documentation.
@ -0,0 +1,82 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denislulz, no the one before was perfectly fine, I just thought you can use it there (please keep your version!)
@ -0,0 +1,82 @@/* LibTomCrypt, modular cryptographic library -- Tom St DenisOK, I'll put it back than :)
how about renaming it to
ecc_get_curve(const char *id, const ltc_ecc_curve **cu)?@ -0,0 +1,82 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denishow about returning
CRYPT_BUFFER_OVERFLOWetc.?I know it's still a "private" function, but then it'd already be "public-ready" :)
@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St Deniscan you please add this as parameter so we don't have to do init&clear for each compare operation?
I know that this shouldn't be called this often, but still it'd be an easy improvement
@ -0,0 +1,129 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denisdone
@ -0,0 +1,82 @@/* LibTomCrypt, modular cryptographic library -- Tom St Denisdone
Do we really need something like
ecc_curve_get_name()? What would be the use case?For me the doc should be sufficient. ...and I kinda like the shorter name. But I'd like to suggest we extend the arg
idtoidentifierwhich can more readily encompass and associate both name and OID.ecc_get_curve(const char *identifier, const ltc_ecc_curve **cu)In the end I have decided for:
I just saw that. Bingo!
A CLI tool similar to
openssl ecparam -list_curves | grep 'prime field'?We can also leave it as it is for now and add the API as soon as that tool is written ;)
@fperrad could you please check (const params + usual stuff) with your linter
pr/ecc-non-asn1-partbranch?@karel-m here, the harvest:
@fperrad I have fixed all of them except:
These look like false positives. Please re-run the check again.
I agree look like false positive.
There are 2 new ones:
I missed this one:
0001-don-t-use-deprecated-LTC_ECCxxx-defines-anymore.patch.txt
what do you think about applying this patch?
The patch is good. Please commit&push it to this branch.
applied & I was a bit surprised when the
./test ecc-run returned anopinstead of apassed:)Should I start working on doc update or do we plan more changes to API / struct & co. ?
Go for the docs! 👍
And feel free to squash again :)
FYI: I have tried to build dropbear with the API changes included in this PR. Unfortunately we are going to break dropbear build quite severely. I'll try to investigate whether we can somehow lower the impact.
Or maybe we should simply submit a patch for dropbear alongside the release of this new API.
I'll have a look and probably @mkj can add his two cents as well :)
I'm fine with updating Dropbear to work with the new API - I've only had a quick look over it so far though.
ecc_set_key()can replace Dropbear'sbuf_get_ecc_raw_pubkey(), and places Dropbear useskey->dpwill useltc_ecc_curveinstead.My main concern is how the ABI breakage happens if people have built Dropbear using system libtomcrypt on Debian - I guess the shared library version number will bump?
@mkj my first draft (not in a pull-request quality yet) is here
github.com/karel-m/dropbear@4530ff6897Ad ABI breakage we definitely will bump ABI & SO versions (as it turned out it is necessary even with smaller API changes than this one)
After the Xth try to review I arrived at the end of the PR :)
Great I'll squash it to a single commit and merge to develop.
I'll open a separate PR for doc changes