@@ -76,12 +76,43 @@ def initialize_card(self, label: str = "sc_test", so_pin: str = "12345678", user
7676 self .cli .command ("softhsm2-util --init-token" , args ), env = {"SOFTHSM2_CONF" : self .SOFTHSM2_CONF_PATH }
7777 )
7878
79+ def initialize_additional_token (self , label : str , so_pin : str = "12345678" , user_pin : str = "123456" ) -> None :
80+ """
81+ Initializes an additional SoftHSM token without wiping existing tokens.
82+
83+ Unlike :meth:`initialize_card`, this method does **not** remove the
84+ existing token storage directory. It simply calls
85+ ``softhsm2-util --init-token --free`` with the given *label* so that a
86+ new slot is allocated alongside any tokens that are already present.
87+
88+ Use this after an initial :meth:`initialize_card` call to create a
89+ multi-token environment (simulating multiple physical smart cards).
90+
91+ :param label: Token label (must be unique among existing tokens).
92+ :type label: str
93+ :param so_pin: Security Officer PIN, defaults to "12345678"
94+ :type so_pin: str, optional
95+ :param user_pin: User PIN, defaults to "123456"
96+ :type user_pin: str, optional
97+ """
98+ args : CLIBuilderArgs = {
99+ "label" : (self .cli .option .VALUE , label ),
100+ "free" : (self .cli .option .SWITCH , True ),
101+ "so-pin" : (self .cli .option .VALUE , so_pin ),
102+ "pin" : (self .cli .option .VALUE , user_pin ),
103+ }
104+ self .host .conn .run (
105+ self .cli .command ("softhsm2-util --init-token" , args ), env = {"SOFTHSM2_CONF" : self .SOFTHSM2_CONF_PATH }
106+ )
107+
79108 def add_cert (
80109 self ,
81110 cert_path : str ,
82111 cert_id : str = "01" ,
83112 pin : str = "123456" ,
84113 private : bool | None = False ,
114+ token_label : str | None = None ,
115+ label : str | None = None ,
85116 ) -> None :
86117 """
87118 Adds a certificate or private key to the smart card.
@@ -94,6 +125,15 @@ def add_cert(
94125 :type pin: str, optional
95126 :param private: Whether the object is a private key. Defaults to False.
96127 :type private: bool, optional
128+ :param token_label: Label of the target token. When ``None`` (the
129+ default) ``pkcs11-tool`` writes to the first available token.
130+ Set this when multiple tokens exist to target a specific one.
131+ :type token_label: str | None, optional
132+ :param label: Label for the PKCS#11 object being written. Required
133+ when ``p11_child`` accesses the token directly (i.e. without
134+ ``virt_cacard``), because the response parser expects a
135+ non-empty label.
136+ :type label: str | None, optional
97137 """
98138 obj_type = "privkey" if private else "cert"
99139 args : CLIBuilderArgs = {
@@ -104,9 +144,20 @@ def add_cert(
104144 "type" : (self .cli .option .VALUE , obj_type ),
105145 "id" : (self .cli .option .VALUE , cert_id ),
106146 }
147+ if token_label is not None :
148+ args ["token-label" ] = (self .cli .option .VALUE , token_label )
149+ if label is not None :
150+ args ["label" ] = (self .cli .option .VALUE , label )
107151 self .host .conn .run (self .cli .command ("pkcs11-tool" , args ), env = {"SOFTHSM2_CONF" : self .SOFTHSM2_CONF_PATH })
108152
109- def add_key (self , key_path : str , key_id : str = "01" , pin : str = "123456" ) -> None :
153+ def add_key (
154+ self ,
155+ key_path : str ,
156+ key_id : str = "01" ,
157+ pin : str = "123456" ,
158+ token_label : str | None = None ,
159+ label : str | None = None ,
160+ ) -> None :
110161 """
111162 Adds a private key to the smart card.
112163
@@ -116,8 +167,12 @@ def add_key(self, key_path: str, key_id: str = "01", pin: str = "123456") -> Non
116167 :type key_id: str, optional
117168 :param pin: User PIN, defaults to "123456"
118169 :type pin: str, optional
170+ :param token_label: Label of the target token (see :meth:`add_cert`).
171+ :type token_label: str | None, optional
172+ :param label: Label for the PKCS#11 object (see :meth:`add_cert`).
173+ :type label: str | None, optional
119174 """
120- self .add_cert (cert_path = key_path , cert_id = key_id , pin = pin , private = True )
175+ self .add_cert (cert_path = key_path , cert_id = key_id , pin = pin , private = True , token_label = token_label , label = label )
121176
122177 def generate_cert (
123178 self ,
@@ -150,6 +205,40 @@ def generate_cert(
150205 self .host .conn .run (self .cli .command ("openssl req" , args ))
151206 return key_path , cert_path
152207
208+ def register_for_p11_child (self ) -> None :
209+ """
210+ Register SoftHSM as a system-wide p11-kit module visible to ``p11_child``.
211+
212+ This is required for multi-token scenarios where ``p11_child`` must
213+ iterate all PKCS#11 slots. The method:
214+
215+ 1. Ensures ``slots.removable = true`` is set in the SoftHSM config.
216+ 2. Copies the SoftHSM config to ``/etc/softhsm2.conf`` so that
217+ ``p11_child`` (running as the *sssd* user) can find it without
218+ ``SOFTHSM2_CONF`` in its environment.
219+ 3. Registers the SoftHSM module with p11-kit.
220+ 4. Creates a systemd drop-in for ``sssd.service`` that exports
221+ ``SOFTHSM2_CONF``.
222+ 5. Adjusts file permissions so the *sssd* user can access the token
223+ storage directory.
224+ """
225+ conf = self .SOFTHSM2_CONF_PATH
226+ module = "/usr/lib64/pkcs11/libsofthsm2.so"
227+
228+ self .host .conn .run (f"grep -q 'slots.removable' { conf } || echo 'slots.removable = true' >> { conf } " )
229+ self .host .conn .run (f"cp { conf } /etc/softhsm2.conf" )
230+ self .host .conn .run (f'echo "module: { module } " > /etc/pkcs11/modules/softhsm2.module' )
231+ self .fs .mkdir_p ("/etc/systemd/system/sssd.service.d" )
232+ self .host .conn .run (
233+ f'printf "[Service]\\ nEnvironment=SOFTHSM2_CONF={ conf } \\ n" '
234+ f"> /etc/systemd/system/sssd.service.d/softhsm.conf"
235+ )
236+ self .host .conn .run ("systemctl daemon-reload" )
237+ self .host .conn .run ("chmod -R o+rX /opt/test_ca/" )
238+ self .host .conn .run (
239+ f"chown -R sssd:sssd { self .TOKEN_STORAGE_PATH } / " f"&& chmod -R 770 { self .TOKEN_STORAGE_PATH } /"
240+ )
241+
153242 def insert_card (self ) -> None :
154243 """
155244 Simulates card insertion by starting the smart card service.
0 commit comments