Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions notecard/notecard.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,23 @@ def _crc_error(self, rsp_bytes):
# also truncated, and so the CRC will be computed over a different
# string than was originally sent, resulting in a CRC error.
seq_number, crc = rsp_json['crc'].split(':')

# Convert the received CRC and sequence number to integers for later
# comparison.
try:
seq_number_as_int = int(seq_number, 16)
except ValueError:
if self._debug:
print(f'Received sequence number "{seq_number}" cannot be ' + \
'converted to integer.')
return True
try:
crc_as_int = int(crc, 16)
except ValueError:
if self._debug:
print(f'Received CRC "{crc}" cannot be converted to integer.')
return True

# Remove the 'crc' field from the response.
rsp_str = rsp_bytes.decode()
rsp_str_crc_removed = rsp_str.split('"crc":')[0]
Expand All @@ -178,18 +195,19 @@ def _crc_error(self, rsp_bytes):
rsp_str_crc_removed = rsp_str_crc_removed.rstrip() + '}'

# Compute the CRC over the response, with the 'crc' field removed.
bytes_for_crc = rsp_str_crc_removed.encode('utf-8')
computed_crc = '{:08x}'.format(crc32(bytes_for_crc)).upper()
expected_seq_number = '{:04x}'.format(self._last_request_seq_number)
bytes_for_crc_calc = rsp_str_crc_removed.encode('utf-8')
computed_crc = crc32(bytes_for_crc_calc)

if seq_number != expected_seq_number:
if seq_number_as_int != self._last_request_seq_number:
if self._debug:
print('Sequence number mismatch. Expected ' + \
f'{expected_seq_number}, received {seq_number}.')
f'{self._last_request_seq_number}, received ' + \
f'{seq_number_as_int}.')
return True
elif crc != computed_crc:
elif crc_as_int != computed_crc:
if self._debug:
print(f'CRC error. Computed {computed_crc}, received {crc}.')
print(f'CRC error. Computed {computed_crc}, received ' + \
f'{crc_as_int}.')
return True

return False
Expand Down
29 changes: 26 additions & 3 deletions test/test_notecard.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@ def test_crc_error_handles_lack_of_crc_field_correctly(self, crc_supported):

assert error == crc_supported

def test_crc_error_returns_error_if_sequence_number_int_conversion_fails(
self):
card = notecard.Notecard()
# Sequence number is invalid hex.
rsp_bytes = b'{"crc":"000Z:A3A6BF43"}\r\n'

error = card._crc_error(rsp_bytes)

assert error

def test_crc_error_returns_error_if_crc_int_conversion_fails(self):
card = notecard.Notecard()
# CRC is invalid hex.
rsp_bytes = b'{"crc":"0001:A3A6BF4Z"}\r\n'

error = card._crc_error(rsp_bytes)

assert error

def test_crc_error_returns_error_if_sequence_number_wrong(self):
card = notecard.Notecard()
seq_number = 37
Expand All @@ -106,16 +125,20 @@ def test_crc_error_returns_error_if_crc_wrong(self):
'rsp_bytes',
[
# Without CRC, the response is {}.
b'{"crc":"0025:A3A6BF43"}\r\n',
b'{"crc":"002A:A3A6BF43"}\r\n',
# Make sure case of sequence number hex doesn't matter.
b'{"crc":"002a:A3A6BF43"}\r\n',
# Make sure case of CRC hex doesn't matter.
b'{"crc":"002A:a3a6bf43"}\r\n',
# Without CRC, the response is {"connected": true}. This makes sure
# _crc_error handles the "," between the two fields properly.
b'{"connected": true,"crc": "0025:025A2457"}\r\n'
b'{"connected": true,"crc": "002A:025A2457"}\r\n',
]
)
def test_crc_error_returns_no_error_if_sequence_number_and_crc_ok(
self, rsp_bytes):
card = notecard.Notecard()
seq_number = 37
seq_number = 42
card._last_request_seq_number = seq_number

error = card._crc_error(rsp_bytes)
Expand Down