<commit_before># Copyright (c) 2017 Yubico AB
# All rights reserved.
#
#   Redistribution and use in source and binary forms, with or
#   without modification, are permitted provided that the following
#   conditions are met:
#
#    1. Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#    2. Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from __future__ import absolute_import

from ..util import (
    TRANSPORT, get_leaf_certificates, parse_private_key, parse_certificates)
from ..piv import (
    PivController, ALGO, OBJ, SLOT, PIN_POLICY, TOUCH_POLICY,
    DEFAULT_MANAGEMENT_KEY, generate_random_management_key)
from ..piv import (
    AuthenticationBlocked, AuthenticationFailed, KeypairMismatch,
    UnsupportedAlgorithm, WrongPin, WrongPuk)
from ..driver_ccid import APDUError, SW
from .util import (
    click_force_option, click_format_option,
    click_postpone_execution, click_callback,
    prompt_for_touch, EnumChoice)
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend
from binascii import b2a_hex, a2b_hex
import click
import datetime
import logging


logger = logging.getLogger(__name__)


@click_callback()
def click_parse_piv_slot(ctx, param, val):
    try:
        return SLOT(int(val, 16))
    except Exception:
        raise ValueError(val)


@click_callback()
def click_parse_management_key(ctx, param, val):
    try:
        key = a2b_hex(val)
        if key and len(key) != 24:
            raise ValueError('Management key must be exactly 24 bytes '
                             '(48 hexadecimal digits) long.')
        return key
    except Exception:
        raise ValueError(val)


click_slot_argument = click.argument('slot', callback=click_parse_piv_slot)
click_management_key_option = click.option(
    '-m', '--management-key',
    help='The management key.',
    callback=click_parse_management_key)
click_pin_option = click.option(
    '-P', '--pin', help='PIN code.')
click_pin_policy_option = click.option(
    '--pin-policy',
    type=EnumChoice(PIN_POLICY),
    help='PIN policy for slot.')
click_touch_policy_option = click.option(
    '--touch-policy', type=EnumChoice(TOUCH_POLICY),
    help='Touch policy for slot.')


@click.group()
@click.pass_context
@click_postpone_execution
def piv(ctx):
    """
    Manage PIV Application.

    Examples:

    \b
      Generate an ECC P-256 private key and a self-signed certificate in
      slot 9a:
      $ ykman piv generate-key --algorithm ECCP256 9a pubkey.pem
      $ ykman piv generate-certificate --subject "yubico" 9a pubkey.pem

    \b
      Change the PIN from 123456 to 654321:
      $ ykman piv change-pin --pin 123456 --new-pin 654321

    \b
      Reset all PIV data and restore default settings:
      $ ykman piv reset
    """
    try:
        ctx.obj['controller'] = PivController(ctx.obj['dev'].driver)
    except APDUError as e:
        if e.sw == SW.NOT_FOUND:
            ctx.fail("The PIV application can't be found on this YubiKey.")
        raise


@piv.command()
@click.pass_context
def info(ctx):
    """
    Display status of PIV application.
    """
    controller = ctx.obj['controller']
    click.echo('PIV version: %d.%d.%d' % controller.version)

    # Largest possible number of PIN tries to get back is 15
    tries = controller.get_pin_tries()
    tries = '15 or more.' if tries == 15 else tries
    click.echo('PIN tries remaining: %s' % tries)
    if controller.puk_blocked:
        click.echo('PUK blocked.')
    if controller.has_derived_key:
        click.echo('Management key is derived from PIN.')
    if controller.has_stored_key:
        click.echo('Management key is stored on the YubiKey, protected by PIN.')
    try:
        chuid = b2a_hex(controller.get_data(OBJ.CHUID)).decode()
    except APDUError as e:
        if e.sw == SW.NOT_FOUND:
            chuid = 'No data available.'
    click.echo('CHUID:\t' + chuid)

    try:
        ccc = b2a_hex(controller.get_data(OBJ.CAPABILITY)).decode()
    except APDUError as e:
        if e.sw == SW.NOT_FOUND:
            ccc = 'No data available.'
    click.echo('CCC: \t' + ccc)

    for (slot, cert) in controller.list_certificates().items():
        click.echo('Slot %02x:' % slot)

        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')
            subject_cn = cert.subject.get_attributes_for_oid(
                x509.NameOID.COMMON_NAME)
            subject_cn = subject_cn[0].value if subject_cn else 'None'
            issuer_cn = cert.issuer.get_attributes_for_oid(
                x509.NameOID.COMMON_NAME)
            issuer_cn = issuer_cn[0].value if issuer_cn else 'None'
        except ValueError as e:
            # Malformed certificates may throw ValueError
            logger.debug('Failed parsing certificate', exc_info=e)
            click.echo('\tMalformed certificate: {}'.format(e))
            continue

        fingerprint = b2a_hex(cert.fingerprint(hashes.SHA256())).decode('ascii')
        algo = ALGO.from_public_key(cert.public_key())
        serial = cert.serial_number
        try:
            not_before = cert.not_valid_before
        except ValueError as e:
            logger.debug('Failed reading not_valid_before', exc_info=e)
            not_before = None
        try:
            not_after = cert.not_valid_after
            not_after = None
        except ValueError as e:
            logger.debug('Failed reading not_valid_after', exc_info=e)
            not_after = None
        # Print out everything
        click.echo('\tAlgorithm:\t%s' % algo.name)
        if print_dn:
            click.echo('\tSubject DN:\t%s' % subject_dn)
            click.echo('\tIssuer DN:\t%s' % issuer_dn)
        else:
            click.echo('\tSubject CN:\t%s' % subject_cn)<commit_msg>piv: fix previous incorrect fix<commit_after># Copyright (c) 2017 Yubico AB
# All rights reserved.
#
#   Redistribution and use in source and binary forms, with or
#   without modification, are permitted provided that the following
#   conditions are met:
#
#    1. Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#    2. Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from __future__ import absolute_import

from ..util import (
    TRANSPORT, get_leaf_certificates, parse_private_key, parse_certificates)
from ..piv import (
    PivController, ALGO, OBJ, SLOT, PIN_POLICY, TOUCH_POLICY,
    DEFAULT_MANAGEMENT_KEY, generate_random_management_key)
from ..piv import (
    AuthenticationBlocked, AuthenticationFailed, KeypairMismatch,
    UnsupportedAlgorithm, WrongPin, WrongPuk)
from ..driver_ccid import APDUError, SW
from .util import (
    click_force_option, click_format_option,
    click_postpone_execution, click_callback,
    prompt_for_touch, EnumChoice)
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend
from binascii import b2a_hex, a2b_hex
import click
import datetime
import logging


logger = logging.getLogger(__name__)


@click_callback()
def click_parse_piv_slot(ctx, param, val):
    try:
        return SLOT(int(val, 16))
    except Exception:
        raise ValueError(val)


@click_callback()
def click_parse_management_key(ctx, param, val):
    try:
        key = a2b_hex(val)
        if key and len(key) != 24:
            raise ValueError('Management key must be exactly 24 bytes '
                             '(48 hexadecimal digits) long.')
        return key
    except Exception:
        raise ValueError(val)


click_slot_argument = click.argument('slot', callback=click_parse_piv_slot)
click_management_key_option = click.option(
    '-m', '--management-key',
    help='The management key.',
    callback=click_parse_management_key)
click_pin_option = click.option(
    '-P', '--pin', help='PIN code.')
click_pin_policy_option = click.option(
    '--pin-policy',
    type=EnumChoice(PIN_POLICY),
    help='PIN policy for slot.')
click_touch_policy_option = click.option(
    '--touch-policy', type=EnumChoice(TOUCH_POLICY),
    help='Touch policy for slot.')


@click.group()
@click.pass_context
@click_postpone_execution
def piv(ctx):
    """
    Manage PIV Application.

    Examples:

    \b
      Generate an ECC P-256 private key and a self-signed certificate in
      slot 9a:
      $ ykman piv generate-key --algorithm ECCP256 9a pubkey.pem
      $ ykman piv generate-certificate --subject "yubico" 9a pubkey.pem

    \b
      Change the PIN from 123456 to 654321:
      $ ykman piv change-pin --pin 123456 --new-pin 654321

    \b
      Reset all PIV data and restore default settings:
      $ ykman piv reset
    """
    try:
        ctx.obj['controller'] = PivController(ctx.obj['dev'].driver)
    except APDUError as e:
        if e.sw == SW.NOT_FOUND:
            ctx.fail("The PIV application can't be found on this YubiKey.")
        raise


@piv.command()
@click.pass_context
def info(ctx):
    """
    Display status of PIV application.
    """
    controller = ctx.obj['controller']
    click.echo('PIV version: %d.%d.%d' % controller.version)

    # Largest possible number of PIN tries to get back is 15
    tries = controller.get_pin_tries()
    tries = '15 or more.' if tries == 15 else tries
    click.echo('PIN tries remaining: %s' % tries)
    if controller.puk_blocked:
        click.echo('PUK blocked.')
    if controller.has_derived_key:
        click.echo('Management key is derived from PIN.')
    if controller.has_stored_key:
        click.echo('Management key is stored on the YubiKey, protected by PIN.')
    try:
        chuid = b2a_hex(controller.get_data(OBJ.CHUID)).decode()
    except APDUError as e:
        if e.sw == SW.NOT_FOUND:
            chuid = 'No data available.'
    click.echo('CHUID:\t' + chuid)

    try:
        ccc = b2a_hex(controller.get_data(OBJ.CAPABILITY)).decode()
    except APDUError as e:
        if e.sw == SW.NOT_FOUND:
            ccc = 'No data available.'
    click.echo('CCC: \t' + ccc)

    for (slot, cert) in controller.list_certificates().items():
        click.echo('Slot %02x:' % slot)

        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')
            subject_cn = cert.subject.get_attributes_for_oid(
                x509.NameOID.COMMON_NAME)
            subject_cn = subject_cn[0].value if subject_cn else 'None'
            issuer_cn = cert.issuer.get_attributes_for_oid(
                x509.NameOID.COMMON_NAME)
            issuer_cn = issuer_cn[0].value if issuer_cn else 'None'
        except ValueError as e:
            # Malformed certificates may throw ValueError
            logger.debug('Failed parsing certificate', exc_info=e)
            click.echo('\tMalformed certificate: {}'.format(e))
            continue

        fingerprint = b2a_hex(cert.fingerprint(hashes.SHA256())).decode('ascii')
        algo = ALGO.from_public_key(cert.public_key())
        serial = cert.serial_number
        try:
            not_before = cert.not_valid_before
        except ValueError as e:
            logger.debug('Failed reading not_valid_before', exc_info=e)
            not_before = None
        try:
            not_after = cert.not_valid_after
        except ValueError as e:
            logger.debug('Failed reading not_valid_after', exc_info=e)
            not_after = None
        # Print out everything
        click.echo('\tAlgorithm:\t%s' % algo.name)
        if print_dn:
            click.echo('\tSubject DN:\t%s' % subject_dn)
            click.echo('\tIssuer DN:\t%s' % issuer_dn)
        else:
            click.echo('\tSubject CN:\t%s' % subject_cn)
{'fraction_non_alphanumeric': 0.08859789649648256, 'fraction_numerical': 0.011840913839938705, 'mean_word_length': 2.8679956896551726, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '573682', 'n_tokens_mistral': 4508, 'n_tokens_neox': 4210, 'n_words': 1315}