Skip to content

Commit 8e77dbd

Browse files
committed
Remove HSMKey but keep from_hsm method (WIP)
HSMKey is only useful for its from_hsm method. This method is temporarily moved to HSM test module, as verbatim copy with some error handling commented out, and will likely be moved to the HSMSigner in a subsequent commit. **TODO core** - Change signer init to only take public_key and secrets_handler - change signer sign to - open session to first token - login using secrets handler - grab key with keyid 2 - add default scheme "hsm:" - add from_priv_key_uri - move pubkey extraction to from_priv_key_uri XXX: We'll have to change HSMSigner, if we want to do something like hsm:YubiKey%20PIV%20%2315835999?piv_slot=9c **TODO misc** - fix tests/ci (see self-review) - include check_hsm_signer with aggregate_tests (maybe with some skipUnless), and include in ci.yml - move to HSMSigner to _hsm_signer.py Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
1 parent c0f56a5 commit 8e77dbd

File tree

3 files changed

+94
-129
lines changed

3 files changed

+94
-129
lines changed

securesystemslib/signer/__init__.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,7 @@
44
This module provides extensible interfaces for public keys and signers:
55
Some implementations are provided by default but more can be added by users.
66
"""
7-
from securesystemslib.signer._key import (
8-
KEY_FOR_TYPE_AND_SCHEME,
9-
HSMKey,
10-
Key,
11-
SSlibKey,
12-
)
7+
from securesystemslib.signer._key import KEY_FOR_TYPE_AND_SCHEME, Key, SSlibKey
138
from securesystemslib.signer._signature import GPGSignature, Signature
149
from securesystemslib.signer._signer import (
1510
SIGNER_FOR_URI_SCHEME,

securesystemslib/signer/_key.py

+1-119
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,9 @@
44
from typing import Any, Dict, Optional, Tuple, Type
55

66
import securesystemslib.keys as sslib_keys
7-
from securesystemslib import KEY_TYPE_ECDSA, exceptions
7+
from securesystemslib import exceptions
88
from securesystemslib.signer._signature import Signature
99

10-
# pylint: disable=wrong-import-position
11-
CRYPTO_IMPORT_ERROR = None
12-
try:
13-
from cryptography.hazmat.primitives import serialization
14-
from cryptography.hazmat.primitives.asymmetric.ec import (
15-
SECP256R1,
16-
SECP384R1,
17-
EllipticCurvePublicKey,
18-
ObjectIdentifier,
19-
get_curve_for_oid,
20-
)
21-
22-
except ImportError: # pragma: no cover
23-
CRYPTO_IMPORT_ERROR = "'cryptography' required"
24-
25-
PYKCS11_IMPORT_ERROR = None
26-
try:
27-
from PyKCS11 import PyKCS11
28-
29-
except ImportError: # pragma: no cover
30-
PYKCS11_IMPORT_ERROR = "'PyKCS11' required"
31-
32-
ASN1CRYPTO_IMPORT_ERROR = None
33-
try:
34-
from asn1crypto.keys import ECDomainParameters, ECPoint
35-
36-
except ImportError: # pragma: no cover
37-
ASN1CRYPTO_IMPORT_ERROR = "'asn1crypto' required"
38-
# pylint: enable=wrong-import-position
39-
4010
logger = logging.getLogger(__name__)
4111

4212
# NOTE dict for Key dispatch defined here, but filled at end of file when
@@ -213,94 +183,6 @@ def verify_signature(self, signature: Signature, data: bytes) -> None:
213183
) from e
214184

215185

216-
class HSMKey(SSlibKey):
217-
"""Hardware Security Module (HSM) Key
218-
219-
HSMKey is a regular SSlibKey with an additional `from_hsm` method to
220-
export public keys from hardware security modules.
221-
"""
222-
223-
@classmethod
224-
def from_hsm(
225-
cls,
226-
hsm_session: "PyKCS11.Session",
227-
hsm_keyid: Tuple[int, ...],
228-
keyid: str,
229-
):
230-
"""Export public key from HSM
231-
232-
Supports ecdsa on SECG curves secp256r1 (NIST P-256) or secp384r1 (NIST P-384).
233-
234-
Arguments:
235-
hsm_session: An open ``PyKCS11.Session`` to the token with the public key.
236-
hsm_keyid: Key identifier on the token.
237-
keyid: Key identifier that is unique within the metadata it is used in.
238-
239-
Raises:
240-
ValueError: No compatible key for ``hsm_keyid`` found on HSM.
241-
PyKCS11.PyKCS11Error: Various HSM communication errors.
242-
243-
"""
244-
if CRYPTO_IMPORT_ERROR:
245-
raise exceptions.UnsupportedLibraryError(CRYPTO_IMPORT_ERROR)
246-
247-
if PYKCS11_IMPORT_ERROR:
248-
raise exceptions.UnsupportedLibraryError(PYKCS11_IMPORT_ERROR)
249-
250-
# Search for ecdsa public keys with passed keyid on HSM
251-
keys = hsm_session.findObjects(
252-
[
253-
(PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY),
254-
(PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA),
255-
(PyKCS11.CKA_ID, hsm_keyid),
256-
]
257-
)
258-
259-
if len(keys) != 1:
260-
raise ValueError(
261-
f"hsm_keyid must identify one {KEY_TYPE_ECDSA} key, found {len(keys)}"
262-
)
263-
264-
# Extract public key domain parameters and point from HSM
265-
hsm_params, hsm_point = hsm_session.getAttributeValue(
266-
keys[0], [PyKCS11.CKA_EC_PARAMS, PyKCS11.CKA_EC_POINT]
267-
)
268-
269-
params = ECDomainParameters.load(bytes(hsm_params))
270-
271-
# TODO: Define as module level constant and don't hardcode scheme strings
272-
scheme_for_curve = {
273-
SECP256R1: "ecdsa-sha2-nistp256",
274-
SECP384R1: "ecdsa-sha2-nistp384",
275-
}
276-
curve_names = [curve.name for curve in scheme_for_curve]
277-
278-
if params.chosen.native not in curve_names:
279-
raise ValueError(
280-
f"found key on {params.chosen.native}, should be on one of {curve_names}"
281-
)
282-
283-
# Create PEM from key
284-
curve = get_curve_for_oid(ObjectIdentifier(params.chosen.dotted))
285-
public_pem = (
286-
EllipticCurvePublicKey.from_encoded_point(
287-
curve(), ECPoint().load(bytes(hsm_point)).native
288-
)
289-
.public_bytes(
290-
serialization.Encoding.PEM,
291-
serialization.PublicFormat.SubjectPublicKeyInfo,
292-
)
293-
.decode()
294-
)
295-
296-
return HSMKey(
297-
keyid,
298-
KEY_TYPE_ECDSA,
299-
scheme_for_curve[curve],
300-
{"public": public_pem},
301-
)
302-
303-
304186
# Supported key types and schemes, and the Keys implementing them
305187
KEY_FOR_TYPE_AND_SCHEME = {
306188
("ecdsa", "ecdsa-sha2-nistp256"): SSlibKey,

tests/check_hsm_signer.py

+92-4
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,26 @@
88

99
from asn1crypto.keys import ( # pylint: disable=import-error
1010
ECDomainParameters,
11+
ECPoint,
1112
NamedCurve,
1213
)
13-
from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1, SECP384R1
14+
from cryptography.hazmat.primitives import serialization
15+
from cryptography.hazmat.primitives.asymmetric.ec import (
16+
SECP256R1,
17+
SECP384R1,
18+
EllipticCurvePublicKey,
19+
ObjectIdentifier,
20+
get_curve_for_oid,
21+
)
1422
from PyKCS11 import PyKCS11
1523

1624
import securesystemslib.hash
17-
from securesystemslib.signer import HSMKey, HSMSigner
25+
from securesystemslib import KEY_TYPE_ECDSA
26+
from securesystemslib.signer import HSMSigner, SSlibKey
1827

1928

2029
class TestHSM(unittest.TestCase):
21-
"""Test HSMSigner and HSMKey with SoftHSM
30+
"""Test HSMSigner with SoftHSM
2231
2332
Requirements:
2433
- install SoftHSM2
@@ -113,6 +122,85 @@ def tearDownClass(cls):
113122
shutil.rmtree(cls.test_dir)
114123
del os.environ["SOFTHSM2_CONF"]
115124

125+
@staticmethod
126+
def _from_hsm(
127+
hsm_session,
128+
hsm_keyid,
129+
keyid,
130+
):
131+
"""Export public key from HSM
132+
133+
Supports ecdsa on SECG curves secp256r1 (NIST P-256) or secp384r1 (NIST P-384).
134+
135+
Arguments:
136+
hsm_session: An open ``PyKCS11.Session`` to the token with the public key.
137+
hsm_keyid: Key identifier on the token.
138+
keyid: Key identifier that is unique within the metadata it is used in.
139+
140+
Raises:
141+
ValueError: No compatible key for ``hsm_keyid`` found on HSM.
142+
PyKCS11.PyKCS11Error: Various HSM communication errors.
143+
144+
"""
145+
# if CRYPTO_IMPORT_ERROR:
146+
# raise exceptions.UnsupportedLibraryError(CRYPTO_IMPORT_ERROR)
147+
148+
# if PYKCS11_IMPORT_ERROR:
149+
# raise exceptions.UnsupportedLibraryError(PYKCS11_IMPORT_ERROR)
150+
151+
# Search for ecdsa public keys with passed keyid on HSM
152+
keys = hsm_session.findObjects(
153+
[
154+
(PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY),
155+
(PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA),
156+
(PyKCS11.CKA_ID, hsm_keyid),
157+
]
158+
)
159+
160+
# if len(keys) != 1:
161+
# raise ValueError(
162+
# f"hsm_keyid must identify one {KEY_TYPE_ECDSA} key, found {len(keys)}"
163+
# )
164+
165+
# Extract public key domain parameters and point from HSM
166+
hsm_params, hsm_point = hsm_session.getAttributeValue(
167+
keys[0], [PyKCS11.CKA_EC_PARAMS, PyKCS11.CKA_EC_POINT]
168+
)
169+
170+
params = ECDomainParameters.load(bytes(hsm_params))
171+
172+
# TODO: Define as module level constant and don't hardcode scheme strings
173+
scheme_for_curve = {
174+
SECP256R1: "ecdsa-sha2-nistp256",
175+
SECP384R1: "ecdsa-sha2-nistp384",
176+
}
177+
# curve_names = [curve.name for curve in scheme_for_curve]
178+
179+
# if params.chosen.native not in curve_names:
180+
# raise ValueError(
181+
# f"found key on {params.chosen.native}, should be on one of {curve_names}"
182+
# )
183+
184+
# Create PEM from key
185+
curve = get_curve_for_oid(ObjectIdentifier(params.chosen.dotted))
186+
public_pem = (
187+
EllipticCurvePublicKey.from_encoded_point(
188+
curve(), ECPoint().load(bytes(hsm_point)).native
189+
)
190+
.public_bytes(
191+
serialization.Encoding.PEM,
192+
serialization.PublicFormat.SubjectPublicKeyInfo,
193+
)
194+
.decode()
195+
)
196+
197+
return SSlibKey(
198+
keyid,
199+
KEY_TYPE_ECDSA,
200+
scheme_for_curve[curve],
201+
{"public": public_pem},
202+
)
203+
116204
def test_hsm(self):
117205
"""Test public key export, HSM signing, and verification w/o HSM"""
118206

@@ -131,7 +219,7 @@ def _pre_hash(data, scheme):
131219
data = b"deadbeef"
132220

133221
for hsm_keyid in self.hsm_keyids:
134-
public_key = HSMKey.from_hsm(session, hsm_keyid, keyid)
222+
public_key = self._from_hsm(session, hsm_keyid, keyid)
135223

136224
session.login(self.hsm_user_pin) # Login for signing
137225
signer = HSMSigner(session, hsm_keyid, public_key)

0 commit comments

Comments
 (0)