def find_hash_name(security_infos: List[bytes]) -> str:
    """
    Return the hashing algorithm for ECC AA from SecurityInfos (DG14)
    """
    for sec_info in security_infos:
        i = asn1_node_root(sec_info)
        i = asn1_node_first_child(sec_info, i)  # get OID for this SecurityInfo
        si_oid = asn1_get_all(sec_info, i)
        if si_oid != encode_oid_string(
                "2.23.136.1.1.5"):  # id-icao-mrtd-security-aaProtocolObject
            continue
        i = asn1_node_next(sec_info, i)
        if asn1_get_value_of_type(sec_info, i, "INTEGER") != b"\x01":
            raise ValueError("[-] Version mismatch in DG14 AA SecurityInfo!")
        i = asn1_node_next(sec_info, i)
        signature_algorithm = asn1_get_all(sec_info, i)
        if signature_algorithm == encode_oid_string("0.4.0.127.0.7.1.1.4.1.2"):
            return "sha224"
        if signature_algorithm == encode_oid_string("0.4.0.127.0.7.1.1.4.1.3"):
            return "sha256"
        if signature_algorithm == encode_oid_string("0.4.0.127.0.7.1.1.4.1.4"):
            return "sha384"
        if signature_algorithm == encode_oid_string("0.4.0.127.0.7.1.1.4.1.5"):
            return "sha512"

    raise ValueError("[-] Unsupported signature algorithm in AA SecurityInfo!")
Ejemplo n.º 2
0
def extract_certificates(signed_data: bytes) -> List[X509]:
    """
    Return CSCA certificates from Master List
    """
    certs = []

    i = asn1_node_root(signed_data)
    last_byte = i[2]
    i = asn1_node_first_child(signed_data, i)
    i = asn1_node_next(signed_data, i)
    i = asn1_node_first_child(signed_data, i)
    while True:
        data = asn1_get_all(signed_data, i)
        certs.append(load_certificate(FILETYPE_ASN1, data))
        if i[2] == last_byte:
            break
        i = asn1_node_next(signed_data, i)

    print(f"\t[+] Extracted {len(certs)} certs")
    return certs
Ejemplo n.º 3
0
def get_dg_numbers(data_group_hash_values: bytes) -> Dict[bytes, str]:
    """
    Get DG numbers from EF.SOD data group hash values.
    """
    dg_list = []

    i = asn1_node_root(data_group_hash_values)
    last = i[2]
    i = asn1_node_first_child(data_group_hash_values, i)

    j = asn1_node_first_child(data_group_hash_values, i)
    dg_list.append(asn1_get_value_of_type(data_group_hash_values, j,
                                          "INTEGER"))
    while i[2] != last:
        i = asn1_node_next(data_group_hash_values, i)
        j = asn1_node_first_child(data_group_hash_values, i)
        dg_list.append(
            asn1_get_value_of_type(data_group_hash_values, j, "INTEGER"))

    tag2dg: Dict[bytes, str] = {
        b"\x1E": "EF.COM",
        b"\x01": "EF.DG1",
        b"\x02": "EF.DG2",
        b"\x03": "EF.DG3",
        b"\x04": "EF.DG4",
        b"\x05": "EF.DG5",
        b"\x06": "EF.DG6",
        b"\x07": "EF.DG7",
        b"\x08": "EF.DG8",
        b"\x09": "EF.DG9",
        b"\x0A": "EF.DG10",
        b"\x0B": "EF.DG11",
        b"\x0C": "EF.DG12",
        b"\x0D": "EF.DG13",
        b"\x0E": "EF.DG14",
        b"\x0F": "EF.DG15",
        b"\x10": "EF.DG16",
        b"\x1D": "EF.SOD",
    }

    return {byte: tag2dg[byte] for byte in dg_list}
def add_seconds_to_certificate(cert: bytes, seconds: int) -> bytes:
    from datetime import datetime, timedelta

    i = asn1_node_root(cert)  # Certificate
    i = asn1_node_first_child(cert, i)  # tbsCertificate
    i = asn1_node_first_child(cert, i)  # version
    i = asn1_node_next(cert, i)  # serialNumber
    i = asn1_node_next(cert, i)  # signature
    i = asn1_node_next(cert, i)  # issuer
    i = asn1_node_next(cert, i)  # validity
    i = asn1_node_first_child(cert, i)  # notBefore
    not_before = asn1_get_value(cert, i)
    if len(not_before) == 13:
        date_format = "%y%m%d%H%M%SZ"
    else:
        date_format = "%Y%m%d%H%M%SZ"
    date_time_obj = datetime.strptime(not_before.decode("utf-8"), date_format)
    cert = (
        cert[:i[1]] +
        (date_time_obj +
         timedelta(seconds=seconds)).strftime(date_format).encode("utf_8") +
        cert[i[2] + 1:])

    i = asn1_node_next(cert, i)  # notAfter
    not_after = asn1_get_value(cert, i)
    if len(not_after) == 13:
        date_format = "%y%m%d%H%M%SZ"
    else:
        date_format = "%Y%m%d%H%M%SZ"
    date_time_obj = datetime.strptime(not_after.decode("utf-8"), date_format)
    cert = (
        cert[:i[1]] +
        (date_time_obj +
         timedelta(seconds=seconds)).strftime(date_format).encode("utf_8") +
        cert[i[2] + 1:])

    return cert
Ejemplo n.º 5
0
def find_paceinfos(security_infos: List[bytes]) -> List[Tuple[bytes, int]]:
    """
    From SecurityInfos find Chip Authentication Public Keys and return them as\n
    list(list(key_id, pub_key))\n
    If key id doesn't exist, it is an empty bytes b""
    """
    id_pace = "0.4.0.127.0.7.2.2.4"
    pace_protocol_dict = {
        encode_oid_string(id_pace + ".1.1"): "id-PACE-DH-GM-3DES-CBC-CBC",
        encode_oid_string(id_pace + ".1.2"): "id-PACE-DH-GM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".1.3"): "id-PACE-DH-GM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".1.4"): "id-PACE-DH-GM-AES-CBC-CMAC-256",
        encode_oid_string(id_pace + ".2.1"): "id-PACE-ECDH-GM-3DES-CBC-CBC",
        encode_oid_string(id_pace + ".2.2"): "id-PACE-ECDH-GM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".2.3"): "id-PACE-ECDH-GM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".2.4"): "id-PACE-ECDH-GM-AES-CBC-CMAC-256",
        encode_oid_string(id_pace + ".3.1"): "id-PACE-DH-IM-3DES-CBC-CBC",
        encode_oid_string(id_pace + ".3.2"): "id-PACE-DH-IM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".3.3"): "id-PACE-DH-IM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".3.4"): "id-PACE-DH-IM-AES-CBC-CMAC-256",
        encode_oid_string(id_pace + ".4.1"): "id-PACE-ECDH-IM-3DES-CBC-CBC",
        encode_oid_string(id_pace + ".4.2"): "id-PACE-ECDH-IM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".4.3"): "id-PACE-ECDH-IM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".4.4"): "id-PACE-ECDH-IM-AES-CBC-CMAC-256",
        encode_oid_string(id_pace + ".6.2"): "id-PACE-ECDH-CAM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".6.3"): "id-PACE-ECDH-CAM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".6.4"): "id-PACE-ECDH-CAM-AES-CBC-CMAC-256",
    }

    supported_pace: List[Tuple[bytes, int]] = []

    for sec_info in security_infos:
        i = asn1_node_root(sec_info)
        i = asn1_node_first_child(sec_info, i)  # get OID for this SecurityInfo
        si_oid = asn1_get_all(sec_info, i)
        if si_oid not in pace_protocol_dict:
            continue
        i = asn1_node_next(sec_info, i)  # get version for this PACEInfo
        si_ver = asn1_get_value_of_type(sec_info, i, "INTEGER")
        if si_ver != b"\x02":  # Other versions are not supported
            continue

        i = asn1_node_next(sec_info, i)  # get parameterID for this PACEInfo
        si_parameter_info = int.from_bytes(
            asn1_get_value_of_type(sec_info, i, "INTEGER"), byteorder="big"
        )
        supported_pace.append((si_oid, si_parameter_info))

    return supported_pace
Ejemplo n.º 6
0
def parse_security_infos(dg14: bytes) -> List[bytes]:
    """
    Return a list of SecurityInfo from SecurityInfos
    """
    i = asn1_node_root(dg14)

    if dg14[0] == 0x6E:  # strip DG14 tag (6E)
        i = asn1_node_first_child(dg14, i)

    last_byte = i[2]

    security_infos = []

    i = asn1_node_first_child(dg14, i)
    security_infos.append(asn1_get_all(dg14, i))
    if i[2] != last_byte:
        while True:
            i = asn1_node_next(dg14, i)
            security_infos.append(asn1_get_all(dg14, i))

            if i[2] == last_byte:
                break

    return security_infos
Ejemplo n.º 7
0
def assert_dg_hash(dg_file: bytes, data_group_hash_values: bytes,
                   hash_alg: str, dg_number_bytes: bytes) -> bool:
    """
    Calculate the hash over the DG file and compare that in the EF.SOD.
    """
    dg_number = int.from_bytes(dg_number_bytes, byteorder="big")
    # Only hashes for DG1-DG16 exist
    if dg_number < 1 and dg_number > 16:
        raise ValueError("[-] Only hashes for DG1-DG16 exist!")

    hash_object = hashlib.new(hash_alg)

    hash_object.update(dg_file)
    file_hash = hash_object.digest()

    current = 0
    i = asn1_node_root(data_group_hash_values)
    i = asn1_node_first_child(data_group_hash_values, i)
    while True:
        j = asn1_node_first_child(data_group_hash_values, i)
        current = int.from_bytes(
            asn1_get_value_of_type(data_group_hash_values, j, "INTEGER"),
            byteorder="big",
        )
        if current == dg_number:
            break
        i = asn1_node_next(data_group_hash_values, i)

    j = asn1_node_next(data_group_hash_values, j)
    hash_in_dg = asn1_get_value(data_group_hash_values, j)

    if not hmac.compare_digest(file_hash, hash_in_dg):
        print("[-] Potentially cloned document, hashes do not match!")
        return False
    print(f"[+] DG {dg_number} hash matches that on the EF.SOD.")
    return True
Ejemplo n.º 8
0
def pace(security_infos_efca: List[bytes], sm_object: SMObject, secret: bytes, pub_key_ref: str):
    """
    List of Security Infos from EF.CardAccesss
    SMObject,
    Secret (MRZ info or CAN)
    pub_key_ref choices ("MRZ" or "CAN")
    """
    openssl_nid_to_name = {
        409: "prime192v1",
        923: "brainpoolP192r1",
        713: "secp224r1",
        925: "brainpoolP224r1",
        415: "prime256v1",
        927: "brainpoolP256r1",
        929: "brainpoolP320r1",
        715: "secp384r1",
        931: "brainpoolP384r1",
        933: "brainpoolP512r1",
        716: "secp521r1",
    }

    domain_parameters: Dict[int, Union[Tuple[str], Tuple[int, str]]] = {
        0: ("1024-bit MODP Group with 160-bit Prime Order Subgroup",),
        1: ("2048-bit MODP Group with 224-bit Prime Order Subgroup",),
        2: ("2048-bit MODP Group with 256-bit Prime Order Subgroup",),
        3: ("Reserved for Future Use",),
        4: ("Reserved for Future Use",),
        5: ("Reserved for Future Use",),
        6: ("Reserved for Future Use",),
        7: ("Reserved for Future Use",),
        8: (
            409,
            "NIST P-192 (secp192r1)",
        ),  #  https://stackoverflow.com/a/41953717/6077951
        9: (
            923,
            "BrainpoolP192r1",
        ),
        10: (
            713,
            "NIST P-224 (secp224r1) (can't be used with im)",
        ),
        11: (
            925,
            "BrainpoolP224r1",
        ),
        12: (
            415,
            "NIST P-256 (secp256r1)",
        ),  #  https://stackoverflow.com/a/41953717/6077951
        13: (
            927,
            "BrainpoolP256r1",
        ),
        14: (
            929,
            "BrainpoolP320r1",
        ),
        15: (
            715,
            "NIST P-384 (secp384r1)",
        ),
        16: (
            931,
            "BrainpoolP384r1",
        ),
        17: (
            933,
            "BrainpoolP512r1",
        ),
        18: (
            716,
            "NIST P-521 (secp521r1)",
        ),
        19: ("Reserved for Future Use",),
        20: ("Reserved for Future Use",),
        21: ("Reserved for Future Use",),
        22: ("Reserved for Future Use",),
        23: ("Reserved for Future Use",),
        24: ("Reserved for Future Use",),
        25: ("Reserved for Future Use",),
        26: ("Reserved for Future Use",),
        27: ("Reserved for Future Use",),
        28: ("Reserved for Future Use",),
        29: ("Reserved for Future Use",),
        30: ("Reserved for Future Use",),
        31: ("Reserved for Future Use",),
    }

    id_pace = "0.4.0.127.0.7.2.2.4"
    pace_protocol_dict = {
        encode_oid_string(id_pace + ".1.1"): "id-PACE-DH-GM-3DES-CBC-CBC",
        encode_oid_string(id_pace + ".1.2"): "id-PACE-DH-GM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".1.3"): "id-PACE-DH-GM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".1.4"): "id-PACE-DH-GM-AES-CBC-CMAC-256",
        encode_oid_string(id_pace + ".2.1"): "id-PACE-ECDH-GM-3DES-CBC-CBC",
        encode_oid_string(id_pace + ".2.2"): "id-PACE-ECDH-GM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".2.3"): "id-PACE-ECDH-GM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".2.4"): "id-PACE-ECDH-GM-AES-CBC-CMAC-256",
        encode_oid_string(id_pace + ".3.1"): "id-PACE-DH-IM-3DES-CBC-CBC",
        encode_oid_string(id_pace + ".3.2"): "id-PACE-DH-IM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".3.3"): "id-PACE-DH-IM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".3.4"): "id-PACE-DH-IM-AES-CBC-CMAC-256",
        encode_oid_string(id_pace + ".4.1"): "id-PACE-ECDH-IM-3DES-CBC-CBC",
        encode_oid_string(id_pace + ".4.2"): "id-PACE-ECDH-IM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".4.3"): "id-PACE-ECDH-IM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".4.4"): "id-PACE-ECDH-IM-AES-CBC-CMAC-256",
        encode_oid_string(id_pace + ".6.2"): "id-PACE-ECDH-CAM-AES-CBC-CMAC-128",
        encode_oid_string(id_pace + ".6.3"): "id-PACE-ECDH-CAM-AES-CBC-CMAC-192",
        encode_oid_string(id_pace + ".6.4"): "id-PACE-ECDH-CAM-AES-CBC-CMAC-256",
    }

    supported_pace: List[Tuple[bytes, int]] = find_paceinfos(security_infos_efca)

    if supported_pace == []:
        raise PACEError("[-] No supported PACEInfo found.")

    used_pace = supported_pace[0]
    used_pace_str = pace_protocol_dict[used_pace[0]].split("-")

    if pub_key_ref == "MRZ":
        key_seed = hashlib.sha1(secret).digest()
    if used_pace_str[4] == "3DES":
        k_dec = compute_key(key_seed, "pace", "3DES")
    else:  # AES
        k_dec = compute_key(key_seed, "pace", "AES-" + used_pace_str[7])

    payload = b"\x80" + used_pace[0][1:]
    if pub_key_ref == "MRZ":
        payload += b"\x83" + b"\x01" + b"\x01"
    elif pub_key_ref == "CAN":
        payload += b"\x83" + b"\x01" + b"\x02"
    else:
        raise PACEError("[-] Only MRZ and CAN are supported.")

    # if more than one set of DomainParameters is available:
    # payload = b"\x84" + reference of ...

    # MSE: AT
    ########data = send(sm_object, APDU(b"\x00", b"\x22", b"\xC1", b"\xA4", Lc=nb(len(payload)), cdata=payload))

    # Query encrypted nonce
    ########data = send(sm_object,APDU(b"\x10", b"\x86", b"\x00", b"\x00", Lc=b"\x02", cdata=b"\x7C\x00", Le=b"\x00"))
    data = bytes.fromhex("7C12801095A3A016522EE98D01E76CB6B98B42C3")
    # THE EXAMPLES ARE TAKEN FROM ICAO Doc 9303-11 App G-1

    i = asn1_node_root(data)  # Dynamic authentication data (0x7C)
    i = asn1_node_first_child(data, i)  # Encrypted Nonce (0x80)
    encrypted_nonce = asn1_get_value(data, i)

    if used_pace_str[4] == "3DES":
        decrypted_nonce = DES3.new(k_dec, DES3.MODE_CBC, iv=bytes([0] * 8)).decrypt(encrypted_nonce)
    else:
        decrypted_nonce = AES.new(k_dec, AES.MODE_CBC, iv=bytes([0] * 16)).decrypt(encrypted_nonce)

    assert decrypted_nonce == bytes.fromhex("3F00C4D39D153F2B2A214A078D899B22")

    if used_pace_str[3] == "GM":
        if used_pace_str[2] == "ECDH":
            used_domain_par = domain_parameters[used_pace[1]]
            if not isinstance(used_domain_par[0], int):
                raise PACEError("[-] These Domain parameters are not supported.")

            # ec_key_pair = EC.gen_params(used_domain_par[0])
            # ec_key_pair.gen_key()
            ec_key_pair = EC.load_key("tests/brainpoolP256r1.pem")
            ec_pub_key = ec_key_pair.pub().get_key()

            payload = b"\x81" + asn1_len(ec_pub_key) + ec_pub_key
            payload = b"\x7C" + asn1_len(payload) + payload

            # data = send(sm_object,APDU(b"\x10", b"\x86", b"\x00", b"\x00", Lc=nb(len(payload)), cdata=payload, Le=b"\x00"))
            data = bytes.fromhex(
                "7C43824104824FBA91C9CBE26BEF53A0EBE7342A3BF178CEA9F45DE0B70AA601651FBA3F5730D8C879AAA9C9F73991E61B58F4D52EB87A0A0C709A49DC63719363CCD13C54"
            )

            i = asn1_node_root(data)  # Dynamic Authentication Data (0x7C)
            i = asn1_node_first_child(data, i)  # Mapping Data (0x82)

            card_pace_pub_key = EC.pub_key_from_params(
                used_domain_par[0], asn1_get_value(data, i)
            )  # https://gitlab.com/m2crypto/m2crypto/-/blob/master/tests/test_ecdsa.py

            print((card_pace_pub_key.pub().get_key().hex()))
            # A different ecdsa library might be needed.
            shared_secret = ec_key_pair.compute_dh_key(card_pace_pub_key.pub())

            ec_pem_buf = BIO.MemoryBuffer()
            card_pace_pub_key.save_pub_key_bio(ec_pem_buf)
            ec_pem = ec_pem_buf.read()

            cmd = (
                "openssl ecparam -name "
                + openssl_nid_to_name[used_domain_par[0]]
                + " -param_enc explicit -text -noout"
            )
            ec_parameters, err = execute(cmd, ec_pem)

            generator = bytes.fromhex(
                ec_parameters.split(b"Generator (uncompressed):", 1)[1]
                .split(b"Order:", 1)[0]
                .replace(b"    ", b"")
                .replace(b":", b"")
                .replace(b"\n", b"")
                .decode("utf-8")
            )
            if generator[0] != 0x04:
                raise PACEError("[-] Problem in openssl.")

            generator = generator[1:]
            generator_x = generator[: len(generator) // 2]
            generator_y = generator[len(generator) // 2 :]
def passive_auth(
    efsod: bytes,
    ee_deviant_doc: bool = False,
    dump: bool = False
) -> Tuple[str, bytes, bytes, Optional[List[PassiveAuthenticationError]]]:
    """
    Do Passive Authentication

    :returns:
    hash_alg that is used to hash DGs,
    data_group_hash_values
    """
    exceptions: List[PassiveAuthenticationError] = []

    # get root node
    i = asn1_node_root(efsod)
    # unpack application data 0x77
    i = asn1_node_first_child(efsod, i)
    # unpack sequence
    i = asn1_node_first_child(efsod, i)
    # print id-signedData OBJECT IDENTIFIER
    if dump:
        print(dump_asn1(asn1_get_all(efsod, i)))
    # get 2nd item inside (SignedData EXPLICIT tagged)
    i = asn1_node_next(efsod, i)
    # unpack SignedData EXPLICIT tag
    i = asn1_node_first_child(efsod, i)
    # get 1st item inside (CMSVersion Value = v3)
    i = asn1_node_first_child(efsod, i)
    # get 2nd item (DigestAlgorithmIdentifiers) collection of message digest algorithm identifiers.
    # There MAY be any number of elements in the collection, including zero.
    i = asn1_node_next(efsod, i)
    # get 3rd item (EncapsulatedContentInfo) (LDS Document Security Object)
    i = asn1_node_next(efsod, i)

    # get 1st item inside (eContentType)
    # (OID joint-iso-itu-t (2) international(23) icao(136) mrtd(1) security(1) ldsSecurityObject(1))
    j = asn1_node_first_child(efsod, i)
    e_content_type = asn1_get_all(efsod, j)
    # get the EXPLICIT tagged encoded contents of ldsSecurityObject
    j = asn1_node_next(efsod, j)
    # get the encoded contents of ldsSecurityObject
    j = asn1_node_first_child(efsod, j)
    # print the value of eContent hash
    encapsulated_content = asn1_get_value_of_type(efsod, j, "OCTET STRING")
    del j

    signer_infos, certificates, crls = None, None, None
    while signer_infos is None:
        # https://stackoverflow.com/a/52041365/6077951
        # get 4th item
        i = asn1_node_next(efsod, i)
        if efsod[i[0]] == 0xA0:
            # Constructed, Context-Specific 0
            certificates = i
            print("[+] CertificateSet exist")
        elif efsod[i[0]] == 0xA1:
            # Constructed, Context-Specific 1
            crls = i
            print("[+] Crls exist")
        else:
            signer_infos = i

    # The inspection system SHALL build and validate a certification path
    # from a Trust Anchor to the Document Signer Certificate used to
    # sign the Document Security Object (SOD) according to Doc 9303-11.

    # store was already built in the first run
    store = build_store.store

    doc_sig_cert = asn1_get_value(efsod, certificates)

    if certificates is not None:
        if ee_deviant_doc:
            doc_sig_cert = add_seconds_to_certificate(doc_sig_cert, 43200)
        CDS = load_certificate(FILETYPE_ASN1, doc_sig_cert)
        store_ctx = X509StoreContext(store, CDS)
        try:
            if store_ctx.verify_certificate() is None:
                print(
                    "[+] Document Signer Certificate is signed by a CSCA certificate"
                )
        except X509StoreContextError as ex:
            exceptions.append(
                PassiveAuthenticationError(
                    "[-] Document Signer Certificate is not signed "
                    "by a CSCA certificate or is invalid!\n" + str(ex)))
    else:
        raise PassiveAuthenticationCriticalError(
            "[-] This application doesn't support this kind of document yet!")

    # get 1st signerInfo inside signerInfos
    i = asn1_node_first_child(efsod, signer_infos)
    # get 1st item inside 1st signerInfo (CMSVersion)
    i = asn1_node_first_child(efsod, i)
    signer_info_ver = int.from_bytes(asn1_get_value_of_type(
        efsod, i, "INTEGER"),
                                     byteorder="big")

    issuer_and_serial_number, subject_key_identifier = None, None
    # get 2nd item inside 1st signerInfo (SignerIdentifier)
    i = asn1_node_next(efsod, i)
    if signer_info_ver == 1:
        issuer_and_serial_number = i
    elif signer_info_ver == 3:
        subject_key_identifier = i

    if dump:
        print(
            dump_asn1(
                asn1_get_all(
                    efsod, issuer_and_serial_number
                    or subject_key_identifier)))

    # get 3rd item inside 1st signerInfo (DigestAlgorithmIdentifier)
    i = asn1_node_next(efsod, i)
    # get hash algorithm used for encapsulatedContent and SignedAttrs
    hash_alg = asn1_get_all(efsod, asn1_node_first_child(efsod, i))
    try:
        hash_alg = get_digestalg_name(hash_alg)
    except ValueError as ex:
        raise PassiveAuthenticationCriticalError(
            "[-] Hash algorithm is not recognized.") from ex

    # get 4th item inside 1st signerInfo ([0] IMPLICIT SignedAttributes)
    i = asn1_node_next(efsod, i)
    # use EXPLICIT SET OF tag, rather than of the IMPLICIT [0] tag
    signed_attrs = asn1_get_all(efsod, i)
    signed_attrs = b"\x31" + signed_attrs[1:]

    # get the first Attribute from SignedAttributes
    j = asn1_node_first_child(efsod, i)
    content_type, signed_attrs_hash = None, None
    while content_type is None or signed_attrs_hash is None:
        # get the content-type and the message-digest
        k = asn1_node_first_child(efsod, j)
        # contentType
        if asn1_get_all(efsod, k) == encode_oid_string("1.2.840.113549.1.9.3"):
            # then the content-type attribute value MUST match
            # the SignedData encapContentInfo eContentType value.
            # checked in line 195
            k = asn1_node_next(efsod, k)
            k = asn1_node_first_child(efsod, k)
            content_type = asn1_get_all(efsod, k)
        # messageDigest
        elif asn1_get_all(efsod,
                          k) == encode_oid_string("1.2.840.113549.1.9.4"):
            k = asn1_node_next(efsod, k)
            k = asn1_node_first_child(efsod, k)
            signed_attrs_hash = asn1_get_value_of_type(efsod, k,
                                                       "OCTET STRING")
        j = asn1_node_next(efsod, j)
    del k, j

    hash_object = hashlib.new(hash_alg)
    hash_object.update(encapsulated_content)
    e_content_hash = hash_object.digest()
    del hash_object
    # print("[+] Calculated hash of eContent =", eContent_hash.hex())
    # print("[+] Hash of eContent in SignedAttributes =", signedAttrs_hash.hex())

    if e_content_type == content_type:
        print(
            "[+] Content Type of eContent match with the Content Type in SignedAttributes"
        )
    else:
        exceptions.append(
            PassiveAuthenticationError(
                "[-] Content Type of eContent DOES NOT "
                "match with the Content Type in SignedAttributes."))

    if hmac.compare_digest(signed_attrs_hash, e_content_hash):
        print("[+] Hash of eContent match with the hash in SignedAttributes")
    else:
        exceptions.append(
            PassiveAuthenticationError(
                "[+] Hash of eContent DOES NOT match with the hash in SignedAttributes."
            ))

    # get 4th item inside 1st signerInfo (SignatureAlgorithmIdentifier)
    i = asn1_node_next(efsod, i)
    # get 5th item inside 1st signerInfo (SignatureValue)
    i = asn1_node_next(efsod, i)
    signature = asn1_get_value_of_type(efsod, i, "OCTET STRING")

    # Verify the signature with DS_cert using hash_alg
    try:
        if verify(CDS, signature, signed_attrs, hash_alg) is None:
            print("[+] The signature on EF_SOD is valid.")
    except ex:
        exceptions.append(
            PassiveAuthenticationError(
                "[-] The signature on EF_SOD is not valid or failed."))

    i = asn1_node_root(encapsulated_content)
    i = asn1_node_first_child(encapsulated_content, i)
    i = asn1_node_next(encapsulated_content, i)
    i = asn1_node_next(encapsulated_content, i)

    data_group_hash_values = asn1_get_all(encapsulated_content, i)

    if len(exceptions) == 0:
        return hash_alg, data_group_hash_values, doc_sig_cert, None
    return hash_alg, data_group_hash_values, doc_sig_cert, exceptions
def active_auth(dg15: bytes, sm_object: SMObject, security_infos: List[bytes]):
    """
    Do active authentication with DG15
    """
    # Generate 8 random bytes
    rnd_ifd = urandom(8)

    # exception caught in main program loop
    data = send(
        sm_object,
        APDU(b"\x00",
             b"\x88",
             b"\x00",
             b"\x00",
             Lc=b"\x08",
             cdata=rnd_ifd,
             Le=b"\x00"))

    if data == b"":
        raise ActiveAuthenticationError("[-] No reply from card.")

    i = asn1_node_root(dg15)
    i = asn1_node_first_child(dg15, i)
    pub_key = asn1_get_all(dg15, i)

    i = asn1_node_first_child(dg15, i)
    i = asn1_node_first_child(dg15, i)

    if asn1_get_all(dg15, i) == encode_oid_string("1.2.840.10045.2.1"):  # ECC
        r = data[:len(data) // 2]
        s = data[len(data) // 2:]
        signature = asn1_sequence(asn1_integer(r) + asn1_integer(s))
        ec_pub = EC.pub_key_from_der(pub_key)
        if ec_pub.check_key() != 1:
            raise ActiveAuthenticationError(
                "[-] Active Authentication (AA) failed! Problem in EC Public Key!"
            )

        try:
            hash_type = find_hash_name(security_infos)
        except ValueError as ex:
            raise ActiveAuthenticationError(
                "[-] Active Authentication (AA) failed! Problem in Security Infos hash type!"
            ) from ex
        # for hash_type in ["sha224", "sha256", "sha384", "sha512"]:
        try:
            result = ec_pub.verify_dsa_asn1(
                hashlib.new(hash_type, rnd_ifd).digest(), signature)
        except EC.ECError as ex:
            print("[-] Error in EC function " + ex)
            raise ActiveAuthenticationError(
                "[-] Error in verify_dsa_asn1 of M2Crypto.EC") from ex
        if result == 1:
            print("[+] Active Authentication (AA) completed successfully!")
        else:
            raise ActiveAuthenticationError(
                "[-] Active Authentication (AA) failed!")

    elif asn1_get_all(dg15,
                      i) == encode_oid_string("1.2.840.113549.1.1.1"):  # RSA
        j = asn1_node_root(pub_key)
        j = asn1_node_first_child(pub_key, j)
        j = asn1_node_next(pub_key, j)

        rsa_pub_key = asn1_get_value_of_type(pub_key, j, "BIT STRING")
        if rsa_pub_key[0] != 0x00:
            raise ActiveAuthenticationError(
                "[-] An issue with the RSA key! Padding 0x00 is expected")

        rsa_pub_key = rsa_pub_key[1:]
        j = asn1_node_root(rsa_pub_key)
        j = asn1_node_first_child(rsa_pub_key, j)
        n_der = asn1_get_value_of_type(rsa_pub_key, j, "INTEGER")
        j = asn1_node_next(rsa_pub_key, j)
        e_der = asn1_get_value_of_type(rsa_pub_key, j, "INTEGER")
        n = int.from_bytes(n_der, byteorder="big")
        e = int.from_bytes(e_der, byteorder="big")

        # rsa_key = RSA.import_key(pub_key)
        # https://stackoverflow.com/a/60132608/6077951

        msg = int.from_bytes(data, byteorder="big")
        dec = pow(msg, e, n).to_bytes(len(data), byteorder="big")

        if dec[-1] == 0xCC:
            if dec[-2] == 0x38:
                hash_alg = "sha224"
            elif dec[-2] == 0x34:
                hash_alg = "sha256"
            elif dec[-2] == 0x36:
                hash_alg = "sha384"
            elif dec[-2] == 0x35:
                hash_alg = "sha512"
            t = 2
        elif dec[-1] == 0xBC:
            hash_alg = "sha1"
            t = 1
        else:
            raise ActiveAuthenticationError(
                "[-] Error while Active Authentication!")

        def compare_aa(hash_object):
            # k = rsa_key.size_in_bits()
            # Lh = hash_object.digest_size * 8
            # Lm1 = (k - Lh - (8 * t) - 4 - 4) // 8
            D = dec[-hash_object.digest_size - t:-t]
            M1 = dec[1:-hash_object.digest_size - t]
            Mstar = M1 + rnd_ifd
            hash_object.update(Mstar)
            Dstar = hash_object.digest()
            return hmac.compare_digest(D, Dstar)

        hash_object = hashlib.new(hash_alg)
        if compare_aa(hash_object):
            print("[+] Active Authentication (AA) completed successfully!")
        else:
            raise ActiveAuthenticationError(
                "[-] Active Authentication (AA) failed!")