Skip to content
This repository was archived by the owner on Mar 27, 2026. It is now read-only.

Commit 6e1c9d9

Browse files
committed
Allow repeated tags in Data Object Lists
This is apparently allowed and used by some applications fixes #22
1 parent 9088960 commit 6e1c9d9

3 files changed

Lines changed: 19 additions & 9 deletions

File tree

emv/protocol/structures.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def __repr__(self):
129129
return ret
130130

131131

132-
class DOL(OrderedDict):
132+
class DOL(list):
133133
"""Data Object List.
134134
This is sent by the card to the terminal to define a structure for
135135
future transactions, consisting of an ordered list of data elements and lengths.
@@ -148,12 +148,12 @@ def unmarshal(cls, data):
148148
i += tag_len
149149
length = data[i]
150150
i += 1
151-
dol[Tag(tag)] = length
151+
dol.append((Tag(tag), length))
152152
return dol
153153

154154
def size(self):
155155
"""Total size of the resulting structure in bytes."""
156-
return sum(self.values())
156+
return sum([val[1] for val in self])
157157

158158
def unserialise(self, data):
159159
"""Parse an input stream of bytes and return a TLV object."""
@@ -165,18 +165,24 @@ def unserialise(self, data):
165165

166166
tlv = TLV()
167167
i = 0
168-
for tag, length in self.items():
168+
for tag, length in self:
169169
tlv[tag] = data[i : i + length]
170170
i += length
171171

172172
return tlv
173173

174+
def __contains__(self, val):
175+
for v in self:
176+
if v[0] == val:
177+
return True
178+
return False
179+
174180
def serialise(self, data):
175181
"""Given a dictionary of tag -> value, write this data out
176182
according to the DOL. Missing data will be null.
177183
"""
178184
output = []
179-
for tag, length in self.items():
185+
for tag, length in self:
180186
value = data.get(tag, [0x0] * length)
181187
if len(value) < length:
182188
# If the length is shorter than required, left-pad it.

emv/protocol/test/test_structures.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,15 @@ def test_invalid_tlv():
9292
def test_dol():
9393
dol = DOL.unmarshal(dol_data)
9494
assert 0x9A in dol
95-
assert dol[0x9A] == 3
9695
assert (0x9F, 0x37) in dol
97-
assert dol[(0x9F, 0x37)] == 4
9896
assert dol.size() == 29
9997

98+
# This DOL has a repeated 0x9A entry, for some reason. Apparently this is allowed.
99+
data = unformat_bytes("9F 66 04 9F 02 06 9F 03 06 9F 1A 02 95 05 5F 2A 02 9A 03 9C 01 9F 37 04 9A 03")
100+
dol = DOL.unmarshal(data)
101+
102+
assert len([t for t in dol if t[0] == 0x9A]) == 2
103+
100104

101105
def test_serialise():
102106
dol = DOL.unmarshal(dol_data)
@@ -166,4 +170,4 @@ def test_taglist():
166170

167171
def test_cvmlist():
168172
data = unformat_bytes("00 00 00 00 00 00 00 00 41 03 1E 03 02 03 1F 03")
169-
print(CVMList.unmarshal(data))
173+
CVMList.unmarshal(data)

emv/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def format_bytes(data):
4444

4545

4646
def unformat_bytes(data):
47-
data = re.split(r"\s+", data)
47+
data = re.split(r"(?:\s+|:)", data)
4848
return [int(i, 16) for i in data]
4949

5050

0 commit comments

Comments
 (0)