def refresh_piv(self): with self._open_device([SmartCardConnection]) as conn: session = PivSession(conn) pivman = get_pivman_data(session) try: key_type = session.get_management_key_metadata().key_type except NotSupportedError: key_type = MANAGEMENT_KEY_TYPE.TDES return success({ 'piv_data': { 'certs': self._piv_list_certificates(session), 'has_derived_key': pivman.has_derived_key, 'has_protected_key': pivman.has_protected_key, 'has_stored_key': pivman.has_stored_key, 'pin_tries': session.get_pin_attempts(), 'puk_blocked': pivman.puk_blocked, 'supported_algorithms': _supported_algorithms( self._dev_info['version'].split('.')), 'key_type': key_type, }, })
def get_piv_info(session: PivSession) -> str: """Get human readable information about the PIV configuration.""" pivman = get_pivman_data(session) lines = [] lines.append("PIV version: %d.%d.%d" % session.version) try: pin_data = session.get_pin_metadata() if pin_data.default_value: lines.append("WARNING: Using default PIN!") tries_str = "%d/%d" % (pin_data.attempts_remaining, pin_data.total_attempts) except NotSupportedError: # Largest possible number of PIN tries to get back is 15 tries = session.get_pin_attempts() tries_str = "15 or more." if tries == 15 else str(tries) lines.append(f"PIN tries remaining: {tries_str}") if pivman.puk_blocked: lines.append("PUK blocked.") try: metadata = session.get_management_key_metadata() if metadata.default_value: lines.append("WARNING: Using default Management key!") key_type = metadata.key_type except NotSupportedError: key_type = MANAGEMENT_KEY_TYPE.TDES lines.append(f"Management key algorithm: {key_type.name}") if pivman.has_derived_key: lines.append("Management key is derived from PIN.") if pivman.has_stored_key: lines.append( "Management key is stored on the YubiKey, protected by PIN.") try: chuid = session.get_object(OBJECT_ID.CHUID).hex() except ApduError as e: if e.sw == SW.FILE_NOT_FOUND: chuid = "No data available." lines.append("CHUID:\t" + chuid) try: ccc = session.get_object(OBJECT_ID.CAPABILITY).hex() except ApduError as e: if e.sw == SW.FILE_NOT_FOUND: ccc = "No data available." lines.append("CCC: \t" + ccc) for (slot, cert) in list_certificates(session).items(): lines.append(f"Slot {slot:02x}:") if isinstance(cert, x509.Certificate): try: # Try to read out full DN, fallback to only CN. # Support for DN was added in crytography 2.5 subject_dn = cert.subject.rfc4514_string() issuer_dn = cert.issuer.rfc4514_string() print_dn = True except AttributeError: print_dn = False logger.debug("Failed to read DN, falling back to only CNs") cn = cert.subject.get_attributes_for_oid( x509.NameOID.COMMON_NAME) subject_cn = cn[0].value if cn else "None" cn = cert.issuer.get_attributes_for_oid( x509.NameOID.COMMON_NAME) issuer_cn = cn[0].value if cn else "None" except ValueError as e: # Malformed certificates may throw ValueError logger.debug("Failed parsing certificate", exc_info=e) lines.append(f"\tMalformed certificate: {e}") continue fingerprint = cert.fingerprint(hashes.SHA256()).hex() try: key_algo = KEY_TYPE.from_public_key(cert.public_key()).name except ValueError: key_algo = "Unsupported" serial = cert.serial_number try: not_before: Optional[datetime] = cert.not_valid_before except ValueError as e: logger.debug("Failed reading not_valid_before", exc_info=e) not_before = None try: not_after: Optional[datetime] = cert.not_valid_after except ValueError as e: logger.debug("Failed reading not_valid_after", exc_info=e) not_after = None # Print out everything lines.append(f"\tAlgorithm:\t{key_algo}") if print_dn: lines.append(f"\tSubject DN:\t{subject_dn}") lines.append(f"\tIssuer DN:\t{issuer_dn}") else: lines.append(f"\tSubject CN:\t{subject_cn}") lines.append(f"\tIssuer CN:\t{issuer_cn}") lines.append(f"\tSerial:\t\t{serial}") lines.append(f"\tFingerprint:\t{fingerprint}") if not_before: lines.append(f"\tNot before:\t{not_before}") if not_after: lines.append(f"\tNot after:\t{not_after}") else: lines.append("\tError: Failed to parse certificate.") return "\n".join(lines)