from tools import get_publickey_from_seed, b58_decode, xor_bytes, message_exit from nacl import encoding import nacl.hash from scrypt import hash import pyaes from getpass import getpass from os import path from re import compile, search def auth_method(cli_args): if cli_args.contains_switches('auth-scrypt'): return auth_by_scrypt(cli_args) if cli_args.contains_switches('auth-seed'): return auth_by_seed() if cli_args.contains_switches('auth-file'): return auth_by_auth_file(cli_args) if cli_args.contains_switches('auth-wif'): return auth_by_wif() message_exit("Error: no authentication method") def generate_auth_file(cli_args): if cli_args.contains_definitions('file'): file = cli_args.get_definition('file') else: file = "authfile" seed = auth_method(cli_args) with open(file, "w") as f: f.write(seed) # G1SMS:: Remove verbose text #print("Authfile generated for the public key: ", get_publickey_from_seed(seed)) print(get_publickey_from_seed(seed)) def auth_by_auth_file(cli_args): if cli_args.contains_definitions('file'): file = cli_args.get_definition('file') else: file = "authfile" if not path.isfile(file): message_exit("Error: the file \"" + file + "\" does not exist") with open(file) as f: filetxt = f.read() regex_seed = compile('^[0-9a-fA-F]{64}$') regex_gannonce = compile('^pub: [1-9A-HJ-NP-Za-km-z]{43,44}\nsec: [1-9A-HJ-NP-Za-km-z]{88,90}.*$') # Seed Format if search(regex_seed, filetxt): seed = filetxt[0:64] # gannonce.duniter.org Format elif search(regex_gannonce, filetxt): private_key = filetxt.split("sec: ")[1].split("\n")[0] seed = encoding.HexEncoder.encode(b58_decode(private_key))[0:64].decode("utf-8") else: message_exit("Error: the format of the file is invalid") return seed def auth_by_seed(): seed = input("Please enter your seed on hex format: ") regex = compile('^[0-9a-fA-F]{64}$') if not search(regex, seed): message_exit("Error: the format of the seed is invalid") return seed def auth_by_scrypt(cli_args): # G1SMS:: Accept salt & password from "cli_args" if cli_args.contains_definitions('salt') and cli_args.contains_definitions('password'): salt = cli_args.get_definition('salt') password = cli_args.get_definition('password') else: salt = getpass.getpass("Please enter your Scrypt Salt (Secret identifier): ") password = getpass.getpass("Please enter your Scrypt password (masked): ") if cli_args.contains_definitions('n') and cli_args.contains_definitions('r') and cli_args.contains_definitions('p'): n, r, p = cli_args.get_definition('n'), cli_args.get_definition('r'), cli_args.get_definition('p') if n.isnumeric() and r.isnumeric() and p.isnumeric(): n, r, p = int(n), int(r), int(p) if n <= 0 or n > 65536 or r <= 0 or r > 512 or p <= 0 or p > 32: message_exit("Error: the values of Scrypt parameters are not good") else: message_exit("one of n, r or p is not a number") else: # G1SMS:: Remove text #print("Using default values. Scrypt parameters not specified or wrong format") n, r, p = 4096, 16, 1 #print("Scrypt parameters used: N: {0}, r: {1}, p: {2}".format(n, r, p)) return get_seed_from_scrypt(salt, password, n, r, p) def auth_by_wif(): wif = input("Please enter your WIF or Encrypted WIF address: ") regex = compile('^[1-9A-HJ-NP-Za-km-z]*$') if not search(regex, wif): message_exit("Error: the format of WIF is invalid") wif_bytes = b58_decode(wif) fi = wif_bytes[0:1] if fi == b'\x01': return get_seed_from_wifv1(wif) elif fi == b'\x02': password = getpass("Please enter the " + "password of WIF (masked): ") return get_seed_from_ewifv1(wif, password) message_exit("Error: the format of WIF is invalid or unknown") def get_seed_from_scrypt(salt, password, N=4096, r=16, p=1): seed = hash(password, salt, N, r, p, 32) seedhex = encoding.HexEncoder.encode(seed).decode("utf-8") return seedhex def get_seed_from_wifv1(wif): regex = compile('^[1-9A-HJ-NP-Za-km-z]*$') if not search(regex, wif): message_exit("Error: the format of WIF is invalid") wif_bytes = b58_decode(wif) if len(wif_bytes) != 35: message_exit("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': message_exit("Error: It's not a WIF format") # checksum control checksum = nacl.hash.sha256( nacl.hash.sha256(seed_fi, encoding.RawEncoder), encoding.RawEncoder)[0:2] if checksum_from_wif != checksum: message_exit("Error: bad checksum of the WIF") seedhex = encoding.HexEncoder.encode(seed).decode("utf-8") return seedhex def get_seed_from_ewifv1(ewif, password): regex = compile('^[1-9A-HJ-NP-Za-km-z]*$') if not search(regex, ewif): message_exit("Error: the format of EWIF is invalid") wif_bytes = b58_decode(ewif) if len(wif_bytes) != 39: message_exit("Error: the size of EWIF is invalid") wif_no_checksum = wif_bytes[0:-2] checksum_from_ewif = wif_bytes[-2:] fi = wif_bytes[0:1] salt = wif_bytes[1:5] encryptedhalf1 = wif_bytes[5:21] encryptedhalf2 = wif_bytes[21:37] if fi != b'\x02': message_exit("Error: It's not a EWIF format") # Checksum Control checksum = nacl.hash.sha256( nacl.hash.sha256(wif_no_checksum, encoding.RawEncoder), encoding.RawEncoder)[0:2] if checksum_from_ewif != checksum: message_exit("Error: bad checksum of EWIF address") # SCRYPT password = password.encode("utf-8") scrypt_seed = hash(password, salt, 16384, 8, 8, 64) derivedhalf1 = scrypt_seed[0:32] derivedhalf2 = scrypt_seed[32:64] # AES aes = pyaes.AESModeOfOperationECB(derivedhalf2) decryptedhalf1 = aes.decrypt(encryptedhalf1) decryptedhalf2 = aes.decrypt(encryptedhalf2) # XOR seed1 = xor_bytes(decryptedhalf1, derivedhalf1[0:16]) seed2 = xor_bytes(decryptedhalf2, derivedhalf1[16:32]) seed = seed1+seed2 seedhex = encoding.HexEncoder.encode(seed).decode("utf-8") # Password Control salt_from_seed = nacl.hash.sha256( nacl.hash.sha256( b58_decode(get_publickey_from_seed(seedhex)), encoding.RawEncoder), encoding.RawEncoder)[0:4] if salt_from_seed != salt: message_exit("Error: bad Password of EWIF address") return seedhex