mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 09:01:35 +00:00
cr50: test: move crypto test into its own module
This is a no-op change moving some common code out of tpmtest.py, preparing it to support different testing modes. BRANCH=none BUG=chrome-os-partner:43025 TEST=the AES test still succeeds: $ test/tpm_test/tpmtest.py Starting MPSSE at 800 kHz Connected to device vid:did:rid of 1ae0:0028:00 SUCCESS: AES:ECB common SUCCESS: AES:ECB128 1 SUCCESS: AES:ECB192 1 SUCCESS: AES:ECB256 1 SUCCESS: AES:ECB256 2 SUCCESS: AES:CTR128I 1 SUCCESS: AES:CTR256I 1 Change-Id: Ia6e0e3e89f99875297da0a4f6137de5901c8ca08 Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/314691 Reviewed-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
committed by
chrome-bot
parent
0e9cd95664
commit
e1be8e179c
220
test/tpm_test/crypto_test.py
Normal file
220
test/tpm_test/crypto_test.py
Normal file
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Module for testing cryptography functions using extended commands."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import struct
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import utils
|
||||
|
||||
# Extension 'cryptography' subcommand codes:
|
||||
AES = 0
|
||||
|
||||
# Basic crypto operations
|
||||
DECRYPT = 0
|
||||
ENCRYPT = 1
|
||||
|
||||
class CryptoError(Exception):
|
||||
pass
|
||||
|
||||
def get_attribute(tdesc, attr_name, required=True):
|
||||
"""Retrieve an attribute value from an XML node.
|
||||
|
||||
Args:
|
||||
tdesc: an Element of the ElementTree, a test descriptor containing
|
||||
necessary information to run a single encryption/description
|
||||
session.
|
||||
attr_name: a string, the name of the attribute to retrieve.
|
||||
required: a Boolean, if True - the attribute must be present in the
|
||||
descriptor, otherwise it is considered optional
|
||||
Returns:
|
||||
The attribute value as a string (ascii or binary)
|
||||
Raises:
|
||||
CryptoError: on various format errors, or in case a required attribute is
|
||||
not found, the error message describes the problem.
|
||||
|
||||
"""
|
||||
# Fields stored in hex format by default.
|
||||
default_hex = ('cipher_text', 'iv', 'key')
|
||||
|
||||
data = tdesc.find(attr_name)
|
||||
if data is None:
|
||||
if required:
|
||||
raise CryptoError('node "%s" does not have attribute "%s"' %
|
||||
(tdesc.get('name'), attr_name))
|
||||
return ''
|
||||
|
||||
# Attribute is present, does it have to be decoded from hex?
|
||||
cell_format = data.get('format')
|
||||
if not cell_format:
|
||||
if attr_name in default_hex:
|
||||
cell_format = 'hex'
|
||||
else:
|
||||
cell_format = 'ascii'
|
||||
elif cell_format not in ('hex', 'ascii'):
|
||||
raise CryptoError('%s:%s, unrecognizable format "%s"' %
|
||||
(tdesc.get('name'), attr_name, cell_format))
|
||||
|
||||
text = ' '.join(x.strip() for x in data.text.splitlines() if x)
|
||||
if cell_format == 'ascii':
|
||||
return text
|
||||
|
||||
# Drop spaces from hex representation.
|
||||
text = text.replace(' ', '')
|
||||
if len(text) & 3:
|
||||
raise CryptoError('%s:%s %swrong hex number size' %
|
||||
(tdesc.get('name'), attr_name, utils.hex_dump(text)))
|
||||
# Convert text to binary
|
||||
value = ''
|
||||
for x in range(len(text)/8):
|
||||
try:
|
||||
value += struct.pack('<I', int('0x%s' % text[8*x:8*(x+1)], 16))
|
||||
except ValueError:
|
||||
raise CryptoError('%s:%s %swrong hex value' %
|
||||
(tdesc.get('name'), attr_name, utils.hex_dump(text)))
|
||||
return value
|
||||
|
||||
|
||||
class CryptoD(object):
|
||||
"""A helper object to contain an encryption scheme description.
|
||||
|
||||
Attributes:
|
||||
subcmd: a 16 bit max integer, the extension subcommand to be used with
|
||||
this encryption scheme.
|
||||
sumbodes: an optional dictionary, the keys are strings, names of the
|
||||
encryption scheme submodes, the values are integers to be included in
|
||||
the appropriate subcommand fields to communicat the submode to the
|
||||
device.
|
||||
"""
|
||||
|
||||
def __init__(self, subcommand, submodes=None):
|
||||
self.subcmd = subcommand
|
||||
if not submodes:
|
||||
submodes = {}
|
||||
self.submodes = submodes
|
||||
|
||||
SUPPORTED_MODES = {
|
||||
'AES': CryptoD(AES, {
|
||||
'ECB': 0,
|
||||
'CTR': 1,
|
||||
'CBC': 2,
|
||||
'GCM': 3
|
||||
}),
|
||||
}
|
||||
|
||||
def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm):
|
||||
"""Perform a basic operation(encrypt or decrypt).
|
||||
|
||||
This function creates an extended command with the requested parameters,
|
||||
sends it to the device, and then compares the response to the expected
|
||||
value.
|
||||
|
||||
Args:
|
||||
node_name: a string, the name of the XML node this data comes from. The
|
||||
format of the name is "<enc type>:<submode> ....", where <enc type> is
|
||||
the major encryption mode (say AED or DES) and submode - a variant of
|
||||
the major scheme, if exists.
|
||||
|
||||
op_type: an int, encodes the operation to perform (encrypt/decrypt), passed
|
||||
directly to the device as a field in the extended command
|
||||
key: a binary string
|
||||
iv: a binary string, might be empty
|
||||
in_text: a binary string, the input of the encrypt/decrypt operation
|
||||
out_text: a binary string, might be empty, the expected output of the
|
||||
operation. Note that it could be shorter than actual output (padded to
|
||||
integer number of blocks), in which case only its length of bytes is
|
||||
compared debug_mode: a Boolean, if True - enables tracing on the console
|
||||
tpm: a TPM object to send extended commands to an initialized TPM
|
||||
|
||||
Returns:
|
||||
The actual binary string, result of the operation, if the
|
||||
comparison with the expected value was successful.
|
||||
|
||||
Raises:
|
||||
CryptoError: in case there were problems parsing the node name, or verifying
|
||||
the operation results.
|
||||
"""
|
||||
mode_name, submode_name = node_name.split(':')
|
||||
submode_name = submode_name[:3].upper()
|
||||
|
||||
mode = SUPPORTED_MODES.get(mode_name.upper())
|
||||
if not mode:
|
||||
raise CryptoError('unrecognizable mode in node "%s"' % node_name)
|
||||
|
||||
submode = mode.submodes.get(submode_name, 0)
|
||||
cmd = '%c' % op_type # Encrypt or decrypt
|
||||
cmd += '%c' % submode # A particular type of a generic algorithm.
|
||||
cmd += '%c' % len(key)
|
||||
cmd += key
|
||||
cmd += '%c' % len(iv)
|
||||
if iv:
|
||||
cmd += iv
|
||||
cmd += struct.pack('>H', len(in_text))
|
||||
cmd += in_text
|
||||
if tpm.debug_enabled():
|
||||
print('%d:%d cmd size' % (op_type, mode.subcmd),
|
||||
len(cmd), utils.hex_dump(cmd))
|
||||
wrapped_response = tpm.command(tpm.wrap_ext_command(mode.subcmd, cmd))
|
||||
real_out_text = tpm.unwrap_ext_response(mode.subcmd, wrapped_response)
|
||||
if out_text:
|
||||
if len(real_out_text) > len(out_text):
|
||||
real_out_text = real_out_text[:len(out_text)] # Ignore padding
|
||||
if real_out_text != out_text:
|
||||
if tpm.debug_enabled():
|
||||
print('Out text mismatch in node %s:\n' % node_name)
|
||||
else:
|
||||
raise CryptoError('Out text mismatch in node %s, operation %d:\n'
|
||||
'In text:%sExpected out text:%sReal out text:%s' % (
|
||||
node_name, op_type,
|
||||
utils.hex_dump(in_text),
|
||||
utils.hex_dump(out_text),
|
||||
utils.hex_dump(real_out_text)))
|
||||
return real_out_text
|
||||
|
||||
|
||||
def crypto_test(tdesc, tpm):
|
||||
"""Perform a single test described in the xml file.
|
||||
|
||||
The xml node contains all pertinent information about the test inputs and
|
||||
outputs.
|
||||
|
||||
Args:
|
||||
tdesc: an Element of the ElementTree, a test descriptor containing
|
||||
necessary information to run a single encryption/description
|
||||
session.
|
||||
tpm: a TPM object to send extended commands to an initialized TPM
|
||||
Raises:
|
||||
CryptoError: on various execution errors, the details are included in the
|
||||
error message.
|
||||
"""
|
||||
node_name = tdesc.get('name')
|
||||
key = get_attribute(tdesc, 'key')
|
||||
if len(key) not in (16, 24, 32):
|
||||
raise CryptoError('wrong key size "%s:%s"' % (
|
||||
node_name,
|
||||
''.join('%2.2x' % ord(x) for x in key)))
|
||||
iv = get_attribute(tdesc, 'iv', required=False)
|
||||
if iv and len(iv) != 16:
|
||||
raise CryptoError('wrong iv size "%s:%s"' % (
|
||||
node_name,
|
||||
''.join('%2.2x' % ord(x) for x in iv)))
|
||||
clear_text = get_attribute(tdesc, 'clear_text')
|
||||
if tpm.debug_enabled():
|
||||
print('clear text size', len(clear_text))
|
||||
cipher_text = get_attribute(tdesc, 'cipher_text', required=False)
|
||||
real_cipher_text = crypto_run(node_name, ENCRYPT, key, iv,
|
||||
clear_text, cipher_text, tpm)
|
||||
crypto_run(node_name, DECRYPT, key, iv, real_cipher_text,
|
||||
clear_text, tpm)
|
||||
print(utils.cursor_back() + 'SUCCESS: %s' % node_name)
|
||||
|
||||
def crypto_tests(tpm, xml_file):
|
||||
tree = ET.parse(xml_file)
|
||||
root = tree.getroot()
|
||||
for child in root:
|
||||
crypto_test(child, tpm)
|
||||
@@ -3,7 +3,7 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Module for testing TPM, using both conventional and extended commands."""
|
||||
"""Module for initializing and driving a SPI TPM."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
@@ -11,31 +11,19 @@ import os
|
||||
import struct
|
||||
import sys
|
||||
import traceback
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
# Suppressing pylint warning about an import not at the top of the file. The
|
||||
# path needs to be set *before* the last import.
|
||||
# pylint: disable=C6204
|
||||
root_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||
sys.path.append(os.path.join(root_dir, '..', '..', 'build', 'tpm_test'))
|
||||
import ftdi_spi_tpm
|
||||
|
||||
# Basic crypto operations
|
||||
DECRYPT = 0
|
||||
ENCRYPT = 1
|
||||
import crypto_test
|
||||
import ftdi_spi_tpm
|
||||
|
||||
# Extension command for dcypto testing
|
||||
EXT_CMD = 0xbaccd00a
|
||||
|
||||
# Extension subcommands for encryption types
|
||||
AES = 0
|
||||
|
||||
if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
|
||||
cursor_back = '\x1b[1D' # Move one space to the left.
|
||||
else:
|
||||
cursor_back = ''
|
||||
|
||||
|
||||
class TpmError(Exception):
|
||||
pass
|
||||
|
||||
@@ -59,6 +47,7 @@ class TPM(object):
|
||||
'80 01 00 00 00 0a 00 00 01 00')
|
||||
|
||||
def __init__(self, freq=800*1000, debug_mode=False):
|
||||
self._debug_enabled = debug_mode
|
||||
self._handle = ftdi_spi_tpm
|
||||
if not self._handle.FtdiSpiInit(freq, debug_mode):
|
||||
raise TpmError()
|
||||
@@ -132,229 +121,19 @@ class TPM(object):
|
||||
size, len(response)))
|
||||
return response[header_size:]
|
||||
|
||||
|
||||
def hex_dump(binstr):
|
||||
"""Convert string into its hex representation."""
|
||||
dump_lines = ['',]
|
||||
i = 0
|
||||
while i < len(binstr):
|
||||
strsize = min(16, len(binstr) - i)
|
||||
hexstr = ' '.join('%2.2x' % ord(x) for x in binstr[i:i+strsize])
|
||||
dump_lines.append(hexstr)
|
||||
i += strsize
|
||||
dump_lines.append('')
|
||||
return '\n'.join(dump_lines)
|
||||
|
||||
|
||||
def get_attribute(tdesc, attr_name, required=True):
|
||||
"""Retrieve an attribute value from an XML node.
|
||||
|
||||
Args:
|
||||
|
||||
tdesc: an Element of the ElementTree, a test descriptor containing
|
||||
necessary information to run a single encryption/description
|
||||
session.
|
||||
attr_name: a string, the name of the attribute to retrieve.
|
||||
required: a Boolean, if True - the attribute must be present in the
|
||||
descriptor, otherwise it is considered optional
|
||||
Returns:
|
||||
The attribute value as a string (ascii or binary)
|
||||
Raises:
|
||||
TpmError: on various format errors, or in case a required attribute is not
|
||||
found, the error message describes the problem.
|
||||
|
||||
"""
|
||||
# Fields stored in hex format by default.
|
||||
default_hex = ('cipher_text', 'iv', 'key')
|
||||
|
||||
data = tdesc.find(attr_name)
|
||||
if data is None:
|
||||
if required:
|
||||
raise TpmError('node "%s" does not have attribute "%s"' %
|
||||
(tdesc.get('name'), attr_name))
|
||||
return ''
|
||||
|
||||
# Attribute is present, does it have to be decoded from hex?
|
||||
cell_format = data.get('format')
|
||||
if not cell_format:
|
||||
if attr_name in default_hex:
|
||||
cell_format = 'hex'
|
||||
else:
|
||||
cell_format = 'ascii'
|
||||
elif cell_format not in ('hex', 'ascii'):
|
||||
raise TpmError('%s:%s, unrecognizable format "%s"' %
|
||||
(tdesc.get('name'), attr_name, cell_format))
|
||||
|
||||
text = ' '.join(x.strip() for x in data.text.splitlines() if x)
|
||||
if cell_format == 'ascii':
|
||||
return text
|
||||
|
||||
# Drop spaces from hex representation.
|
||||
text = text.replace(' ', '')
|
||||
if len(text) & 3:
|
||||
raise TpmError('%s:%s %swrong hex number size' %
|
||||
(tdesc.get('name'), attr_name, hex_dump(text)))
|
||||
# Convert text to binary
|
||||
value = ''
|
||||
for x in range(len(text)/8):
|
||||
try:
|
||||
value += struct.pack('<I', int('0x%s' % text[8*x:8*(x+1)], 16))
|
||||
except ValueError:
|
||||
raise TpmError('%s:%s %swrong hex value' %
|
||||
(tdesc.get('name'), attr_name, hex_dump(text)))
|
||||
return value
|
||||
|
||||
|
||||
class CryptoD(object):
|
||||
"""A helper object to contain an encryption scheme description.
|
||||
|
||||
Attributes:
|
||||
subcmd: a 16 bit max integer, the extension subcommand to be used with
|
||||
this encryption scheme.
|
||||
sumbodes: an optional dictionary, the keys are strings, names of the
|
||||
encryption scheme submodes, the values are integers to be included in
|
||||
the appropriate subcommand fields to communicat the submode to the
|
||||
device.
|
||||
"""
|
||||
|
||||
def __init__(self, subcommand, submodes=None):
|
||||
self.subcmd = subcommand
|
||||
if not submodes:
|
||||
submodes = {}
|
||||
self.submodes = submodes
|
||||
|
||||
SUPPORTED_MODES = {
|
||||
'AES': CryptoD(AES, {
|
||||
'ECB': 0,
|
||||
'CTR': 1,
|
||||
'CBC': 2,
|
||||
'GCM': 3
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm, debug_mode):
|
||||
"""Perform a basic operation(encrypt or decrypt).
|
||||
|
||||
This function creates an extended command with the requested parameters,
|
||||
sends it to the device, and then compares the response to the expected
|
||||
value.
|
||||
|
||||
Args:
|
||||
node_name: a string, the name of the XML node this data comes from. The
|
||||
format of the name is "<enc type>:<submode> ....", where <enc type> is
|
||||
the major encryption mode (say AED or DES) and submode - a variant of
|
||||
the major scheme, if exists.
|
||||
|
||||
op_type: an int, encodes the operation to perform (encrypt/decrypt), passed
|
||||
directly to the device as a field in the extended command
|
||||
key: a binary string
|
||||
iv: a binary string, might be empty
|
||||
in_text: a binary string, the input of the encrypt/decrypt operation
|
||||
out_text: a binary string, might be empty, the expected output of the
|
||||
operation. Note that it could be shorter than actual output (padded to
|
||||
integer number of blocks), in which case only its length of bytes is
|
||||
compared debug_mode: a Boolean, if True - enables tracing on the console
|
||||
tpm: a TPM object to send extended commands to an initialized TPM
|
||||
debug_mode: a Boolean, if True - this function and the FTDI driver
|
||||
generate debug messated on the console.
|
||||
|
||||
Returns:
|
||||
The actual binary string, result of the operation, if the
|
||||
comparison with the expected value was successful.
|
||||
|
||||
Raises:
|
||||
TpmError: in case there were problems parsing the node name, or verifying
|
||||
the operation results.
|
||||
"""
|
||||
mode_name, submode_name = node_name.split(':')
|
||||
submode_name = submode_name[:3].upper()
|
||||
|
||||
mode = SUPPORTED_MODES.get(mode_name.upper())
|
||||
if not mode:
|
||||
raise TpmError('unrecognizable mode in node "%s"' % node_name)
|
||||
|
||||
submode = mode.submodes.get(submode_name, 0)
|
||||
cmd = '%c' % op_type # Encrypt or decrypt
|
||||
cmd += '%c' % submode # A particular type of a generic algorithm.
|
||||
cmd += '%c' % len(key)
|
||||
cmd += key
|
||||
cmd += '%c' % len(iv)
|
||||
if iv:
|
||||
cmd += iv
|
||||
cmd += struct.pack('>H', len(in_text))
|
||||
cmd += in_text
|
||||
if debug_mode:
|
||||
print('%d:%d cmd size' % (op_type, mode.subcmd), len(cmd), hex_dump(cmd))
|
||||
wrapped_response = tpm.command(tpm.wrap_ext_command(mode.subcmd, cmd))
|
||||
real_out_text = tpm.unwrap_ext_response(mode.subcmd, wrapped_response)
|
||||
if out_text:
|
||||
if len(real_out_text) > len(out_text):
|
||||
real_out_text = real_out_text[:len(out_text)] # Ignore padding
|
||||
if real_out_text != out_text:
|
||||
if debug_mode:
|
||||
print('Out text mismatch in node %s:\n' % node_name)
|
||||
else:
|
||||
raise TpmError('Out text mismatch in node %s, operation %d:\n'
|
||||
'In text:%sExpected out text:%sReal out text:%s' % (
|
||||
node_name, op_type,
|
||||
hex_dump(in_text),
|
||||
hex_dump(out_text),
|
||||
hex_dump(real_out_text)))
|
||||
return real_out_text
|
||||
|
||||
|
||||
def crypto_test(tdesc, tpm, debug_mode):
|
||||
"""Perform a single test described in the xml file.
|
||||
|
||||
The xml node contains all pertinent information about the test inputs and
|
||||
outputs.
|
||||
|
||||
Args:
|
||||
tdesc: an Element of the ElementTree, a test descriptor containing
|
||||
necessary information to run a single encryption/description
|
||||
session.
|
||||
tpm: a TPM object to send extended commands to an initialized TPM
|
||||
debug_mode: a Boolean, if True - this function and the FTDI driver
|
||||
generate debug messated on the console.
|
||||
Raises:
|
||||
TpmError: on various execution errors, the details are included in the
|
||||
error message.
|
||||
"""
|
||||
node_name = tdesc.get('name')
|
||||
key = get_attribute(tdesc, 'key')
|
||||
if len(key) not in (16, 24, 32):
|
||||
raise TpmError('wrong key size "%s:%s"' % (
|
||||
node_name,
|
||||
''.join('%2.2x' % ord(x) for x in key)))
|
||||
iv = get_attribute(tdesc, 'iv', required=False)
|
||||
if iv and len(iv) != 16:
|
||||
raise TpmError('wrong iv size "%s:%s"' % (
|
||||
node_name,
|
||||
''.join('%2.2x' % ord(x) for x in iv)))
|
||||
clear_text = get_attribute(tdesc, 'clear_text')
|
||||
if debug_mode:
|
||||
print('clear text size', len(clear_text))
|
||||
cipher_text = get_attribute(tdesc, 'cipher_text', required=False)
|
||||
real_cipher_text = crypto_run(node_name, ENCRYPT, key, iv,
|
||||
clear_text, cipher_text, tpm, debug_mode)
|
||||
crypto_run(node_name, DECRYPT, key, iv, real_cipher_text,
|
||||
clear_text, tpm, debug_mode)
|
||||
print(cursor_back + 'SUCCESS: %s' % node_name)
|
||||
def debug_enabled(self):
|
||||
return self._debug_enabled
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
tree = ET.parse(os.path.join(root_dir, 'crypto_test.xml'))
|
||||
root = tree.getroot()
|
||||
try:
|
||||
debug_needed = len(sys.argv) == 2 and sys.argv[1] == '-d'
|
||||
t = TPM(debug_mode=debug_needed)
|
||||
|
||||
for child in root:
|
||||
crypto_test(child, t, debug_needed)
|
||||
except TpmError as e:
|
||||
crypto_test.crypto_tests(t, os.path.join(root_dir, 'crypto_test.xml'))
|
||||
except (TpmError, crypto_test.CryptoError) as e:
|
||||
print()
|
||||
print(e)
|
||||
print('Error:', e)
|
||||
if debug_needed:
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
38
test/tpm_test/utils.py
Normal file
38
test/tpm_test/utils.py
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Support functions for extended command based testing."""
|
||||
|
||||
import sys
|
||||
|
||||
if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
|
||||
cursor_back_cmd = '\x1b[1D' # Move one space to the left.
|
||||
else:
|
||||
cursor_back_cmd = ''
|
||||
|
||||
|
||||
def cursor_back():
|
||||
"""Return a string which would move cursor one space left, if available.
|
||||
|
||||
This is used to remove the remaining 'spinner' character after the test
|
||||
completes and its result is printed on the same line where the 'spinner' was
|
||||
spinning.
|
||||
|
||||
"""
|
||||
return cursor_back_cmd
|
||||
|
||||
|
||||
def hex_dump(binstr):
|
||||
"""Convert binary string into its multiline hex representation."""
|
||||
|
||||
dump_lines = ['',]
|
||||
i = 0
|
||||
while i < len(binstr):
|
||||
strsize = min(16, len(binstr) - i)
|
||||
hexstr = ' '.join('%2.2x' % ord(x) for x in binstr[i:i+strsize])
|
||||
dump_lines.append(hexstr)
|
||||
i += strsize
|
||||
dump_lines.append('')
|
||||
return '\n'.join(dump_lines)
|
||||
Reference in New Issue
Block a user