1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
"""
This module provides classes for working with ISO7816-4 APDUs and an abstract base class for an ISO7816-4 based target.
"""
from abc import abstractmethod, ABC
from dataclasses import dataclass
from typing import Optional
from public import public
from .base import Target
@public
@dataclass
class CommandAPDU(object): # pragma: no cover
"""A command APDU that can be sent to an ISO7816-4 target."""
cls: int
ins: int
p1: int
p2: int
data: Optional[bytes] = None
ne: Optional[int] = None
def __bytes__(self):
if self.data is None or len(self.data) == 0:
if self.ne is None or self.ne == 0:
# Case 1
return bytes([self.cls, self.ins, self.p1, self.p2])
elif self.ne <= 256:
# Case 2s
return bytes(
[
self.cls,
self.ins,
self.p1,
self.p2,
self.ne if self.ne != 256 else 0,
]
)
else:
# Case 2e
return bytes([self.cls, self.ins, self.p1, self.p2]) + (
self.ne.to_bytes(2, "big") if self.ne != 65536 else bytes([0, 0])
)
elif self.ne is None or self.ne == 0:
if len(self.data) <= 255:
# Case 3s
return (
bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)])
+ self.data
)
else:
# Case 3e
return (
bytes([self.cls, self.ins, self.p1, self.p2, 0])
+ len(self.data).to_bytes(2, "big")
+ self.data
)
else:
if len(self.data) <= 255 and self.ne <= 256:
# Case 4s
return (
bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)])
+ self.data
+ bytes([self.ne if self.ne != 256 else 0])
)
else:
# Case 4e
return (
bytes([self.cls, self.ins, self.p1, self.p2, 0])
+ len(self.data).to_bytes(2, "big")
+ self.data
+ (
self.ne.to_bytes(2, "big")
if self.ne != 65536
else bytes([0, 0])
)
)
@public
@dataclass
class ResponseAPDU(object):
"""A response APDU that can be received from an ISO7816-4 target."""
data: bytes
sw: int
@public
class ISO7816Target(Target, ABC):
"""An ISO7816-4 target."""
@property
@abstractmethod
def atr(self) -> bytes:
"""The ATR (Answer To Reset) of the target."""
...
@abstractmethod
def select(self, aid: bytes) -> bool:
"""
Select an applet with `aid`.
:param aid: The AID of the applet to select.
:return: Whether the selection was successful.
"""
...
@abstractmethod
def send_apdu(self, apdu: CommandAPDU) -> ResponseAPDU:
"""
Send an APDU to the selected applet.
:param apdu: The APDU to send.
:return: The response.
"""
...
@public
class ISO7816:
"""A bunch of ISO7816-4 constants (status words)."""
SW_FILE_FULL = 0x6A84
SW_UNKNOWN = 0x6F00
SW_CLA_NOT_SUPPORTED = 0x6E00
SW_INS_NOT_SUPPORTED = 0x6D00
SW_CORRECT_LENGTH_00 = 0x6C00
SW_WRONG_P1P2 = 0x6B00
SW_INCORRECT_P1P2 = 0x6A86
SW_RECORD_NOT_FOUND = 0x6A83
SW_FILE_NOT_FOUND = 0x6A82
SW_FUNC_NOT_SUPPORTED = 0x6A81
SW_WRONG_DATA = 0x6A80
SW_APPLET_SELECT_FAILED = 0x6999
SW_COMMAND_NOT_ALLOWED = 0x6986
SW_CONDITIONS_NOT_SATISFIED = 0x6985
SW_DATA_INVALID = 0x6984
SW_FILE_INVALID = 0x6983
SW_SECURITY_STATUS_NOT_SATISFIED = 0x6982
SW_WRONG_LENGTH = 0x6700
SW_BYTES_REMAINING_00 = 0x6100
SW_NO_ERROR = 0x9000
|