I am trying to use ED25519
signing and verifying blockchain transactions in my blockchain project but I am getting a
ValueError "Invalid DER input: insufficient data
when I add the encode_dss_signature, and decode_dss_signature.
I am first converting the data into json format and then encoding it into (utf-8) format. Then when I try to decode the signature using decode_dss_signature
. it gives the above Value Error.
import json
import uuid
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.asymmetric.utils import(
encode_dss_signature,
decode_dss_signature
)
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import serialization
class Wallet:
"""
An individual wallet for a miner.
Keeps track of the miners's balance.
Allows a miner to authorize transactions.
"""
def __init__(self):
self.address = str(uuid.uuid4())[0:8]
self.private_key = Ed25519PrivateKey.generate()
self.public_key = self.private_key.public_key()
self.serialize_public_key()
def sign(self, data):
"""
Generate a signature based on the data using the local private key.
"""
return decode_dss_signature(self.private_key.sign(
json.dumps(data).encode('utf-8')
))
def serialize_public_key(self):
"""
Reset the public key to its serialized version.
"""
self.public_key = self.public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode('utf-8')
print(f'\n-- self.public_key: {self.public_key}')
@staticmethod
def verify(public_key, signature, data):
"""
Verify signature based on original public_key and data.
"""
deserialized_public_key = serialization.load_pem_public_key(
public_key.encode('utf-8')
)
print(f'\n--signature: {signature}\n')
(r, s) = signature
try:
deserialized_public_key.verify(
encode_dss_signature(r, s),
json.dumps(data).encode('utf-8')
)
return True
except InvalidSignature:
return False
def main():
wallet = Wallet()
print(f'wallet.__dict__: {wallet.__dict__}')
data = { 'foo':'bar' }
signature = wallet.sign(data)
print(f'signature: {signature}')
sign_verification = Wallet.verify(wallet.public_key, signature, data)
print(f'sign_verification: {sign_verification}')
sign_verify_failed = Wallet.verify(Wallet().public_key, signature, data)
print(f'sign_verify_failed: {sign_verify_failed}')
if __name__ == '__main__':
main()
I found across my testing that depending on what private_key
was generated by Ed25519PrivateKey
I ended up getting a different error.
By simply running your program as posted 10,000 times I got all of the following ValueError
types:
Value Error Message | Number of Occurrences | Percent Occurrence |
---|---|---|
Invalid DER input: insufficient data | 7193 | 71.93% |
Invalid DER input: unexpected tag | 2389 | 23.89% |
Invalid DER input: unexpected high tag number | 334 | 3.34% |
Invalid DER input: indefinite length form is not allowed in DER | 39 | 0.39% |
Invalid DER input: length was not minimally-encoded | 31 | 0.31% |
Invalid DER input: trailing data | 14 | 0.14% |
You can try for yourself with:
from collections import Counter
def main():
c = Counter()
for i in range(10_000):
try:
wallet = Wallet()
data = { 'foo':'bar' }
wallet.sign(data)
except ValueError as e:
c.update([str(e)])
print(c)
While, cryptography.hazmat.primitives.asymmetric.utils.decode_dss_signature(signature) theoretically
Takes in signatures generated by the DSA/ECDSA signers and returns a tuple (r, s). These signatures are ASN.1 encoded Dss-Sig-Value sequences (as defined in RFC 3279)
And while theoretically
Ed25519 is an elliptic curve signing algorithm using EdDSA and Curve25519. If you do not have legacy interoperability concerns then you should strongly consider using this signature algorithm.
In terms of this program, the value produced by sign does not conform to the expected input of decode_dss_signature
I tried all possible encodings of the public and private keys before passing it into decode_dss_signature
. While changing the encoding and format did change the rates and types of ValueError
received, I still consistently received the same type of error.
There may be a way to ensure that the generated private key conforms to the expected standard for decode_dss_signature
, but I was unable to find much in terms of documentation or use cases for this particular function.
Since encode_dss_signature
and decode_dss_signature
do not behave as expected with ED25519
, I ended up directly returning the results from sign
and verify
.
import json
import uuid
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
class Wallet:
"""
An individual wallet for a miner.
Keeps track of the miners's balance.
Allows a miner to authorize transactions.
"""
def __init__(self):
self.address = str(uuid.uuid4())[0:8]
self.private_key = Ed25519PrivateKey.generate()
self.public_key = self.private_key.public_key()
self.serialize_public_key()
def sign(self, data):
"""
Generate a signature based on the data using the local private key.
"""
return self.private_key.sign(
json.dumps(data).encode('utf-8')
)
def serialize_public_key(self):
"""
Reset the public key to its serialized version.
"""
self.public_key = self.public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode('utf-8')
@staticmethod
def verify(public_key, signature, data):
"""
Verify signature based on original public_key and data.
"""
deserialized_public_key = serialization.load_pem_public_key(
public_key.encode('utf-8')
)
try:
deserialized_public_key.verify(
signature,
json.dumps(data).encode('utf-8')
)
return True
except InvalidSignature:
return False
def main():
wallet = Wallet()
data = {'foo': 'bar'}
signature = wallet.sign(data)
print(f'signature: {signature}')
sign_verification = Wallet.verify(wallet.public_key, signature, data)
print(f'sign_verification: {sign_verification}')
sign_verify_failed = Wallet.verify(Wallet().public_key, signature, data)
print(f'sign_verify_failed: {sign_verify_failed}')
if __name__ == '__main__':
main()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.