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

issue #50 Add export/import PubSec V1 file and its example

parent 2a5cac04
No related branches found
No related tags found
No related merge requests found
Pipeline #4371 passed
...@@ -79,6 +79,60 @@ class SigningKey(libnacl.sign.Signer): ...@@ -79,6 +79,60 @@ class SigningKey(libnacl.sign.Signer):
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_pubsec_file(cls: Type[SigningKeyType], path: str) -> SigningKeyType:
"""
Return SigningKey instance from Duniter WIF file
:param path: Path to WIF file
"""
with open(path, 'r') as fh:
pubsec_content = fh.read()
# line patterns
regex_pubkey = compile("pub: ([1-9A-HJ-NP-Za-km-z]+)", MULTILINE)
regex_signkey = compile("sec: ([1-9A-HJ-NP-Za-km-z]+)", MULTILINE)
# check public key field
match = search(regex_pubkey, pubsec_content)
if not match:
raise Exception('Error: Bad format PubSec v1 file, missing public key')
# check signkey field
match = search(regex_signkey, pubsec_content)
if not match:
raise Exception('Error: Bad format PubSec v1 file, missing sec key')
# capture signkey
signkey_hex = match.groups()[0]
# extract seed from signkey
seed = bytes(Base58Encoder.decode(signkey_hex)[0:32])
return cls(seed)
def save_pubsec_file(self, path: str) -> None:
"""
Save a Duniter PubSec file (PubSec) v1
:param path: Path to file
"""
# version
version = 1
# base58 encode keys
base58_signing_key = Base58Encoder.encode(self.sk)
base58_public_key = self.pubkey
# save file
with open(path, 'w') as fh:
fh.write(
"""Type: PubSec
Version: {version}
pub: {pubkey}
sec: {signkey}""".format(version=version, pubkey=base58_public_key, signkey=base58_signing_key)
)
@classmethod @classmethod
def from_wif_file(cls: Type[SigningKeyType], path: str) -> SigningKeyType: def from_wif_file(cls: Type[SigningKeyType], path: str) -> SigningKeyType:
""" """
...@@ -89,23 +143,27 @@ class SigningKey(libnacl.sign.Signer): ...@@ -89,23 +143,27 @@ class SigningKey(libnacl.sign.Signer):
with open(path, 'r') as fh: with open(path, 'r') as fh:
wif_content = fh.read() wif_content = fh.read()
# check data field
regex = compile('Data: ([1-9A-HJ-NP-Za-km-z]+)', MULTILINE) regex = compile('Data: ([1-9A-HJ-NP-Za-km-z]+)', MULTILINE)
match = search(regex, wif_content) match = search(regex, wif_content)
if not match: if not match:
raise Exception('Error: Bad format WIF v1 file') raise Exception('Error: Bad format WIF v1 file')
# capture hexa wif key
wif_hex = match.groups()[0] wif_hex = match.groups()[0]
wif_bytes = Base58Encoder.decode(wif_hex) wif_bytes = Base58Encoder.decode(wif_hex)
if len(wif_bytes) != 35: if len(wif_bytes) != 35:
raise Exception("Error: the size of WIF is invalid") raise Exception("Error: the size of WIF is invalid")
# extract data
checksum_from_wif = wif_bytes[-2:] checksum_from_wif = wif_bytes[-2:]
fi = wif_bytes[0:1] fi = wif_bytes[0:1]
seed = wif_bytes[1:-2] seed = wif_bytes[1:-2]
seed_fi = wif_bytes[0:-2] seed_fi = wif_bytes[0:-2]
# check WIF format flag
if fi != b"\x01": if fi != b"\x01":
raise Exception("Error: bad WIF version") raise Exception("Error: bad format version, not WIF")
# checksum control # checksum control
checksum = libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(seed_fi))[0:2] checksum = libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(seed_fi))[0:2]
...@@ -120,7 +178,7 @@ class SigningKey(libnacl.sign.Signer): ...@@ -120,7 +178,7 @@ class SigningKey(libnacl.sign.Signer):
:param path: Path to file :param path: Path to file
""" """
# Cesium v1 # version
version = 1 version = 1
# add format to seed (1=WIF,2=EWIF) # add format to seed (1=WIF,2=EWIF)
...@@ -131,6 +189,7 @@ class SigningKey(libnacl.sign.Signer): ...@@ -131,6 +189,7 @@ class SigningKey(libnacl.sign.Signer):
sha256_v2 = libnacl.crypto_hash_sha256(sha256_v1) sha256_v2 = libnacl.crypto_hash_sha256(sha256_v1)
checksum = sha256_v2[0:2] checksum = sha256_v2[0:2]
# base58 encode key and checksum
wif_key = Base58Encoder.encode(seed_fi + checksum) wif_key = Base58Encoder.encode(seed_fi + checksum)
with open(path, 'w') as fh: with open(path, 'w') as fh:
...@@ -151,16 +210,19 @@ Data: {data}""".format(version=version, data=wif_key) ...@@ -151,16 +210,19 @@ Data: {data}""".format(version=version, data=wif_key)
with open(path, 'r') as fh: with open(path, 'r') as fh:
wif_content = fh.read() wif_content = fh.read()
# check data field
regex = compile('Data: ([1-9A-HJ-NP-Za-km-z]+)', MULTILINE) regex = compile('Data: ([1-9A-HJ-NP-Za-km-z]+)', MULTILINE)
match = search(regex, wif_content) match = search(regex, wif_content)
if not match: if not match:
raise Exception('Error: Bad format EWIF v1 file') raise Exception('Error: Bad format EWIF v1 file')
# capture ewif key
ewif_hex = match.groups()[0] ewif_hex = match.groups()[0]
ewif_bytes = Base58Encoder.decode(ewif_hex) ewif_bytes = Base58Encoder.decode(ewif_hex)
if len(ewif_bytes) != 39: if len(ewif_bytes) != 39:
raise Exception("Error: the size of EWIF is invalid") raise Exception("Error: the size of EWIF is invalid")
# extract data
fi = ewif_bytes[0:1] fi = ewif_bytes[0:1]
checksum_from_ewif = ewif_bytes[-2:] checksum_from_ewif = ewif_bytes[-2:]
ewif_no_checksum = ewif_bytes[0:-2] ewif_no_checksum = ewif_bytes[0:-2]
...@@ -168,8 +230,9 @@ Data: {data}""".format(version=version, data=wif_key) ...@@ -168,8 +230,9 @@ Data: {data}""".format(version=version, data=wif_key)
encryptedhalf1 = ewif_bytes[5:21] encryptedhalf1 = ewif_bytes[5:21]
encryptedhalf2 = ewif_bytes[21:37] encryptedhalf2 = ewif_bytes[21:37]
# check format flag
if fi != b"\x02": if fi != b"\x02":
raise Exception("Error: bad EWIF version") raise Exception("Error: bad format version, not EWIF")
# checksum control # checksum control
checksum = libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(ewif_no_checksum))[0:2] checksum = libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(ewif_no_checksum))[0:2]
...@@ -209,7 +272,7 @@ Data: {data}""".format(version=version, data=wif_key) ...@@ -209,7 +272,7 @@ Data: {data}""".format(version=version, data=wif_key)
:param path: Path to file :param path: Path to file
:param password: :param password:
""" """
# WIF Format version # version
version = 1 version = 1
# add version to seed # add version to seed
......
from duniterpy.key import SigningKey
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_pubsec_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 (PubSec v1 format)
signer.save_pubsec_file(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_pubsec_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)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment