Added AES cipher tests.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos 2023-02-15 19:47:16 +01:00
parent 9279773073
commit 215fdca9f8
No known key found for this signature in database
GPG key ID: C0095B7870A4CCD3
8 changed files with 152 additions and 25 deletions

View file

@ -186,24 +186,38 @@ class Device:
return kids return kids
return [(resp[i],resp[i+1]) for i in range(0, len(resp), 2)] return [(resp[i],resp[i+1]) for i in range(0, len(resp), 2)]
def keypair_generation(self, type, param): def key_generation(self, type, param):
a = ASN1().add_tag(0x5f29, bytes([0])).add_tag(0x42, 'UTCA00001'.encode()) if (type in [KeyType.RSA, KeyType.ECC]):
if (type == KeyType.RSA): a = ASN1().add_tag(0x5f29, bytes([0])).add_tag(0x42, 'UTCA00001'.encode())
if (not 1024 <= param <= 4096): if (type == KeyType.RSA):
raise ValueError('RSA bits must be in the range [1024,4096]') if (not 1024 <= param <= 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()) raise ValueError('RSA bits must be in the range [1024,4096]')
elif (type == KeyType.ECC): a.add_tag(0x7f49, ASN1().add_oid(oid.ID_TA_RSA_V1_5_SHA_256).add_tag(0x2, param.to_bytes(2, 'big')).encode())
if (param not in ('secp192r1', 'secp256r1', 'secp384r1', 'secp521r1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', 'secp192k1', 'secp256k1')): elif (type == KeyType.ECC):
raise ValueError('Wrong elliptic curve name') 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)) dom = ec_domain(Device.EcDummy(param))
pubctx = [dom.P, dom.A, dom.B, dom.G, dom.O, None, dom.F] 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_object(0x7f49, oid.ID_TA_ECDSA_SHA_256, pubctx)
a.add_tag(0x5f20, 'UTCDUMMY00001'.encode()) a.add_tag(0x5f20, 'UTCDUMMY00001'.encode())
data = a.encode() data = a.encode()
keyid = self.get_first_free_id() keyid = self.get_first_free_id()
self.send(command=0x46, p1=keyid, data=list(data)) 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 return keyid
def delete_file(self, fid): 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() 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() kmac = hashlib.sha256((dkek or b'\x00'*32) + b'\x00\x00\x00\x02').digest()
data += kcv data += kcv
pubnum = pkey.public_key().public_numbers()
if (isinstance(pkey, rsa.RSAPrivateKey)): if (isinstance(pkey, rsa.RSAPrivateKey)):
data += b'\x05' data += b'\x05'
algo = b'\x00\x0A\x04\x00\x7F\x00\x07\x02\x02\x02\x01\x02' algo = b'\x00\x0A\x04\x00\x7F\x00\x07\x02\x02\x02\x01\x02'
elif (isinstance(pkey, ec.EllipticCurvePrivateKey)): elif (isinstance(pkey, ec.EllipticCurvePrivateKey)):
data += b'\x0C' data += b'\x0C'
algo = b'\x00\x0A\x04\x00\x7F\x00\x07\x02\x02\x02\x02\x03' 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 += 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) kb = os.urandom(8)
if (isinstance(pkey, rsa.RSAPrivateKey)): if (isinstance(pkey, rsa.RSAPrivateKey)):
kb += int_to_bytes(pkey.key_size, length=2) kb += int_to_bytes(pkey.key_size, length=2)
pubnum = pkey.public_key().public_numbers()
pnum = pkey.private_numbers() pnum = pkey.private_numbers()
kb += int_to_bytes((pnum.d.bit_length()+7)//8, length=2) kb += int_to_bytes((pnum.d.bit_length()+7)//8, length=2)
kb += int_to_bytes(pnum.d) kb += int_to_bytes(pnum.d)
@ -332,6 +352,9 @@ class Device:
p = pkey.public_key().public_bytes(Encoding.X962, PublicFormat.UncompressedPoint) p = pkey.public_key().public_bytes(Encoding.X962, PublicFormat.UncompressedPoint)
kb += int_to_bytes(len(p), length=2) kb += int_to_bytes(len(p), length=2)
kb += p kb += p
elif (isinstance(pkey, bytes)):
kb += int_to_bytes(len(pkey), length=2)
kb += pkey
kb_len_pad = (len(kb)//16)*16 kb_len_pad = (len(kb)//16)*16
if (len(kb) % 16 > 0): if (len(kb) % 16 > 0):
@ -403,6 +426,10 @@ class Device:
def get_challenge(self, length): def get_challenge(self, length):
return self.send(cla=0x80, command=0x84, ne=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") @pytest.fixture(scope="session")
def device(): def device():

View file

@ -20,12 +20,14 @@
import pytest import pytest
from utils import KeyType, DOPrefixes from utils import KeyType, DOPrefixes
def test_gen_initialize(device):
device.initialize()
@pytest.mark.parametrize( @pytest.mark.parametrize(
"curve", ['secp192r1', 'secp256r1', 'secp384r1', 'secp521r1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', 'secp192k1', 'secp256k1'] "curve", ['secp192r1', 'secp256r1', 'secp384r1', 'secp521r1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', 'secp192k1', 'secp256k1']
) )
def test_gen_ecc(device, curve): def test_gen_ecc(device, curve):
device.initialize(retries=3) keyid = device.key_generation(KeyType.ECC, curve)
keyid = device.keypair_generation(KeyType.ECC, curve)
resp = device.list_keys() resp = device.list_keys()
assert((DOPrefixes.KEY_PREFIX.value, keyid) in resp) assert((DOPrefixes.KEY_PREFIX.value, keyid) in resp)
device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid)
@ -35,8 +37,7 @@ def test_gen_ecc(device, curve):
"modulus", [1024, 2048, 4096] "modulus", [1024, 2048, 4096]
) )
def test_gen_rsa(device, modulus): def test_gen_rsa(device, modulus):
device.initialize(retries=3) keyid = device.key_generation(KeyType.RSA, modulus)
keyid = device.keypair_generation(KeyType.RSA, modulus)
resp = device.list_keys() resp = device.list_keys()
assert((DOPrefixes.KEY_PREFIX.value, keyid) in resp) assert((DOPrefixes.KEY_PREFIX.value, keyid) in resp)
device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid)

View file

@ -19,6 +19,7 @@
import pytest import pytest
import hashlib import hashlib
import os
from utils import KeyType, DOPrefixes from utils import KeyType, DOPrefixes
from cryptography.hazmat.primitives.asymmetric import rsa, ec from cryptography.hazmat.primitives.asymmetric import rsa, ec
from const import DEFAULT_RETRIES, DEFAULT_DKEK_SHARES, DEFAULT_DKEK 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()) assert(pubkey.public_numbers() == pkey.public_key().public_numbers())
device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid)
device.delete_file(DOPrefixes.EE_CERTIFICATE_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)

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
"""
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)

View file

@ -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] "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): 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) pubkey = device.public_key(keyid=keyid, param=curve)
if (scheme == Algorithm.ALGO_EC_RAW): if (scheme == Algorithm.ALGO_EC_RAW):
datab = hashlib.sha512(data).digest() 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] "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): 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) pubkey = device.public_key(keyid=keyid)
signature = device.sign(keyid=keyid, scheme=scheme, data=data) signature = device.sign(keyid=keyid, scheme=scheme, data=data)
device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid) device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid)

View file

@ -38,7 +38,7 @@ data = b'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam neque u
) )
def test_decrypt_rsa(device, modulus, pad): 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) pubkey = device.public_key(keyid=keyid)
message = data[:(modulus//8)-100] message = data[:(modulus//8)-100]
ciphered = pubkey.encrypt(message, pad) ciphered = pubkey.encrypt(message, pad)

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
"""
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)

View file

@ -93,8 +93,16 @@ class DOPrefixes(Enum):
class KeyType(Enum): class KeyType(Enum):
RSA = 1 RSA = 1
ECC = 2 ECC = 2
AES = 3
class Algorithm(Enum): 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_RAW = 0x70
ALGO_EC_SHA1 = 0x71 ALGO_EC_SHA1 = 0x71
ALGO_EC_SHA224 = 0x72 ALGO_EC_SHA224 = 0x72
@ -102,6 +110,7 @@ class Algorithm(Enum):
ALGO_EC_SHA384 = 0x74 ALGO_EC_SHA384 = 0x74
ALGO_EC_SHA512 = 0x75 ALGO_EC_SHA512 = 0x75
ALGO_EC_DH = 0x80 ALGO_EC_DH = 0x80
ALGO_EC_DERIVE = 0x98
ALGO_RSA_RAW = 0x20 ALGO_RSA_RAW = 0x20
ALGO_RSA_DECRYPT = 0x21 ALGO_RSA_DECRYPT = 0x21