def FromEntropy(self, entropy_bytes: bytes) -> Mnemonic: """ Generate mnemonic from the specified entropy bytes. Because of the mnemonic encoding algorithm used by Electrum, the specified entropy will only be a starting point to find a suitable one. Therefore, it's very likely that the actual entropy bytes will be different. To get the actual entropy bytes, just decode the generated mnemonic. Please note that, to successfully generate a mnemonic, the bits of the big endian integer encoded entropy shall be at least 121 (for 12 words) or 253 (for 24 words). Otherwise, a mnemonic generation is not possible and a ValueError exception will be raised. Args: entropy_bytes (bytes): Entropy bytes Returns: Mnemonic object: Generated mnemonic Raises: ValueError: If entropy byte length is not valid or a mnemonic cannot be generated """ # Do not waste time trying if the entropy bit are not enough if ElectrumV2EntropyGenerator.AreEntropyBitsEnough(entropy_bytes): # Same of Electrum: increase the entropy until a valid one is found entropy_int = BytesUtils.ToInteger(entropy_bytes) for i in range(ElectrumV2MnemonicGeneratorConst.MAX_ATTEMPTS): new_entropy_int = entropy_int + i try: return self.m_mnemonic_encoder.Encode(IntegerUtils.ToBytes(new_entropy_int)) except ValueError: continue raise ValueError("Unable to generate a valid mnemonic")
def FromBytes(cls, key_bytes: bytes) -> IPublicKey: """ Construct class from key bytes. Args: key_bytes (bytes): Key bytes Returns: IPublicKey: IPublicKey object Raises: ValueError: If key bytes are not valid """ # Remove the 0x00 prefix if present because nacl requires 32-byte length if (len(key_bytes) == cls.CompressedLength() and key_bytes[0] == BytesUtils.ToInteger(Ed25519KeysConst.PUB_KEY_PREFIX)): key_bytes = key_bytes[1:] # nacl doesn't check if the point lies on curve try: ed25519_lib.point_decode(key_bytes) except ValueError as ex: raise ValueError("Invalid public key bytes") from ex try: return cls(signing.VerifyKey(key_bytes)) except (exceptions.RuntimeError, exceptions.ValueError) as ex: raise ValueError("Invalid public key bytes") from ex
def __GetFlagbyteOptions(flagbyte: bytes) -> Tuple[Bip38PubKeyModes, bool]: """ Get the options from the flagbyte. Args: flagbyte (bytes): Flagbyte Returns: tuple[Bip38PubKeyModes, bool]: Public key mode (index 0), has lot/sequence numbers (index 1) """ # Convert flagbyte to integer flagbyte_int = BytesUtils.ToInteger(flagbyte) # Get bit set in flagbyte has_lot_seq = BitUtils.IsBitSet(flagbyte_int, Bip38EcConst.FLAG_BIT_LOT_SEQ) pub_key_mode = (Bip38PubKeyModes.COMPRESSED if BitUtils.IsBitSet(flagbyte_int, Bip38EcConst.FLAG_BIT_COMPRESSED) else Bip38PubKeyModes.UNCOMPRESSED) # Check flagbyte flagbyte_int = BitUtils.ResetBit(flagbyte_int, Bip38EcConst.FLAG_BIT_LOT_SEQ) flagbyte_int = BitUtils.ResetBit(flagbyte_int, Bip38EcConst.FLAG_BIT_COMPRESSED) if flagbyte_int != 0: raise ValueError(f"Invalid flagbyte ({BytesUtils.ToHexString(flagbyte)})") return pub_key_mode, has_lot_seq
def Encode(self, entropy_bytes: bytes) -> Mnemonic: """ Encode bytes to mnemonic phrase. Args: entropy_bytes (bytes): Entropy bytes Returns: Mnemonic object: Encoded mnemonic Raises: ValueError: If bytes length is not valid or a mnemonic cannot be generated """ # Check entropy length entropy_int = BytesUtils.ToInteger(entropy_bytes) if not ElectrumV2EntropyGenerator.AreEntropyBitsEnough(entropy_int): raise ValueError("Entropy bit length is not enough for generating a valid mnemonic") # Encode to words n = self.m_words_list.Length() mnemonic = [] while entropy_int > 0: word_idx = entropy_int % n entropy_int //= n mnemonic.append(self.m_words_list.GetWordAtIdx(word_idx)) # Check if the mnemonic is valid mnemonic_obj = ElectrumV2Mnemonic.FromList(mnemonic) if not ElectrumV2MnemonicUtils.IsValidMnemonic(mnemonic_obj, self.m_mnemonic_type): raise ValueError("Entropy bytes are not suitable for generating a valid mnemonic") return mnemonic_obj
def BytesChunkToWords(bytes_chunk: bytes, words_list: MnemonicWordsList, endianness: str) -> List[str]: """ Get words from a bytes chunk. Args: bytes_chunk (bytes) : Bytes chunk words_list (MnemonicWordsList object): Mnemonic list endianness (str) : Bytes endianness Returns: list[str]: 3 word indexes """ n = words_list.Length() int_chunk = BytesUtils.ToInteger(bytes_chunk, endianness=endianness) word1_idx = int_chunk % n word2_idx = ((int_chunk // n) + word1_idx) % n word3_idx = ((int_chunk // n // n) + word2_idx) % n return [ words_list.GetWordAtIdx(w) for w in (word1_idx, word2_idx, word3_idx) ]
def GeneratePrivateKey(int_passphrase: str, pub_key_mode: Bip38PubKeyModes) -> str: """ Generate a random encrypted private key from the intermediate passphrase. Args: int_passphrase (str) : Intermediate passphrase pub_key_mode (Bip38PubKeyModes): Public key mode Returns: str: Encrypted private key Raises: Base58ChecksumError: If base58 checksum is not valid ValueError: If the intermediate code is not valid """ # Decode intermediate passphrase int_passphrase_bytes = Base58Decoder.CheckDecode(int_passphrase) # Check length if len(int_passphrase_bytes) != Bip38EcConst.INT_PASS_ENC_BYTE_LEN: raise ValueError(f"Invalid intermediate code length ({len(int_passphrase_bytes)})") # Get all the parts back magic = int_passphrase_bytes[:8] owner_entropy = int_passphrase_bytes[8:16] passpoint = Secp256k1PublicKey.FromBytes(int_passphrase_bytes[16:]) # Check magic if magic not in (Bip38EcConst.INT_PASS_MAGIC_NO_LOT_SEQ, Bip38EcConst.INT_PASS_MAGIC_WITH_LOT_SEQ): raise ValueError(f"Invalid magic ({BytesUtils.ToHexString(magic)})") # Generate seedb seedb = os.urandom(Bip38EcConst.SEED_B_BYTE_LEN) # Compute factorb from seedb factorb = CryptoUtils.DoubleSha256(seedb) # Compute address hash address_hash = Bip38Addr.AddressHash( Secp256k1PublicKey.FromPoint(passpoint.Point() * BytesUtils.ToInteger(factorb)), pub_key_mode ) # Derive key halves from the passpoint, address hash and owner entropy derived_half_1, derived_half_2 = _Bip38EcUtils.DeriveKeyHalves(passpoint.RawCompressed().ToBytes(), address_hash, owner_entropy) # Encrypt seedb in two parts encrypted_part_1, encrypted_part_2 = Bip38EcKeysGenerator.__EncryptSeedb(seedb, derived_half_1, derived_half_2) # Get flagbyte by setting bits flagbyte = Bip38EcKeysGenerator.__SetFlagbyteBits(magic, pub_key_mode) # Concatenate all parts enc_key_bytes = (Bip38EcConst.ENC_KEY_PREFIX + flagbyte + address_hash + owner_entropy + encrypted_part_1[:8] + encrypted_part_2) # Encode in Base58Check return Base58Encoder.CheckEncode(enc_key_bytes)
def __ComputePrivateKey(passfactor: bytes, factorb: bytes) -> bytes: """ Compute the private key from passfactor and factorb. Args: passfactor (bytes): Passfactor factorb (bytes) : Factorb Returns: bytes: Private key """ # Private key: (passfactor * factorb) mod N priv_key_int = (BytesUtils.ToInteger(passfactor) * BytesUtils.ToInteger(factorb)) % Secp256k1.Order() return IntegerUtils.ToBytes(priv_key_int, bytes_num=Secp256k1PrivateKey.Length())
def FromBytes(cls, key_bytes: bytes) -> IPublicKey: """ Construct class from key bytes. Args: key_bytes (bytes): Key bytes Returns: IPublicKey: IPublicKey object Raises: ValueError: If key bytes are not valid """ # Remove the 0x00 prefix if present if (len(key_bytes) == cls.CompressedLength() and key_bytes[0] == BytesUtils.ToInteger(Ed25519KeysConst.PUB_KEY_PREFIX)): key_bytes = key_bytes[1:] # The library does not raise any exception in case of length error elif len(key_bytes) != cls.CompressedLength() - 1: raise ValueError("Invalid public key bytes") # The library doesn't check if the point lies on curve try: ed25519_lib.point_decode(key_bytes) except ValueError as ex: raise ValueError("Invalid public key bytes") from ex return cls(ed25519_blake2b.VerifyingKey(key_bytes))
def _CkdPrivEcdsa(cls, bip32_obj: Bip32Base, index: Bip32KeyIndex) -> Bip32Base: """ Create a child key of the specified index using private derivation. Args: bip32_obj (Bip32Base object): Bip32Base object index (Bip32KeyIndex object): Key index Returns: Bip32Base object: Bip32Base object Raises: Bip32KeyError: If the index results in an invalid key """ assert bip32_obj.m_priv_key is not None # Get elliptic curve curve = EllipticCurveGetter.FromType(bip32_obj.CurveType()) # Data for HMAC if index.IsHardened(): data_bytes = b"\x00" + bip32_obj.m_priv_key.Raw().ToBytes() + bytes(index) else: data_bytes = bip32_obj.m_pub_key.RawCompressed().ToBytes() + bytes(index) # Compute HMAC halves i_l, i_r = Bip32BaseUtils.HmacSha512Halves(bip32_obj.ChainCode().ToBytes(), data_bytes) # Construct new key secret from i_l and current private key i_l_int = BytesUtils.ToInteger(i_l) key_int = BytesUtils.ToInteger(bip32_obj.m_priv_key.Raw().ToBytes()) new_key_int = (i_l_int + key_int) % curve.Order() # Convert to string and pad with zeros new_priv_key_bytes = IntegerUtils.ToBytes(new_key_int).rjust(curve.PrivateKeyClass().Length(), b"\x00") # Construct and return a new Bip32 object return cls(priv_key=new_priv_key_bytes, pub_key=None, key_data=Bip32KeyData(chain_code=i_r, depth=bip32_obj.Depth().Increase(), index=index, parent_fprint=bip32_obj.m_pub_key.FingerPrint()), curve_type=bip32_obj.CurveType(), key_net_ver=bip32_obj.KeyNetVersions())
def FromBytes(cls, point_bytes: bytes) -> IPoint: """ Construct class from point bytes. Args: point_bytes (bytes): Point bytes Returns: IPoint: IPoint object """ try: return cls( ellipticcurve.PointJacobi.from_bytes(curve_256, point_bytes)) except keys.MalformedPointError as ex: raise ValueError("Invalid point key bytes") from ex # ECDSA < 0.17 doesn't have from_bytes method for PointJacobi except AttributeError: return cls.FromCoordinates( BytesUtils.ToInteger( point_bytes[:EcdsaKeysConst.POINT_COORD_BYTE_LEN]), BytesUtils.ToInteger( point_bytes[EcdsaKeysConst.POINT_COORD_BYTE_LEN:]))
def ComputeKeys(self, minor_idx: int, major_idx: int) -> Tuple[MoneroPublicKey, MoneroPublicKey]: """ Compute the public keys of the specified subaddress. Args: minor_idx (int): Minor index (i.e. subaddress index) major_idx (int): Major index (i.e. account index) Returns: tuple[MoneroPublicKey, MoneroPublicKey]: Computed public spend key (index 0) and public view key (index 1) Raises: ValueError: If one of the indexes is not valid """ if minor_idx < 0 or minor_idx > MoneroSubaddressConst.SUBADDR_MAX_IDX: raise ValueError(f"Invalid minor index ({minor_idx})") if major_idx < 0 or major_idx > MoneroSubaddressConst.SUBADDR_MAX_IDX: raise ValueError(f"Invalid major index ({major_idx})") # Subaddress 0,0 is the primary address if minor_idx == 0 and major_idx == 0: return self.m_pub_skey, self.m_pub_vkey # Convert indexes to bytes major_idx_bytes = IntegerUtils.ToBytes(major_idx, bytes_num=MoneroSubaddressConst.SUBADDR_IDX_BYTE_LEN, endianness="little") minor_idx_bytes = IntegerUtils.ToBytes(minor_idx, bytes_num=MoneroSubaddressConst.SUBADDR_IDX_BYTE_LEN, endianness="little") # m = Kekkak256("SubAddr" + master_priv_vkey + major_idx + minor_idx) m = CryptoUtils.Kekkak256(MoneroSubaddressConst.SUBADDR_PREFIX + self.m_priv_vkey.Raw().ToBytes() + major_idx_bytes + minor_idx_bytes) m_int = BytesUtils.ToInteger(m, endianness="little") # Compute subaddress public spend key # D = master_pub_skey + m * B subaddr_pub_skey_point = self.m_pub_skey.KeyObject().Point() + (Ed25519Monero.Generator() * m_int) # Compute subaddress public view key # C = master_priv_vkey * D subaddr_pub_vkey_point = subaddr_pub_skey_point * self.m_priv_vkey.Raw().ToInt("little") return (MoneroPublicKey.FromBytes(subaddr_pub_skey_point.RawEncoded().ToBytes()), MoneroPublicKey.FromBytes(subaddr_pub_vkey_point.RawEncoded().ToBytes()))
def FromBytes(cls, index_bytes: bytes) -> Bip32KeyIndex: """ Construct class from bytes. Args: index_bytes (bytes): Key index bytes Returns: Bip32KeyIndex object: Bip32KeyIndex object Raises: ValueError: If the index is not valid """ return cls(BytesUtils.ToInteger(index_bytes))
def TweakPublicKey(pub_key: IPublicKey) -> bytes: """ Tweak a public key as defined by BIP-0086. tweaked_pub_key = lift_x(pub_key.X()) + int(HashTapTweak(bytes(pub_key.X()))) * G Args: pub_key (IPublicKey object): Public key Returns: bytes: X coordinate of the tweaked public key """ h = _P2TRUtils.HashTapTweak(pub_key) out_point = _P2TRUtils.LiftX(pub_key) + (BytesUtils.ToInteger(h) * Secp256k1.Generator()) return IntegerUtils.ToBytes(out_point.X())
def PassPoint(passfactor: bytes) -> bytes: """ Compute the passpoint as specified in BIP38 (with EC multiplication). Args: passfactor (bytes): Passfactor Returns: bytes: Passpoint bytes in compressed format """ # Compute passpoint passpoint = Secp256k1PublicKey.FromPoint(Secp256k1.Generator() * BytesUtils.ToInteger(passfactor)) # Return it as a compressed public key return passpoint.RawCompressed().ToBytes()
def ScReduce(data_bytes: bytes) -> bytes: """ Convert the specified bytes to integer and return its lowest 32-bytes modulo ed25519-order. This ensures that the result is a valid ed25519 scalar to be used as Monero private key. Args: data_bytes (bytes): Data bytes Returns: bytes: Lowest 32-bytes modulo ed25519-order """ data_int = BytesUtils.ToInteger(data_bytes, endianness="little") return IntegerUtils.ToBytes(data_int % Ed25519Monero.Order(), bytes_num=32, endianness="little")
def AreEntropyBitsEnough(entropy: Union[bytes, int]) -> bool: """ Get if the entropy bits are enough to generate a valid mnemonic. Args: entropy (bytes or int): Entropy Returns: bool: True if enough, false otherwise """ if isinstance(entropy, bytes): entropy = BytesUtils.ToInteger(entropy) entropy_bit_len = 0 if entropy <= 0 else math.floor( math.log(entropy, 2)) return ElectrumV2EntropyGenerator.IsValidEntropyBitLen(entropy_bit_len)
def _CkdPub(self, index: Bip32KeyIndex) -> Bip32Base: """ Create a child key of the specified index using public derivation. Args: index (Bip32KeyIndex object): Key index Returns: Bip32Base object: Bip32Base object Raises: Bip32KeyError: If the index results in an invalid key """ # Get elliptic curve curve = EllipticCurveGetter.FromType(self.CurveType()) # Get index bytes index_bytes = index.ToBytes(endianness="little") # Compute Z and chain code z_bytes = CryptoUtils.HmacSha512( self.ChainCode().ToBytes(), b"\x02" + self.m_pub_key.RawCompressed().ToBytes()[1:] + index_bytes) chain_code_bytes = Bip32BaseUtils.HmacSha512Halves( self.ChainCode().ToBytes(), b"\x03" + self.m_pub_key.RawCompressed().ToBytes()[1:] + index_bytes)[1] # ZL is the left 28-byte part of Z zl_int = BytesUtils.ToInteger(z_bytes[:28], endianness="little") # Compute the new public key point: PKEY + 8ZL * G pub_key_point = self.m_pub_key.Point() + ( (zl_int * 8) * curve.Generator()) # If the public key is the identity point (0, 1) discard the child if pub_key_point.X() == 0 and pub_key_point.Y() == 1: raise Bip32KeyError( "Computed public child key is not valid, very unlucky index") return Bip32Ed25519Kholaw( priv_key=None, pub_key=Ed25519KholawPublicKey.FromPoint(pub_key_point), key_data=Bip32KeyData(chain_code=chain_code_bytes, depth=self.Depth().Increase(), index=index, parent_fprint=self.m_pub_key.FingerPrint()), curve_type=self.CurveType(), key_net_ver=self.KeyNetVersions())
def __DeriveKey(self, change_idx: int, addr_idx: int) -> IPrivateKey: """ Derive the key with the specified change and address indexes. Args: change_idx (int): Change index addr_idx (int) : Address index Returns: Bip32Base object: Bip32Base object """ seq_bytes = self.__GetSequence(change_idx, addr_idx) priv_key_int = (self.MasterPrivateKey().Raw().ToInt() + BytesUtils.ToInteger(seq_bytes)) % Secp256k1.Order() return Secp256k1PrivateKey.FromBytes( IntegerUtils.ToBytes(priv_key_int, Secp256k1PrivateKey.Length()))
def _CkdPubEcdsa(cls, bip32_obj: Bip32Base, index: Bip32KeyIndex) -> Bip32Base: """ Create a child key of the specified index using public derivation. Args: bip32_obj (Bip32Base object): Bip32Base object index (Bip32KeyIndex object): Key index Returns: Bip32Base object: Bip32Base object Raises: Bip32KeyError: If the index results in an invalid key """ curve = EllipticCurveGetter.FromType(bip32_obj.CurveType()) # Data for HMAC, same of __CkdPriv() for public child key data_bytes = bip32_obj.m_pub_key.RawCompressed().ToBytes() + bytes(index) # Get HMAC of data i_l, i_r = Bip32BaseUtils.HmacSha512Halves(bip32_obj.ChainCode().ToBytes(), data_bytes) # Try to construct a new public key from the curve point: pub_key_point + G*i_l try: new_point = bip32_obj.m_pub_key.Point() + (curve.Generator() * BytesUtils.ToInteger(i_l)) pub_key = curve.PublicKeyClass().FromPoint(new_point) except ValueError as ex: raise Bip32KeyError("Computed public child key is not valid, very unlucky index") from ex # Construct and return a new Bip32 object return cls(priv_key=None, pub_key=pub_key, key_data=Bip32KeyData(chain_code=i_r, depth=bip32_obj.Depth().Increase(), index=index, parent_fprint=bip32_obj.m_pub_key.FingerPrint()), curve_type=bip32_obj.CurveType(), key_net_ver=bip32_obj.KeyNetVersions())
def Encode(data_bytes: bytes, alph_idx: Base58Alphabets = Base58Alphabets.BITCOIN) -> str: """ Encode bytes into a Base58 string. Args: data_bytes (bytes) : Data bytes alph_idx (Base58Alphabets, optional): Alphabet index, Bitcoin by default Returns: str: Encoded string Raises: TypeError: If alphabet index is not a Base58Alphabets enumerative """ if not isinstance(alph_idx, Base58Alphabets): raise TypeError( "Alphabet index is not an enumerative of Base58Alphabets") enc = "" # Get alphabet alphabet = Base58Const.ALPHABETS[alph_idx] # Convert bytes to integer val = BytesUtils.ToInteger(data_bytes) # Algorithm implementation while val > 0: val, mod = divmod(val, Base58Const.RADIX) enc = alphabet[mod] + enc # Get number of leading zeros n = len(data_bytes) - len(data_bytes.lstrip(b"\x00")) # Add padding return (alphabet[0] * n) + enc
def _CkdPriv(self, index: Bip32KeyIndex) -> Bip32Base: """ Create a child key of the specified index using private derivation. Args: index (Bip32KeyIndex object): Key index Returns: Bip32Base object: Bip32Base object Raises: Bip32KeyError: If the index results in an invalid key """ assert self.m_priv_key is not None # Get elliptic curve curve = EllipticCurveGetter.FromType(self.CurveType()) # Get index bytes index_bytes = index.ToBytes(endianness="little") # Compute Z and chain code if index.IsHardened(): z_bytes = CryptoUtils.HmacSha512( self.ChainCode().ToBytes(), b"\x00" + self.m_priv_key.Raw().ToBytes() + index_bytes) chain_code_bytes = Bip32BaseUtils.HmacSha512Halves( self.ChainCode().ToBytes(), b"\x01" + self.m_priv_key.Raw().ToBytes() + index_bytes)[1] else: z_bytes = CryptoUtils.HmacSha512( self.ChainCode().ToBytes(), b"\x02" + self.m_pub_key.RawCompressed().ToBytes()[1:] + index_bytes) chain_code_bytes = Bip32BaseUtils.HmacSha512Halves( self.ChainCode().ToBytes(), b"\x03" + self.m_pub_key.RawCompressed().ToBytes()[1:] + index_bytes)[1] # ZL is the left 28-byte part of Z zl_int = BytesUtils.ToInteger(z_bytes[:28], endianness="little") # ZR is the right 32-byte part of Z zr_int = BytesUtils.ToInteger(z_bytes[32:], endianness="little") # Compute kL kl_int = (zl_int * 8) + BytesUtils.ToInteger( self.m_priv_key.Raw().ToBytes()[:32], endianness="little") if kl_int % curve.Order() == 0: raise Bip32KeyError( "Computed private child key is not valid, very unlucky index") # Compute kR kr_int = (zr_int + BytesUtils.ToInteger(self.m_priv_key.Raw().ToBytes()[32:], endianness="little")) % 2**256 # Compute private key priv_key = Ed25519KholawPrivateKey.FromBytes( IntegerUtils.ToBytes(kl_int, Ed25519KholawPrivateKey.Length() // 2, endianness="little") + IntegerUtils.ToBytes(kr_int, Ed25519KholawPrivateKey.Length() // 2, endianness="little")) return Bip32Ed25519Kholaw( priv_key=priv_key, pub_key=None, key_data=Bip32KeyData(chain_code=chain_code_bytes, depth=self.Depth().Increase(), index=index, parent_fprint=self.m_pub_key.FingerPrint()), curve_type=self.CurveType(), key_net_ver=self.KeyNetVersions())
def decodeint(s): #return sum(2 ** i * bit(s, i) for i in range(0, b)) # Modified return BytesUtils.ToInteger(s, endianness="little")
def decode_int(int_bytes: bytes) -> int: return BytesUtils.ToInteger(int_bytes, endianness="little")