Skip to content

Commit 1c243db

Browse files
committed
Update padding logic for EmvTreeView to match emv-decode
Identification of possible padding will now apply to nested constructed fields as well, and the resulting EmvTreeItem objects will be clickable.
1 parent 13e202b commit 1c243db

File tree

5 files changed

+134
-51
lines changed

5 files changed

+134
-51
lines changed

viewer/emv-viewer-mainwindow.cpp

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ EmvViewerMainWindow::EmvViewerMainWindow(
6363
// changes the value to be different from the initial state.
6464
highlighter->setEmphasiseTags(tagsCheckBox->isChecked());
6565
highlighter->setIgnorePadding(paddingCheckBox->isChecked());
66+
treeView->setIgnorePadding(paddingCheckBox->isChecked());
6667
treeView->setDecodeFields(decodeCheckBox->isChecked());
6768

6869
// Load previous UI values
@@ -173,7 +174,6 @@ void EmvViewerMainWindow::parseData()
173174
QString str;
174175
int validLen;
175176
QByteArray data;
176-
bool paddingIsPossible = false;
177177
unsigned int validBytes;
178178

179179
str = dataEdit->toPlainText();
@@ -202,49 +202,21 @@ void EmvViewerMainWindow::parseData()
202202
validLen -= 1;
203203
}
204204

205-
// Determine whether invalid data might be padding
206-
if (paddingCheckBox->isChecked() && validLen == str.length()) {
207-
// Input data is a valid hex string and therefore the possibility
208-
// exists that if BER decoding fails, the remaining data might be
209-
// cryptographic padding
210-
paddingIsPossible = true;
211-
}
212-
213205
data = QByteArray::fromHex(str.left(validLen).toUtf8());
214206
validBytes = treeView->populateItems(data);
215207
validLen = validBytes * 2;
216208

217209
if (validLen < str.length()) {
218-
bool isPadding;
219-
QString itemStr;
220-
QColor itemColor;
221-
222-
// Determine whether invalid data is padding and prepare item details
223-
// accordingly
224-
if (paddingIsPossible &&
225-
data.size() - validBytes > 0 &&
226-
(
227-
((data.size() & 0x7) == 0 && data.size() - validBytes < 8) ||
228-
((data.size() & 0xF) == 0 && data.size() - validBytes < 16)
229-
)
230-
) {
231-
// Invalid data is likely to be padding
232-
isPadding = true;
233-
itemStr = QStringLiteral("Padding: ");
234-
itemColor = Qt::darkGray;
235-
} else {
236-
// Invalid data is either absent or unlikely to be padding
237-
isPadding = false;
238-
itemStr = QStringLiteral("Remaining invalid data: ");
239-
itemColor = Qt::red;
240-
}
241-
210+
// Remaining data is invalid and unlikely to be padding
242211
QTreeWidgetItem* item = new QTreeWidgetItem(
243212
treeView->invisibleRootItem(),
244-
QStringList(itemStr + str.right(str.length() - validLen))
213+
QStringList(
214+
QStringLiteral("Remaining invalid data: ") +
215+
str.right(str.length() - validLen)
216+
)
245217
);
246-
item->setDisabled(isPadding);
247-
item->setForeground(0, itemColor);
218+
item->setDisabled(true);
219+
item->setForeground(0, Qt::red);
248220
}
249221
}
250222

@@ -287,6 +259,10 @@ void EmvViewerMainWindow::on_paddingCheckBox_stateChanged(int state)
287259
// view item associated with invalid data or padding as well.
288260
highlighter->setIgnorePadding(state != Qt::Unchecked);
289261
highlighter->rehighlight();
262+
263+
// Note that tree view data must be reparsed when padding state changes
264+
treeView->setIgnorePadding(state != Qt::Unchecked);
265+
parseData();
290266
}
291267

292268
void EmvViewerMainWindow::on_decodeCheckBox_stateChanged(int state)

viewer/emvtreeitem.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636

3737
// Helper functions
3838
static bool valueStrIsList(const QByteArray& str);
39+
static QString buildSimpleFieldString(
40+
QString str,
41+
qsizetype length,
42+
const std::uint8_t* value
43+
);
3944
static QString buildSimpleFieldString(
4045
unsigned int tag,
4146
qsizetype length,
@@ -91,6 +96,25 @@ EmvTreeItem::EmvTreeItem(
9196
setExpanded(autoExpand);
9297
}
9398

99+
EmvTreeItem::EmvTreeItem(
100+
QTreeWidgetItem* parent,
101+
unsigned int srcOffset,
102+
unsigned int srcLength,
103+
QString str,
104+
const void* value
105+
)
106+
: QTreeWidgetItem(parent, EmvTreeItemType),
107+
m_srcOffset(srcOffset),
108+
m_srcLength(srcLength),
109+
m_constructed(false)
110+
{
111+
m_simpleFieldStr = m_decodedFieldStr =
112+
buildSimpleFieldString(str, srcLength, static_cast<const uint8_t*>(value));
113+
114+
// Render the widget according to the current state
115+
render(false);
116+
}
117+
94118
void EmvTreeItem::deleteChildren()
95119
{
96120
QList<QTreeWidgetItem*> list;
@@ -184,6 +208,29 @@ static bool valueStrIsList(const QByteArray& str)
184208
return str[qstrnlen(str.constData(), str.size()) - 1] == '\n';
185209
}
186210

211+
static QString buildSimpleFieldString(
212+
QString str,
213+
qsizetype length,
214+
const std::uint8_t* value
215+
)
216+
{
217+
if (value) {
218+
return
219+
str +
220+
QString::asprintf(" : [%zu] ", static_cast<std::size_t>(length)) +
221+
// Create an uppercase hex string, with spaces, from the
222+
// field's value bytes
223+
QByteArray::fromRawData(
224+
reinterpret_cast<const char*>(value),
225+
length
226+
).toHex(' ').toUpper().constData();
227+
} else {
228+
return
229+
str +
230+
QString::asprintf(" : [%zu]", static_cast<std::size_t>(length));
231+
}
232+
}
233+
187234
static QString buildSimpleFieldString(
188235
unsigned int tag,
189236
qsizetype length,

viewer/emvtreeitem.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ class EmvTreeItem : public QTreeWidgetItem
4141
bool autoExpand = true
4242
);
4343

44+
EmvTreeItem(
45+
QTreeWidgetItem* parent,
46+
unsigned int srcOffset,
47+
unsigned int srcLength,
48+
QString str,
49+
const void* value
50+
);
51+
4452
unsigned int srcOffset() const { return m_srcOffset; }
4553
unsigned int srcLength() const { return m_srcLength; }
4654
QString tagName() const { return m_tagName; }

viewer/emvtreeview.cpp

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@ static bool parseData(
3434
QTreeWidgetItem* parent,
3535
const void* ptr,
3636
unsigned int len,
37+
bool ignorePadding,
3738
bool decode,
38-
unsigned int* validBytes
39+
unsigned int* totalValidBytes
3940
)
4041
{
4142
int r;
43+
unsigned int validBytes = 0;
4244
struct iso8825_ber_itr_t itr;
4345
struct iso8825_tlv_t tlv;
44-
bool valid;
4546

4647
r = iso8825_ber_itr_init(ptr, len, &itr);
4748
if (r) {
@@ -50,10 +51,12 @@ static bool parseData(
5051
}
5152

5253
while ((r = iso8825_ber_itr_next(&itr, &tlv)) > 0) {
54+
unsigned int fieldLength = r;
55+
5356
EmvTreeItem* item = new EmvTreeItem(
5457
parent,
55-
*validBytes,
56-
r,
58+
*totalValidBytes,
59+
fieldLength,
5760
&tlv,
5861
decode
5962
);
@@ -62,32 +65,72 @@ static bool parseData(
6265
// If the field is constructed, only consider the tag and length
6366
// to be valid until the value has been parsed. The fields inside
6467
// the value will be added when they are parsed.
65-
*validBytes += (r - tlv.length);
68+
validBytes += (r - tlv.length);
69+
*totalValidBytes += (r - tlv.length);
6670

6771
// Recursively parse constructed fields
68-
valid = parseData(item, tlv.value, tlv.length, decode, validBytes);
72+
bool valid;
73+
valid = parseData(
74+
item,
75+
tlv.value,
76+
tlv.length,
77+
ignorePadding,
78+
decode,
79+
totalValidBytes
80+
);
6981
if (!valid) {
70-
qDebug("parseBerData() failed; validBytes=%u", *validBytes);
82+
qDebug("parseData() failed; totalValidBytes=%u", *totalValidBytes);
83+
84+
// Return here instead of breaking out to avoid repeated
85+
// processing of the error by recursive callers
7186
return false;
7287
}
88+
validBytes += tlv.length;
7389

7490
} else {
7591
// If the field is not constructed, consider all of the bytes to
7692
// be valid BER encoded data
77-
*validBytes += r;
93+
validBytes += r;
94+
*totalValidBytes += r;
7895
}
7996
}
8097
if (r < 0) {
81-
qDebug("iso8825_ber_itr_next() failed; r=%d", r);
82-
return false;
98+
// Determine whether invalid data is padding and prepare item details
99+
// accordingly
100+
if (ignorePadding &&
101+
len - validBytes > 0 &&
102+
(
103+
((len & 0x7) == 0 && len - validBytes < 8) ||
104+
((len & 0xF) == 0 && len - validBytes < 16)
105+
)
106+
) {
107+
// Invalid data is likely to be padding
108+
EmvTreeItem* item = new EmvTreeItem(
109+
parent,
110+
*totalValidBytes,
111+
len - validBytes,
112+
"Padding",
113+
reinterpret_cast<const char*>(ptr) + validBytes
114+
);
115+
item->setForeground(0, Qt::darkGray);
116+
117+
// If the remaining bytes appear to be padding, consider these
118+
// bytes to be valid
119+
*totalValidBytes += len - validBytes;
120+
validBytes = len;
121+
122+
} else {
123+
qDebug("iso8825_ber_itr_next() failed; r=%d", r);
124+
return false;
125+
}
83126
}
84127

85128
return true;
86129
}
87130

88131
unsigned int EmvTreeView::populateItems(const QByteArray& data)
89132
{
90-
unsigned int validBytes = 0;
133+
unsigned int totalValidBytes = 0;
91134

92135
// For now, clear the widget before repopulating it. In future, the widget
93136
// should be updated incrementally instead.
@@ -97,15 +140,22 @@ unsigned int EmvTreeView::populateItems(const QByteArray& data)
97140
invisibleRootItem(),
98141
data.constData(),
99142
data.size(),
143+
m_ignorePadding,
100144
m_decodeFields,
101-
&validBytes
145+
&totalValidBytes
102146
);
103147

104-
return validBytes;
148+
return totalValidBytes;
105149
}
106150

107151
void EmvTreeView::setDecodeFields(bool enabled)
108152
{
153+
if (m_decodeFields == enabled) {
154+
// No change
155+
return;
156+
}
157+
m_decodeFields = enabled;
158+
109159
// Visit all EMV children recursively and re-render them according to the
110160
// current state
111161
QTreeWidgetItemIterator itr (this);
@@ -117,6 +167,4 @@ void EmvTreeView::setDecodeFields(bool enabled)
117167
}
118168
++itr;
119169
}
120-
121-
m_decodeFields = enabled;
122170
}

viewer/emvtreeview.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,23 @@
2626
class EmvTreeView : public QTreeWidget
2727
{
2828
Q_OBJECT
29+
Q_PROPERTY(bool ignorePadding READ ignorePadding WRITE setIgnorePadding)
2930
Q_PROPERTY(bool decodeFields READ decodeFields WRITE setDecodeFields)
3031

3132
public:
3233
EmvTreeView(QWidget* parent);
3334

3435
public slots:
3536
unsigned int populateItems(const QByteArray& data);
37+
void setIgnorePadding(bool enabled) { m_ignorePadding = enabled; }
3638
void setDecodeFields(bool enabled);
3739

3840
public:
41+
bool ignorePadding() const { return m_ignorePadding; }
3942
bool decodeFields() const { return m_decodeFields; }
4043

4144
private:
45+
bool m_ignorePadding = false;
4246
bool m_decodeFields = true;
4347
};
4448

0 commit comments

Comments
 (0)