1#  Copyright (C) 2024 The Android Open Source Project
2#
3#  Licensed under the Apache License, Version 2.0 (the "License");
4#  you may not use this file except in compliance with the License.
5#  You may obtain a copy of the License at
6#
7#       http://www.apache.org/licenses/LICENSE-2.0
8#
9#  Unless required by applicable law or agreed to in writing, software
10#  distributed under the License is distributed on an "AS IS" BASIS,
11#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12#  See the License for the specific language governing permissions and
13#  limitations under the License.
14
15# Lint as: python3
16
17from binascii import hexlify
18
19_NUM_POLLING_LOOPS = 50
20
21def create_select_apdu(aid_hex):
22    """Creates a select APDU command for the given AID"""
23    aid_bytes = bytearray.fromhex(aid_hex)
24    return bytearray.fromhex("00A40400") + bytearray([len(aid_bytes)]) + aid_bytes
25
26def poll_and_transact(pn532, command_apdus, response_apdus, custom_frame = None):
27    """Polls for an NFC Type-A tag 50 times. If tag is found, performs a transaction.
28
29    :param pn532: PN532 device
30    :param command_apdus: Command APDUs in transaction
31    :param response_apdus: Response APDUs in transaction
32    :param custom_frame: A custom frame to send as part of te polling loop other than pollA().
33
34    :return: [if tag is found, if transaction was successful]
35    """
36    transacted = False
37    tag = None
38    for i in range(_NUM_POLLING_LOOPS):
39        tag = pn532.poll_a()
40        if tag is not None:
41            transacted = tag.transact(command_apdus, response_apdus)
42            pn532.mute()
43            break
44        if custom_frame is not None:
45            pn532.send_broadcast(bytearray.fromhex(custom_frame))
46        pn532.mute()
47    return tag is not None, transacted
48
49def parse_protocol_params(sak, ats):
50    """
51    Helper function to check whether protocol parameters are properly set.
52    :param sak: SAK byte
53    :param ats: ATS bytearray
54    :return: whether bits are set correctly, message to print
55    """
56    msg = ""
57    success = True
58    msg += "SAK:\n"
59    if sak & 0x20 != 0:
60        msg += "    (OK) ISO-DEP bit (0x20) is set.\n"
61    else:
62        success = False
63        msg += "    (FAIL) ISO-DEP bit (0x20) is NOT set.\n"
64    if sak & 0x40 != 0:
65        msg += "    (OK) P2P bit (0x40) is set.\n"
66    else:
67        msg += "    (WARN) P2P bit (0x40) is NOT set.\n"
68
69    ta_present = False
70    tb_present = False
71    tc_present = False
72    atsIndex = 0
73    if ats[atsIndex] & 0x40 != 0:
74        msg += "        (OK) T(C) is present (bit 7 is set).\n"
75        tc_present = True
76    else:
77        success = False
78        msg += "        (FAIL) T(C) is not present (bit 7 is NOT set).\n"
79    if ats[atsIndex] and 0x20 != 0:
80        msg += "        (OK) T(B) is present (bit 6 is set).\n"
81        tb_present = True
82    else:
83        success = False
84        msg += "        (FAIL) T(B) is not present (bit 6 is NOT set).\n"
85    if ats[atsIndex] and 0x10 != 0:
86        msg += "        (OK) T(A) is present (bit 5 is set).\n"
87        ta_present = True
88    else:
89        success = False
90        msg += "        (FAIL) T(A) is not present (bit 5 is NOT set).\n"
91    fsc = ats[atsIndex] & 0x0F
92    if fsc > 8:
93        success = False
94        msg += "        (FAIL) FSC " + str(fsc) + " is > 8\n"
95    elif fsc < 2:
96        msg += "        (FAIL EMVCO) FSC " + str(fsc) + " is < 2\n"
97    else:
98        msg += "        (OK) FSC = " + str(fsc) + "\n"
99
100    atsIndex += 1
101    if ta_present:
102        msg += "    TA: 0x" + str(ats[atsIndex] & 0xff) + "\n"
103        if ats[atsIndex] & 0x80 != 0:
104            msg += "        (OK) bit 8 set, indicating only same bit rate divisor.\n"
105        else:
106            msg += "        (FAIL EMVCO) bit 8 NOT set, indicating support for asymmetric bit rate divisors. EMVCo requires bit 8 set.\n"
107        if ats[atsIndex] & 0x70 != 0:
108            msg += "        (FAIL EMVCO) EMVCo requires bits 7 to 5 set to 0.\n"
109        else:
110            msg += "        (OK) bits 7 to 5 indicating only 106 kbit/s L->P supported.\n"
111        if ats[atsIndex] & 0x7 != 0:
112            msg += "        (FAIL EMVCO) EMVCo requires bits 3 to 1 set to 0.\n"
113        else:
114            msg += "        (OK) bits 3 to 1 indicating only 106 kbit/s P->L supported.\n"
115        atsIndex += 1
116
117    if tb_present:
118        msg += "    TB: 0x" + str(ats[3] & 0xFF) + "\n"
119        fwi = (ats[atsIndex] & 0xF0) >> 4
120        if fwi > 8:
121            msg += "        (FAIL) FWI=" + str(fwi) + ", should be <= 8\n"
122        elif fwi == 8:
123            msg += "        (FAIL EMVCO) FWI=" + str(fwi) + ", EMVCo requires <= 7\n"
124        else:
125            msg += "        (OK) FWI=" + str(fwi) + "\n"
126        sfgi = ats[atsIndex] & 0x0F
127        if sfgi > 8:
128            success = False
129            msg += "        (FAIL) SFGI=" + str(sfgi) + ", should be <= 8\n"
130        else:
131            msg += "        (OK) SFGI=" + str(sfgi) + "\n"
132        atsIndex += 1
133    if tc_present:
134        msg += "    TC: 0x" + str(ats[atsIndex] & 0xFF) + "\n"
135        nadSupported = ats[atsIndex] & 0x01 != 0
136        if nadSupported:
137            success = False
138            msg += "        (FAIL) NAD bit is not allowed to be set.\n"
139        else:
140            msg += "        (OK) NAD bit is not set.\n"
141        atsIndex += 1
142        if atsIndex + 1 < len(ats):
143            bytes_to_copy = len(ats) - atsIndex
144            historical_bytes = bytearray(ats[atsIndex:atsIndex + bytes_to_copy])
145            msg +=  "\n(OK) Historical bytes: " + hexlify(historical_bytes).decode()
146    return success, msg
147
148def get_apdus(nfc_emulator, service_name):
149    """
150    Gets apdus for a given service.
151    :param nfc_emulator: emulator snippet.
152    :param service_name: Service name of APDU sequence to fetch.
153    :return: [command APDU byte array, response APDU byte array]
154    """
155    command_apdus = nfc_emulator.getCommandApdus(service_name)
156    response_apdus = nfc_emulator.getResponseApdus(service_name)
157    return [bytearray.fromhex(apdu) for apdu in command_apdus], [
158        (bytearray.fromhex(apdu) if apdu != "*" else apdu) for apdu in response_apdus]
159
160def to_byte_array(apdu_array):
161    return [bytearray.fromhex(apdu) for apdu in apdu_array]
162