Skip to main content

XCHAIN-CRYPTO encrypts a master phrase to a keystore

Project description

XCHAIN-CRYPTO

The XCHAIN CRYPTO package is a crypto package used by all XCHAIN clients.

XCHAIN-CRYPTO encrypts a master phrase to a keystore. This keystore can then be exported to other XCHAIN wallets or stored securely.

Users can export their phrase and import them into other wallets since it is a BIP39 compatible phrase.

Design

Typically keystore files encrypt a seed to a file, however this is not appropriate or UX friendly, since the phrase cannot be recovered after the fact.

Crypto design:

[entropy] -> [phrase] -> [seed] -> [privateKey] -> [publicKey] -> [address]

Instead, XCHAIN-CRYPTO stores the phrase in a keystore file, then decrypts and passes this phrase to other clients:

[keystore] -> XCHAIN-CRYPTO -> [phrase] -> ChainClient

The ChainClients can then convert this into their respective key-pairs and addresses. Users can also export their phrases after the fact, ensuring they have saved it securely. This could enhance UX onboarding since users aren't forced to write their phrases down immediately for empty or test wallets.

# Crypto Constants for xchain
CIPHER = AES.MODE_CTR
NBITS = 128
KDF = "pbkdf2"
PRF = "hmac-sha256"
DKLEN = 32
C = 262144
HASHFUNCTION = SHA256
META = "xchain-keystore"

Usage

Basic usage

from xchainpy_crypto.crypto import validate_phrase, encrypt_to_keystore, decrypt_from_keystore, generate_mnemonic

phrase = generate_mnemonic(size=12, language='english')
print(phrase)
is_correct = validate_phrase(phrase)
print(is_correct)
password = 'thorchain'
keystore = await encrypt_to_keystore(phrase, password)
phrase_decrypted = await decrypt_from_keystore(keystore, password)
print(phrase_decrypted)

Keystore Model

class Keystore:
    def __init__(self, crypto:CryptoStruct, id:str, version:int, meta:str):
        self._crypto = crypto
        self._id = id
        self._version = version
        self._meta = meta

    @classmethod
    def from_dict(cls, keystore):
        new_keystore = cls.__new__(cls)
        for key in keystore:
            setattr(new_keystore, key, keystore[key])
        return new_keystore

    @property
    def crypto(self):
        return self._crypto

    @crypto.setter
    def crypto(self, crypto):
        if isinstance(crypto, dict):
            self._crypto = CryptoStruct.from_dict(crypto)
        else:
            self._crypto = crypto

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, id):
        self._id = id

    @property
    def version(self):
        return self._version

    @version.setter
    def version(self, version):
        self._version = version

    @property
    def meta(self):
        return self._meta

    @meta.setter
    def meta(self, meta):
        self._meta = meta

    def to_json(self):
        return json.dumps(self, default=lambda o: {key.lstrip('_'): value for key, value in o.__dict__.items()})

CipherParams

class CipherParams:
    def __init__(self, iv:str):
        self._iv = iv

    @classmethod
    def from_dict(cls, cipherparams):
        new_cipherparams = cls.__new__(cls)
        for key in cipherparams:
            setattr(new_cipherparams, key, cipherparams[key])
        return new_cipherparams

    @property
    def iv(self):
        return self._iv

    @iv.setter
    def iv(self, iv):
        self._iv = iv

CryptoStruct

class CryptoStruct:
    def __init__(
        self,
        cipher: int,
        ciphertext: str,
        cipherparams: CipherParams,
        kdf: str,
        kdfparams: KdfParams,
        mac: str,
    ):
        self._cipher = cipher
        self._ciphertext = ciphertext
        self._cipherparams = cipherparams
        self._kdf = kdf
        self._kdfparams = kdfparams
        self._mac = mac

    @classmethod
    def from_dict(cls, crypto):
        new_crypto = cls.__new__(cls)
        for key in crypto:
            setattr(new_crypto, key, crypto[key])
        return new_crypto

    @property
    def cipher(self):
        return self._cipher

    @cipher.setter
    def cipher(self, cipher):
        self._cipher = cipher

    @property
    def ciphertext(self):
        return self._ciphertext

    @ciphertext.setter
    def ciphertext(self, ciphertext):
        self._ciphertext = ciphertext

    @property
    def cipherparams(self):
        return self._cipherparams

    @cipherparams.setter
    def cipherparams(self, cipherparams):
        if isinstance(cipherparams, dict):
            self._cipherparams = CipherParams.from_dict(cipherparams)
        else:
            self._cipherparams = cipherparams

    @property
    def kdf(self):
        return self._kdf

    @kdf.setter
    def kdf(self, kdf):
        self._kdf = kdf

    @property
    def kdfparams(self):
        return self._kdfparams

    @kdfparams.setter
    def kdfparams(self, kdfparams):
        if isinstance(kdfparams, dict):
            self._kdfparams = KdfParams.from_dict(kdfparams)
        else:
            self._kdfparams = kdfparams

    @property
    def mac(self):
        return self._mac

    @mac.setter
    def mac(self, mac):
        self._mac = mac

KdfParams

class KdfParams:
    def __init__(self, prf:str , dklen:int , salt:str , c:int):
        self._prf = prf
        self._dklen = dklen
        self._salt = salt
        self._c = c

    @classmethod
    def from_dict(cls, kdfparams):
        new_kdfparams = cls.__new__(cls)
        for key in kdfparams:
            setattr(new_kdfparams, key, kdfparams[key])
        return new_kdfparams
    
    @property
    def prf(self):
        return self._prf

    @prf.setter
    def prf(self, prf):
        self._prf = prf

    @property
    def dklen(self):
        return self._dklen
    
    @dklen.setter
    def dklen(self, dklen):
        self._dklen = dklen
    
    @property
    def salt(self):
        return self._salt

    @salt.setter
    def salt(self, salt):
        self._salt = salt

    @property
    def c(self):
        return self._c

    @c.setter
    def c(self, c):
        self._c = c

Tests

These packages needed to run tests:

  • pytest pip install pytest

How to run test ?

$ python -m pytest xchainpy/xchainpy_crypto/tests

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xchainpy_crypto-0.1.7.tar.gz (7.1 kB view details)

Uploaded Source

Built Distribution

xchainpy_crypto-0.1.7-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

Details for the file xchainpy_crypto-0.1.7.tar.gz.

File metadata

  • Download URL: xchainpy_crypto-0.1.7.tar.gz
  • Upload date:
  • Size: 7.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.0 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.10

File hashes

Hashes for xchainpy_crypto-0.1.7.tar.gz
Algorithm Hash digest
SHA256 7651c2004b7bab302e24ce0beb152a9ff31099318b6f4e7fd9d78b419d818a84
MD5 1b70b9bd7d8dc466eb88590330626b2e
BLAKE2b-256 3ff12bfbc943775ea151335f5f2bfe9e950a617ecc3422362bf02b951bf238e4

See more details on using hashes here.

File details

Details for the file xchainpy_crypto-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: xchainpy_crypto-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 8.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.0 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.10

File hashes

Hashes for xchainpy_crypto-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 66edad03ebab797dc9f87c6b7e717e942ce1f6d5d431c8b738765ed0995f2a1e
MD5 5448def8ad46af9f15496198d4c9c9de
BLAKE2b-256 b66ac3b855d90ce94d7e331d98f34e788839912a5dcd6ad2c17b8b56cf1e95f3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page