Source code for stellar_sdk.sep.mnemonic

"""
SEP: 0005
Title: Key Derivation Methods for Stellar Accounts
"""

import hashlib
import hmac
import os
import struct
from enum import Enum, unique
from typing import Union

from mnemonic import Mnemonic
from mnemonic.mnemonic import PBKDF2_ROUNDS

from ..exceptions import ValueError


[docs]@unique class Language(Enum): """The type of language supported by the mnemonic.""" JAPANESE = "japanese" FRENCH = "french" ENGLISH = "english" SPANISH = "spanish" ITALIAN = "italian" KOREAN = "korean" CHINESE_SIMPLIFIED = "chinese_simplified" CHINESE_TRADITIONAL = "chinese_traditional"
[docs]class StellarMnemonic(Mnemonic): """Please use :meth:`Keypair.generate_mnemonic_phrase` and :meth:`Keypair.from_mnemonic_phrase`""" STELLAR_ACCOUNT_PATH_FORMAT = "m/44'/148'/%d'" FIRST_HARDENED_INDEX = 0x80000000 SEED_MODIFIER = b"ed25519 seed" def __init__(self, language: Union[str, Language] = Language.ENGLISH) -> None: if isinstance(language, Language): language = language.value else: if language not in set(item.value for item in Language): raise ValueError("This language is not supported.") super().__init__(language) def to_seed(self, mnemonic: str, passphrase: str = "", index: int = 0) -> bytes: if not self.check(mnemonic): raise ValueError( "Invalid mnemonic, please check if the mnemonic is correct, " "or if the language is set correctly." ) mnemonic = self.normalize_string(mnemonic) passphrase = self.normalize_string(passphrase) passphrase = "mnemonic" + passphrase mnemonic_bytes = mnemonic.encode("utf-8") passphrase_bytes = passphrase.encode("utf-8") stretched = hashlib.pbkdf2_hmac( "sha512", mnemonic_bytes, passphrase_bytes, PBKDF2_ROUNDS ) return self.derive(stretched[:64], index) def generate(self, strength: int = 128) -> str: if strength not in (128, 160, 192, 224, 256): raise ValueError( f"Strength should be one of the following (128, 160, 192, 224, 256), but it is not ({strength})." ) return self.to_mnemonic(os.urandom(strength // 8)) @staticmethod def derive(seed: bytes, index: int) -> bytes: # References https://github.com/satoshilabs/slips/blob/master/slip-0010.md master_hmac = hmac.new(StellarMnemonic.SEED_MODIFIER, digestmod=hashlib.sha512) master_hmac.update(seed) il = master_hmac.digest()[:32] ir = master_hmac.digest()[32:] path = StellarMnemonic.STELLAR_ACCOUNT_PATH_FORMAT % index for x in path.split("/")[1:]: data = ( struct.pack("x") + il + struct.pack(">I", StellarMnemonic.FIRST_HARDENED_INDEX + int(x[:-1])) ) i = hmac.new(ir, digestmod=hashlib.sha512) i.update(data) il = i.digest()[:32] ir = i.digest()[32:] return il