Source code for stellar_sdk.keypair

import os
from typing import Any, Union

import nacl.signing as ed25519
from nacl.exceptions import BadSignatureError as NaclBadSignatureError

from .sep.mnemonic import Language, StellarMnemonic
from .exceptions import (
    BadSignatureError,
    MissingEd25519SecretSeedError,
    TypeError,
    AttributeError,
)
from .strkey import StrKey
from .xdr import Xdr

__all__ = ["Keypair"]


[docs]class Keypair: """The :class:`Keypair` object, which represents a signing and verifying key for use with the Stellar network. Instead of instantiating the class directly, we recommend using one of several class methods: * :meth:`Keypair.random` * :meth:`Keypair.from_secret` * :meth:`Keypair.from_public_key` :param verify_key: The verifying (public) Ed25519 key in the keypair. :param signing_key: The signing (private) Ed25519 key in the keypair. """ def __init__( self, verify_key: ed25519.VerifyKey, signing_key: ed25519.SigningKey = None ) -> None: self.verify_key: ed25519.VerifyKey = _get_key_of_expected_type( verify_key, ed25519.VerifyKey ) self.signing_key: ed25519.SigningKey = _get_key_of_expected_type( signing_key, ed25519.SigningKey )
[docs] @classmethod def random(cls) -> "Keypair": """Generate a :class:`Keypair` object from a randomly generated seed. :return: A new :class:`Keypair` instance derived by the randomly seed. """ seed = os.urandom(32) return cls.from_raw_ed25519_seed(seed)
[docs] @classmethod def from_secret(cls, secret: str) -> "Keypair": """Generate a :class:`Keypair` object from a secret seed. :param secret: strkey ed25519 seed, for example: `SB2LHKBL24ITV2Y346BU46XPEL45BDAFOOJLZ6SESCJZ6V5JMP7D6G5X` :return: A new :class:`Keypair` instance derived by the secret. :raise: :exc:`Ed25519SecretSeedInvalidError <stellar_sdk.exceptions.Ed25519SecretSeedInvalidError>`: if ``secret`` is not a valid ed25519 secret seed. """ raw_secret = StrKey.decode_ed25519_secret_seed(secret) return cls.from_raw_ed25519_seed(raw_secret)
[docs] @classmethod def from_public_key(cls, public_key: str) -> "Keypair": """Generate a :class:`Keypair` object from a public key. :param public_key: strkey ed25519 public key, for example: `GATPGGOIE6VWADVKD3ER3IFO2IH6DTOA5G535ITB3TT66FZFSIZEAU2B` :return: A new :class:`Keypair` instance derived by the public key. :raise: :exc:`Ed25519PublicKeyInvalidError <stellar_sdk.exceptions.Ed25519PublicKeyInvalidError>`: if ``public_key`` is not a valid ed25519 public key. """ key = StrKey.decode_ed25519_public_key(public_key) return cls.from_raw_ed25519_public_key(key)
[docs] @classmethod def from_raw_ed25519_seed(cls, raw_seed: bytes) -> "Keypair": """Generate a :class:`Keypair` object from ed25519 secret key seed raw bytes. :param raw_seed: ed25519 secret key seed raw bytes :return: A new :class:`Keypair` instance derived by the ed25519 secret key seed raw bytes """ signing_key = ed25519.SigningKey(raw_seed) verify_key = signing_key.verify_key return cls(verify_key, signing_key)
[docs] @classmethod def from_raw_ed25519_public_key(cls, raw_public_key: bytes) -> "Keypair": """Generate a :class:`Keypair` object from ed25519 public key raw bytes. :param raw_public_key: ed25519 public key raw bytes :return: A new :class:`Keypair` instance derived by the ed25519 public key raw bytes """ verify_key = ed25519.VerifyKey(raw_public_key) return cls(verify_key)
@property def secret(self) -> str: """Returns secret key associated with this :class:`Keypair` instance :return: secret key :raise: :exc:`MissingEd25519SecretSeedError <stellar_sdk.exceptions.MissingEd25519SecretSeedError>` The :class:`Keypair` does not contain secret seed """ if not self.signing_key: raise MissingEd25519SecretSeedError( "The keypair does not contain secret seed. Use Keypair.from_secret or " "Keypair.random to create a new keypair with a secret seed." ) return StrKey.encode_ed25519_secret_seed(self.raw_secret_key()) @secret.setter def secret(self, secret: str) -> None: raise AttributeError( "Please use `Keypair.from_secret` to generate a new Keypair object." ) @property def public_key(self) -> str: """Returns public key associated with this :class:`Keypair` instance :return: public key """ return StrKey.encode_ed25519_public_key(self.raw_public_key()) @public_key.setter def public_key(self, public_key: str) -> None: raise AttributeError( "Please use `Keypair.from_public_key` to generate a new Keypair object." )
[docs] def xdr_public_key(self) -> Xdr.types.PublicKey: """ :return: xdr public key """ return Xdr.types.PublicKey(Xdr.const.KEY_TYPE_ED25519, bytes(self.verify_key))
def xdr_account_id(self) -> Xdr.types.PublicKey: return self.xdr_public_key() def xdr_muxed_account(self) -> Xdr.types.MuxedAccount: return Xdr.types.MuxedAccount( Xdr.const.KEY_TYPE_ED25519, bytes(self.verify_key) )
[docs] def raw_public_key(self) -> bytes: """Returns raw public key. :return: raw public key """ return bytes(self.verify_key)
[docs] def signature_hint(self) -> bytes: """Returns signature hint associated with this :class:`Keypair` instance :return: signature hint """ return bytes(self.xdr_account_id().ed25519[-4:])
[docs] def raw_secret_key(self) -> bytes: """Returns raw secret key. :return: raw secret key """ return bytes(self.signing_key)
[docs] def can_sign(self) -> bool: """Returns `True` if this :class:`Keypair` object contains secret key and can sign. :return: `True` if this :class:`Keypair` object contains secret key and can sign """ return self.signing_key is not None
[docs] def sign(self, data: bytes) -> bytes: """Sign the provided data with the keypair's private key. :param data: The data to sign. :return: signed bytes :raise: :exc:`MissingEd25519SecretSeedError <stellar_sdk.exceptions.MissingEd25519SecretSeedError>`: if :class:`Keypair` does not contain secret seed. """ if not self.can_sign(): raise MissingEd25519SecretSeedError( "The keypair does not contain secret seed. Use Keypair.from_secret or " "Keypair.random to create a new keypair with a secret seed." ) return self.signing_key.sign(data).signature
[docs] def verify(self, data: bytes, signature: bytes) -> None: """Verify the provided data and signature match this keypair's public key. :param data: The data that was signed. :param signature: The signature. :raise: :exc:`BadSignatureError <stellar_sdk.exceptions.BadSignatureError>`: if the verification failed and the signature was incorrect. """ try: self.verify_key.verify(data, signature) except NaclBadSignatureError: raise BadSignatureError("Signature verification failed.")
[docs] @staticmethod def generate_mnemonic_phrase( language: Union[Language, str] = Language.ENGLISH, strength: int = 128 ): """Generate a mnemonic phrase. :param language: The language of the mnemonic phrase, defaults to english. :param strength: The complexity of the mnemonic. :return: A mnemonic phrase. """ mnemonic_phrase = StellarMnemonic(language).generate(strength) return mnemonic_phrase
[docs] @classmethod def from_mnemonic_phrase( cls, mnemonic_phrase: str, passphrase: str = "", index: int = 0 ): """Generate a :class:`Keypair` object via a mnemonic phrase. :param mnemonic_phrase: A unique string used to deterministically generate keypairs. :param passphrase: An optional passphrase used as part of the salt during PBKDF2 rounds when generating the seed from the mnemonic. :param index: The index of the keypair generated by the mnemonic. This allows for multiple Keypairs to be derived from the same mnemonic, such as:: >>> from stellar_sdk.keypair import Keypair >>> mnemonic = 'update hello cry airport drive chunk elite boat shaft sea describe number' # Don't use this mnemonic in practice. >>> kp1 = Keypair.from_mnemonic_phrase(mnemonic, index=0) >>> kp2 = Keypair.from_mnemonic_phrase(mnemonic, index=1) >>> kp3 = Keypair.from_mnemonic_phrase(mnemonic, index=2) :return: A new :class:`Keypair` instance derived from the mnemonic. """ raw_ed25519_seed = StellarMnemonic.to_seed(mnemonic_phrase, passphrase, index) return cls.from_raw_ed25519_seed(raw_ed25519_seed)
[docs] def sign_decorated(self, data) -> Xdr.types.DecoratedSignature: """Sign the provided data with the keypair's private key and returns DecoratedSignature. :param data: signed bytes :return: sign decorated """ signature = self.sign(data) hint = self.signature_hint() return Xdr.types.DecoratedSignature(hint, signature)
def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented # pragma: no cover return ( self.verify_key == other.verify_key and self.signing_key == other.signing_key ) def __str__(self): return f"<Keypair [public_key={self.public_key}]>"
def _get_key_of_expected_type(key: Any, expected_type: Any) -> Any: if key is not None and not isinstance(key, expected_type): raise TypeError( f"The given key_type={type(key)} is not of type {expected_type}." ) return key