-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix encoding problem #28
Conversation
Current coverage is 85.94% (diff: 94.11%)@@ master #28 diff @@
==========================================
Files 17 17
Lines 818 804 -14
Methods 0 0
Messages 0 0
Branches 0 0
==========================================
- Hits 707 691 -16
- Misses 111 113 +2
Partials 0 0
|
@@ -3,13 +3,13 @@ | |||
|
|||
# ED25519 | |||
VK_HEX_ILP = b'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf' | |||
VK_B64_ILP = b'7Bcrk61eVjv0kyxw4SRQNMNUZ+8u/U1k6/gZaDRn4r8' | |||
VK_B64_ILP = b'7Bcrk61eVjv0kyxw4SRQNMNUZ+8u/U1k6/gZaDRn4r8=' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious as to why the =
was added?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was some unnecessary adding and removing of padding
""" | ||
Sign data with private key | ||
|
||
Args: | ||
data (str, bytes): data to sign | ||
prefix: | ||
encoding (str): base64, hex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should be encoding (str): {'base58'|'base64'|'base32'|'base16'|'hex'}
?
""" | ||
PrivateKey instance | ||
""" | ||
|
||
def __init__(self, key): | ||
def __init__(self, key, encoding='base58'): | ||
""" | ||
Instantiate the private key with the private_value encoded in base58 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These docs should probably also be updated.
class Ed25519VerifyingKey(ed25519.VerifyingKey, VerifyingKey): | ||
|
||
def __init__(self, key): | ||
def __init__(self, key, encoding='base58'): | ||
""" | ||
Instantiate the public key with the compressed public value encoded in base58 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably update docs.
""" | ||
Verify if the signature signs the data with this verifying key | ||
|
||
Args: | ||
data (bytes|str): data to be signed | ||
data (bytes|str): data verify |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small typo: data to verify
# The reason for using raw_signatures here is because the verify method of pynacl expects the message | ||
# and the signature to have the same encoding. Basically pynacl does: | ||
# encoder.decode(signature + message) | ||
raw_signature = _get_nacl_encoder(encoding).decode(signature) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the encoder throws; can we put this above the try
?
|
||
# Public key | ||
public_value_compressed_base58 = Ed25519VerifyingKey(base58.b58encode(vk.to_bytes())).to_ascii() | ||
public_value_compressed_base58 = sk.verify_key.encode(encoder=Base58Encoder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No sure if I want to do that inside this library.
If I do it for keys I also need to do it for signatures.
I feel like its more consistent if the this library uses only byte strings throughout and we can decode / encode what we need in the bigchaindb wrappers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dict of fullfillments also decodes the key (although the signature isn't).
Perhaps this is something about external/internal handling; to me this method is only applicable to external users so it should be decoded. I agree that internally, we should just use byte strings.
raise exceptions.UnknownEncodingError("Unknown or unsupported encoding") | ||
|
||
|
||
class Ed25519SigningKey(nacl.signing.SigningKey): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we don't use the generate()
classmethod (and it probably doesn't work?), perhaps composition here would be preferable to inheritance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
composition is a bit messy in python, as you need to explicitly implement each method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But... you realize we've already re-implemented each method anyway? And if we hadn't, the non-overridden functions would've likely been broken if accessed from an instance of this class (e.g. generate
before being re-implemented)?
composition is a bit messy in python
This is simply not true 😄.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is simply not true .
you'll need to reimplement each method as you are wrapping the object in an attribute... messy/inefficient
Haven't encountered a good example of composition in python
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW: in this case it seems not worth spending effort in discussing the most elegant pattern... bigger fish
|
||
|
||
def ed25519_generate_key_pair(): | ||
""" | ||
Generate a new key pair and return the pair encoded in base58 | ||
""" | ||
sk, vk = ed25519.create_keypair() | ||
sk = nacl.signing.SigningKey.generate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does seems slightly awkward that we just defined our own version of SigningKey
and then use the pynacl
one here. Maybe we should expose generate
in our class and use it here?
Really like how much nicer the So glad we don't have things like this |
@@ -154,4 +154,4 @@ def validate(self, message=None, **kwargs): | |||
if not (message and self.signature): | |||
return False | |||
|
|||
return self.public_key.verify(message, self.signature, encoding=None) | |||
return self.public_key.verify(message, self.signature, encoding='bytes') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably update the docstring to specify that message
has to be a bytestring.
@@ -66,7 +66,7 @@ def sign(self, message, private_key): | |||
# .update(Buffer.concat([this.messagePrefix, this.message])) | |||
# .digest() | |||
|
|||
self.signature = sk.sign(message, encoding=None) | |||
self.signature = sk.sign(message, encoding='bytes') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably update the docstring to specify that message
has to be a bytestring
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure it is in line with the cc spec. Buffer means bytes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This didn't change anything. encoding='bytes'
is that same as the old to_bytes()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from the docstring: message (string): message to be signed
needs to be:
message (bytes): message to be signed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great stuff - minor changes
raise exceptions.UnknownEncodingError("Unknown or unsupported encoding") | ||
|
||
|
||
class Ed25519SigningKey(nacl.signing.SigningKey): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
composition is a bit messy in python, as you need to explicitly implement each method
|
||
Args: | ||
key (base58): base58 encoded private key | ||
key (str): encoded private value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
str
or bytes
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe also typecheck on either
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works well we both. I guess the encoders handle both cases.
But to be consistent I will put bytes
Instantiate the public key with the public value. | ||
|
||
Args: | ||
key (str): encoded compressed value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
str
or bytes
? typecheck?
# Private key | ||
private_value_base58 = Ed25519SigningKey(base58.b58encode(sk.to_bytes())).to_ascii() | ||
private_value_base58 = sk.encode(encoding='base58') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to signing_key_base58
or sk_base58
or better: add encoding
as a kwarg
: ed25519_generate_key_pair(encoding='base58')
|
||
# Public key | ||
public_value_compressed_base58 = Ed25519VerifyingKey(base58.b58encode(vk.to_bytes())).to_ascii() | ||
public_value_compressed_base58 = sk.get_verifying_key().encode(encoding='base58') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to vk_base58
or verifying_key_base58
consider encoding
as kwarg
@@ -57,7 +57,7 @@ def sign(self, message, private_key): | |||
private_key (string) Ed25519 private key | |||
""" | |||
sk = private_key | |||
vk = VerifyingKey(base58.b58encode(sk.get_verifying_key().to_bytes())) | |||
vk = VerifyingKey(base58.b58encode(sk.get_verifying_key().encode(encoding='bytes'))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vk = sk.get_verifying_key().encode(encoding='base58')
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does get_verifying_key
come from nacl
inheritance and runs a nacl.signing.VerifyKey
?
maybe we can override that method to return a Ed25519VerifyingKey
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get_verifying_key
was already part of the Ed25519SigningKey
api. Didn't want to change it since its also used in the rest of the cc code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, but you can get rid of the base58.b58encode
@@ -66,7 +66,7 @@ def sign(self, message, private_key): | |||
# .update(Buffer.concat([this.messagePrefix, this.message])) | |||
# .digest() | |||
|
|||
self.signature = sk.sign(message, encoding=None) | |||
self.signature = sk.sign(message, encoding='bytes') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure it is in line with the cc spec. Buffer means bytes
agreed |
👍 mergeable! |
resolves #22
This pull request replaces the ed25519 python library with pynacl.
pynacl is a python binding for libsodium and is under active development.
Besides replacing the libraries, also change the way encoding is handled. Now instead of the
encode
,to_ascii
, andto_bytes
methods we have onlyencode
and we can pass the encoding that we want. Supported encoders are hex, bytes, base16, base32, base58, base64TODO: