mirror of
https://github.com/polhenarejos/pico-hsm.git
synced 2026-01-17 09:28:05 +00:00
Add key derivation tests (HKDF, PBKDF2 and X963).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
parent
20c01eb08d
commit
61359c7ebd
5 changed files with 295 additions and 0 deletions
|
|
@ -443,6 +443,8 @@ class Device:
|
||||||
algo = b'\x2A\x86\x48\x86\xF7\x0D\x02\x0A'
|
algo = b'\x2A\x86\x48\x86\xF7\x0D\x02\x0A'
|
||||||
elif (hash == hashes.SHA512):
|
elif (hash == hashes.SHA512):
|
||||||
algo = b'\x2A\x86\x48\x86\xF7\x0D\x02\x0B'
|
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)
|
data = [0x06, len(algo)] + list(algo) + [0x81, len(data)] + list(data)
|
||||||
resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=0x51, data=data)
|
resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=0x51, data=data)
|
||||||
return resp
|
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)
|
resp = self.send(cla=0x80, command=0x78, p1=keyid, p2=Algorithm.ALGO_AES_CMAC.value, data=data)
|
||||||
return resp
|
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")
|
@pytest.fixture(scope="session")
|
||||||
def device():
|
def device():
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ def test_mac_hmac(device, size, algo):
|
||||||
h = hmac.HMAC(pkey, algo())
|
h = hmac.HMAC(pkey, algo())
|
||||||
h.update(MESSAGE)
|
h.update(MESSAGE)
|
||||||
resB = h.finalize()
|
resB = h.finalize()
|
||||||
|
device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid)
|
||||||
assert(bytes(resA) == resB)
|
assert(bytes(resA) == resB)
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
@ -56,5 +57,6 @@ def test_mac_cmac(device, size):
|
||||||
c = cmac.CMAC(algorithms.AES(pkey))
|
c = cmac.CMAC(algorithms.AES(pkey))
|
||||||
c.update(MESSAGE)
|
c.update(MESSAGE)
|
||||||
resB = c.finalize()
|
resB = c.finalize()
|
||||||
|
device.delete_file(DOPrefixes.KEY_PREFIX.value << 8 | keyid)
|
||||||
assert(bytes(resA) == resB)
|
assert(bytes(resA) == resB)
|
||||||
|
|
||||||
|
|
|
||||||
81
tests/pico-hsm/test_070_hkdf.py
Normal file
81
tests/pico-hsm/test_070_hkdf.py
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
|
||||||
|
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))
|
||||||
85
tests/pico-hsm/test_071_pbkdf2.py
Normal file
85
tests/pico-hsm/test_071_pbkdf2.py
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
|
||||||
|
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))
|
||||||
76
tests/pico-hsm/test_072_x963.py
Normal file
76
tests/pico-hsm/test_072_x963.py
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
|
||||||
|
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))
|
||||||
Loading…
Add table
Reference in a new issue