From 11a30863e863e46c230cc430300b67824acab19f Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 20 Mar 2023 17:05:46 +0100 Subject: [PATCH] Using new package pypicohsm. Signed-off-by: Pol Henarejos --- tests/conftest.py | 613 +--------------------- tests/const.py | 6 - tests/pico-hsm/test_000_info.py | 1 - tests/pico-hsm/test_004_key_domains.py | 15 +- tests/pico-hsm/test_005_dkek.py | 4 +- tests/pico-hsm/test_010_pin.py | 23 +- tests/pico-hsm/test_020_keypair_gen.py | 16 +- tests/pico-hsm/test_021_key_import.py | 13 +- tests/pico-hsm/test_022_key_exchange.py | 11 +- tests/pico-hsm/test_023_key_generation.py | 6 +- tests/pico-hsm/test_025_key_export.py | 30 +- tests/pico-hsm/test_030_signature.py | 6 +- tests/pico-hsm/test_040_decrypt.py | 5 +- tests/pico-hsm/test_050_cipher.py | 7 +- tests/pico-hsm/test_060_mac.py | 9 +- tests/pico-hsm/test_070_hkdf.py | 9 +- tests/pico-hsm/test_071_pbkdf2.py | 9 +- tests/pico-hsm/test_072_x963.py | 9 +- tests/pico-hsm/test_080_pka.py | 11 +- tests/pico-hsm/test_090_xkek.py | 17 +- tests/utils.py | 141 ----- 21 files changed, 104 insertions(+), 857 deletions(-) delete mode 100644 tests/utils.py diff --git a/tests/conftest.py b/tests/conftest.py index 5c9b718..bce03b1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,622 +19,15 @@ import sys import pytest -import os -from binascii import hexlify -from utils import APDUResponse, DOPrefixes, KeyType, Algorithm, Padding, int_to_bytes -from const import * -import hashlib try: - from cvc.asn1 import ASN1 - from cvc import oid - from cvc.certificates import CVC - from cvc.ec_curves import ec_domain, find_curve + from picohsm import PicoHSM except ModuleNotFoundError: - print('ERROR: cvc module not found! Install pycvc package.\nTry with `pip install pycvc`') + print('ERROR: picohsm module not found! Install picohsm package.\nTry with `pip install picohsm`') sys.exit(-1) -try: - from smartcard.CardType import AnyCardType - from smartcard.CardRequest import CardRequest - from smartcard.Exceptions import CardRequestTimeoutException, CardConnectionException -except ModuleNotFoundError: - print('ERROR: smarctard module not found! Install pyscard package.\nTry with `pip install pyscard`') - sys.exit(-1) - -try: - from cryptography.hazmat.primitives.asymmetric import ec, rsa, utils, padding - from cryptography.hazmat.primitives import hashes, cmac - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat -except ModuleNotFoundError: - print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`') - sys.exit(-1) - - -class Device: - class EcDummy: - def __init__(self, name): - self.name = name - - def __init__(self,pin='648219'): - self.__pin = pin - cardtype = AnyCardType() - try: - # request card insertion - cardrequest = CardRequest(timeout=10, cardType=cardtype) - self.__card = cardrequest.waitforcard() - - # connect to the card and perform a few transmits - self.__card.connection.connect() - - except CardRequestTimeoutException: - raise Exception('time-out: no card inserted during last 10s') - self.select_applet() - data = self.get_contents(p1=0x2f02) - self.device_id = CVC().decode(data).chr() - - def select_applet(self): - self.__card.connection.transmit([0x00, 0xA4, 0x04, 0x00, 0xB, 0xE8, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xC3, 0x1F, 0x02, 0x01, 0x0]) - - def send(self, command, cla=0x00, p1=0x00, p2=0x00, ne=None, data=None, codes=[]): - lc = [] - dataf = [] - if (data): - lc = [0x00] + list(len(data).to_bytes(2, 'big')) - dataf = list(data) - else: - lc = [0x00*3] - if (ne is None): - le = [0x00, 0x00] - else: - le = list(ne.to_bytes(2, 'big')) - if (isinstance(command, list) and len(command) > 1): - apdu = command - else: - apdu = [cla, command] - - apdu = apdu + [p1, p2] + lc + dataf + le - try: - response, sw1, sw2 = self.__card.connection.transmit(apdu) - except CardConnectionException: - self.__card.connection.reconnect() - response, sw1, sw2 = self.__card.connection.transmit(apdu) - - code = (sw1<<8|sw2) - if (sw1 != 0x90): - if (sw1 == 0x63 and sw2 & 0xF0 == 0xC0): - pass - # elif (code == 0x6A82): - # self.select_applet() - # if (sw1 == 0x90): - # response, sw1, sw2 = self.__card.connection.transmit(apdu) - # if (sw1 == 0x90): - # return response - elif (code == 0x6982): - response, sw1, sw2 = self.__card.connection.transmit([0x00, 0x20, 0x00, 0x81, len(self.__pin)] + list(self.__pin.encode()) + [0x0]) - if (sw1 == 0x90): - response, sw1, sw2 = self.__card.connection.transmit(apdu) - if (sw1 == 0x90): - return response - if (code not in codes): - raise APDUResponse(sw1, sw2) - if (len(codes) > 1): - return response, code - return response - - def get_login_retries(self): - self.select_applet() - try: - self.send(command=0x20, p2=0x81) - except APDUResponse as e: - if (e.sw1 == 0x63 and e.sw2 & 0xF0 == 0xC0): - return e.sw2 & 0x0F - raise e - - def is_logged(self): - try: - self.send(command=0x20, p2=0x81) - return True - except APDUResponse: - pass - return False - - def logout(self): - self.select_applet() - - def initialize(self, pin=DEFAULT_PIN, sopin=DEFAULT_SOPIN, options=None, retries=DEFAULT_RETRIES, dkek_shares=None, puk_auts=None, puk_min_auts=None, key_domains=None): - if (retries is not None and not 0 < retries <= 10): - raise ValueError('Retries must be in the range (0,10]') - if (dkek_shares is not None and not 0 <= dkek_shares <= 10): - raise ValueError('DKEK shares must be in the range [0,10]') - if ((puk_auts is not None and puk_min_auts is None) or (puk_auts is None and puk_min_auts is not None)): - raise ValueError('PUK Auts and PUK Min Auts must be specified both') - if (puk_auts is not None and not 0 < puk_auts <= 8): - raise ValueError('PUK Auts must be in the range (0,8]') - if (puk_min_auts is not None and not 0 < puk_min_auts <= 8): - raise ValueError('PUK Min Auts must be in the range (0,8]') - if (puk_auts is not None and puk_min_auts is not None and puk_min_auts > puk_auts): - raise ValueError('PUK Min Auts must be less or equal to PUK Auts') - if (key_domains is not None and not 0 < key_domains <= 8): - raise ValueError('Key Domains must be in the range (0,8]') - - a = ASN1() - if (pin is not None): - a = a.add_tag(0x81, pin.encode()) - if (sopin is not None): - a = a.add_tag(0x82, sopin.encode()) - if (retries is not None): - a = a.add_tag(0x91, bytes([retries])) - if (dkek_shares is not None): - a = a.add_tag(0x92, bytes([dkek_shares])) - if (puk_auts is not None and puk_min_auts is not None): - a = a.add_tag(0x93, bytes([puk_auts, puk_min_auts])) - if (key_domains is not None): - a = a.add_tag(0x97, bytes([key_domains])) - - data = a.encode() - - self.send(cla=0x80, command=0x50, data=data) - - def login(self, pin=None): - if (pin is None): - pin = self.__pin - self.send(command=0x20, p2=0x81, data=pin.encode()) - - def get_first_free_id(self): - kids = self.list_keys(prefix=DOPrefixes.KEY_PREFIX) - mset = set(range(max(kids)))-set(kids) - if (len(mset) > 0): - return min(mset) - if (max(kids) == 255): - raise ValueError('Max number of key id reached') - return max(kids)+1 - - def list_keys(self, prefix=None): - resp = self.send(command=0x58) - if (prefix is not None): - grouped = [(resp[i],resp[i+1]) for i in range(0, len(resp), 2) if resp[i] == prefix.value] - _, kids = zip(*grouped) - return kids - return [(resp[i],resp[i+1]) for i in range(0, len(resp), 2)] - - def key_generation(self, type, param, use_counter=None, algorithms=None, key_domain=None): - meta_data = b'' - if (use_counter is not None): - meta_data += b'\x90\x04' + use_counter.to_bytes(4, 'big') - if (algorithms is not None): - meta_data += b'\x91' + bytes([len(algorithms)] + algorithms) - if (key_domain is not None): - meta_data += b'\x92\x01' + bytes([key_domain]) - if (type in [KeyType.RSA, KeyType.ECC]): - a = ASN1().add_tag(0x5f29, bytes([0])).add_tag(0x42, 'UTCA00001'.encode()) - if (type == KeyType.RSA): - if (not 1024 <= param <= 4096): - raise ValueError('RSA bits must be in the range [1024,4096]') - a.add_tag(0x7f49, ASN1().add_oid(oid.ID_TA_RSA_V1_5_SHA_256).add_tag(0x2, param.to_bytes(2, 'big')).encode()) - elif (type == KeyType.ECC): - if (param not in ('secp192r1', 'secp256r1', 'secp384r1', 'secp521r1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', 'secp192k1', 'secp256k1')): - raise ValueError('Bad elliptic curve name') - - dom = ec_domain(Device.EcDummy(param)) - pubctx = {1: dom.P, 2: dom.A, 3: dom.B, 4: dom.G, 5: dom.O, 7: dom.F} - a.add_object(0x7f49, oid.ID_TA_ECDSA_SHA_256, pubctx) - a.add_tag(0x5f20, 'UTCDUMMY00001'.encode()) - data = a.encode() - - keyid = self.get_first_free_id() - self.send(command=0x46, p1=keyid, data=list(data + meta_data)) - elif (type == KeyType.AES): - if (param == 128): - p2 = 0xB0 - elif (param == 192): - p2 = 0xB1 - elif (param == 256): - p2 = 0xB2 - else: - raise ValueError('Bad AES key size') - keyid = self.get_first_free_id() - self.send(command=0x48, p1=keyid, p2=p2) - else: - raise ValueError('Bad KeyType') - return keyid - - def delete_file(self, p1, p2=None): - if (p2): - self.send(command=0xE4, data=[p1, p2]) - else: - resp = self.delete_file(p1=p1 >> 8, p2=p1 & 0xff) - - def get_contents(self, p1, p2=None): - if (p2): - resp = self.send(command=0xB1, p1=p1, p2=p2, data=[0x54, 0x02, 0x00, 0x00]) - else: - resp = self.get_contents(p1=p1 >> 8, p2=p1 & 0xff) - return bytes(resp) - - def put_contents(self, p1, p2=None, data=None): - if (p2): - self.send(command=0xD7, p1=p1, p2=p2, data=[0x54, 0x02, 0x00, 0x00, 0x53, len(data) if data else 0] + list(data) if data else []) - else: - self.put_contents(p1=p1 >> 8, p2=p1 & 0xff, data=data) - - def public_key(self, keyid, param=None): - response = self.get_contents(p1=DOPrefixes.EE_CERTIFICATE_PREFIX.value, p2=keyid) - - cert = bytearray(response) - roid = CVC().decode(cert).pubkey().oid() - if (roid == oid.ID_TA_ECDSA_SHA_256): - curve = find_curve(ec_domain(Device.EcDummy(param)).P) - Y = bytes(CVC().decode(cert).pubkey().find(0x86).data()) - return ec.EllipticCurvePublicKey.from_encoded_point( - curve, - Y, - ) - elif (roid == oid.ID_TA_RSA_V1_5_SHA_256): - n = int.from_bytes(bytes(CVC().decode(cert).pubkey().find(0x81).data()), 'big') - e = int.from_bytes(bytes(CVC().decode(cert).pubkey().find(0x82).data()), 'big') - return rsa.RSAPublicNumbers(e, n).public_key() - return None - - def sign(self, keyid, scheme, data): - resp = self.send(cla=0x80, command=0x68, p1=keyid, p2=scheme.value, data=data) - return resp - - def verify(self, pubkey, data, signature, scheme): - if (Algorithm.ALGO_EC_RAW.value <= scheme.value <= Algorithm.ALGO_EC_SHA512.value): - if (scheme == Algorithm.ALGO_EC_SHA1): - hsh = hashes.SHA1() - elif (scheme == Algorithm.ALGO_EC_SHA224): - hsh = hashes.SHA224() - elif (scheme == Algorithm.ALGO_EC_SHA256): - hsh = hashes.SHA256() - elif (scheme == Algorithm.ALGO_EC_RAW): - hsh = utils.Prehashed(hashes.SHA512()) - elif (scheme == Algorithm.ALGO_EC_SHA384): - hsh = hashes.SHA384() - elif (scheme == Algorithm.ALGO_EC_SHA512): - hsh = hashes.SHA512() - return pubkey.verify(signature, data, ec.ECDSA(hsh)) - elif (Algorithm.ALGO_RSA_PKCS1_SHA1.value <= scheme.value <= Algorithm.ALGO_RSA_PSS_SHA512.value): - if (scheme == Algorithm.ALGO_RSA_PKCS1_SHA1 or scheme == Algorithm.ALGO_RSA_PSS_SHA1): - hsh = hashes.SHA1() - elif (scheme == Algorithm.ALGO_RSA_PKCS1_SHA224 or scheme == Algorithm.ALGO_RSA_PSS_SHA224): - hsh = hashes.SHA224() - elif (scheme == Algorithm.ALGO_RSA_PKCS1_SHA256 or scheme == Algorithm.ALGO_RSA_PSS_SHA256): - hsh = hashes.SHA256() - elif (scheme == Algorithm.ALGO_RSA_PKCS1_SHA384 or scheme == Algorithm.ALGO_RSA_PSS_SHA384): - hsh = hashes.SHA384() - elif (scheme == Algorithm.ALGO_RSA_PKCS1_SHA512 or scheme == Algorithm.ALGO_RSA_PSS_SHA512): - hsh = hashes.SHA512() - if (Algorithm.ALGO_RSA_PKCS1_SHA1.value <= scheme.value <= Algorithm.ALGO_RSA_PKCS1_SHA512.value): - padd = padding.PKCS1v15() - elif (Algorithm.ALGO_RSA_PSS_SHA1.value <= scheme.value <= Algorithm.ALGO_RSA_PSS_SHA512.value): - padd = padding.PSS( - mgf=padding.MGF1(hsh), - salt_length=padding.PSS.AUTO - ) - return pubkey.verify(signature, data, padd, hsh) - - def decrypt(self, keyid, data, pad): - if (isinstance(pad, padding.OAEP)): - p2 = Padding.OAEP.value - elif (isinstance(pad, padding.PKCS1v15)): - p2 = Padding.PKCS.value - else: - p2 = Padding.RAW.value - resp = self.send(command=0x62, p1=keyid, p2=p2, data=list(data)) - return bytes(resp) - - def import_dkek(self, dkek, key_domain=0): - resp = self.send(cla=0x80, command=0x52, p1=0x0, p2=key_domain, data=dkek) - return resp - - def import_key(self, pkey, dkek=None, purposes=None): - data = b'' - kcv = hashlib.sha256(dkek or b'\x00'*32).digest()[:8] - kenc = hashlib.sha256((dkek or b'\x00'*32) + b'\x00\x00\x00\x01').digest() - kmac = hashlib.sha256((dkek or b'\x00'*32) + b'\x00\x00\x00\x02').digest() - data += kcv - if (isinstance(pkey, rsa.RSAPrivateKey)): - data += b'\x05' - algo = b'\x00\x0A\x04\x00\x7F\x00\x07\x02\x02\x02\x01\x02' - elif (isinstance(pkey, ec.EllipticCurvePrivateKey)): - data += b'\x0C' - algo = b'\x00\x0A\x04\x00\x7F\x00\x07\x02\x02\x02\x02\x03' - elif (isinstance(pkey, bytes)): - data += b'\x0F' - algo = b'\x00\x08\x60\x86\x48\x01\x65\x03\x04\x01' - - data += algo - if (not purposes and isinstance(pkey, bytes)): - purposes = [Algorithm.ALGO_AES_CBC_ENCRYPT.value, Algorithm.ALGO_AES_CBC_DECRYPT.value, Algorithm.ALGO_AES_CMAC.value, Algorithm.ALGO_AES_DERIVE.value, Algorithm.ALGO_EXT_CIPHER_ENCRYPT.value, Algorithm.ALGO_EXT_CIPHER_DECRYPT.value] - if (purposes): - data += b'\x00' + bytes([len(purposes)]) + bytes(purposes) + b'\x00'*4 - else: - data += b'\x00'*6 - - kb = os.urandom(8) - if (isinstance(pkey, rsa.RSAPrivateKey)): - kb += int_to_bytes(pkey.key_size, length=2) - pubnum = pkey.public_key().public_numbers() - pnum = pkey.private_numbers() - kb += int_to_bytes((pnum.d.bit_length()+7)//8, length=2) - kb += int_to_bytes(pnum.d) - kb += int_to_bytes((pubnum.n.bit_length()+7)//8, length=2) - kb += int_to_bytes(pubnum.n) - kb += int_to_bytes((pubnum.e.bit_length()+7)//8, length=2) - kb += int_to_bytes(pubnum.e) - elif (isinstance(pkey, ec.EllipticCurvePrivateKey)): - curve = ec_domain(pkey.curve) - kb += int_to_bytes(len(curve.P)*8, length=2) - kb += int_to_bytes(len(curve.A), length=2) - kb += curve.A - kb += int_to_bytes(len(curve.B), length=2) - kb += curve.B - kb += int_to_bytes(len(curve.P), length=2) - kb += curve.P - kb += int_to_bytes(len(curve.O), length=2) - kb += curve.O - kb += int_to_bytes(len(curve.G), length=2) - kb += curve.G - kb += int_to_bytes((pkey.private_numbers().private_value.bit_length()+7)//8, length=2) - kb += int_to_bytes(pkey.private_numbers().private_value) - p = pkey.public_key().public_bytes(Encoding.X962, PublicFormat.UncompressedPoint) - kb += int_to_bytes(len(p), length=2) - kb += p - elif (isinstance(pkey, bytes)): - kb += int_to_bytes(len(pkey), length=2) - kb += pkey - - kb_len_pad = (len(kb)//16)*16 - if (len(kb) % 16 > 0): - kb_len_pad = (len(kb)//16 + 1)*16 - if (len(kb) < kb_len_pad): - kb += b'\x80' - kb += b'\x00' * (kb_len_pad-len(kb)) - cipher = Cipher(algorithms.AES(kenc), modes.CBC(b'\x00'*16)) - encryptor = cipher.encryptor() - ct = encryptor.update(kb) + encryptor.finalize() - data += ct - c = cmac.CMAC(algorithms.AES(kmac)) - c.update(data) - data += c.finalize() - - p1 = self.get_first_free_id() - _ = self.send(cla=0x80, command=0x74, p1=p1, p2=0x93, data=data) - return p1 - - def export_key(self, keyid): - resp = self.send(cla=0x80, command=0x72, p1=keyid, p2=0x92) - return resp - - def exchange(self, keyid, pubkey): - resp = self.send(cla=0x80, command=0x62, p1=keyid, p2=Algorithm.ALGO_EC_ECDH.value, data=pubkey.public_bytes(Encoding.X962, PublicFormat.UncompressedPoint)) - return resp[1:] - - def parse_cvc(self, data): - car = CVC().decode(data).car() - chr = CVC().decode(data).chr() - return {'car': car, 'chr': chr} - - def get_termca(self): - resp = self.get_contents(EF_TERMCA) - cv_data = self.parse_cvc(resp) - a = ASN1().decode(resp).find(0x7f21).data() - tlen = len(ASN1.calculate_len(len(a))) - ret = {'cv': cv_data} - if (len(a)+2+tlen < len(resp)): # There's more certificate - resp = resp[2+len(a)+tlen:] - dv_data = self.parse_cvc(resp) - ret['dv'] = dv_data - return ret - - def get_version(self): - resp = self.send(cla=0x80, command=0x50) - return resp[5]+0.1*resp[6] - - def get_key_domain(self, key_domain=0): - resp, code = self.send(cla=0x80, command=0x52, p2=key_domain, codes=[0x9000, 0x6A88, 0x6A86]) - if (code == 0x9000): - ret = { - 'dkek': { - 'total': resp[0], - 'missing': resp[1] - }, - 'kcv': resp[2:10] - } - if (len(resp) > 10): - ret.update({'xkek': resp[10:]}) - return ret - return {'error': code} - - def get_key_domains(self): - for k in range(0xFF): - _, code = self.send(cla=0x80, command=0x52, p2=k, codes=[0x9000, 0x6A88, 0x6A86]) - if (code == 0x6A86): - return k - return 0 - - def set_key_domain(self, key_domain=0, total=DEFAULT_DKEK_SHARES): - resp = self.send(cla=0x80, command=0x52, p1=0x1, p2=key_domain, data=[total]) - return resp - - def clear_key_domain(self, key_domain=0): - resp = self.send(cla=0x80, command=0x52, p1=0x4, p2=key_domain) - return resp - - def delete_key_domain(self, key_domain=0): - self.send(cla=0x80, command=0x52, p1=0x3, p2=key_domain, codes=[0x6A88]) - - def get_challenge(self, length): - return self.send(cla=0x80, command=0x84, ne=length) - - def cipher(self, algo, keyid, data): - resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=algo.value, data=data) - return resp - - def hmac(self, hash, keyid, data): - if (hash == hashes.SHA1): - algo = b'\x2A\x86\x48\x86\xF7\x0D\x02\x07' - elif (hash == hashes.SHA224): - algo = b'\x2A\x86\x48\x86\xF7\x0D\x02\x08' - elif (hash == hashes.SHA256): - algo = b'\x2A\x86\x48\x86\xF7\x0D\x02\x09' - elif (hash == hashes.SHA384): - algo = b'\x2A\x86\x48\x86\xF7\x0D\x02\x0A' - elif (hash == hashes.SHA512): - algo = b'\x2A\x86\x48\x86\xF7\x0D\x02\x0B' - else: - raise ValueError("Hash not supported") - data = [0x06, len(algo)] + list(algo) + [0x81, len(data)] + list(data) - resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=0x51, data=data) - return resp - - def cmac(self, keyid, data): - resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=Algorithm.ALGO_AES_CMAC.value, data=data) - return resp - - def hkdf(self, hash, keyid, data, salt, out_len=None): - if (hash == hashes.SHA256): - algo = b'\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x1D' - elif (hash == hashes.SHA384): - algo = b'\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x1E' - elif (hash == hashes.SHA512): - algo = b'\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x1F' - data = [0x06, len(algo)] + list(algo) + [0x81, len(data)] + list(data) + [0x82, len(salt)] + list(salt) - resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=0x51, data=data, ne=out_len) - return resp - - def pbkdf2(self, hash, keyid, salt, iterations, out_len=None): - oid = b'\x2A\x86\x48\x86\xF7\x0D\x01\x05\x0C' - salt = b'\x04' + bytes([len(salt)]) + salt - iteration = b'\x02' + bytes([len(int_to_bytes(iterations))]) + int_to_bytes(iterations) - prf = b'\x30\x0A\x06\x08\x2A\x86\x48\x86\xF7\x0D\x02' - if (hash == hashes.SHA1): - prf += b'\x07' - elif (hash == hashes.SHA224): - prf += b'\x08' - elif (hash == hashes.SHA256): - prf += b'\x09' - elif (hash == hashes.SHA384): - prf += b'\x0A' - elif (hash == hashes.SHA512): - prf += b'\x0B' - data = list(salt + iteration + prf) - data = [0x06, len(oid)] + list(oid) + [0x81, len(data)] + list(data) - resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=0x51, data=data, ne=out_len) - return resp - - def x963(self, hash, keyid, data, out_len=None): - oid = b'\x2B\x81\x05\x10\x86\x48\x3F' - enc = b'\x2A\x86\x48\x86\xF7\x0D\x02' - if (hash == hashes.SHA1): - enc += b'\x07' - elif (hash == hashes.SHA224): - enc += b'\x08' - elif (hash == hashes.SHA256): - enc += b'\x09' - elif (hash == hashes.SHA384): - enc += b'\x0A' - elif (hash == hashes.SHA512): - enc += b'\x0B' - else: - raise ValueError("Hash not supported") - data = [0x06, len(oid)] + list(oid) + [0x81, len(enc)] + list(enc) + [0x83, len(data)] + list(data) - resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=0x51, data=data, ne=out_len) - return resp - - def verify_certificate(self, cert): - chr = CVC().decode(cert).chr() - pukref = ASN1().add_tag(0x83, chr).encode() - _, code = self.send(command=0x22, p1=0x81, p2=0xB6, data=pukref, codes=[0x9000, 0x6A88]) - if (code == 0x9000): - return - - car = CVC().decode(cert).car() - pukref = ASN1().add_tag(0x83, car).encode() - self.send(command=0x22, p1=0x81, p2=0xB6, data=pukref) - - data = ASN1().decode(cert).find(0x7F21).data() - self.send(command=0x2A, p2=0xBE, data=data) - - def register_puk(self, puk, devcert, dicacert, replace=0): - self.verify_certificate(devcert) - self.verify_certificate(dicacert) - - car = CVC().decode(puk).outer_car() - pukref = ASN1().add_tag(0x83, car).encode() - self.send(command=0x22, p1=0x81, p2=0xB6, data=pukref) - - data = ASN1().decode(puk).find(0x67).data() - p1,p2 = 0,0 - if (replace > 0): - p1 = 0x1 - p2 = replace - status = self.send(cla=0x80, command=0x54, p1=p1, p2=p2, data=data) - return status - - def get_puk_status(self): - status = self.send(cla=0x80, command=0x54) - return status - - def enumerate_puk(self): - puk_no = self.get_puk_status()[0] - puks = [] - for i in range(puk_no): - bin, code = self.send(cla=0x80, command=0x54, p1=0x02, p2=i, codes=[0x9000, 0x9001, 0x6A88]) - if (code == 0x6A88): - puks.append({'status': -1}) - else: - puks.append({'status': code & 0x1, 'chr': bin}) - return puks - - def is_puk(self): - _, code = self.send(cla=0x80, command=0x54, p1=0x02, codes=[0x9000, 0x9001, 0x6A88, 0x6A86]) - return code != 0x6A86 - - def check_puk_key(self, chr): - pukref = ASN1().add_tag(0x83, chr).encode() - _, code = self.send(command=0x22, p1=0x81, p2=0xA4, data=pukref, codes=[0x9000, 0x6A88, 0x6985]) - if (code == 0x9000): - return 0 - elif (code == 0x6985): - return 1 - return -1 - - def puk_prepare_signature(self): - challenge = self.send(command=0x84, ne=8) - input = self.device_id + bytes(challenge) - return input - - def authenticate_puk(self, chr, signature): - pukref = ASN1().add_tag(0x83, chr).encode() - self.send(command=0x22, p1=0x81, p2=0xA4, data=pukref) - self.send(command=0x82, data=signature) - - def create_xkek(self, kdm): - dicacert = ASN1().decode(kdm).find(0x30).find(0x61).data() - devcert = ASN1().decode(kdm).find(0x30).find(0x62).data() - gskcert = ASN1().decode(kdm).find(0x30).find(0x63).data() - gsksign = ASN1().decode(kdm).find(0x30).find(0x54).data(return_tag=True) - gskdata = CVC().decode(gskcert).req().data() - self.verify_certificate(devcert) - self.verify_certificate(dicacert) - status = self.send(cla=0x80, command=0x52, p1=0x02, data=gskdata + gsksign) - return status[2:10], status[10:] - - def generate_xkek_key(self, key_domain=0): - key_id = self.key_generation(KeyType.ECC, 'brainpoolP256r1', algorithms=[Algorithm.ALGO_EC_ECDH_XKEK.value], key_domain=key_domain) - return key_id - - def derive_xkek(self, keyid, cert): - self.send(cla=0x80, command=0x62, p1=keyid, p2=Algorithm.ALGO_EC_ECDH_XKEK.value, data=cert) - - def delete_xkek(self, key_domain=0): - self.send(cla=0x80, command=0x52, p1=0x04, p2=key_domain) @pytest.fixture(scope="session") def device(): - dev = Device() + dev = PicoHSM() return dev diff --git a/tests/const.py b/tests/const.py index 8eb4597..382c1f3 100644 --- a/tests/const.py +++ b/tests/const.py @@ -19,13 +19,7 @@ from binascii import unhexlify -DEFAULT_PIN = '648219' -DEFAULT_SOPIN = '57621880' -DEFAULT_RETRIES = 3 DEFAULT_DKEK = [0x1] * 32 -DEFAULT_DKEK_SHARES = 2 - -EF_TERMCA = 0x2f02 TERM_CERT = unhexlify('7F2181E57F4E819E5F290100421045535049434F48534D445630303030317F494F060A04007F00070202020203864104F571E53AA8E75C929D925081CF0F893CB5991D48BD546C1A3F22199F037E4B12D601ACD91C67C88D3C5B3D04C08EC0A372485F7A248E080EE0C6237C1B075E1C5F201045535049434F48534D54525A474E50327F4C0E060904007F0007030102025301005F25060203000300055F24060204000300045F374041BF5E970739135770DBCC5DDA81FFD8B13419A9257D44CAF8404267C644E8F435B43F5E57EB2A8CF4B198045ACD094E0CB34E6217D9C8922CFB9BBEFD4088AD') DICA_CERT = unhexlify('7F2181E97F4E81A25F290100421045535049434F48534D434130303030317F494F060A04007F0007020202020386410421EE4A21C16A10F737F12E78E5091B266612038CDABEBB722B15BF6D41B877FBF64D9AB69C39B9831B1AE00BEF2A4E81976F7688D45189BB232A24703D8A96A55F201045535049434F48534D445630303030317F4C12060904007F000703010202530580000000005F25060202000801085F24060203000601045F37403F75C08FFFC9186B56E6147199E82BFC327CEEF72495BC567961CD54D702F13E3C2766FCD1D11BD6A9D1F4A229B76B248CEB9AF88D59A74D0AB149448705159B') diff --git a/tests/pico-hsm/test_000_info.py b/tests/pico-hsm/test_000_info.py index 9e5cf0a..e1c3ca4 100644 --- a/tests/pico-hsm/test_000_info.py +++ b/tests/pico-hsm/test_000_info.py @@ -18,7 +18,6 @@ """ import pytest -from const import EF_TERMCA def test_select(device): device.select_applet() diff --git a/tests/pico-hsm/test_004_key_domains.py b/tests/pico-hsm/test_004_key_domains.py index c92421a..2957ba4 100644 --- a/tests/pico-hsm/test_004_key_domains.py +++ b/tests/pico-hsm/test_004_key_domains.py @@ -19,8 +19,9 @@ import pytest import hashlib -from const import DEFAULT_DKEK_SHARES, DEFAULT_DKEK -from utils import SWCodes, APDUResponse +from const import DEFAULT_DKEK +from picohsm import APDUResponse, SWCodes +from picohsm.const import DEFAULT_DKEK_SHARES KEY_DOMAINS = 3 TEST_KEY_DOMAIN = 1 @@ -40,17 +41,17 @@ def test_key_domains(device): def test_import_dkek_wrong_key_domain(device): with pytest.raises(APDUResponse) as e: device.import_dkek(DEFAULT_DKEK, key_domain=0) - assert(e.value.sw == SWCodes.SW_COMMAND_NOT_ALLOWED.value) + assert(e.value.sw == SWCodes.SW_COMMAND_NOT_ALLOWED) def test_import_dkek_fail(device): with pytest.raises(APDUResponse) as e: device.import_dkek(DEFAULT_DKEK, key_domain=TEST_KEY_DOMAIN) - assert(e.value.sw == SWCodes.SW_COMMAND_NOT_ALLOWED.value) + assert(e.value.sw == SWCodes.SW_COMMAND_NOT_ALLOWED) def test_set_key_domain_fail(device): with pytest.raises(APDUResponse) as e: device.set_key_domain(key_domain=10) - assert(e.value.sw == SWCodes.SW_INCORRECT_P1P2.value) + assert(e.value.sw == SWCodes.SW_INCORRECT_P1P2) def test_set_key_domain_ok(device): kd = device.get_key_domain(key_domain=TEST_KEY_DOMAIN) @@ -80,7 +81,7 @@ def test_import_dkek_ok(device): def test_clear_key_domain(device): kd = device.get_key_domain(key_domain=0) assert('error' in kd) - assert(kd['error'] == SWCodes.SW_REFERENCE_NOT_FOUND.value) + assert(kd['error'] == SWCodes.SW_REFERENCE_NOT_FOUND) kd = device.get_key_domain(key_domain=TEST_KEY_DOMAIN) assert(kd['dkek']['total'] == DEFAULT_DKEK_SHARES) @@ -95,7 +96,7 @@ def test_delete_key_domain(device): assert(kd['dkek']['total'] == DEFAULT_DKEK_SHARES) with pytest.raises(APDUResponse) as e: device.delete_key_domain(key_domain=0) - assert(e.value.sw == SWCodes.SW_INCORRECT_P1P2.value) + assert(e.value.sw == SWCodes.SW_INCORRECT_P1P2) def test_delete_key_domain(device): assert(device.get_key_domains() == KEY_DOMAINS) diff --git a/tests/pico-hsm/test_005_dkek.py b/tests/pico-hsm/test_005_dkek.py index 0e1d974..4156f28 100644 --- a/tests/pico-hsm/test_005_dkek.py +++ b/tests/pico-hsm/test_005_dkek.py @@ -19,8 +19,8 @@ import pytest import hashlib -from utils import APDUResponse, SWCodes -from const import DEFAULT_PIN, DEFAULT_RETRIES, DEFAULT_DKEK, DEFAULT_DKEK_SHARES +from picohsm.const import DEFAULT_DKEK_SHARES, DEFAULT_PIN, DEFAULT_RETRIES +from const import DEFAULT_DKEK def test_dkek(device): device.initialize(retries=DEFAULT_RETRIES, dkek_shares=DEFAULT_DKEK_SHARES) diff --git a/tests/pico-hsm/test_010_pin.py b/tests/pico-hsm/test_010_pin.py index 7a2d366..a37d782 100644 --- a/tests/pico-hsm/test_010_pin.py +++ b/tests/pico-hsm/test_010_pin.py @@ -18,36 +18,35 @@ """ import pytest -from utils import APDUResponse, SWCodes -from const import DEFAULT_PIN, DEFAULT_RETRIES +from picohsm import APDUResponse, SWCodes +from picohsm.const import DEFAULT_PIN, DEFAULT_RETRIES WRONG_PIN = '112233' -RETRIES = DEFAULT_RETRIES def test_pin_init_retries(device): - device.initialize(retries=RETRIES) + device.initialize(retries=DEFAULT_RETRIES) retries = device.get_login_retries() - assert(retries == RETRIES) + assert(retries == DEFAULT_RETRIES) def test_pin_login(device): - device.initialize(retries=RETRIES) + device.initialize(retries=DEFAULT_RETRIES) device.login(DEFAULT_PIN) def test_pin_retries(device): - device.initialize(retries=RETRIES) + device.initialize(retries=DEFAULT_RETRIES) device.login(DEFAULT_PIN) - for ret in range(RETRIES-1): + for ret in range(DEFAULT_RETRIES-1): with pytest.raises(APDUResponse) as e: device.login(WRONG_PIN) - assert(e.value.sw1 == 0x63 and e.value.sw2 == (0xC0 | (RETRIES-1-ret))) + assert(e.value.sw1 == 0x63 and e.value.sw2 == (0xC0 | (DEFAULT_RETRIES-1-ret))) with pytest.raises(APDUResponse) as e: device.login(WRONG_PIN) - assert(e.value.sw == SWCodes.SW_PIN_BLOCKED.value) + assert(e.value.sw == SWCodes.SW_PIN_BLOCKED) - device.initialize(retries=RETRIES) + device.initialize(retries=DEFAULT_RETRIES) retries = device.get_login_retries() - assert(retries == RETRIES) + assert(retries == DEFAULT_RETRIES) diff --git a/tests/pico-hsm/test_020_keypair_gen.py b/tests/pico-hsm/test_020_keypair_gen.py index 06b46a9..486d10a 100644 --- a/tests/pico-hsm/test_020_keypair_gen.py +++ b/tests/pico-hsm/test_020_keypair_gen.py @@ -18,7 +18,7 @@ """ import pytest -from utils import KeyType, DOPrefixes +from picohsm import KeyType, DOPrefixes def test_gen_initialize(device): device.initialize() @@ -29,11 +29,11 @@ def test_gen_initialize(device): def test_gen_ecc(device, curve): keyid = device.key_generation(KeyType.ECC, curve) resp = device.list_keys() - assert((DOPrefixes.KEY_PREFIX.value, keyid) in resp) - device.delete_file(DOPrefixes.KEY_PREFIX.value, keyid) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value, keyid) + assert((DOPrefixes.KEY_PREFIX, keyid) in resp) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid) resp = device.list_keys() - assert((DOPrefixes.KEY_PREFIX.value, keyid) not in resp) + assert((DOPrefixes.KEY_PREFIX, keyid) not in resp) @pytest.mark.parametrize( "modulus", [1024, 2048, 4096] @@ -41,7 +41,7 @@ def test_gen_ecc(device, curve): def test_gen_rsa(device, modulus): keyid = device.key_generation(KeyType.RSA, modulus) resp = device.list_keys() - assert((DOPrefixes.KEY_PREFIX.value, keyid) in resp) - device.delete_file(DOPrefixes.KEY_PREFIX.value, keyid) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value, keyid) + assert((DOPrefixes.KEY_PREFIX, keyid) in resp) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid) diff --git a/tests/pico-hsm/test_021_key_import.py b/tests/pico-hsm/test_021_key_import.py index e857cab..8b89010 100644 --- a/tests/pico-hsm/test_021_key_import.py +++ b/tests/pico-hsm/test_021_key_import.py @@ -20,9 +20,10 @@ import pytest import hashlib import os -from utils import KeyType, DOPrefixes +from picohsm import DOPrefixes from cryptography.hazmat.primitives.asymmetric import rsa, ec -from const import DEFAULT_RETRIES, DEFAULT_DKEK_SHARES, DEFAULT_DKEK +from picohsm.const import DEFAULT_RETRIES, DEFAULT_DKEK_SHARES +from const import DEFAULT_DKEK def test_prepare_dkek(device): device.initialize(retries=DEFAULT_RETRIES, dkek_shares=DEFAULT_DKEK_SHARES) @@ -42,8 +43,8 @@ def test_import_rsa(device, modulus): keyid = device.import_key(pkey) pubkey = device.public_key(keyid) assert(pubkey.public_numbers() == pkey.public_key().public_numbers()) - device.delete_file(DOPrefixes.KEY_PREFIX.value, keyid) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value, keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid) @pytest.mark.parametrize( @@ -54,8 +55,8 @@ def test_import_ecc(device, curve): keyid = device.import_key(pkey) pubkey = device.public_key(keyid, param=curve().name) assert(pubkey.public_numbers() == pkey.public_key().public_numbers()) - device.delete_file(DOPrefixes.KEY_PREFIX.value, keyid) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value, keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid) @pytest.mark.parametrize( "size", [128, 192, 256] diff --git a/tests/pico-hsm/test_022_key_exchange.py b/tests/pico-hsm/test_022_key_exchange.py index e28298a..ceab9a1 100644 --- a/tests/pico-hsm/test_022_key_exchange.py +++ b/tests/pico-hsm/test_022_key_exchange.py @@ -19,9 +19,10 @@ import pytest import hashlib -from utils import KeyType, DOPrefixes -from cryptography.hazmat.primitives.asymmetric import rsa, ec -from const import DEFAULT_RETRIES, DEFAULT_DKEK_SHARES, DEFAULT_DKEK +from picohsm import DOPrefixes +from cryptography.hazmat.primitives.asymmetric import ec +from picohsm.const import DEFAULT_RETRIES, DEFAULT_DKEK_SHARES +from const import DEFAULT_DKEK def test_prepare_dkek(device): device.initialize(retries=DEFAULT_RETRIES, dkek_shares=DEFAULT_DKEK_SHARES) @@ -48,5 +49,5 @@ def test_exchange_ecc(device, curve): sharedAA = pkeyA.exchange(ec.ECDH(), pbkeyB) assert(bytes(sharedA) == sharedAA) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid) diff --git a/tests/pico-hsm/test_023_key_generation.py b/tests/pico-hsm/test_023_key_generation.py index 327081d..62c4b3a 100644 --- a/tests/pico-hsm/test_023_key_generation.py +++ b/tests/pico-hsm/test_023_key_generation.py @@ -18,7 +18,7 @@ """ import pytest -from utils import KeyType, DOPrefixes +from picohsm import KeyType, DOPrefixes @pytest.mark.parametrize( "size", [128, 192, 256] @@ -26,5 +26,5 @@ from utils import KeyType, DOPrefixes def test_gen_aes(device, size): keyid = device.key_generation(KeyType.AES, size) resp = device.list_keys() - assert((DOPrefixes.KEY_PREFIX.value, keyid) in resp) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + assert((DOPrefixes.KEY_PREFIX, keyid) in resp) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) diff --git a/tests/pico-hsm/test_025_key_export.py b/tests/pico-hsm/test_025_key_export.py index 1082bb1..716ccc8 100644 --- a/tests/pico-hsm/test_025_key_export.py +++ b/tests/pico-hsm/test_025_key_export.py @@ -18,7 +18,7 @@ """ import pytest -from utils import KeyType, DOPrefixes, APDUResponse, SWCodes +from picohsm import KeyType, DOPrefixes, APDUResponse, SWCodes from binascii import hexlify import hashlib from const import DEFAULT_DKEK @@ -38,28 +38,28 @@ keyid_out = -1 def test_key_generation_no_key_domain(device): global keyid_out keyid_out = device.key_generation(KeyType.ECC, 'brainpoolP256r1') - device.put_contents(p1=DOPrefixes.PRKD_PREFIX.value, p2=keyid_out, data=[0xA0]) + device.put_contents(p1=DOPrefixes.PRKD_PREFIX, p2=keyid_out, data=[0xA0]) resp = device.list_keys() - assert((DOPrefixes.KEY_PREFIX.value, keyid_out) in resp) - assert((DOPrefixes.PRKD_PREFIX.value, keyid_out) in resp) + assert((DOPrefixes.KEY_PREFIX, keyid_out) in resp) + assert((DOPrefixes.PRKD_PREFIX, keyid_out) in resp) def test_key_generation_with_key_domain(device): global keyid_in keyid_in = device.key_generation(KeyType.ECC, 'brainpoolP256r1', key_domain=0) - device.put_contents(p1=DOPrefixes.PRKD_PREFIX.value, p2=keyid_in, data=[0xA0]) + device.put_contents(p1=DOPrefixes.PRKD_PREFIX, p2=keyid_in, data=[0xA0]) resp = device.list_keys() - assert((DOPrefixes.KEY_PREFIX.value, keyid_in) in resp) - assert((DOPrefixes.PRKD_PREFIX.value, keyid_in) in resp) + assert((DOPrefixes.KEY_PREFIX, keyid_in) in resp) + assert((DOPrefixes.PRKD_PREFIX, keyid_in) in resp) def test_export_key_out(device): with pytest.raises(APDUResponse) as e: device.export_key(keyid_out) - assert(e.value.sw == SWCodes.SW_REFERENCE_NOT_FOUND.value) + assert(e.value.sw == SWCodes.SW_REFERENCE_NOT_FOUND) def test_export_key_in_fail(device): with pytest.raises(APDUResponse) as e: device.export_key(keyid_in) - assert(e.value.sw == SWCodes.SW_REFERENCE_NOT_FOUND.value) + assert(e.value.sw == SWCodes.SW_REFERENCE_NOT_FOUND) def test_export_import_dkek(device): resp = device.import_dkek(DEFAULT_DKEK, key_domain=0) @@ -79,10 +79,10 @@ def test_export_key_in_ok(device): assert(resCMAC == resp[-16:]) def test_delete_keys_in_out(device): - device.delete_file(DOPrefixes.KEY_PREFIX.value, keyid_in) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value, keyid_in) - device.delete_file(DOPrefixes.KEY_PREFIX.value, keyid_out) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value, keyid_out) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid_in) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid_in) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid_out) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid_out) def test_export_import(device): pkey_gen = ec.generate_private_key(ec.BrainpoolP256R1()) @@ -133,5 +133,5 @@ def test_export_import(device): assert(pkey_gen.private_bytes(serialization.Encoding.DER, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()) == pkey_ex.private_bytes(serialization.Encoding.DER, serialization.PrivateFormat.PKCS8, serialization.NoEncryption())) assert(pkey_gen.public_key().public_bytes(serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint) == pkey_ex.public_key().public_bytes(serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint)) - device.delete_file(DOPrefixes.KEY_PREFIX.value, keyid) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value, keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid) diff --git a/tests/pico-hsm/test_030_signature.py b/tests/pico-hsm/test_030_signature.py index 8c8189b..23fb71f 100644 --- a/tests/pico-hsm/test_030_signature.py +++ b/tests/pico-hsm/test_030_signature.py @@ -18,7 +18,7 @@ """ import pytest -from utils import KeyType, DOPrefixes, Algorithm +from picohsm import KeyType, DOPrefixes, Algorithm from binascii import hexlify import hashlib @@ -39,7 +39,7 @@ def test_signature_ecc(device, curve, scheme): else: datab = data signature = device.sign(keyid=keyid, scheme=scheme, data=datab) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) device.verify(pubkey, datab, signature, scheme) @pytest.mark.parametrize( @@ -52,6 +52,6 @@ def test_signature_rsa(device, modulus, scheme): keyid = device.key_generation(KeyType.RSA, modulus) pubkey = device.public_key(keyid=keyid) signature = device.sign(keyid=keyid, scheme=scheme, data=data) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) device.verify(pubkey, data, signature, scheme) diff --git a/tests/pico-hsm/test_040_decrypt.py b/tests/pico-hsm/test_040_decrypt.py index 3f6a3ac..b098856 100644 --- a/tests/pico-hsm/test_040_decrypt.py +++ b/tests/pico-hsm/test_040_decrypt.py @@ -18,9 +18,8 @@ """ import pytest -from utils import KeyType, DOPrefixes, Algorithm +from picohsm import KeyType, DOPrefixes from binascii import hexlify -import hashlib from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import hashes @@ -43,6 +42,6 @@ def test_decrypt_rsa(device, modulus, pad): message = data[:(modulus//8)-100] ciphered = pubkey.encrypt(message, pad) datab = device.decrypt(keyid, ciphered, pad) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) assert(datab == message) diff --git a/tests/pico-hsm/test_050_cipher.py b/tests/pico-hsm/test_050_cipher.py index 448d978..4b167af 100644 --- a/tests/pico-hsm/test_050_cipher.py +++ b/tests/pico-hsm/test_050_cipher.py @@ -20,8 +20,9 @@ import pytest import os from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from utils import Algorithm, DOPrefixes -from const import DEFAULT_DKEK_SHARES, DEFAULT_DKEK +from picohsm import Algorithm, DOPrefixes +from picohsm.const import DEFAULT_DKEK_SHARES +from const import DEFAULT_DKEK MESSAGE = b'a secret message' @@ -47,6 +48,6 @@ def test_cipher_aes_cipher(device, size): decryptor = cipher.decryptor() plA = decryptor.update(ctA) + decryptor.finalize() plB = device.cipher(Algorithm.ALGO_AES_CBC_DECRYPT, keyid, ctA) - device.delete_file(DOPrefixes.KEY_PREFIX.value, keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) assert(bytes(plB) == plA) assert(bytes(plB) == MESSAGE) diff --git a/tests/pico-hsm/test_060_mac.py b/tests/pico-hsm/test_060_mac.py index 8ba8786..5f73fef 100644 --- a/tests/pico-hsm/test_060_mac.py +++ b/tests/pico-hsm/test_060_mac.py @@ -21,8 +21,9 @@ import pytest import os from cryptography.hazmat.primitives import hashes, hmac, cmac from cryptography.hazmat.primitives.ciphers import algorithms -from utils import Algorithm, DOPrefixes -from const import DEFAULT_DKEK_SHARES, DEFAULT_DKEK +from picohsm import DOPrefixes +from picohsm.const import DEFAULT_DKEK_SHARES +from const import DEFAULT_DKEK MESSAGE = b'a secret message' @@ -44,7 +45,7 @@ def test_mac_hmac(device, size, algo): h = hmac.HMAC(pkey, algo()) h.update(MESSAGE) resB = h.finalize() - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) assert(bytes(resA) == resB) @pytest.mark.parametrize( @@ -57,6 +58,6 @@ def test_mac_cmac(device, size): c = cmac.CMAC(algorithms.AES(pkey)) c.update(MESSAGE) resB = c.finalize() - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) assert(bytes(resA) == resB) diff --git a/tests/pico-hsm/test_070_hkdf.py b/tests/pico-hsm/test_070_hkdf.py index c7af050..8728ef7 100644 --- a/tests/pico-hsm/test_070_hkdf.py +++ b/tests/pico-hsm/test_070_hkdf.py @@ -22,8 +22,9 @@ import os from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.hkdf import HKDF from cryptography import exceptions -from const import DEFAULT_DKEK_SHARES, DEFAULT_DKEK -from utils import DOPrefixes +from picohsm.const import DEFAULT_DKEK_SHARES +from const import DEFAULT_DKEK +from picohsm import DOPrefixes INFO = b'info message' @@ -47,7 +48,7 @@ class TestHKDF: keyid = device.import_key(pkey) salt = os.urandom(16) resA = device.hkdf(algo, keyid, INFO, salt, out_len=out_len) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) hkdf = HKDF( algorithm=algo(), length=out_len, @@ -69,7 +70,7 @@ class TestHKDF: keyid = device.import_key(pkey) salt = os.urandom(16) resA = device.hkdf(algo, keyid, INFO, salt, out_len=out_len) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) hkdf = HKDF( algorithm=algo(), length=out_len, diff --git a/tests/pico-hsm/test_071_pbkdf2.py b/tests/pico-hsm/test_071_pbkdf2.py index 75fe062..c4b26db 100644 --- a/tests/pico-hsm/test_071_pbkdf2.py +++ b/tests/pico-hsm/test_071_pbkdf2.py @@ -22,8 +22,9 @@ import os from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography import exceptions -from const import DEFAULT_DKEK_SHARES, DEFAULT_DKEK -from utils import DOPrefixes +from picohsm.const import DEFAULT_DKEK_SHARES +from const import DEFAULT_DKEK +from picohsm import DOPrefixes INFO = b'info message' @@ -50,7 +51,7 @@ class TestPBKDF2: keyid = device.import_key(pkey) salt = os.urandom(16) resA = device.pbkdf2(algo, keyid, salt, iterations=iterations, out_len=out_len) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) kdf = PBKDF2HMAC( algorithm=algo(), length=out_len, @@ -72,7 +73,7 @@ class TestPBKDF2: keyid = device.import_key(pkey) salt = os.urandom(16) resA = device.pbkdf2(algo, keyid, salt, iterations=iterations, out_len=out_len) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) kdf = PBKDF2HMAC( algorithm=algo(), diff --git a/tests/pico-hsm/test_072_x963.py b/tests/pico-hsm/test_072_x963.py index f3ccf69..1707998 100644 --- a/tests/pico-hsm/test_072_x963.py +++ b/tests/pico-hsm/test_072_x963.py @@ -22,8 +22,9 @@ import os from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF from cryptography import exceptions -from const import DEFAULT_DKEK_SHARES, DEFAULT_DKEK -from utils import DOPrefixes +from picohsm.const import DEFAULT_DKEK_SHARES +from const import DEFAULT_DKEK +from picohsm import DOPrefixes INFO = b'shared message' @@ -46,7 +47,7 @@ class TestX963: pkey = os.urandom(size // 8) keyid = device.import_key(pkey) resA = device.x963(algo, keyid, INFO, out_len=out_len) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) xkdf = X963KDF( algorithm=algo(), length=out_len, @@ -65,7 +66,7 @@ class TestX963: pkey = os.urandom(size // 8) keyid = device.import_key(pkey) resA = device.x963(algo, keyid, INFO, out_len=out_len) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) xkdf = X963KDF( algorithm=algo(), length=out_len, diff --git a/tests/pico-hsm/test_080_pka.py b/tests/pico-hsm/test_080_pka.py index 7fe91ec..e09a1f1 100644 --- a/tests/pico-hsm/test_080_pka.py +++ b/tests/pico-hsm/test_080_pka.py @@ -18,16 +18,13 @@ """ import pytest -import os from binascii import unhexlify, hexlify from cvc.certificates import CVC -from cvc.asn1 import ASN1 -from utils import int_to_bytes -from utils import APDUResponse, SWCodes +from picohsm.utils import int_to_bytes +from picohsm import APDUResponse, SWCodes from const import TERM_CERT, DICA_CERT from cryptography.hazmat.primitives.asymmetric import ec, utils from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat AUT_KEY = unhexlify('0A40E11E672C28C558B72C25D93BCF28C08D39AFDD5A1A2FD3BAF7A6B27F0C2E') aut_pk = ec.derive_private_key(int.from_bytes(AUT_KEY, 'big'), ec.BrainpoolP256R1()) @@ -87,7 +84,7 @@ def test_authentication_fail(device): signature = list(int_to_bytes(r) + int_to_bytes(s)) with pytest.raises(APDUResponse) as e: device.authenticate_puk(term_chr, signature) - assert(e.value.sw == SWCodes.SW_CONDITIONS_NOT_SATISFIED.value) + assert(e.value.sw == SWCodes.SW_CONDITIONS_NOT_SATISFIED) status = device.get_puk_status() assert(status == [1,0,1,0]) @@ -146,4 +143,4 @@ def test_register_puk_with_no_puk(device): device.initialize() with pytest.raises(APDUResponse) as e: device.register_puk(AUT_PUK, TERM_CERT, DICA_CERT) - assert(e.value.sw == SWCodes.SW_FILE_NOT_FOUND.value) + assert(e.value.sw == SWCodes.SW_FILE_NOT_FOUND) diff --git a/tests/pico-hsm/test_090_xkek.py b/tests/pico-hsm/test_090_xkek.py index d9c1f21..e58f0cf 100644 --- a/tests/pico-hsm/test_090_xkek.py +++ b/tests/pico-hsm/test_090_xkek.py @@ -19,14 +19,13 @@ import pytest from binascii import unhexlify, hexlify -from utils import APDUResponse, SWCodes -from utils import int_to_bytes +from picohsm.utils import int_to_bytes from const import TERM_CERT, DICA_CERT from cvc.asn1 import ASN1 from cvc.certificates import CVC from cvc import oid from cryptography.hazmat.primitives.asymmetric import ec -from utils import DOPrefixes +from picohsm import DOPrefixes, APDUResponse, SWCodes KDM = unhexlify(b'30820420060b2b0601040181c31f0402016181ed7f2181e97f4e81a25f290100421045535049434f48534d434130303030317f494f060a04007f0007020202020386410421ee4a21c16a10f737f12e78e5091b266612038cdabebb722b15bf6d41b877fbf64d9ab69c39b9831b1ae00bef2a4e81976f7688d45189bb232a24703d8a96a55f201045535049434f48534d445630303030317f4c12060904007f000703010202530580000000005f25060202000801085f24060203000601045f37403f75c08fffc9186b56e6147199e82bfc327ceef72495bc567961cd54d702f13e3c2766fcd1d11bd6a9d1f4a229b76b248ceb9af88d59a74d0ab149448705159b6281e97f2181e57f4e819e5f290100421045535049434f48534d445630303030317f494f060a04007f00070202020203864104c8561b41e54fea81bb80dd4a6d537e7c3904344e8ca90bc5f668111811e02c8d5d51ca93ca89558f2a8a9cbb147434e3441ec174505ff980fd7a7106286196915f201045535049434f48534d54524a444736387f4c0e060904007f0007030102025301005f25060203000300065f24060204000300055f3740983de63d0975b715ebd8a93cb38fa9638882c8b7064d51a6facabed693b92edc098e458b713203413ef6de0958c44772cbdbc264205c7b1bdb8b4fcb2516437f638201f1678201ed7f218201937f4e82014b5f290100421045535049434f48534d54524a444736387f4982011d060a04007f000702020202038120a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e537782207d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9832026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b68441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f0469978520a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a78641048b1f450912a2e4d428b7eefc5fa05618a9ef295e90009a61cbb0970181b333474ea94f94cde5a11aba0589e85d4225002789ff1cdcf25756f059647b49fc2a158701015f201045535049434f48534d54524a444736385f3740372407c20de7257c89dae1e6606c8a046ca65efaa010c0a22b75c402ee243de51f5f1507457193679ed9db4fbbfe8efb9d695b684492b665ad8ba98c1f84ea38421045535049434f48534d54524a444736385f374098718e2e14a44386b689b71a101530316b65ab49a91bab0dd56099c5161ecb8aadff6cf27449f94034e58b7306f01e6ffa2766a2f5bb1281e12e5f1f9174733454400cf8926ca5bec9a91bcd47bf391c15d94ef6e3243d5fd1fffeaafd586766bc3221eafd808f17f8450f238cc1fe7ab1854443db31d622f53a2b3fdb3ad750d5ce') @@ -37,7 +36,7 @@ def test_initialize(device): def test_create_xkek(device): with pytest.raises(APDUResponse) as e: device.create_xkek(KDM) - assert(e.value.sw == SWCodes.SW_CONDITIONS_NOT_SATISFIED.value) + assert(e.value.sw == SWCodes.SW_CONDITIONS_NOT_SATISFIED) device.login() kcv, did = device.create_xkek(KDM) @@ -54,7 +53,7 @@ def test_derive_xkek(device): keyid = device.generate_xkek_key() resp = device.list_keys() - assert((DOPrefixes.KEY_PREFIX.value, keyid) in resp) + assert((DOPrefixes.KEY_PREFIX, keyid) in resp) xkek_dom = device.get_key_domain()['xkek'] pkey = ec.generate_private_key(ec.BrainpoolP256R1()) @@ -83,10 +82,10 @@ def test_delete_xkek(device): def test_delete_domain_with_key(device): with pytest.raises(APDUResponse) as e: device.delete_key_domain() - assert(e.value.sw == SWCodes.SW_FILE_EXISTS.value) + assert(e.value.sw == SWCodes.SW_FILE_EXISTS) - device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) - device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value << 8 | keyid) + device.delete_file(DOPrefixes.KEY_PREFIX, keyid) + device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX, keyid) def test_delete_domain(device): device.delete_key_domain() @@ -95,5 +94,5 @@ def test_delete_domain(device): assert('kcv' not in resp) assert('xkek' not in resp) assert('error' in resp) - assert(resp['error'] == SWCodes.SW_REFERENCE_NOT_FOUND.value) + assert(resp['error'] == SWCodes.SW_REFERENCE_NOT_FOUND) diff --git a/tests/utils.py b/tests/utils.py deleted file mode 100644 index ea992ef..0000000 --- a/tests/utils.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -/* - * This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm). - * Copyright (c) 2022 Pol Henarejos. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -""" - -from enum import Enum - -class SWCodes(Enum): - SW_BYTES_REMAINING_00 = 0x6100 - SW_WARNING_STATE_UNCHANGED = 0x6200 - SW_WARNING_CORRUPTED = 0x6281 - SW_WARNING_EOF = 0x6282 - SW_WARNING_EF_DEACTIVATED = 0x6283 - SW_WARNING_WRONG_FCI = 0x6284 - SW_WARNING_EF_TERMINATED = 0x6285 - - SW_WARNING_NOINFO = 0x6300 - SW_WARNING_FILLUP = 0x6381 - - SW_EXEC_ERROR = 0x6400 - - SW_SECURE_MESSAGE_EXEC_ERROR = 0x6600 - - SW_WRONG_LENGTH = 0x6700 - - SW_LOGICAL_CHANNEL_NOT_SUPPORTED = 0x6881 - SW_SECURE_MESSAGING_NOT_SUPPORTED = 0x6882 - - SW_COMMAND_INCOMPATIBLE = 0x6981 - SW_SECURITY_STATUS_NOT_SATISFIED = 0x6982 - SW_PIN_BLOCKED = 0x6983 - SW_DATA_INVALID = 0x6984 - SW_CONDITIONS_NOT_SATISFIED = 0x6985 - SW_COMMAND_NOT_ALLOWED = 0x6986 - SW_SECURE_MESSAGING_MISSING_DO = 0x6987 - SW_SECURE_MESSAGING_INCORRECT_DO = 0x6988 - SW_APPLET_SELECT_FAILED = 0x6999 - - SW_INCORRECT_PARAMS = 0x6A80 - SW_FUNC_NOT_SUPPORTED = 0x6A81 - SW_FILE_NOT_FOUND = 0x6A82 - SW_RECORD_NOT_FOUND = 0x6A83 - SW_FILE_FULL = 0x6A84 - SW_WRONG_NE = 0x6A85 - SW_INCORRECT_P1P2 = 0x6A86 - SW_WRONG_NC = 0x6A87 - SW_REFERENCE_NOT_FOUND = 0x6A88 - SW_FILE_EXISTS = 0x6A89 - - SW_WRONG_P1P2 = 0x6B00 - - SW_CORRECT_LENGTH_00 = 0x6C00 - - SW_INS_NOT_SUPPORTED = 0x6D00 - - SW_CLA_NOT_SUPPORTED = 0x6E00 - - SW_UNKNOWN = 0x6F00 - - SW_OK = 0x900 - -class APDUResponse(Exception): - def __init__(self, sw1, sw2): - self.sw1 = sw1 - self.sw2 = sw2 - self.sw = sw1 << 8 | sw2 - super().__init__(f'SW:{sw1:02X}{sw2:02X}') - -class DOPrefixes(Enum): - PRKD_PREFIX = 0xC4 - CD_PREFIX = 0xC8 - DCOD_PREFIX = 0xC9 - CA_CERTIFICATE_PREFIX = 0xCA - KEY_PREFIX = 0xCC - PROT_DATA_PREFIX = 0xCD - EE_CERTIFICATE_PREFIX = 0xCE - DATA_PREFIX = 0xCF - -class KeyType(Enum): - RSA = 1 - ECC = 2 - AES = 3 - -class Algorithm(Enum): - ALGO_AES_CBC_ENCRYPT = 0x10 - ALGO_AES_CBC_DECRYPT = 0x11 - ALGO_AES_CMAC = 0x18 - ALGO_EXT_CIPHER_ENCRYPT = 0x51 - ALGO_EXT_CIPHER_DECRYPT = 0x52 - ALGO_AES_DERIVE = 0x99 - - ALGO_EC_RAW = 0x70 - ALGO_EC_SHA1 = 0x71 - ALGO_EC_SHA224 = 0x72 - ALGO_EC_SHA256 = 0x73 - ALGO_EC_SHA384 = 0x74 - ALGO_EC_SHA512 = 0x75 - ALGO_EC_ECDH = 0x80 - ALGO_EC_ECDH_XKEK = 0x84 - ALGO_EC_DERIVE = 0x98 - - ALGO_RSA_RAW = 0x20 - ALGO_RSA_DECRYPT = 0x21 - ALGO_RSA_DECRYPT_PKCS1 = 0x22 - ALGO_RSA_DECRYPT_OEP = 0x23 - ALGO_RSA_PKCS1 = 0x30 - ALGO_RSA_PKCS1_SHA1 = 0x31 - ALGO_RSA_PKCS1_SHA224 = 0x32 - ALGO_RSA_PKCS1_SHA256 = 0x33 - ALGO_RSA_PKCS1_SHA384 = 0x34 - ALGO_RSA_PKCS1_SHA512 = 0x35 - ALGO_RSA_PSS = 0x40 - ALGO_RSA_PSS_SHA1 = 0x41 - ALGO_RSA_PSS_SHA224 = 0x42 - ALGO_RSA_PSS_SHA256 = 0x43 - ALGO_RSA_PSS_SHA384 = 0x44 - ALGO_RSA_PSS_SHA512 = 0x45 - - - -class Padding(Enum): - RAW = 0x21 - PKCS = 0x22 - OAEP = 0x23 - -def int_to_bytes(x, length=None, byteorder='big'): - return x.to_bytes(length or (x.bit_length() + 7) // 8, byteorder=byteorder)