Skip to content

Commit

Permalink
Fix conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
freQniK committed Mar 28, 2024
2 parents 4b34240 + 0b3ec78 commit 745ed92
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 24 deletions.
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@
"mnemonic",
"bech32",
"pywgkey",
"sentinel-sdk"
"sentinel-sdk",
"bcrypt",
"jwcrypto"
],
package_data={
"conf": ["config/config.ini"],
Expand Down
138 changes: 115 additions & 23 deletions src/cli/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from cli.v2ray import V2RayHandler, V2RayConfiguration

import base64
import bcrypt
from jwcrypto import jwe, jwk
import uuid
import configparser
import socket
Expand Down Expand Up @@ -57,38 +59,128 @@ def __init__(self, **kwargs):
# Migrate existing wallet to v2
self.__migrate_wallets()

@staticmethod
def decode_jwt_file(fpath: str, password: str) -> dict:
encrypted_jwe = open(fpath).read()
jwkey = {'kty': 'oct', 'k': base64.b64encode(password.encode()).decode("utf-8")}
jwetoken = jwe.JWE()
jwetoken.deserialize(encrypted_jwe)
jwetoken.decrypt(jwk.JWK(**jwkey))
return json.loads(jwetoken.payload)

@staticmethod
def decode_wallet_record(data: bytes) -> dict:
# First byte, padding
data = data[1:]
# Prefix keyname: \r\xad\x15=\n
data = data.removeprefix(b"\r\xad\x15=\n")
# Another, padding
data = data[1:]
# Key name until: \x12&\xebZ\xe9\x87!
keyname = data[:(data.find(b"\x12&\xebZ\xe9\x87!"))]
data = data.removeprefix(keyname + b"\x12&\xebZ\xe9\x87!")
pubkey = data[:33]
data = data.removeprefix(pubkey)
# Padding privatekey \x1a%\xe1\xb0\xf7\x9b
data = data.removeprefix(b"\x1a%\xe1\xb0\xf7\x9b ")
privkey = data[:32]
data = data.removeprefix(privkey)
curve = data[2:].decode()

s = hashlib.new("sha256", pubkey).digest()
r = hashlib.new("ripemd160", s).digest()

hex_address = r.hex()
account_address = bech32.bech32_encode("sent", bech32.convertbits(r, 8, 5))

pubkey = base64.b64encode(pubkey).decode()
privkey = privkey.hex()
keyname = keyname.decode()

return {
"keyname": keyname,
"pubkey": pubkey,
"hex_address": hex_address,
"account_address": account_address,
"privkey": privkey,
"curve": curve,
}

def __migrate_wallets(self):
# https://github.com/MathNodes/meile-gui/blob/main/src/cli/wallet.py#L215-L230
# Before continue make sure that sentinelcli still exist
if path.isfile(sentinelcli):
CONFIG = MeileConfig.read_configuration(MeileConfig.CONFFILE)
PASSWORD = CONFIG['wallet'].get('password', '')
KEYNAME = CONFIG['wallet'].get('keyname', '')

kr = self.__keyring(PASSWORD)
if kr.get_password("meile-gui", KEYNAME) is None: # TODO: very ungly
export_cmd = f"{sentinelcli} keys export {KEYNAME} --unsafe --unarmored-hex --keyring-backend file --keyring-dir {ConfParams.KEYRINGDIR}"
child = pexpect.spawn(export_cmd)
child.expect(".*")
child.sendline("y")
child.expect("Enter*")
child.sendline(PASSWORD)

outputs = child.readlines()
private_key = outputs[-1].strip().decode("utf-8")

child.expect(pexpect.EOF)
CONFIG = MeileConfig.read_configuration(MeileConfig.CONFFILE)
PASSWORD = CONFIG['wallet'].get('password', '')
KEYNAME = CONFIG['wallet'].get('keyname', '')

kr = self.__keyring(PASSWORD)
if kr.get_password("meile-gui", KEYNAME) is not None: # TODO: very ungly
# Wallet was already migrated because exist a valid keyname in our keyring
return

kr = self.__keyring(PASSWORD)
kr.set_password("meile-gui", KEYNAME, private_key)
# For each key record, we actually write 2 items:
# - one with key `<uid>.info`, with Data = the serialized protobuf key
# - another with key `<addr_as_hex>.address`, with Data = the uid (i.e. the key name)
# https://github.com/cosmos/cosmos-sdk/blob/main/crypto/keyring/keyring.go

# We have two way to export private key:
# 1. Decode `<uid>.info` and then, assuming the curve is secp256k1, .replace(b"\"\tsecp256k1", b"")[-32:]
# 2. Follow me.

# First all check if the keyring has a valid key-hash file and if the hash can be compared with our password
keyhash_fpath = path.join(ConfParams.KEYRINGDIR, "keyring-file", "keyhash")
if path.isfile(keyhash_fpath):
keyhash = open(keyhash_fpath, "r").read()
if bcrypt.checkpw(PASSWORD.encode(), keyhash.strip().encode()) is True:
# Search for `<uid>.info` / keyname .info file
keyname_dotinfo = path.join(ConfParams.KEYRINGDIR, "keyring-file", f"{KEYNAME}.info")
if path.isfile(keyname_dotinfo) is True:
payload = HandleWalletFunctions.decode_jwt_file(keyname_dotinfo, PASSWORD)

# Double verify, we could also remove this assert
assert payload.get('Key', None) == f"{KEYNAME}.info"
data = payload["Data"]
data = base64.b64decode(data)

wallet_record = HandleWalletFunctions.decode_wallet_record(data)
# Double verify, we could also remove this assert
assert wallet_record["keyname"] == KEYNAME

hex_address = wallet_record["hex_address"]

# Anothe double verification, let's find the `<addr_as_hex>.address` and verify if data match with our uuid
hex_address_fpath = path.join(ConfParams.KEYRINGDIR, "keyring-file", f"{hex_address}.address")
if path.isfile(hex_address_fpath) is True:
payload = HandleWalletFunctions.decode_jwt_file(hex_address_fpath, PASSWORD)
# Double verify, we could also remove this assert
assert payload.get('Key', None) == f"{hex_address}.address"

data = payload["Data"]
data = base64.b64decode(data)
# Double verify, we could also remove this assert
assert data.decode() == f"{KEYNAME}.info"

# TODO: just for debugging purpose
privkey = wallet_record["privkey"]
del wallet_record["privkey"]
print(wallet_record)

kr.set_password("meile-gui", KEYNAME, privkey)
else:
print(f"{hex_address}.address doesn't exist")
else:
print(f"{KEYNAME}.info doesn't exist")
else:
print("bcrypt hash doesn't match")
else:
print(f"{keyhash_fpath} doesn't exist")


def __keyring(self, keyring_passphrase: str):
kr = CryptFileKeyring()
kr.filename = "keyring.cfg"
print(ConfParams.KEYRINGDIR)
# print(ConfParams.KEYRINGDIR)
kr.file_path = path.join(ConfParams.KEYRINGDIR, kr.filename)
print(kr.file_path)
# print(kr.file_path)
kr.keyring_key = keyring_passphrase
return kr

Expand Down

0 comments on commit 745ed92

Please sign in to comment.