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))