AntonKueltz / fastecdsa

Python library for fast elliptic curve crypto
https://pypi.python.org/pypi/fastecdsa
The Unlicense
264 stars 77 forks source link

Key Import and export #14

Closed devssh closed 6 years ago

devssh commented 6 years ago

Easy way to do this, with multiple formats like hex

AntonKueltz commented 6 years ago

Is this a feature request?

If so, I think the best option would be to use formats for import / export that are commonly used for cryptographic keys. There's some RFCs on these subjects and openssl could be a good tool for testing import / export e.g. parsing exported keys with openssl and importing keys generated by openssl.

There already exist functions for exporting public and private keys in a way that tools like openssl understand, fastecdsa.keys.encode_keypair and fastecdsa.keys.encode_public_key but they're not robustly tested. Import is still missing, mainly because it requires all sorts of (really fun) ASN.1 parsing. It'll probably get implemented eventually but it's not a high priority for me.

devssh commented 6 years ago

Yes, this is kind of a feature request. Import is quite essential if you are planning on saving the keys or using this in any app that needs to reuse the keys to validate things. The scope of fastecdsa is limited to in-memory use and if an application is stopped all is lost but if import was available it would find a wider audience.

The package is quite well made and I can manage export on my own but the lack of import is kind of a deal breaker.

AntonKueltz commented 6 years ago

Would support for import of keys in the form specified in RFC 5915 Elliptic Curve Private Key Structure be sufficient?

devssh commented 6 years ago

Yes, it would be sufficient! Anything easier than having to store the coordinates on the curve, preferably just a path to import keys from a string converted to some format

AntonKueltz commented 6 years ago

Commit c2d0bf5 has functionality for import / export, see the following functions in fastecdsa.keys:

Let me know if there are any issues, ASN.1 has a million little edge cases that I may have missed in this initial impl.

devssh commented 6 years ago

So can I save it in a human readable compact format like hex(125455157...longnumber) and then import it! That's nice

I think the import_key function should take the long directly, leave the job of reading/parsing the files to others. Since having to specify the filepath forces it to be in a file of its own rather than a csv of keys or database

AntonKueltz commented 6 years ago

If you're saving the values in a database or csv I recommend just storing them as hex values, something like:

from binascii import hexlify

private_key_hex =  hexlify(private_key)
public_key_x_hex = hexlify(public_key.x)
public_key_y_hex = hexlify(public_key.y)

# now save the hex values to database or to csv

Same goes for reading, just use python builtins to parse the data and then use the Point constructor to rebuild the public key:

from fastecdsa.point import Point

private_key_hex =  # read from DB or csv
public_key_x_hex =  # read from DB or csv
public_key_y_hex =  # read from DB or csv

private_key = int(private_key_hex, 16)

public_key_x = int(public_key_x_hex, 16)
public_key_y = int(public_key_y_hex, 16)
public_key = Point(public_key_x, public_key_y, curve=...)  # fill in whichever curve the key is on

You can also always recover the public key for a private key, so if you store the private key no need store the public key too:

from fastecdsa import keys

private_key_hex =  # read from DB or csv
private_key = int(private_key_hex, 16)

public_key = keys.get_public_key(private_key, curve=...)  # fill in whichever curve the key is on

No need for special import functions in this case, it's up to you to figure out how to store values in the DB and how to retrieve them and parse them correctly, that's not a job of this library.

devssh commented 6 years ago

No you see, hexlify is sad because we don't need binaries. The reason we convert the numbers to higher base is to make it more human readable and writable. So the number 35433438520218993187164445964186767644316745553452984628160110043964158397160 is better read and written as P/a0n.TX{lorxp Also saving P/a0n.TX{lorxp also saves space on the database or csv.

Also the int(private_key, 256) doesn't work and it doesn't work for very long numbers, so python has a problem representing things in higher base in open source. Base 16 is too small a change, think more like base 256 or 128 to make it intuitive for humans.

At least I can now import the number from the DB, save it as a file and import it using fastecdsa so kudos to that.

Check these to see what I mean Cryptojs Github Google cryptojs

AntonKueltz commented 6 years ago

I'm not sure I quite understand. Are you saying you change bases because representations in higher bases take up less space? I suppose this is true if you store the encodings as strings where each character is 8 bits, but the underlying data being represented is the same e.g. the binary data does not change. You're not compressing the data when you switch bases, you're just changing the length of the string encoding the data (since characters in different bases encode different amounts of bits).

So if I get this right you're taking your private key, converting it to binary data, and then encoding that data in base256 to "save space" when storing it? I suppose in that case you'll need to figure out a way to decode base256 strings to binary data in python.

Again, this library isn't really aimed at providing encoding / decoding options (beyond those that are specific to elliptic curve cryptographic keys). This really has nothing to do with any of the topics or functionality the library looks to address. If you want to use exotic encodings not supported by the standard lib best bet would be to write a stand-alone encoder / decoder. As a side note, how are you currently using any of your keys if you don't already have a way of decoding the base256 data that the DB / csv returns to you?

devssh commented 6 years ago

It will still take the same amount of space, whether in binary, encoded string in higher base or decimal. The difference is that if I can encode it in Base 56, I can easily type the public keys and verify them visually by comparing. It is always easier to compare character by character a key like rpshnaf39wBU than 5230453472379659273967392793749372769373927 It's for the human experience and more importantly security verification purposes.

Also keys like 5230453472379659273967392793749372769373927 seem very secure because of how long they are but rpshnaf39wBU gives a feeling that maybe we should be increasing the characters. It's a bold challenge by the library to increase the bitsize and be performant to scale and deliver in less time. Such stats may encourage more people to use this than write their own code for scalability. At this point fastecdsa is good for people building production systems to fork and improvise on their own.

But I do get your point in how this feature may not be strictly what the library intends to do, I just pointed out a use case which would speed up adoption