summaryrefslogtreecommitdiff
path: root/test/tpm_test/crypto_test.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/tpm_test/crypto_test.py')
-rw-r--r--test/tpm_test/crypto_test.py413
1 files changed, 199 insertions, 214 deletions
diff --git a/test/tpm_test/crypto_test.py b/test/tpm_test/crypto_test.py
index 36253952c5..c11528c8f2 100644
--- a/test/tpm_test/crypto_test.py
+++ b/test/tpm_test/crypto_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
# 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.
@@ -7,7 +7,6 @@
from __future__ import print_function
-import binascii
import struct
import xml.etree.ElementTree as ET
@@ -19,225 +18,211 @@ DECRYPT = 0
ENCRYPT = 1
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:
- subcmd.TpmTestError: 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 = ('aad', 'cipher_text', 'iv', 'key', 'tag')
-
- data = tdesc.find(attr_name)
- if data is None:
- if required:
- raise subcmd.TpmTestError('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 subcmd.TpmTestError('%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(' ', '')
-
- # Convert hex-text to little-endian binary (in 4-byte word chunks)
- 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 subcmd.TpmTestError('%s:%s %swrong hex value' %
- (tdesc.get('name'), attr_name, utils.hex_dump(text)))
-
- # Unpack remaining hex text, without introducing a zero pad.
- for x in range(-1, -(len(text) % 8), -1):
- value += chr(int(text[2*x:len(text) + (2*x)+2], 16))
-
- 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
+ """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:
+ subcmd.TpmTestError: 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 = ('aad', 'cipher_text', 'iv', 'key', 'tag')
+
+ data = tdesc.find(attr_name)
+ if data is None:
+ if required:
+ raise subcmd.TpmTestError('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 subcmd.TpmTestError('%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(' ', '')
+
+ # Convert hex-text to little-endian binary (in 4-byte word chunks)
+ value = ''
+ for block in range(len(text)/8):
+ try:
+ value += struct.pack('<I', int('0x%s' % text[8*block:8*(block+1)],
+ 16))
+ except ValueError:
+ raise subcmd.TpmTestError('%s:%s %swrong hex value' %
+ (tdesc.get('name'), attr_name,
+ utils.hex_dump(text)))
+
+ # Unpack remaining hex text, without introducing a zero pad.
+ for block in range(-1, -(len(text) % 8), -1):
+ value += chr(int(text[2*block:len(text) + (2*block)+2], 16))
+
+ return value
SUPPORTED_MODES = {
- 'AES': CryptoD(subcmd.AES, {
- 'ECB': 0,
- 'CTR': 1,
- 'CBC': 2,
- 'GCM': 3,
- 'OFB': 4,
- 'CFB': 5
- }),
+ 'AES': (subcmd.AES, {
+ 'ECB': 0,
+ 'CTR': 1,
+ 'CBC': 2,
+ 'GCM': 3,
+ 'OFB': 4,
+ 'CFB': 5
+ }),
}
-def crypto_run(node_name, op_type, key, iv, aad, 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
- aad: additional authenticated data
- 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:
- subcmd.TpmTestError: 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 subcmd.TpmTestError('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 += '%c' % len(aad)
- if aad:
- cmd += aad
- 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 subcmd.TpmTestError(
- 'Out text mismatch in node %s, operation %s:\n'
- 'In text:%sExpected out text:%sReal out text:%s' % (
- node_name, 'ENCRYPT' if op_type == ENCRYPT else 'DECRYPT',
- utils.hex_dump(in_text),
- utils.hex_dump(out_text),
- utils.hex_dump(real_out_text)))
- return real_out_text
+# pylint: disable=too-many-arguments, too-many-locals
+def crypto_run(node_name, op_type, key, init_vec, aad, 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
+ init_vec: a binary string, might be empty
+ aad: additional authenticated data
+ 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:
+ subcmd.TpmTestError: 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()
+
+ # commands below will raise exception if incorrect mode/submode used
+ try:
+ mode_cmd, submodes = SUPPORTED_MODES[mode_name.upper()]
+ submode = submodes[submode_name]
+ except KeyError:
+ raise subcmd.TpmTestError('unrecognizable mode in node "%s"' %
+ node_name)
+
+ 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(init_vec)
+ if init_vec:
+ cmd += init_vec
+ cmd += '%c' % len(aad)
+ if aad:
+ cmd += aad
+ cmd += struct.pack('>H', len(in_text))
+ cmd += in_text
+ if tpm.debug_enabled():
+ print('%d:%d cmd size' % (op_type, mode_cmd),
+ len(cmd), utils.hex_dump(cmd))
+ wrapped_response = tpm.command(tpm.wrap_ext_command(mode_cmd, cmd))
+ real_out_text = tpm.unwrap_ext_response(mode_cmd, 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 subcmd.TpmTestError(
+ 'Out text mismatch in node %s, operation %s:\n'
+ 'In text:%sExpected out text:%sReal out text:%s' % (
+ node_name, 'ENCRYPT' if op_type == ENCRYPT else 'DECRYPT',
+ 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:
- subcmd.TpmTestError: 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 subcmd.TpmTestError('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 not node_name.startswith('AES:GCM') and len(iv) != 16:
- raise subcmd.TpmTestError('wrong iv size "%s:%s"' % (
- node_name,
- ''.join('%2.2x' % ord(x) for x in iv)))
- clear_text = get_attribute(tdesc, 'clear_text', required=False)
- if clear_text:
- clear_text_len = get_attribute(tdesc, 'clear_text_len', required=False)
+ """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:
+ subcmd.TpmTestError: 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 subcmd.TpmTestError('wrong key size "%s:%s"' % (
+ node_name,
+ ''.join('%2.2x' % ord(x) for x in key)))
+ init_vec = get_attribute(tdesc, 'iv', required=False)
+ if init_vec and not node_name.startswith('AES:GCM') and len(init_vec) != 16:
+ raise subcmd.TpmTestError('wrong iv size "%s:%s"' % (
+ node_name,
+ ''.join('%2.2x' % ord(x) for x in init_vec)))
+ clear_text = get_attribute(tdesc, 'clear_text', required=False)
+ if clear_text:
+ clear_text_len = get_attribute(tdesc, 'clear_text_len', required=False)
+ if clear_text_len:
+ clear_text = clear_text[:int(clear_text_len)]
+ else:
+ clear_text_len = None
+ if tpm.debug_enabled():
+ print('clear text size', len(clear_text))
+ cipher_text = get_attribute(tdesc, 'cipher_text', required=False)
if clear_text_len:
- clear_text = clear_text[:int(clear_text_len)]
- else:
- clear_text_len = None
- if tpm.debug_enabled():
- print('clear text size', len(clear_text))
- cipher_text = get_attribute(tdesc, 'cipher_text', required=False)
- if clear_text_len:
- cipher_text = cipher_text[:int(clear_text_len)]
- tag = get_attribute(tdesc, 'tag', required=False)
- aad = get_attribute(tdesc, 'aad', required=False)
- if aad:
- aad_len = get_attribute(tdesc, 'aad_len', required=False)
- if aad_len:
- aad = aad[:int(aad_len)]
- real_cipher_text = crypto_run(node_name, ENCRYPT, key, iv,
- aad or '', clear_text, cipher_text + tag, tpm)
- crypto_run(node_name, DECRYPT, key, iv, aad or '',
- real_cipher_text[:len(real_cipher_text) - len(tag)],
- clear_text + tag, tpm)
- print(utils.cursor_back() + 'SUCCESS: %s' % node_name)
+ cipher_text = cipher_text[:int(clear_text_len)]
+ tag = get_attribute(tdesc, 'tag', required=False)
+ aad = get_attribute(tdesc, 'aad', required=False)
+ if aad:
+ aad_len = get_attribute(tdesc, 'aad_len', required=False)
+ if aad_len:
+ aad = aad[:int(aad_len)]
+ real_cipher_text = crypto_run(node_name, ENCRYPT, key, init_vec,
+ aad or '', clear_text, cipher_text + tag, tpm)
+ crypto_run(node_name, DECRYPT, key, init_vec, aad or '',
+ real_cipher_text[:len(real_cipher_text) - len(tag)],
+ clear_text + tag, 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)
+ """Run AES cryptographic tests"""
+ tree = ET.parse(xml_file)
+ root = tree.getroot()
+ for child in root:
+ crypto_test(child, tpm)