#!/usr/bin/python # Copyright 2016 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 ecc functions using extended commands.""" import binascii import hashlib import os import struct import subcmd import utils _ECC_OPCODES = { 'SIGN': 0x00, 'VERIFY': 0x01, 'KEYGEN': 0x02, 'KEYDERIVE': 0x03, } _ECC_CURVES = { 'NIST-P256': 0x03, } # TPM2 signature codes. _SIGN_MODE = { 'NONE': 0x00, 'ECDSA': 0x18, # TODO(ngm): add support for SCHNORR. # 'SCHNORR': 0x1c } # TPM2 ALG codes. _HASH = { 'NONE': 0x00, 'SHA1': 0x04, 'SHA256': 0x0B } _HASH_FUNC = { 'NIST-P256': hashlib.sha256 } # Command format. # # 0x00 OP # 0x00 CURVE_ID # 0x00 SIGN_MODE # 0x00 HASHING # 0x00 MSB IN LEN # 0x00 LSB IN LEN # .... IN # 0x00 MSB DIGEST LEN # 0x00 LSB DIGEST LEN # .... DIGEST # _ECC_CMD_FORMAT = '{o:c}{c:c}{s:c}{h:c}{ml:s}{msg}{dl:s}{dig}' def _sign_cmd(curve_id, hash_func, sign_mode, msg): op = _ECC_OPCODES['SIGN'] digest = hash_func(msg).digest() digest_len = len(digest) return _ECC_CMD_FORMAT.format(o=op, c=curve_id, s=sign_mode, h=_HASH['NONE'], ml=struct.pack('>H', 0), msg='', dl=struct.pack('>H', digest_len), dig=digest) def _verify_cmd(curve_id, hash_func, sign_mode, msg, sig): op = _ECC_OPCODES['VERIFY'] sig_len = len(sig) digest = hash_func(msg).digest() digest_len = len(digest) return _ECC_CMD_FORMAT.format(o=op, c=curve_id, s=sign_mode, h=_HASH['NONE'], ml=struct.pack('>H', sig_len), msg=sig, dl=struct.pack('>H', digest_len), dig=digest) def _keygen_cmd(curve_id): op = _ECC_OPCODES['KEYGEN'] return _ECC_CMD_FORMAT.format(o=op, c=curve_id, s=_SIGN_MODE['NONE'], h=_HASH['NONE'], ml=struct.pack('>H', 0), msg='', dl=struct.pack('>H', 0), dig='') def _keyderive_cmd(curve_id, seed): op = _ECC_OPCODES['KEYDERIVE'] seed_len = len(seed) return _ECC_CMD_FORMAT.format(o=op, c=curve_id, s=_SIGN_MODE['NONE'], h=_HASH['NONE'], ml=struct.pack('>H', seed_len), msg=seed, dl=struct.pack('>H', 0), dig='') _SIGN_INPUTS = ( ('NIST-P256', 'ECDSA'), ) _KEYGEN_INPUTS = ( ('NIST-P256',), ) _KEYDERIVE_INPUTS = ( # Curve-id, random seed size. ('NIST-P256', 32), ) def _sign_test(tpm): msg = 'Hello CR50' for data in _SIGN_INPUTS: curve_id, sign_mode = data test_name = 'ECC-SIGN:%s:%s' % data cmd = _sign_cmd(_ECC_CURVES[curve_id], _HASH_FUNC[curve_id], _SIGN_MODE[sign_mode], msg) wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.ECC, cmd)) signature = tpm.unwrap_ext_response(subcmd.ECC, wrapped_response) cmd = _verify_cmd(_ECC_CURVES[curve_id], _HASH_FUNC[curve_id], _SIGN_MODE[sign_mode], msg, signature) wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.ECC, cmd)) verified = tpm.unwrap_ext_response(subcmd.ECC, wrapped_response) expected = '\x01' if verified != expected: raise subcmd.TpmTestError('%s error:%s:%s' % ( test_name, utils.hex_dump(verified), utils.hex_dump(expected))) print('%sSUCCESS: %s' % (utils.cursor_back(), test_name)) def _keygen_test(tpm): for data in _KEYGEN_INPUTS: curve_id, = data test_name = 'ECC-KEYGEN:%s' % data cmd = _keygen_cmd(_ECC_CURVES[curve_id]) wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.ECC, cmd)) valid = tpm.unwrap_ext_response(subcmd.ECC, wrapped_response) expected = '\x01' if valid != expected: raise subcmd.TpmTestError('%s error:%s:%s' % ( test_name, utils.hex_dump(valid), utils.hex_dump(expected))) print('%sSUCCESS: %s' % (utils.cursor_back(), test_name)) def _keyderive_test(tpm): for data in _KEYDERIVE_INPUTS: curve_id, seed_bytes = data seed = os.urandom(seed_bytes) test_name = 'ECC-KEYDERIVE:%s' % data[0] cmd = _keyderive_cmd(_ECC_CURVES[curve_id], seed) wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.ECC, cmd)) valid = tpm.unwrap_ext_response(subcmd.ECC, wrapped_response) expected = '\x01' if valid != expected: raise subcmd.TpmTestError('%s error:%s:%s' % ( test_name, utils.hex_dump(valid), utils.hex_dump(expected))) print('%sSUCCESS: %s' % (utils.cursor_back(), test_name)) def ecc_test(tpm): _sign_test(tpm) _keygen_test(tpm) _keyderive_test(tpm)