Skip to content

Commit c148a2f

Browse files
authored
Merge pull request #97 from aXedge-arm/aes_key_derivation_extensions
Add AES Key Derivation Support (ECB,CBC Encrypt)
2 parents d0ba146 + 33faa60 commit c148a2f

File tree

5 files changed

+238
-0
lines changed

5 files changed

+238
-0
lines changed

dev-requirements.in

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ setuptools_scm
44
# Used for tests
55
oscrypto
66
cryptography
7+
parameterized
78

89
sphinx
910
sphinx-rtd-theme

dev-requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ markupsafe==1.1.1 # via jinja2
2222
mccabe==0.6.1 # via flake8
2323
oscrypto==1.2.0 # via -r dev-requirements.in
2424
packaging==20.1 # via sphinx
25+
parameterized==0.7.4 # via -r dev-requirements.in
2526
pycodestyle==2.5.0 # via flake8
2627
pycparser==2.19 # via cffi
2728
pyflakes==2.1.1 # via flake8

pkcs11/_pkcs11.pyx

+18
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ cdef class MechanismWithParam:
109109
cdef CK_RSA_PKCS_OAEP_PARAMS *oaep_params
110110
cdef CK_RSA_PKCS_PSS_PARAMS *pss_params
111111
cdef CK_ECDH1_DERIVE_PARAMS *ecdh1_params
112+
cdef CK_KEY_DERIVATION_STRING_DATA *aes_ecb_params
113+
cdef CK_AES_CBC_ENCRYPT_DATA_PARAMS *aes_cbc_params
112114

113115
# Unpack mechanism parameters
114116
if mechanism is Mechanism.RSA_PKCS_OAEP:
@@ -163,6 +165,22 @@ cdef class MechanismWithParam:
163165
ecdh1_params.pPublicData = public_data
164166
ecdh1_params.ulPublicDataLen = <CK_ULONG> len(public_data)
165167

168+
elif mechanism is Mechanism.AES_ECB_ENCRYPT_DATA:
169+
paramlen = sizeof(CK_KEY_DERIVATION_STRING_DATA)
170+
self.param = aes_ecb_params = \
171+
<CK_KEY_DERIVATION_STRING_DATA *> PyMem_Malloc(paramlen)
172+
aes_ecb_params.pData = <CK_BYTE *> param
173+
aes_ecb_params.ulLen = len(param)
174+
175+
elif mechanism is Mechanism.AES_CBC_ENCRYPT_DATA:
176+
paramlen = sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS)
177+
self.param = aes_cbc_params = \
178+
<CK_AES_CBC_ENCRYPT_DATA_PARAMS *> PyMem_Malloc(paramlen)
179+
(iv, data) = param
180+
aes_cbc_params.iv = iv[:16]
181+
aes_cbc_params.pData = <CK_BYTE *> data
182+
aes_cbc_params.length = len(data)
183+
166184
elif isinstance(param, bytes):
167185
self.data.pParameter = <CK_BYTE *> param
168186
paramlen = len(param)

pkcs11/_pkcs11_defn.pxd

+9
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,15 @@ cdef extern from '../extern/cryptoki.h':
243243
CK_ULONG ulPublicDataLen
244244
CK_BYTE *pPublicData
245245

246+
ctypedef struct CK_KEY_DERIVATION_STRING_DATA:
247+
CK_BYTE *pData
248+
CK_ULONG ulLen
249+
250+
ctypedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS:
251+
CK_BYTE iv[16]
252+
CK_BYTE *pData
253+
CK_ULONG length
254+
246255
cdef struct CK_FUNCTION_LIST:
247256
CK_VERSION version
248257
## pointers to library functions are stored here

tests/test_aes.py

+209
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from . import TestCase, requires, FIXME
99

10+
from parameterized import parameterized
1011

1112
class AESTests(TestCase):
1213

@@ -123,3 +124,211 @@ def test_wrap(self):
123124

124125
self.assertEqual(key[pkcs11.Attribute.VALUE],
125126
key2[pkcs11.Attribute.VALUE])
127+
128+
@parameterized.expand([
129+
("POSITIVE_128_BIT", 128, 16, TestCase.assertIsNotNone),
130+
("POSITIVE_128_BIT_LONG_IV", 128, 32, TestCase.assertIsNotNone),
131+
("NEGATIVE_128_BIT_BAD_IV", 128, 15, TestCase.assertIsNone),
132+
("POSITIVE_256_BIT_LONG_IV", 256, 32, TestCase.assertIsNotNone),
133+
("NEGATIVE_256_BIT_SHORT_IV", 256, 16, TestCase.assertIsNone),
134+
("NEGATIVE_256_BIT_BAD_IV", 256, 31, TestCase.assertIsNone),
135+
])
136+
@requires(Mechanism.AES_ECB_ENCRYPT_DATA)
137+
@FIXME.opencryptoki # can't set key attributes
138+
def test_derive_using_ecb_encrypt(self, test_type, test_key_length, iv_length, assert_fn):
139+
"""Function to test AES Key Derivation using the ECB_ENCRYPT Mechanism.
140+
141+
Refer to Section 2.15 of http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/errata01/os/pkcs11-curr-v2.40-errata01-os-complete.html#_Toc441850521
142+
"""
143+
144+
# Create the Master Key
145+
capabilities = pkcs11.defaults.DEFAULT_KEY_CAPABILITIES[pkcs11.KeyType.AES]
146+
capabilities |= pkcs11.MechanismFlag.DERIVE
147+
key = self.session.generate_key(pkcs11.KeyType.AES, key_length=test_key_length,
148+
capabilities=capabilities,
149+
template={
150+
pkcs11.Attribute.EXTRACTABLE: True,
151+
pkcs11.Attribute.DERIVE: True,
152+
pkcs11.Attribute.SENSITIVE: False,
153+
})
154+
155+
self.assertTrue(key is not None, "Failed to create {}-bit Master Key".format(test_key_length))
156+
157+
# Derive a Key from the Master Key
158+
iv = b'0' * iv_length
159+
try:
160+
derived_key = key.derive_key(pkcs11.KeyType.AES, key_length=test_key_length,
161+
capabilities=capabilities,
162+
mechanism=Mechanism.AES_ECB_ENCRYPT_DATA,
163+
mechanism_param=iv,
164+
template={
165+
pkcs11.Attribute.EXTRACTABLE: True,
166+
pkcs11.Attribute.SENSITIVE: False,
167+
})
168+
except (pkcs11.exceptions.MechanismParamInvalid,
169+
pkcs11.exceptions.FunctionFailed) as e:
170+
derived_key = None
171+
172+
assert_fn(self, derived_key, "{}-bit Key Derivation Failure".format(test_key_length))
173+
174+
@parameterized.expand([
175+
("POSITIVE_128_BIT", 128, 16),
176+
("POSITIVE_256_BIT_LONG_IV", 256, 32),
177+
])
178+
@requires(Mechanism.AES_ECB_ENCRYPT_DATA)
179+
@FIXME.opencryptoki # can't set key attributes
180+
def test_encrypt_with_key_derived_using_ecb_encrypt(self, test_type, test_key_length, iv_length):
181+
"""Function to test Data Encryption/Decryption using a Derived AES Key.
182+
183+
Function to test Data Encryption/Decryption using an AES Key
184+
Derived by the ECB_ENCRYPT Mechanism.
185+
186+
Refer to Section 2.15 of http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/errata01/os/pkcs11-curr-v2.40-errata01-os-complete.html#_Toc441850521
187+
"""
188+
189+
# Create the Master Key
190+
capabilities = pkcs11.defaults.DEFAULT_KEY_CAPABILITIES[pkcs11.KeyType.AES]
191+
capabilities |= pkcs11.MechanismFlag.DERIVE
192+
key = self.session.generate_key(pkcs11.KeyType.AES, key_length=test_key_length,
193+
capabilities=capabilities,
194+
template={
195+
pkcs11.Attribute.EXTRACTABLE: True,
196+
pkcs11.Attribute.DERIVE: True,
197+
pkcs11.Attribute.SENSITIVE: False,
198+
})
199+
200+
self.assertTrue(key is not None, "Failed to create {}-bit Master Key".format(test_key_length))
201+
202+
# Derive a Key from the Master Key
203+
iv = b'0' * iv_length
204+
try:
205+
derived_key = key.derive_key(pkcs11.KeyType.AES, key_length=test_key_length,
206+
capabilities=capabilities,
207+
mechanism=Mechanism.AES_ECB_ENCRYPT_DATA,
208+
mechanism_param=iv,
209+
template={
210+
pkcs11.Attribute.EXTRACTABLE: True,
211+
pkcs11.Attribute.SENSITIVE: False,
212+
})
213+
except (pkcs11.exceptions.MechanismParamInvalid,
214+
pkcs11.exceptions.FunctionFailed) as e:
215+
derived_key = None
216+
217+
self.assertTrue(derived_key is not None, "Failed to derive {}-bit Derived Key".format(test_key_length))
218+
219+
# Test capability of Key to Encrypt/Decrypt data
220+
data = b'HELLO WORLD' * 1024
221+
222+
iv = self.session.generate_random(128)
223+
crypttext = self.key.encrypt(data, mechanism_param=iv)
224+
text = self.key.decrypt(crypttext, mechanism_param=iv)
225+
226+
self.assertEqual(text, data)
227+
228+
@parameterized.expand([
229+
("POSITIVE_128_BIT", 128, 16, 16, TestCase.assertIsNotNone),
230+
("POSITIVE_128_BIT_LONG_DATA", 128, 16, 64, TestCase.assertIsNotNone),
231+
("NEGATIVE_128_BIT_BAD_IV", 128, 15, 16, TestCase.assertIsNone),
232+
("NEGATIVE_128_BIT_BAD_DATA", 128, 16, 31, TestCase.assertIsNone),
233+
("POSITIVE_256_BIT", 256, 16, 32, TestCase.assertIsNotNone),
234+
("POSITIVE_256_BIT_LONG_DATA", 256, 16, 64, TestCase.assertIsNotNone),
235+
("NEGATIVE_256_BIT_BAD_IV", 256, 15, 16, TestCase.assertIsNone),
236+
("NEGATIVE_256_BIT_BAD_DATA", 256, 16, 31, TestCase.assertIsNone),
237+
("NEGATIVE_256_BIT_SHORT_DATA", 256, 16, 16, TestCase.assertIsNone),
238+
])
239+
@requires(Mechanism.AES_CBC_ENCRYPT_DATA)
240+
@FIXME.opencryptoki # can't set key attributes
241+
def test_derive_using_cbc_encrypt(self, test_type, test_key_length, iv_length, data_length, assert_fn):
242+
"""Function to test AES Key Derivation using the CBC_ENCRYPT Mechanism.
243+
244+
Refer to Section 2.15 of http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/errata01/os/pkcs11-curr-v2.40-errata01-os-complete.html#_Toc441850521
245+
"""
246+
247+
# Create the Master Key
248+
capabilities = pkcs11.defaults.DEFAULT_KEY_CAPABILITIES[pkcs11.KeyType.AES]
249+
capabilities |= pkcs11.MechanismFlag.DERIVE
250+
key = self.session.generate_key(pkcs11.KeyType.AES, key_length=test_key_length,
251+
capabilities=capabilities,
252+
template={
253+
pkcs11.Attribute.EXTRACTABLE: True,
254+
pkcs11.Attribute.DERIVE: True,
255+
pkcs11.Attribute.SENSITIVE: False,
256+
})
257+
258+
self.assertTrue(key is not None, "Failed to create {}-bit Master Key".format(test_key_length))
259+
260+
# Derive a Key from the Master Key
261+
iv = b'0' * iv_length
262+
data = b'1' * data_length
263+
try:
264+
derived_key = key.derive_key(pkcs11.KeyType.AES, key_length=test_key_length,
265+
capabilities=capabilities,
266+
mechanism=Mechanism.AES_CBC_ENCRYPT_DATA,
267+
mechanism_param=(iv, data),
268+
template={
269+
pkcs11.Attribute.EXTRACTABLE: True,
270+
pkcs11.Attribute.SENSITIVE: False,
271+
})
272+
except (pkcs11.exceptions.MechanismParamInvalid,
273+
pkcs11.exceptions.FunctionFailed,
274+
IndexError) as e:
275+
derived_key = None
276+
277+
assert_fn(self, derived_key, "{}-bit Key Derivation Failure".format(test_key_length))
278+
279+
@parameterized.expand([
280+
("POSITIVE_128_BIT", 128, 16, 16),
281+
("POSITIVE_256_BIT", 256, 16, 32),
282+
("POSITIVE_256_BIT_LONG_DATA", 256, 16, 64),
283+
])
284+
@requires(Mechanism.AES_CBC_ENCRYPT_DATA)
285+
@FIXME.opencryptoki # can't set key attributes
286+
def test_encrypt_with_key_derived_using_cbc_encrypt(self, test_type, test_key_length, iv_length, data_length):
287+
"""Function to test Data Encryption/Decryption using a Derived AES Key.
288+
289+
Function to test Data Encryption/Decryption using an AES Key
290+
Derived by the CBC_ENCRYPT Mechanism.
291+
292+
Refer to Section 2.15 of http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/errata01/os/pkcs11-curr-v2.40-errata01-os-complete.html#_Toc441850521
293+
"""
294+
295+
# Create the Master Key
296+
capabilities = pkcs11.defaults.DEFAULT_KEY_CAPABILITIES[pkcs11.KeyType.AES]
297+
capabilities |= pkcs11.MechanismFlag.DERIVE
298+
key = self.session.generate_key(pkcs11.KeyType.AES, key_length=test_key_length,
299+
capabilities=capabilities,
300+
template={
301+
pkcs11.Attribute.EXTRACTABLE: True,
302+
pkcs11.Attribute.DERIVE: True,
303+
pkcs11.Attribute.SENSITIVE: False,
304+
})
305+
306+
self.assertTrue(key is not None, "Failed to create {}-bit Master Key".format(test_key_length))
307+
308+
# Derive a Key from the Master Key
309+
iv = b'0' * iv_length
310+
data = b'1' * data_length
311+
try:
312+
derived_key = key.derive_key(pkcs11.KeyType.AES, key_length=test_key_length,
313+
capabilities=capabilities,
314+
mechanism=Mechanism.AES_CBC_ENCRYPT_DATA,
315+
mechanism_param=(iv, data),
316+
template={
317+
pkcs11.Attribute.EXTRACTABLE: True,
318+
pkcs11.Attribute.SENSITIVE: False,
319+
})
320+
except (pkcs11.exceptions.MechanismParamInvalid,
321+
pkcs11.exceptions.FunctionFailed,
322+
IndexError) as e:
323+
derived_key = None
324+
325+
self.assertTrue(derived_key is not None, "Failed to derive {}-bit Derived Key".format(test_key_length))
326+
327+
# Test capability of Key to Encrypt/Decrypt data
328+
data = b'HELLO WORLD' * 1024
329+
330+
iv = self.session.generate_random(128)
331+
crypttext = self.key.encrypt(data, mechanism_param=iv)
332+
text = self.key.decrypt(crypttext, mechanism_param=iv)
333+
334+
self.assertEqual(text, data)

0 commit comments

Comments
 (0)