diff --git a/tests/conftest.py b/tests/conftest.py index 38e872b..120a767 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -443,6 +443,8 @@ class Device: 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 @@ -451,6 +453,55 @@ class Device: 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 @pytest.fixture(scope="session") def device(): diff --git a/tests/pico-hsm/test_060_mac.py b/tests/pico-hsm/test_060_mac.py index e4a75f7..8ba8786 100644 --- a/tests/pico-hsm/test_060_mac.py +++ b/tests/pico-hsm/test_060_mac.py @@ -44,6 +44,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) assert(bytes(resA) == resB) @pytest.mark.parametrize( @@ -56,5 +57,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) assert(bytes(resA) == resB) diff --git a/tests/pico-hsm/test_070_hkdf.py b/tests/pico-hsm/test_070_hkdf.py new file mode 100644 index 0000000..c7af050 --- /dev/null +++ b/tests/pico-hsm/test_070_hkdf.py @@ -0,0 +1,81 @@ +""" +/* + * 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 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 + +INFO = b'info message' + +def test_prepare_kd(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] +) +@pytest.mark.parametrize( + "algo", [hashes.SHA256, hashes.SHA384, hashes.SHA512] +) +@pytest.mark.parametrize( + "out_len", [32, 64, 256, 1024] +) +class TestHKDF: + def test_hkdf_ok(self, device, size, algo, out_len): + pkey = os.urandom(size // 8) + 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) + hkdf = HKDF( + algorithm=algo(), + length=out_len, + salt=salt, + info=INFO, + ) + resB = hkdf.derive(pkey) + assert(bytes(resA) == resB) + hkdf = HKDF( + algorithm=algo(), + length=out_len, + salt=salt, + info=INFO, + ) + hkdf.verify(pkey, bytes(resA)) + + def test_hkdf_fail(self, device, size, algo, out_len): + pkey = os.urandom(size // 8) + 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) + hkdf = HKDF( + algorithm=algo(), + length=out_len, + salt=salt, + info=INFO, + ) + pkey = os.urandom(size // 8) + with pytest.raises(exceptions.InvalidKey): + hkdf.verify(pkey, bytes(resA)) diff --git a/tests/pico-hsm/test_071_pbkdf2.py b/tests/pico-hsm/test_071_pbkdf2.py new file mode 100644 index 0000000..75fe062 --- /dev/null +++ b/tests/pico-hsm/test_071_pbkdf2.py @@ -0,0 +1,85 @@ +""" +/* + * 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 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 + +INFO = b'info message' + +def test_prepare_kd(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] +) +@pytest.mark.parametrize( + "algo", [hashes.SHA1, hashes.SHA224, hashes.SHA256, hashes.SHA384, hashes.SHA512] +) +@pytest.mark.parametrize( + "out_len", [32, 64, 256, 1024] +) +@pytest.mark.parametrize( + "iterations", [1024, 2048] +) +class TestPBKDF2: + def test_pbkdf2_ok(self, device, size, algo, out_len, iterations): + pkey = os.urandom(size // 8) + 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) + kdf = PBKDF2HMAC( + algorithm=algo(), + length=out_len, + salt=salt, + iterations=iterations, + ) + resB = kdf.derive(pkey) + assert(bytes(resA) == resB) + kdf = PBKDF2HMAC( + algorithm=algo(), + length=out_len, + salt=salt, + iterations=iterations, + ) + kdf.verify(pkey, bytes(resA)) + + def test_pbkdf2_fail(self, device, size, algo, out_len, iterations): + pkey = os.urandom(size // 8) + 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) + + kdf = PBKDF2HMAC( + algorithm=algo(), + length=out_len, + salt=salt, + iterations=iterations, + ) + pkey = os.urandom(size // 8) + with pytest.raises(exceptions.InvalidKey): + kdf.verify(pkey, bytes(resA)) diff --git a/tests/pico-hsm/test_072_x963.py b/tests/pico-hsm/test_072_x963.py new file mode 100644 index 0000000..f3ccf69 --- /dev/null +++ b/tests/pico-hsm/test_072_x963.py @@ -0,0 +1,76 @@ +""" +/* + * 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 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 + +INFO = b'shared message' + +def test_prepare_kd(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] +) +@pytest.mark.parametrize( + "algo", [hashes.SHA1, hashes.SHA224, hashes.SHA256, hashes.SHA384, hashes.SHA512] +) +@pytest.mark.parametrize( + "out_len", [32, 64, 256, 1024] +) +class TestX963: + def test_x963_ok(self, device, size, algo, out_len): + 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) + xkdf = X963KDF( + algorithm=algo(), + length=out_len, + sharedinfo=INFO, + ) + resB = xkdf.derive(pkey) + assert(bytes(resA) == resB) + xkdf = X963KDF( + algorithm=algo(), + length=out_len, + sharedinfo=INFO, + ) + xkdf.verify(pkey, bytes(resA)) + + def test_x963_fail(self, device, size, algo, out_len): + 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) + xkdf = X963KDF( + algorithm=algo(), + length=out_len, + sharedinfo=INFO, + ) + pkey = os.urandom(size // 8) + with pytest.raises(exceptions.InvalidKey): + xkdf.verify(pkey, bytes(resA))