:py:mod:`cryptnoxpy` ==================== .. py:module:: cryptnoxpy .. autoapi-nested-parse:: This is a library for communicating with Cryptnox cards See the README.md for API details and general information. Subpackages ----------- .. toctree:: :titlesonly: :maxdepth: 3 card/index.rst cryptos/index.rst Submodules ---------- .. toctree:: :titlesonly: :maxdepth: 1 binary_utils/index.rst conf/index.rst connection/index.rst crypto_utils/index.rst enums/index.rst exceptions/index.rst factory/index.rst reader/index.rst Package Contents ---------------- Classes ~~~~~~~ .. autoapisummary:: cryptnoxpy.Card cryptnoxpy.Connection cryptnoxpy.Derivation cryptnoxpy.KeyType cryptnoxpy.SlotIndex cryptnoxpy.AuthType cryptnoxpy.SeedSource Attributes ~~~~~~~~~~ .. autoapisummary:: cryptnoxpy.__version__ .. py:class:: Card(connection: cryptnoxpy.connection.Connection, data: List[int] = None, debug: bool = False) Object that contains information about the card that is in the reader. :param Connection connection: Connection to use for card initialization :param bool debug: Show debug information to the user. :var List[int] applet_version: Version of the applet on the card. :var int serial_number: Serial number of card. :var str session_public_key: Public key of the session. :var bool initialized: The card has been initialized with secrets. :raises CardTypeException: The card in the reader is not a Cryptnox card .. py:attribute:: _ALGORITHM .. py:attribute:: PUK_LENGTH :annotation: = 15 .. py:method:: select_apdu() -> List[int] :property: :return: Value to add to select command to select the applet on the card :rtype: List[int] .. py:method:: type() -> int :property: :return: Card type :rtype: int .. py:method:: pin_rule() -> str :property: Human readable PIN code rule :return: Human readable PIN code rule :rtype: str .. py:method:: puk_rule() -> str :property: Human readable PUK code rule :return: Human readable PUK code rule :rtype: str .. py:method:: alive(self) -> bool :property: :return: The connection to the card is established and the card hasn't been changed :rtype: bool .. py:method:: change_pairing_key(self, index: int, pairing_key: bytes, puk: str = '') -> None :abstractmethod: Set the pairing key of the card :param int index: Index of the pairing key :param bytes pairing_key: Pairing key to set for the card :param str puk: PUK code of the card :raises DataValidationException: input data is not valid :raises SecureChannelException: operation not allowed :raises PukException: PUK code is not valid .. py:method:: change_pin(self, new_pin: str) -> None :abstractmethod: Change the current pin code of the card to a new pin code. The method will set the given pin code as the pin code of the card. For it to work the card first must be opened with the current pin code. :requires: - PIN code or challenge-response validated :param str new_pin: The desired PIN code to be set for the card (4-9 digits). .. py:method:: change_puk(self, current_puk: str, new_puk: str) -> None :abstractmethod: Change the current pin code of the card to a new pin code. The method will set the given pin code as the pin code of the card. For it to work the card first must be opened with the current pin code. :param str current_puk: The current PUK code of the card :param str new_puk: The desired PUK code to be set for the card .. py:method:: check_init(self) -> None Check if the initialization has been done on the card. It can be useful to check if the card is initialized before doing anything else, like asking for pin code from the user. :raises InitializationException: The card is not initialized .. py:method:: derive(self, key_type: cryptnoxpy.enums.KeyType = KeyType.K1, path: str = '') :abstractmethod: Derive key on path and make it the current key in the card :requires: - PIN code or challenge-response validated - Seed must exist :param KeyType key_type: Key type to do derive on :param str path: Path on which to do derivation .. py:method:: dual_seed_public_key(self, pin: str = '') -> bytes :abstractmethod: Get the public key from the card for dual initialization of the cards :requires: - PIN code or challenge-response validated :param str pin: PIN code of card if it was opened with a PIN check :return: Public key and signature that can be sent into the other card :rtype: bytes :raises DataException: The received data is invalid .. py:method:: dual_seed_load(self, data: bytes, pin: str = '') -> None :abstractmethod: Load public key and signature from the other card into the card to generate same seed. :requires: - PIN code or challenge-response validated :param str pin: PIN code of card if it was opened with a PIN check :param bytes data: Public key and signature of public key from the other card .. py:method:: extended_public_key(self) -> bool :property: :return: Extended public key turned on :rtype: bool .. py:method:: generate_random_number(self, size: int) -> bytes :abstractmethod: Generate random number on the car and return it. :param int size: Output data size in bytes (between 16 and 64, mod 4) :return: Random number generated by the chip :rtype: bytes :raises DataValidationException: size in not a number between 16 and 64 or is not divisible by 4 .. py:method:: generate_seed(self, pin: str = '') -> bytes :abstractmethod: Generate a seed directly on the card. :requires: - PIN code or challenge-response validated :param pin: PIN code of the card. Can be empty if card is opened with challenge-response validation :type pin: str, optional :return: Primary node "m" UID (hash of public key) :rtype: bytes :raises KeyGenerationException: There was an issue with generating the key :raises KeyAlreadyGenerated: The card already has a seed generated .. py:method:: get_public_key(self, derivation: cryptnoxpy.enums.Derivation, key_type: cryptnoxpy.enums.KeyType = KeyType.K1, path: str = '', compressed: bool = True) -> str :abstractmethod: Get the public key from the card. :requires: - PIN code or challenge-response validated, except for PIN-less path - Seed must exist :param Derivation derivation: Derivation to use. :param KeyType key_type: Key type to use :param str path: :param bool compressed: The returned value is in compressed format. :return: The public key for the given path in hexadecimal string format :rtype: str :raises DerivationSelectionException: Card is not initialized with seed :raises ReadPublicKeyException: Invalid data received from card .. py:method:: history(self, index: int = 0) -> NamedTuple :abstractmethod: Get history of hashes the card has signed regardless of any parameters given to sign :requires: - PIN code or challenge-response validated :param int index: Index of entry in history :return: Return entry containing signing_counter, representing index of sign call, and hashed_data, the data that was signed :rtype: NamedTuple .. py:method:: info(self) -> Dict[str, Any] :property: Get relevant information about the card. :return: Dictionary containing information for the card :rtype: Dict[str, Any] .. py:method:: init(self, name: str, email: str, pin: str, puk: str, pairing_secret: bytes) -> bytes :abstractmethod: Initialize the Cryptnox card. Initialize the Cryptnox card with the owners name and email address. Set the PIN and PUK codes for authenticating with the card to be able to use it. :param str name: Name of the card owner :param str email: Email of the card owner :param str pin: PIN code that will be used to open the card :param str puk: PUK code that will be used to open the card :param bytes pairing_secret: Pairing secret to use with the card :return: Pairing secret :rtype: bytes :raises InitializationException: There was an issue with initialization .. py:method:: initialized(self) -> bool :property: :return: Whether the card is initialized :rtype: bool .. py:method:: load_seed(self, seed: bytes, pin: str = '') -> None :abstractmethod: Load the given seed into the Cryptnox card. :requires: - PIN code or challenge-response validated :param bytes seed: Seed to initialize the card with :param pin: PIN code of the card. Can be empty if card is opened with challenge-response validation :type pin: str, optional :raises KeyGenerationException: Data is not correct .. py:method:: open(self) -> bool :property: :return: Whether the user has authenticated using the PIN code or challenge-response validation :rtype: bool .. py:method:: pin_authentication(self) -> bool :property: :return: Whether the PIN code can be used for authentication :rtype: bool .. py:method:: pinless_enabled(self) -> bool :property: :return: Return whether the card has a pinless path :rtype: bool .. py:method:: reset(self, puk: str) -> None :abstractmethod: Reset the card and return it to factory settings. :param puk: PUK code associated with the card .. py:method:: seed_source(self) -> cryptnoxpy.enums.SeedSource :property: :return: How the seed was generated :rtype: SeedSource .. py:method:: set_pin_authentication(self, status: bool, puk: str) -> None :abstractmethod: Turn on/off authentication with the PIN code. Other methods can still be used. :param bool status: Status of PIN authentication :param str puk: PUK code associated with the card :raises DataValidationException: input data is not valid :raises PukException: PUK code is not valid .. py:method:: set_pinless_path(self, path: bytes, puk: str) -> None :abstractmethod: Enable working with the card without a PIN on path. :param bytes path: Path to be available without a PIN code :param str puk: PUK code of the card :raises DataValidationException: input data is not valid :raises PukException: PUK code is not valid .. py:method:: set_extended_public_key(self, status: bool, puk: str) -> None :abstractmethod: Turn on/off extended public key output. :requires: - Seed must be loaded :param bool status: Status of PIN authentication :param str puk: PUK code associated with the card :raises DataValidationException: input data is not valid :raises PukException: PUK code is not valid :raises KeyException: Seed not found .. py:method:: sign(self, data: bytes, derivation: cryptnoxpy.enums.Derivation, key_type: cryptnoxpy.enums.KeyType = KeyType.K1, path: str = '', pin: str = '', filter_eos: bool = False) -> bytes :abstractmethod: Sign the message using given derivation. :requires: - PIN code provided, authenticate with user key by signing same message or PIN-less path used - Seed must be loaded :param bytes data: Data to sign :param Derivation derivation: Derivation to use. :param key_type: Key type to use. Defaults to K1 :type key_type: KeyType, optional :param path: Path of the key. If empty use main key :type path: str, optional :param pin: PIN code of the card :type pin: str, optional :param bool filter_eos: Filter signature so it is valid for EOS network, might take longer. Defaults to False :type filter_eos: str, optional :return: The signature generated by the card in DER common format. :rtype: bytes :raises DataException: Invalid data received during signature .. py:method:: signing_counter(self) -> int :property: :return: Counter of how many times the card has been used to sign :rtype: int .. py:method:: unblock_pin(self, puk: str, new_pin: str) -> None Verifies the user using the PUK code and sets a new PIN code on the card. Method should be used when the user has forgotten this/hers PIN code. By entering the PUK code the user verifies his/hers identity and can set the new PIN code on the card. Can be used only if the card is locked. :requires: - User PIN must be locked - PIN code authentication must be enabled :param str puk: PUK code for verification of the user, before changing the PIN code. :param str new_pin: The desired PIN code to be set for the card (4-9 digits). :raises PukException: PUK code not valid :raises CardNotBlocked: Card is not blocked, operation can't be done .. py:method:: user_data(self) -> bytes :property: :return: Read user data that was written into the card. :rtype: bytes .. py:method:: user_key_add(self, slot: cryptnoxpy.enums.SlotIndex, data_info: str, public_key: bytes, puk_code: str, cred_id: bytes = b'') -> None :abstractmethod: Add user public key into the card for user authentication :param int slot: Slot to write the public key to 1 - EC256R1 2 - RSA key, 2048 bits, public exponent must be 65537 3 - FIDO key :param bytes data_info: 64 bytes of user data :param bytes public_key: Public key of the secure element to be used for authentication :param str puk_code: PUK code of the card :param cred_id: Cred id. Used for FIDO2 authentication :type cred_id: bytes, optional :raises DataValidationException: Invalid input data .. py:method:: user_key_delete(self, slot: cryptnoxpy.enums.SlotIndex, puk_code: str) -> None :abstractmethod: Delete the user key from slot and free up for insertion :param SlotIndex slot: Slot to remove the key from :param str puk_code: PUK code of the card :raises DataValidationException: Invalid input data .. py:method:: user_key_info(self, slot: cryptnoxpy.enums.SlotIndex) -> Tuple[str, str] :abstractmethod: Get the description and public key of the user key :requires: - PIN code or challenge-response validated :param SlotIndex slot: Index of slot for which to fetch the description :return: Description and public key in slot :rtype: tuple[str, str] .. py:method:: user_key_enabled(self, slot_index: cryptnoxpy.enums.SlotIndex) -> bool :abstractmethod: Check if user key is present in given slot :param SlotIndex slot_index: Slot index to check for :return: Whether the user key for slot is present :rtype: bool .. py:method:: user_key_challenge_response_nonce(self) -> bytes :abstractmethod: Get 32 bytes random value from the card that is used to open the card with a user key Take nonce value from the card. Sign it with a third party application, like TPM. Send the signature back into the card using :func:`~cryptnoxpy.card.base.Base.user_key_challenge_response_open` :return: 32 bytes random value used as nonce :rtype: bytes .. py:method:: user_key_challenge_response_open(self, slot: cryptnoxpy.enums.SlotIndex, signature: bytes) -> bool :abstractmethod: Send the nonce signature to the card to open it for operations, like it was opened by a PIN code :param SlotIndex slot: Slot to use to open the card :param bytes signature: Signature generated by a third party like TPM. :return: Whether the challenge response authentication succeeded :rtype: bool :raises DataValidationException: invalid input data .. py:method:: user_key_signature_open(self, slot: cryptnoxpy.enums.SlotIndex, message: bytes, signature: bytes) -> bool :abstractmethod: Used for opening the card to sign the given message :param SlotIndex slot: Slot to use to open the card :param bytes message: Message that will be sent to sign operation :param bytes signature: Signature generated by a third party, like TPM, on the same message :return: Whether the challenge response authentication succeeded :rtype: bool :raises DataValidationException: invalid input data .. py:method:: valid_key(self) -> bool :property: Check if the card has a valid key :return: Whether the card has a valid key. :rtype: bool .. py:method:: valid_pin(pin: str, pin_name: str = 'pin') -> str :staticmethod: :abstractmethod: Check if provided pin is valid :param str pin: The pin to check if valid :param str pin_name: Value used in DataValidationException for pin name :return str: Provided pin in str format if valid :raise DataValidationException: Provided pin is not valid .. py:method:: valid_puk(puk: str, puk_name: str = 'puk') -> str :staticmethod: :abstractmethod: Check if provided puk is valid :param str puk: The puk to check if valid :param puk_name: Value used in DataValidationException for puk name. Defaults to: puk :type puk_name: str, optional :return str: Provided puk in str format if valid :raise DataValidationException: Provided puk is not valid .. py:method:: verify_pin(self, pin: str) -> None :abstractmethod: Check PIN code and open the card for operations that are protected. The method is sending the PIN code to the card to open it for other operations. If there is an issue an exception will be raised. :param str pin: PIN code to check against the card. :raises PinException: Invalid PIN code :raises DataValidationException: Invalid length or PIN code authentication disabled :raises SoftLock: The card has been locked and needs power cycling before it can be used again .. py:method:: _owner(self) -> User :property: Get the available information about the owner of the card from the card When the card is initialized the owner name and email address are stored on the card. This method will read and return them. :return: A dictionary containing the owner name and email address :rtype: Dict[str, str] :raises CryptnoxCard.PinException: PIN code wasn't validated. :raises CryptnoxCard.SecureChannelException: Secure channel not opened. .. py:method:: __subclasshook__(cls, c) :classmethod: Abstract classes can override this to customize issubclass(). This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached). .. py:method:: __repr__(self) Return repr(self). .. py:class:: Connection(index: int = 0, debug: bool = False) Bases: :py:obj:`contextlib.ContextDecorator` Connection to the reader. Sends and receives messages from the card using the reader. :param int index: Index of the reader to initialize the connection with :param bool debug: Show debug information during requests :var Card self.card: Information about the card. .. py:method:: _init_reader(self, index) .. py:method:: __del__(self) .. py:method:: send_apdu(self, apdu: List[int]) -> Tuple[List[int], int, int] Send data to the card in plain format :param int apdu: list of the APDU header :return bytes: Result of the query that was sent to the card :rtype: bytes :raises ConnectionException: Issue in the connection .. py:method:: send_encrypted(self, apdu: List[int], data: bytes, receive_long: bool = False) -> bytes Send data to the card in encrypted format :param int apdu: list of the APDU header :param data: bytes of the data payload (in clear, will be encrypted) :param bool receive_long: :return bytes: Result of the query that was sent to the card :rtype: bytes :raises CryptnoxException: General exceptions .. py:method:: _check_response_code(code1: int, code2: int) -> None :staticmethod: .. py:method:: _decode(self, rep: bytes, mac_value: bytes) -> bytes .. py:method:: _encrypt(self, apdu: List[int], data: bytes, receive_long: bool) -> Tuple[List[int], Union[int, bytes]] .. py:method:: _open_secure_channel(self, pairing_secret: bytes = b'', pairing_key_index: int = 0) -> None .. py:class:: Derivation Bases: :py:obj:`enum.IntEnum` Predefined values to use for parameters as Derivation. .. py:attribute:: CURRENT_KEY :annotation: = 0 .. py:attribute:: DERIVE :annotation: = 1 .. py:attribute:: DERIVE_AND_MAKE_CURRENT :annotation: = 2 .. py:attribute:: PINLESS_PATH :annotation: = 3 .. py:class:: KeyType Bases: :py:obj:`enum.IntEnum` Predefined values to use for parameters as KeyType. .. py:attribute:: K1 :annotation: = 0 .. py:attribute:: R1 :annotation: = 16 .. py:class:: SlotIndex Bases: :py:obj:`enum.IntEnum` Predefined values to use for parameters as SlotIndex. .. py:attribute:: EC256R1 :annotation: = 1 .. py:attribute:: RSA :annotation: = 2 .. py:attribute:: FIDO :annotation: = 3 .. py:class:: AuthType Bases: :py:obj:`enum.Enum` Predefined values for authentication type. .. py:attribute:: NO_AUTH :annotation: = 0 .. py:attribute:: PIN :annotation: = 1 .. py:attribute:: USER_KEY :annotation: = 2 .. py:method:: __bool__(self) .. py:class:: SeedSource Bases: :py:obj:`enum.Enum` Predefined values for how seed was created .. py:attribute:: NO_SEED :annotation: = 0 .. py:attribute:: SINGLE .. py:attribute:: EXTENDED .. py:attribute:: EXTERNAL .. py:attribute:: INTERNAL .. py:attribute:: DUAL .. py:exception:: CryptnoxException Bases: :py:obj:`Exception` Base exception for the class exceptions. .. py:exception:: CardClosedException Bases: :py:obj:`Exception` The card wasn't opened with PIN code or challenge-response .. py:exception:: CardException Bases: :py:obj:`CryptnoxException` No card was detected in the card reader. .. py:exception:: CardTypeException Bases: :py:obj:`CryptnoxException` The detected card is not supported by this library .. py:exception:: CertificateException Bases: :py:obj:`CryptnoxException` There was an issue with the certification .. py:exception:: ConnectionException Bases: :py:obj:`CryptnoxException` An issue occurred in the communication with the reader .. py:exception:: DataException Bases: :py:obj:`CryptnoxException` The reader returned an empty message. .. py:exception:: DataValidationException Bases: :py:obj:`CryptnoxException` The sent data is not valid. .. py:exception:: DerivationSelectionException Bases: :py:obj:`CryptnoxException` Not a valid derivation selection. .. py:exception:: EOSKeyError Bases: :py:obj:`CryptnoxException` The signature wasn't compatible with EOS standard after 10 tries .. py:exception:: FirmwareException Bases: :py:obj:`CryptnoxException` There is an issue with the firmware on the card .. py:exception:: GenuineCheckException Bases: :py:obj:`CryptnoxException` The detected card is not a genuine Cryptnox product. .. py:exception:: GenericException(status: bytes) Bases: :py:obj:`CryptnoxException` Generic exception that can mean multiple things depending on the call to the card Process stats and throw a specific Exception from it. .. py:exception:: InitializationException Bases: :py:obj:`CryptnoxException` The card hasn't been initialized. .. py:exception:: KeyAlreadyGenerated Bases: :py:obj:`CryptnoxException` Key can not be generated twice. .. py:exception:: SeedException Bases: :py:obj:`CryptnoxException` Keys weren't found on the card. .. py:exception:: KeyGenerationException Bases: :py:obj:`CryptnoxException` Error in key generation. .. py:exception:: PinAuthenticationException Bases: :py:obj:`CryptnoxException` Error in turning off PIN authentication. There is no user key in the card .. py:exception:: PinException(number_of_retries: int, message: str = 'Wrong pin') Bases: :py:obj:`CryptnoxException` Sent PIN code is not valid. :param int number_of_retries: Number of retries to send the PIN code before the card is locked. :param str message: Optional message .. py:exception:: PukException(number_of_retries: int = 0, message: str = 'Wrong PUK') Bases: :py:obj:`CryptnoxException` Sent PUK code is not valid. :param int number_of_retries: Number of retries to send the PIN code before the card is locked. :param str message: Optional message .. py:exception:: ReadPublicKeyException Bases: :py:obj:`CryptnoxException` Data received during public key reading is not valid. .. py:exception:: ReaderException Bases: :py:obj:`CryptnoxException` Card reader wasn't found attached to the device. .. py:exception:: SecureChannelException Bases: :py:obj:`CryptnoxException` Secure channel couldn't be established. .. py:exception:: SoftLock Bases: :py:obj:`CryptnoxException` The card is soft locked, and requires power cycle before it can be opened .. py:exception:: CardNotBlocked Bases: :py:obj:`CryptnoxException` Trying to unlock unblocked card .. py:data:: __version__ :annotation: = 1.0.4