|
4 | 4 | import shutil |
5 | 5 | import tempfile |
6 | 6 | import unittest |
| 7 | +from unittest.mock import Mock |
7 | 8 |
|
8 | 9 | from asn1crypto.keys import ( |
9 | 10 | ECDomainParameters, |
@@ -31,18 +32,22 @@ class TestHSM(unittest.TestCase): |
31 | 32 | hsm_keyid = 1 |
32 | 33 | hsm_keyid_default = 2 |
33 | 34 | hsm_keyid_odd = 258 |
| 35 | + hsm_keyid_priv = 512 |
34 | 36 | hsm_user_pin = "123456" |
35 | 37 |
|
36 | 38 | @staticmethod |
37 | | - def _generate_key_pair(session, keyid, curve): |
| 39 | + def _generate_key_pair(session, keyid, curve, is_pubkey_private=False): |
38 | 40 | "Create ecdsa key pair on hsm" |
39 | 41 | params = ECDomainParameters(name="named", value=NamedCurve(curve.name)).dump() |
40 | 42 |
|
41 | 43 | cka_id = list(keyid.to_bytes((keyid.bit_length() + 7) // 8 or 1, "big")) |
42 | 44 |
|
43 | 45 | public_template = [ |
44 | 46 | (PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY), |
45 | | - (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE), |
| 47 | + ( |
| 48 | + PyKCS11.CKA_PRIVATE, |
| 49 | + PyKCS11.CK_TRUE if is_pubkey_private else PyKCS11.CK_FALSE, |
| 50 | + ), |
46 | 51 | (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE), |
47 | 52 | (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_FALSE), |
48 | 53 | (PyKCS11.CKA_VERIFY, PyKCS11.CK_TRUE), |
@@ -104,6 +109,9 @@ def setUpClass(cls): |
104 | 109 | cls._generate_key_pair(session, cls.hsm_keyid, SECP256R1) |
105 | 110 | cls._generate_key_pair(session, cls.hsm_keyid_default, SECP384R1) |
106 | 111 | cls._generate_key_pair(session, cls.hsm_keyid_odd, SECP256R1) |
| 112 | + cls._generate_key_pair( |
| 113 | + session, cls.hsm_keyid_priv, SECP256R1, is_pubkey_private=True |
| 114 | + ) |
107 | 115 |
|
108 | 116 | session.logout() |
109 | 117 | session.closeSession() |
@@ -147,6 +155,48 @@ def test_hsm_uri(self): |
147 | 155 | with self.assertRaises(UnverifiedSignatureError): |
148 | 156 | key.verify_signature(sig, b"NOT DATA") |
149 | 157 |
|
| 158 | + def test_hsm_private_pubkey(self): |
| 159 | + """Test PIN""" |
| 160 | + |
| 161 | + # Test pub key not found without pin |
| 162 | + with self.assertRaises(Exception) as context: |
| 163 | + _, _ = HSMSigner.import_(self.hsm_keyid_priv, self.token_filter) |
| 164 | + self.assertTrue("could not fin" in context.exception) |
| 165 | + |
| 166 | + # or with bad pin |
| 167 | + bad_pinh = Mock(return_value="987") |
| 168 | + with self.assertRaises(Exception) as context: |
| 169 | + _, _ = HSMSigner.import_( |
| 170 | + self.hsm_keyid_priv, self.token_filter, pin_handler=bad_pinh |
| 171 | + ) |
| 172 | + self.assertTrue("could not fin" in context.exception) |
| 173 | + self.assertTrue(bad_pinh.assert_called_once) |
| 174 | + |
| 175 | + # And check sign ok cases (good pin or not required pin) |
| 176 | + for hsm_keyid in [ |
| 177 | + self.hsm_keyid, |
| 178 | + self.hsm_keyid_default, |
| 179 | + self.hsm_keyid_odd, |
| 180 | + self.hsm_keyid_priv, |
| 181 | + ]: |
| 182 | + good_pinh = Mock(return_value=self.hsm_user_pin) |
| 183 | + _, key = HSMSigner.import_( |
| 184 | + hsm_keyid, self.token_filter, pin_handler=good_pinh |
| 185 | + ) |
| 186 | + # Pin handler must be called only if public key is private |
| 187 | + if hsm_keyid == self.hsm_keyid_priv: |
| 188 | + good_pinh.assert_called_once() |
| 189 | + else: |
| 190 | + good_pinh.assert_not_called() |
| 191 | + |
| 192 | + signer = HSMSigner(hsm_keyid, self.token_filter, key, good_pinh) |
| 193 | + |
| 194 | + sig = signer.sign(b"DATA") |
| 195 | + key.verify_signature(sig, b"DATA") |
| 196 | + |
| 197 | + with self.assertRaises(UnverifiedSignatureError): |
| 198 | + key.verify_signature(sig, b"NOT DATA") |
| 199 | + |
150 | 200 |
|
151 | 201 | # Run the unit tests. |
152 | 202 | if __name__ == "__main__": |
|
0 commit comments