Skip to content
Snippets Groups Projects
Commit ced4a798 authored by Vincent Texier's avatar Vincent Texier
Browse files

issue #50 Add export/import WIF V1 file and its example (BC Broken)

SigningKey class has a new signature with seed as parameter like the parent class.
To create an instance from credentials, use from_credentials class method.
parent 41090ede
No related branches found
No related tags found
No related merge requests found
Pipeline #4343 passed
...@@ -3,7 +3,8 @@ duniter public and private keys ...@@ -3,7 +3,8 @@ duniter public and private keys
@author: inso @author: inso
""" """
from typing import Optional, Union from re import compile, MULTILINE, search
from typing import Optional, Union, TypeVar, Type
import libnacl.sign import libnacl.sign
from pylibscrypt import scrypt from pylibscrypt import scrypt
...@@ -29,11 +30,26 @@ class ScryptParams: ...@@ -29,11 +30,26 @@ class ScryptParams:
self.p = p self.p = p
# required to type hint cls in classmethod
SigningKeyType = TypeVar('SigningKeyType', bound='SigningKey')
class SigningKey(libnacl.sign.Signer): class SigningKey(libnacl.sign.Signer):
def __init__(self, salt: Union[str, bytes], password: Union[str, bytes],
scrypt_params: Optional[ScryptParams] = None) -> None: def __init__(self, seed: bytes) -> None:
"""
Init pubkey property
:param str seed: Hexadecimal seed string
""" """
Init a SigningKey object from credentials super().__init__(seed)
self.pubkey = Base58Encoder.encode(self.vk)
@classmethod
def from_credentials(cls: Type[SigningKeyType], salt: Union[str, bytes], password: Union[str, bytes],
scrypt_params: Optional[ScryptParams] = None) -> SigningKeyType:
"""
Create a SigningKey object from credentials
:param salt: Secret salt passphrase credential :param salt: Secret salt passphrase credential
:param password: Secret password credential :param password: Secret password credential
...@@ -48,8 +64,7 @@ class SigningKey(libnacl.sign.Signer): ...@@ -48,8 +64,7 @@ class SigningKey(libnacl.sign.Signer):
scrypt_params.N, scrypt_params.r, scrypt_params.p, scrypt_params.N, scrypt_params.r, scrypt_params.p,
SEED_LENGTH) SEED_LENGTH)
super().__init__(seed) return cls(seed)
self.pubkey = Base58Encoder.encode(self.vk)
def decrypt_seal(self, message: bytes) -> str: def decrypt_seal(self, message: bytes) -> str:
""" """
...@@ -62,3 +77,64 @@ class SigningKey(libnacl.sign.Signer): ...@@ -62,3 +77,64 @@ class SigningKey(libnacl.sign.Signer):
curve25519_public_key = libnacl.crypto_sign_ed25519_pk_to_curve25519(self.vk) curve25519_public_key = libnacl.crypto_sign_ed25519_pk_to_curve25519(self.vk)
curve25519_secret_key = libnacl.crypto_sign_ed25519_sk_to_curve25519(self.sk) curve25519_secret_key = libnacl.crypto_sign_ed25519_sk_to_curve25519(self.sk)
return libnacl.crypto_box_seal_open(message, curve25519_public_key, curve25519_secret_key).decode('utf-8') return libnacl.crypto_box_seal_open(message, curve25519_public_key, curve25519_secret_key).decode('utf-8')
@classmethod
def from_wif_file(cls: Type[SigningKeyType], path: str) -> SigningKeyType:
"""
Return SigningKey instance from Duniter WIF v1 file
:param path: Path to WIF file
"""
with open(path, 'r') as fh:
wif_content = fh.read()
regex = compile('Data: ([1-9A-HJ-NP-Za-km-z]+)', MULTILINE)
match = search(regex, wif_content)
if not match:
raise Exception('Error: Bad format WIF v1 file')
wif_hex = match.groups()[0]
wif_bytes = Base58Encoder.decode(wif_hex)
if len(wif_bytes) != 35:
raise Exception("Error: the size of WIF is invalid")
checksum_from_wif = wif_bytes[-2:]
fi = wif_bytes[0:1]
seed = wif_bytes[1:-2]
seed_fi = wif_bytes[0:-2]
if fi != b"\x01":
raise Exception("Error: bad WIF version")
# checksum control
checksum = libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(seed_fi))[0:2]
if checksum_from_wif != checksum:
raise Exception("Error: bad checksum of the WIF")
return cls(seed)
def save_wif(self, path: str) -> None:
"""
Save a Wallet Import Format file (v1)
:param path: Path to file
"""
# Cesium v1
version = 1
# add version to seed
seed_fi = version.to_bytes(version, 'little') + self.seed
# calculate checksum
sha256_v1 = libnacl.crypto_hash_sha256(seed_fi)
sha256_v2 = libnacl.crypto_hash_sha256(sha256_v1)
checksum = sha256_v2[0:2]
wif_key = Base58Encoder.encode(seed_fi + checksum)
with open(path, 'w') as fh:
fh.write(
"""Type: WIF
Version: {version}
Data: {data}""".format(version=1, data=wif_key)
)
...@@ -33,7 +33,7 @@ def get_identity_document(current_block: dict, uid: str, salt: str, password: st ...@@ -33,7 +33,7 @@ def get_identity_document(current_block: dict, uid: str, salt: str, password: st
timestamp = BlockUID(current_block['number'], current_block['hash']) timestamp = BlockUID(current_block['number'], current_block['hash'])
# create keys from credentials # create keys from credentials
key = SigningKey(salt, password) key = SigningKey.from_credentials(salt, password)
# create identity document # create identity document
identity = Identity( identity = Identity(
......
...@@ -11,7 +11,7 @@ if __name__ == '__main__': ...@@ -11,7 +11,7 @@ if __name__ == '__main__':
password = getpass.getpass("Enter your password: ") password = getpass.getpass("Enter your password: ")
# Create key object # Create key object
key = SigningKey(salt, password) key = SigningKey.from_credentials(salt, password)
# Display your public key # Display your public key
print("Public key for your credentials: %s" % key.pubkey) print("Public key for your credentials: %s" % key.pubkey)
...@@ -21,7 +21,7 @@ if __name__ == '__main__': ...@@ -21,7 +21,7 @@ if __name__ == '__main__':
password = getpass.getpass("Enter your password: ") password = getpass.getpass("Enter your password: ")
# Create key object # Create key object
signing_key_instance = SigningKey(salt, password) signing_key_instance = SigningKey.from_credentials(salt, password)
# open encrypted message file # open encrypted message file
with open(signed_message_path, 'rb') as file_handler: with open(signed_message_path, 'rb') as file_handler:
......
...@@ -31,7 +31,7 @@ password = getpass.getpass("Enter your password: ") ...@@ -31,7 +31,7 @@ password = getpass.getpass("Enter your password: ")
pubkey = input("Enter your public key: ") pubkey = input("Enter your public key: ")
# init signer instance # init signer instance
signer = SigningKey(salt, password) signer = SigningKey.from_credentials(salt, password)
# check public key # check public key
if signer.pubkey != pubkey: if signer.pubkey != pubkey:
......
from duniterpy.key import SigningKey
from libnacl.utils import load_key
import getpass
import os
if "XDG_CONFIG_HOME" in os.environ:
home_path = os.environ["XDG_CONFIG_HOME"]
elif "HOME" in os.environ:
home_path = os.environ["HOME"]
elif "APPDATA" in os.environ:
home_path = os.environ["APPDATA"]
else:
home_path = os.path.dirname(__file__)
# CONFIG #######################################
# WARNING : Hide this file in a safe and secure place
# If one day you forget your credentials,
# you'll have to use one of your private keys instead
PRIVATE_KEY_FILE_PATH = os.path.join(home_path, ".duniter_account_wif_v1.duniterkey")
################################################
# prompt hidden user entry
salt = getpass.getpass("Enter your passphrase (salt): ")
# prompt hidden user entry
password = getpass.getpass("Enter your password: ")
# prompt public key
pubkey = input("Enter your public key: ")
# init signer instance
signer = SigningKey.from_credentials(salt, password)
# check public key
if signer.pubkey != pubkey:
print("Bad credentials!")
exit(1)
# save private key in a file (WIF v1 format)
signer.save_wif(PRIVATE_KEY_FILE_PATH)
# document saved
print("Private key for public key %s saved in %s" % (signer.pubkey, PRIVATE_KEY_FILE_PATH))
try:
# load private keys from file
loaded_signer = SigningKey.from_wif_file(PRIVATE_KEY_FILE_PATH)
# check public key from file
print("Public key %s loaded from file %s" % (loaded_signer.pubkey, PRIVATE_KEY_FILE_PATH))
except Exception as e:
print(e)
exit(1)
exit(0)
...@@ -14,7 +14,7 @@ if __name__ == '__main__': ...@@ -14,7 +14,7 @@ if __name__ == '__main__':
password = getpass.getpass("Enter your password: ") password = getpass.getpass("Enter your password: ")
# Create key object # Create key object
key = SigningKey(salt, password) key = SigningKey.from_credentials(salt, password)
# Display your public key # Display your public key
print("Public key for your credentials: %s" % key.pubkey) print("Public key for your credentials: %s" % key.pubkey)
......
...@@ -86,7 +86,7 @@ def get_signed_raw_revocation_document(identity: Identity, salt: str, password: ...@@ -86,7 +86,7 @@ def get_signed_raw_revocation_document(identity: Identity, salt: str, password:
""" """
revocation = Revocation(PROTOCOL_VERSION, identity.currency, identity, "") revocation = Revocation(PROTOCOL_VERSION, identity.currency, identity, "")
key = SigningKey(salt, password) key = SigningKey.from_credentials(salt, password)
revocation.sign([key]) revocation.sign([key])
return revocation.signed_raw() return revocation.signed_raw()
...@@ -112,7 +112,7 @@ async def main(): ...@@ -112,7 +112,7 @@ async def main():
pubkey = input("Enter your public key: ") pubkey = input("Enter your public key: ")
# init signer instance # init signer instance
signer = SigningKey(salt, password) signer = SigningKey.from_credentials(salt, password)
# check public key # check public key
if signer.pubkey != pubkey: if signer.pubkey != pubkey:
......
...@@ -74,7 +74,7 @@ def get_certification_document(current_block: dict, self_cert_document: Identity ...@@ -74,7 +74,7 @@ def get_certification_document(current_block: dict, self_cert_document: Identity
identity=self_cert_document, identity=self_cert_document,
timestamp=BlockUID(current_block['number'], current_block['hash']), signature="") timestamp=BlockUID(current_block['number'], current_block['hash']), signature="")
# sign document # sign document
key = SigningKey(salt, password) key = SigningKey.from_credentials(salt, password)
certification.sign([key]) certification.sign([key])
return certification return certification
......
...@@ -33,7 +33,7 @@ def get_identity_document(current_block: dict, uid: str, salt: str, password: st ...@@ -33,7 +33,7 @@ def get_identity_document(current_block: dict, uid: str, salt: str, password: st
timestamp = BlockUID(current_block['number'], current_block['hash']) timestamp = BlockUID(current_block['number'], current_block['hash'])
# create keys from credentials # create keys from credentials
key = SigningKey(salt, password) key = SigningKey.from_credentials(salt, password)
# create identity document # create identity document
identity = Identity( identity = Identity(
...@@ -69,7 +69,7 @@ def get_membership_document(membership_type: str, current_block: dict, identity: ...@@ -69,7 +69,7 @@ def get_membership_document(membership_type: str, current_block: dict, identity:
timestamp = BlockUID(current_block['number'], current_block['hash']) timestamp = BlockUID(current_block['number'], current_block['hash'])
# create keys from credentials # create keys from credentials
key = SigningKey(salt, password) key = SigningKey.from_credentials(salt, password)
# create identity document # create identity document
membership = Membership( membership = Membership(
......
...@@ -119,7 +119,7 @@ async def main(): ...@@ -119,7 +119,7 @@ async def main():
transaction = get_transaction_document(current_block, source, pubkey_from, pubkey_to) transaction = get_transaction_document(current_block, source, pubkey_from, pubkey_to)
# create keys from credentials # create keys from credentials
key = SigningKey(salt, password) key = SigningKey.from_credentials(salt, password)
# sign document # sign document
transaction.sign([key]) transaction.sign([key])
......
...@@ -6,7 +6,7 @@ import unittest ...@@ -6,7 +6,7 @@ import unittest
class TestVerifyingKey(unittest.TestCase): class TestVerifyingKey(unittest.TestCase):
def test_from_sign_to_verify(self): def test_from_sign_to_verify(self):
sign_key = SigningKey("saltsalt", "passwordpassword", ScryptParams(4096, 16, 1)) sign_key = SigningKey.from_credentials("saltsalt", "passwordpassword", ScryptParams(4096, 16, 1))
verify_key = VerifyingKey(sign_key.pubkey) verify_key = VerifyingKey(sign_key.pubkey)
self.assertEqual(verify_key.vk, sign_key.vk) self.assertEqual(verify_key.vk, sign_key.vk)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment