Skip to content

Commit 9163f86

Browse files
Support UNWRAP_TEMPLATE, DERIVE_TEMPLATE etc.
Fixes #51.
1 parent 22e501e commit 9163f86

File tree

4 files changed

+144
-17
lines changed

4 files changed

+144
-17
lines changed

pkcs11/_pkcs11.pyx

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ cdef assertRV(rv) with gil:
4747
raise map_rv_to_error(rv)
4848

4949

50+
cdef inline CK_BBOOL _is_template_attr(CK_ATTRIBUTE_TYPE type):
51+
cdef CK_ULONG mask = 0xf
52+
cdef CK_ULONG mask_result = (type ^ ATTR_TEMPLATE_ATTRIBUTE) & ~mask
53+
return <CK_BBOOL> (not mask_result)
54+
55+
56+
def template_as_attribute_list(template, attribute_mapper=None):
57+
return AttributeList.from_template(template, attribute_mapper or AttributeMapper())
58+
59+
5060
cdef class AttributeList:
5161
"""
5262
A list of CK_ATTRIBUTE objects.
@@ -81,15 +91,22 @@ cdef class AttributeList:
8191
cdef bytes value_bytes
8292
cdef CK_CHAR * value_ptr
8393
cdef Py_ssize_t value_len
94+
cdef AttributeList template_list
8495
for index, (key, value) in enumerate(template.items()):
8596
lst.data[index].type = key
86-
value_bytes = attribute_mapper.pack_attribute(key, value)
87-
value_len = len(value_bytes)
88-
# copy the result into a pointer that we manage, for consistency with the other init method
89-
value_ptr = <CK_CHAR *> PyMem_Malloc(value_len)
90-
if value_ptr is NULL:
91-
raise MemoryError()
92-
memcpy(value_ptr, <CK_CHAR *> value_bytes, <size_t> value_len)
97+
if _is_template_attr(key):
98+
template_list = AttributeList.from_template(value, attribute_mapper)
99+
value_len = <Py_ssize_t> template_list.count * sizeof(CK_ATTRIBUTE)
100+
value_ptr = <CK_CHAR *> template_list.data
101+
template_list.data = NULL
102+
else:
103+
value_bytes = attribute_mapper.pack_attribute(key, value)
104+
value_len = len(value_bytes)
105+
# copy the result into a pointer that we manage, for consistency with the other init method
106+
value_ptr = <CK_CHAR *> PyMem_Malloc(value_len)
107+
if value_ptr is NULL:
108+
raise MemoryError()
109+
memcpy(value_ptr, <CK_CHAR *> value_bytes, <size_t> value_len)
93110
lst.data[index].pValue = <CK_CHAR *> value_ptr
94111
lst.data[index].ulValueLen = <CK_ULONG> value_len
95112

@@ -102,10 +119,21 @@ cdef class AttributeList:
102119
cdef CK_ATTRIBUTE * attr
103120
if index < self.count:
104121
attr = &self.data[index]
105-
return attribute_mapper.unpack_attributes(
106-
attr.type,
107-
PyBytes_FromStringAndSize(<char *> attr.pValue, <Py_ssize_t> attr.ulValueLen)
108-
)
122+
if _is_template_attr(attr.type):
123+
template_lst = AttributeList.from_owned_pointer(
124+
<CK_ATTRIBUTE *> attr.pValue,
125+
attr.ulValueLen // sizeof(CK_ATTRIBUTE)
126+
)
127+
result = template_lst.as_dict(attribute_mapper)
128+
# prevent __dealloc__ from cleaning up this memory,
129+
# we just needed the .as_dict(...)
130+
template_lst.data = NULL
131+
return result
132+
else:
133+
return attribute_mapper.unpack_attributes(
134+
attr.type,
135+
PyBytes_FromStringAndSize(<char *> attr.pValue, <Py_ssize_t> attr.ulValueLen)
136+
)
109137
else:
110138
raise IndexError()
111139

@@ -125,12 +153,25 @@ cdef class AttributeList:
125153
return self.at_index(index, attribute_mapper)
126154
raise KeyError(item)
127155

128-
def __dealloc__(self):
156+
cdef _free(self):
129157
cdef CK_ULONG index = 0
158+
cdef CK_ATTRIBUTE current
130159
if self.data is not NULL:
131160
for index in range(self.count):
132-
PyMem_Free(self.data[index].pValue)
161+
current = self.data[index]
162+
if _is_template_attr(current.type):
163+
# ensure template lists are cleaned up
164+
AttributeList.from_owned_pointer(
165+
<CK_ATTRIBUTE *> current.pValue,
166+
current.ulValueLen // sizeof(CK_ATTRIBUTE)
167+
)._free()
168+
else:
169+
PyMem_Free(current.pValue)
133170
PyMem_Free(self.data)
171+
self.data = NULL
172+
173+
def __dealloc__(self):
174+
self._free()
134175

135176

136177
cdef class MechanismWithParam:

pkcs11/constants.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def __repr__(self):
6262
_ARRAY_ATTRIBUTE = 0x40000000
6363
"""Attribute consists of an array of values."""
6464

65+
ATTR_TEMPLATE_ATTRIBUTE = _ARRAY_ATTRIBUTE | 0x210
66+
6567

6668
class Attribute(IntEnum):
6769
"""
@@ -298,9 +300,9 @@ class Attribute(IntEnum):
298300

299301
WRAP_WITH_TRUSTED = 0x00000210
300302
"""Key can only be wrapped with a `TRUSTED` key."""
301-
WRAP_TEMPLATE = _ARRAY_ATTRIBUTE | 0x00000211
302-
UNWRAP_TEMPLATE = _ARRAY_ATTRIBUTE | 0x00000212
303-
DERIVE_TEMPLATE = _ARRAY_ATTRIBUTE | 0x00000213
303+
WRAP_TEMPLATE = ATTR_TEMPLATE_ATTRIBUTE | 0x1
304+
UNWRAP_TEMPLATE = ATTR_TEMPLATE_ATTRIBUTE | 0x2
305+
DERIVE_TEMPLATE = ATTR_TEMPLATE_ATTRIBUTE | 0x3
304306

305307
OTP_FORMAT = 0x00000220
306308
OTP_LENGTH = 0x00000221

tests/test_aes.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from parameterized import parameterized
66

77
import pkcs11
8-
from pkcs11 import ArgumentsBad, CTRParams, GCMParams, Mechanism, PKCS11Error
8+
from pkcs11 import ArgumentsBad, CTRParams, GCMParams, Mechanism, PKCS11Error, TemplateInconsistent
99

1010
from . import FIXME, TestCase, requires
1111

@@ -203,6 +203,38 @@ def test_wrap(self):
203203

204204
self.assertEqual(key[pkcs11.Attribute.VALUE], key2[pkcs11.Attribute.VALUE])
205205

206+
@requires(Mechanism.AES_KEY_WRAP)
207+
def test_wrap_with_unwrap_template(self):
208+
wrapping_key = self.session.generate_key(
209+
pkcs11.KeyType.AES,
210+
128,
211+
template={
212+
pkcs11.Attribute.UNWRAP_TEMPLATE: {
213+
pkcs11.Attribute.EXTRACTABLE: False,
214+
}
215+
},
216+
)
217+
key = self.session.generate_key(
218+
pkcs11.KeyType.AES,
219+
128,
220+
template={
221+
pkcs11.Attribute.EXTRACTABLE: True,
222+
pkcs11.Attribute.SENSITIVE: False,
223+
},
224+
)
225+
data = wrapping_key.wrap_key(key)
226+
227+
with self.assertRaises(TemplateInconsistent):
228+
wrapping_key.unwrap_key(
229+
pkcs11.ObjectClass.SECRET_KEY,
230+
pkcs11.KeyType.AES,
231+
data,
232+
template={
233+
# forbidden by the unwrapping template
234+
pkcs11.Attribute.EXTRACTABLE: True,
235+
},
236+
)
237+
206238
@parameterized.expand(
207239
[
208240
("POSITIVE_128_BIT", 128, 16, TestCase.assertIsNotNone),

tests/test_attribute_lists.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import unittest
2+
3+
from pkcs11 import Attribute
4+
from pkcs11._pkcs11 import template_as_attribute_list
5+
from pkcs11.attributes import AttributeMapper
6+
7+
8+
class AttributeListWithTemplateTest(unittest.TestCase):
9+
def test_unwrap_template_readback(self):
10+
template = {
11+
Attribute.SENSITIVE: True,
12+
Attribute.EXTRACTABLE: False,
13+
Attribute.WRAP: True,
14+
Attribute.UNWRAP: True,
15+
Attribute.UNWRAP_TEMPLATE: {Attribute.EXTRACTABLE: False},
16+
Attribute.LABEL: "test",
17+
}
18+
mapper = AttributeMapper()
19+
lst = template_as_attribute_list(template)
20+
self.assertEqual("test", lst.get(Attribute.LABEL, mapper))
21+
self.assertEqual({Attribute.EXTRACTABLE: False}, lst.get(Attribute.UNWRAP_TEMPLATE, mapper))
22+
self.assertEqual(template, lst.as_dict(mapper))
23+
24+
def test_derive_template_readback(self):
25+
template = {
26+
Attribute.SENSITIVE: True,
27+
Attribute.EXTRACTABLE: False,
28+
Attribute.DERIVE: True,
29+
Attribute.DERIVE_TEMPLATE: {Attribute.EXTRACTABLE: False},
30+
Attribute.LABEL: "test",
31+
}
32+
mapper = AttributeMapper()
33+
lst = template_as_attribute_list(template)
34+
self.assertEqual("test", lst.get(Attribute.LABEL, mapper))
35+
self.assertEqual({Attribute.EXTRACTABLE: False}, lst.get(Attribute.DERIVE_TEMPLATE, mapper))
36+
self.assertEqual(template, lst.as_dict(mapper))
37+
38+
def test_nested_template(self):
39+
template = {
40+
Attribute.SENSITIVE: True,
41+
Attribute.EXTRACTABLE: False,
42+
Attribute.DERIVE: True,
43+
Attribute.DERIVE_TEMPLATE: {
44+
Attribute.EXTRACTABLE: False,
45+
Attribute.DERIVE_TEMPLATE: {Attribute.EXTRACTABLE: False},
46+
},
47+
Attribute.LABEL: "test",
48+
}
49+
mapper = AttributeMapper()
50+
lst = template_as_attribute_list(template)
51+
self.assertEqual("test", lst.get(Attribute.LABEL, mapper))
52+
self.assertEqual(template, lst.as_dict(mapper))

0 commit comments

Comments
 (0)