From 215fdca9f80e9c31a63f24856975cf8a346bec32 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 15 Feb 2023 19:47:16 +0100 Subject: [PATCH] Added AES cipher tests. Signed-off-by: Pol Henarejos --- tests/conftest.py | 63 ++++++++++++++++------- tests/pico-hsm/test_020_keypair_gen.py | 9 ++-- tests/pico-hsm/test_021_key_import.py | 8 +++ tests/pico-hsm/test_023_key_generation.py | 30 +++++++++++ tests/pico-hsm/test_030_signature.py | 4 +- tests/pico-hsm/test_040_decrypt.py | 2 +- tests/pico-hsm/test_050_cipher.py | 52 +++++++++++++++++++ tests/utils.py | 9 ++++ 8 files changed, 152 insertions(+), 25 deletions(-) create mode 100644 tests/pico-hsm/test_023_key_generation.py create mode 100644 tests/pico-hsm/test_050_cipher.py diff --git a/tests/conftest.py b/tests/conftest.py index 1bcaa59..ac4f743 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -186,24 +186,38 @@ class Device: return kids return [(resp[i],resp[i+1]) for i in range(0, len(resp), 2)] - def keypair_generation(self, type, param): - 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('Wrong elliptic curve name') + def key_generation(self, type, param): + 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 = [dom.P, dom.A, dom.B, dom.G, dom.O, None, dom.F] - a.add_object(0x7f49, oid.ID_TA_ECDSA_SHA_256, pubctx) - a.add_tag(0x5f20, 'UTCDUMMY00001'.encode()) - data = a.encode() + dom = ec_domain(Device.EcDummy(param)) + pubctx = [dom.P, dom.A, dom.B, dom.G, dom.O, None, 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)) + keyid = self.get_first_free_id() + self.send(command=0x46, p1=keyid, data=list(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, fid): @@ -293,20 +307,26 @@ class Device: 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 - pubnum = pkey.public_key().public_numbers() 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 - data += b'\x00'*6 + if (isinstance(pkey, bytes)): + data += b'\x00\x04\x10\x11\x18\x99' + 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) @@ -332,6 +352,9 @@ class Device: 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): @@ -403,6 +426,10 @@ class Device: 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 + @pytest.fixture(scope="session") def device(): diff --git a/tests/pico-hsm/test_020_keypair_gen.py b/tests/pico-hsm/test_020_keypair_gen.py index abff9d1..be0775a 100644 --- a/tests/pico-hsm/test_020_keypair_gen.py +++ b/tests/pico-hsm/test_020_keypair_gen.py @@ -20,12 +20,14 @@ import pytest from utils import KeyType, DOPrefixes +def test_gen_initialize(device): + device.initialize() + @pytest.mark.parametrize( "curve", ['secp192r1', 'secp256r1', 'secp384r1', 'secp521r1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', 'secp192k1', 'secp256k1'] ) def test_gen_ecc(device, curve): - device.initialize(retries=3) - keyid = device.keypair_generation(KeyType.ECC, 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 << 8 | keyid) @@ -35,8 +37,7 @@ def test_gen_ecc(device, curve): "modulus", [1024, 2048, 4096] ) def test_gen_rsa(device, modulus): - device.initialize(retries=3) - keyid = device.keypair_generation(KeyType.RSA, 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 << 8 | keyid) diff --git a/tests/pico-hsm/test_021_key_import.py b/tests/pico-hsm/test_021_key_import.py index 5b7a341..b0e0b7f 100644 --- a/tests/pico-hsm/test_021_key_import.py +++ b/tests/pico-hsm/test_021_key_import.py @@ -19,6 +19,7 @@ import pytest import hashlib +import os from utils import KeyType, DOPrefixes from cryptography.hazmat.primitives.asymmetric import rsa, ec from const import DEFAULT_RETRIES, DEFAULT_DKEK_SHARES, DEFAULT_DKEK @@ -55,3 +56,10 @@ def test_import_ecc(device, curve): assert(pubkey.public_numbers() == pkey.public_key().public_numbers()) device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) device.delete_file(DOPrefixes.EE_CERTIFICATE_PREFIX.value << 8 | keyid) + +@pytest.mark.parametrize( + "size", [128, 192, 256] +) +def test_import_aes(device, size): + pkey = os.urandom(size // 8) + keyid = device.import_key(pkey) diff --git a/tests/pico-hsm/test_023_key_generation.py b/tests/pico-hsm/test_023_key_generation.py new file mode 100644 index 0000000..327081d --- /dev/null +++ b/tests/pico-hsm/test_023_key_generation.py @@ -0,0 +1,30 @@ +""" +/* + * 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 . + */ +""" + +import pytest +from utils import KeyType, DOPrefixes + +@pytest.mark.parametrize( + "size", [128, 192, 256] +) +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) diff --git a/tests/pico-hsm/test_030_signature.py b/tests/pico-hsm/test_030_signature.py index 887d239..8c8189b 100644 --- a/tests/pico-hsm/test_030_signature.py +++ b/tests/pico-hsm/test_030_signature.py @@ -32,7 +32,7 @@ data = b'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam neque u "scheme", [Algorithm.ALGO_EC_RAW, Algorithm.ALGO_EC_SHA1, Algorithm.ALGO_EC_SHA224, Algorithm.ALGO_EC_SHA256, Algorithm.ALGO_EC_SHA384, Algorithm.ALGO_EC_SHA512] ) def test_signature_ecc(device, curve, scheme): - keyid = device.keypair_generation(KeyType.ECC, curve) + keyid = device.key_generation(KeyType.ECC, curve) pubkey = device.public_key(keyid=keyid, param=curve) if (scheme == Algorithm.ALGO_EC_RAW): datab = hashlib.sha512(data).digest() @@ -49,7 +49,7 @@ def test_signature_ecc(device, curve, scheme): "scheme", [Algorithm.ALGO_RSA_PKCS1_SHA1, Algorithm.ALGO_RSA_PKCS1_SHA224, Algorithm.ALGO_RSA_PKCS1_SHA256, Algorithm.ALGO_RSA_PKCS1_SHA384, Algorithm.ALGO_RSA_PKCS1_SHA512, Algorithm.ALGO_RSA_PSS_SHA1, Algorithm.ALGO_RSA_PSS_SHA224, Algorithm.ALGO_RSA_PSS_SHA256, Algorithm.ALGO_RSA_PSS_SHA384, Algorithm.ALGO_RSA_PSS_SHA512] ) def test_signature_rsa(device, modulus, scheme): - keyid = device.keypair_generation(KeyType.RSA, modulus) + 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) diff --git a/tests/pico-hsm/test_040_decrypt.py b/tests/pico-hsm/test_040_decrypt.py index 8890d2e..3f6a3ac 100644 --- a/tests/pico-hsm/test_040_decrypt.py +++ b/tests/pico-hsm/test_040_decrypt.py @@ -38,7 +38,7 @@ data = b'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam neque u ) def test_decrypt_rsa(device, modulus, pad): - keyid = device.keypair_generation(KeyType.RSA, modulus) + keyid = device.key_generation(KeyType.RSA, modulus) pubkey = device.public_key(keyid=keyid) message = data[:(modulus//8)-100] ciphered = pubkey.encrypt(message, pad) diff --git a/tests/pico-hsm/test_050_cipher.py b/tests/pico-hsm/test_050_cipher.py new file mode 100644 index 0000000..24a2f2a --- /dev/null +++ b/tests/pico-hsm/test_050_cipher.py @@ -0,0 +1,52 @@ +""" +/* + * 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 . + */ +""" + +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 + +MESSAGE = b'a secret message' + +def test_prepare_aes(device): + device.initialize(dkek_shares=DEFAULT_DKEK_SHARES) + resp = device.import_dkek(DEFAULT_DKEK) + resp = device.import_dkek(DEFAULT_DKEK) + +@pytest.mark.parametrize( + "size", [128, 192, 256] +) +def test_cipher_aes_cipher(device, size): + pkey = os.urandom(size // 8) + iv = b'\x00'*16 + keyid = device.import_key(pkey) + + cipher = Cipher(algorithms.AES(pkey), modes.CBC(iv)) + encryptor = cipher.encryptor() + ctA = encryptor.update(MESSAGE) + encryptor.finalize() + ctB = device.cipher(Algorithm.ALGO_AES_CBC_ENCRYPT, keyid, MESSAGE) + assert(bytes(ctB) == ctA) + + 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 << 8 | keyid) + assert(bytes(plB) == plA) + assert(bytes(plB) == MESSAGE) diff --git a/tests/utils.py b/tests/utils.py index d59505c..25510ff 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -93,8 +93,16 @@ class DOPrefixes(Enum): 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 @@ -102,6 +110,7 @@ class Algorithm(Enum): ALGO_EC_SHA384 = 0x74 ALGO_EC_SHA512 = 0x75 ALGO_EC_DH = 0x80 + ALGO_EC_DERIVE = 0x98 ALGO_RSA_RAW = 0x20 ALGO_RSA_DECRYPT = 0x21