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"
Installation
pip install xchainpy_crypto
Before install the package on M1 Mac, execute this command:
brew install autoconf automake libffi libtool pkg-config
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 - pytest-asyncio
pip install pytest-asyncio
How to run test ?
$ python -m pytest xchainpy/xchainpy_crypto/tests
Metadata
Release files for xchainpy-crypto 0.1.11
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| xchainpy_crypto-0.1.11.tar.gz | 8.0 kB | Without provenance | Details for xchainpy_crypto-0.1.11.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| xchainpy_crypto-0.1.11-py3-none-any.whl | Python 3 | none | any | Details for xchainpy_crypto-0.1.11-py3-none-any.whl |
Total release size: 17.4 kB
Release files / xchainpy_crypto-0.1.11.tar.gz
| Download URL | xchainpy_crypto-0.1.11.tar.gz |
|---|---|
| Size | 8.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5f1adba9f5ee31e167391db7a68f8bf4a72579a2acca6173594d976f335cf19a
|
|
BLAKE2b-256 checksum How to use checksums |
c1173467f986b14ef81b216178bac5b8012d2469e1ac9837002f0ed77407e9c5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.2
|
Release files / xchainpy_crypto-0.1.11-py3-none-any.whl
| Download URL | xchainpy_crypto-0.1.11-py3-none-any.whl |
|---|---|
| Size | 9.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e1b1d481ec1881d5581579a512404d0f3afbc7db2eb82b7e30c18d57c659c3ba
|
|
BLAKE2b-256 checksum How to use checksums |
b926139878014492efc0ae24932410abe06fb94e7e2288c69f0cdf8be064de01
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.2
|
Security for xchainpy-crypto 0.1.11
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.