Repository: thin-edge/thin-edge.io
Pull request #3623: feat: Connect summary cryptoki show mode and key URI
Author: login: Bravo555
Labels: name: theme:hsm
description: Hardware Security Module related topics
Created: 2025-05-14T14:58:44Z | Merged: 2025-05-20T15:40:09Z

## Proposed changes

Change `cryptoki:` line in connection summary from simple `true/false` to display mode and key URI:

- If mode is off: `cryptoki: off`
- If mode is not off and `key_uri` is not set: `cryptoki: MODE`
- If mode is not off and `key_uri` is set: `cryptoki: MODE (key: KEY)`

## Types of changes

<!--
What types of changes does your code introduce to `thin-edge.io`?
_Put an `x` in the boxes that apply_
-->

- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [x] Improvement (general improvements like code refactoring that doesn't explicitly fix a bug or add any new functionality)
- [ ] Documentation Update (if none of the other choices apply)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)

## Paste Link to the issue
<br/>

## Checklist

<!--
_Put an `x` in the boxes that apply. You can also fill these out after
creating the PR. If you're unsure about any of them, don't hesitate to ask.
We're here to help! This is simply a reminder of what we are going to look for
before merging your code._
-->

- [x] I have read the [CONTRIBUTING](https://github.com/thin-edge/thin-edge.io/blob/main/CONTRIBUTING.md) doc
- [x] I have signed the [CLA](https://github.com/thin-edge/thin-edge.io/blob/main/CONTRIBUTOR-LICENSE-AGREEMENT.md) (in all commits with git commit -s. You can activate automatic signing by running `just prepare-dev` once)
- [x] I ran `just format` as mentioned in [CODING_GUIDELINES](https://github.com/thin-edge/thin-edge.io/blob/main/CODING_GUIDELINES.md)
- [x] I used `just check` as mentioned in [CODING_GUIDELINES](https://github.com/thin-edge/thin-edge.io/blob/main/CODING_GUIDELINES.md)
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] I have added necessary documentation (if appropriate)

## Further comments

I'm not sure if this is the best way to format the information in the summary, I'm open to suggestions for changes.

An alternative would be to instead of separate `cryptoki:` line, to add a `private key:` line, and put there `key_uri` and `key_path` config settings, depending whether cryptoki is enabled or not.



Diff:
diff --git a/crates/common/tedge_config/src/tedge_toml/tedge_config.rs b/crates/common/tedge_config/src/tedge_toml/tedge_config.rs
index 794d20638d9..539958d696a 100644
--- a/crates/common/tedge_config/src/tedge_toml/tedge_config.rs
+++ b/crates/common/tedge_config/src/tedge_toml/tedge_config.rs
@@ -982,6 +982,14 @@ impl TEdgeConfigReader {
         .unwrap()
     }
 
+    pub fn as_cloud_config(&self, cloud: Cloud<'_>) -> Result<&dyn CloudConfig, MultiError> {
+        Ok(match cloud {
+            Cloud::C8y(profile) => self.c8y.try_get(profile)?,
+            Cloud::Az(profile) => self.az.try_get(profile)?,
+            Cloud::Aws(profile) => self.aws.try_get(profile)?,
+        })
+    }
+
     pub fn device_key_path<'a>(
         &self,
         cloud: Option<impl Into<Cloud<'a>>>,
diff --git a/crates/core/tedge/src/cli/connect/command.rs b/crates/core/tedge/src/cli/connect/command.rs
index 60b8b0aa744..152811796e4 100644
--- a/crates/core/tedge/src/cli/connect/command.rs
+++ b/crates/core/tedge/src/cli/connect/command.rs
@@ -32,6 +32,7 @@ use anyhow::bail;
 use c8y_api::http_proxy::read_c8y_credentials;
 use camino::Utf8Path;
 use camino::Utf8PathBuf;
+use certificate::parse_root_certificate::CryptokiConfig;
 use mqtt_channel::Topic;
 use std::borrow::Cow;
 use std::collections::HashMap;
@@ -100,7 +101,24 @@ impl Command for ConnectCommand {
             bridge_config(&tedge_config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(&tedge_config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = tedge_config.device.cryptoki.mode.enabled();
+
+        let cloud = tedge_config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_key_uri = tedge_config
+            .device
+            .cryptoki_config(cloud)?
+            .map(|c| match c {
+                CryptokiConfig::Direct(d) => d.uri,
+                CryptokiConfig::SocketService { uri, .. } => uri,
+            });
+        let cryptoki_mode = tedge_config.device.cryptoki.mode.clone();
+        let cryptoki_status = match cryptoki_key_uri {
+            None => "off".to_string(),
+            Some(None) => format!("{cryptoki_mode}"),
+            Some(Some(key)) => format!("{cryptoki_mode} (key: {key})"),
+        };
 
         ConfigLogger::log(
             self.description(),
@@ -108,7 +126,7 @@ impl Command for ConnectCommand {
             &*self.service_manager,
             &self.cloud,
             credentials_path,
-            use_cryptoki,
+            &cryptoki_status,
             tedge_config.proxy.address.or_none(),
             tedge_config.proxy.username.or_none().map(|u| u.as_str()),
         );
diff --git a/crates/core/tedge/src/cli/log.rs b/crates/core/tedge/src/cli/log.rs
index f19d38f7266..d3e465e5373 100644
--- a/crates/core/tedge/src/cli/log.rs
+++ b/crates/core/tedge/src/cli/log.rs
@@ -256,7 +256,7 @@ pub struct ConfigLogger<'a> {
     mosquitto_version: Option<&'a str>,
     cloud: &'a MaybeBorrowedCloud<'a>,
     credentials_path: Option<&'a Utf8Path>,
-    cryptoki: bool,
+    cryptoki: &'a str,
     proxy_url: Option<&'a ProxyUrl>,
     proxy_username: Option<&'a str>,
 }
@@ -270,7 +270,7 @@ impl<'a> ConfigLogger<'a> {
         service_manager: &'a dyn SystemServiceManager,
         cloud: &'a MaybeBorrowedCloud<'a>,
         credentials_path: Option<&'a Utf8Path>,
-        use_cryptoki: bool,
+        cryptoki: &'a str,
         proxy_url: Option<&'a ProxyUrl>,
         proxy_username: Option<&'a str>,
     ) {
@@ -287,7 +287,7 @@ impl<'a> ConfigLogger<'a> {
                 service_manager,
                 mosquitto_version: config.mosquitto_version.as_deref(),
                 cloud,
-                cryptoki: use_cryptoki,
+                cryptoki,
                 proxy_url,
                 proxy_username,
             }
diff --git a/tests/RobotFramework/tests/pkcs11/private_key_storage.robot b/tests/RobotFramework/tests/pkcs11/private_key_storage.robot
index 3bc57867bbc..79f12e88f74 100644
--- a/tests/RobotFramework/tests/pkcs11/private_key_storage.robot
+++ b/tests/RobotFramework/tests/pkcs11/private_key_storage.robot
@@ -5,6 +5,7 @@ Documentation       Test thin-edge.io MQTT client authentication using a Hardwar
 ...                 cryptographic tokens that will be read by thin-edge. In real production environments a dedicated
 ...                 hardware device would be used.
 
+# it would be good to explain here why we use the tedge-p11-server exclusively and not the module mode
 Resource            ../resources/common.resource
 Library             Cumulocity
 Library             ThinEdgeIO
@@ -19,14 +20,18 @@ Test Tags           adapter:docker    theme:cryptoki
 Use Private Key in SoftHSM2 using tedge-p11-server
     Tedge Reconnect Should Succeed
 
-Select Private key using PKCS#11 URI
+Select Private key using tedge-p11-server URI
     [Documentation]    Make sure that we can select different keys and tokens using a PKCS#11 URI.
     ...    The URI can either point to a specific key, or to a specific token where we will attempt to find a key.
     ...
     ...    To ensure that correct key is selected and reduce the need to generate and upload different keys and
     ...    certificates, we'll only be using one key and we'll only import it to the chosen token, keeping other tokens
     ...    empty.
+    ...
+    ...    We set the URI on tedge-p11-server, which means that all connecting clients will use the selected key until
+    ...    tedge-p11-server is restarted with a different URI.
 
+    Set tedge-p11-server Uri    value=
     Tedge Reconnect Should Succeed
 
     # expect failure if we try to use a token that doesn't exist
@@ -53,6 +58,25 @@ Select Private key using PKCS#11 URI
     Set tedge-p11-server Uri    value=pkcs11:token=tedge;object=tedge
     Tedge Reconnect Should Succeed
 
+    Set tedge-p11-server Uri    value=
+
+Select Private key using a request URI
+    [Documentation]    Like above, we select the key using a URI, but this time we include it in a request, which means
+    ...    we can select different keys without restarting tedge-p11-server.
+
+    Execute Command    cmd=tedge config set device.key_uri pkcs11:token=token123
+    ${stdout}=    Tedge Reconnect Should Fail With    Failed to find a signing key
+    Should Contain    ${stdout}    item=cryptoki: socket (key: pkcs11:token=token123)
+
+    Execute Command    cmd=tedge config unset device.key_uri
+    Execute Command    cmd=tedge config set c8y.device.key_uri pkcs11:token=token123
+    ${stdout}=    Tedge Reconnect Should Fail With    Failed to find a signing key
+    Should Contain    ${stdout}    item=cryptoki: socket (key: pkcs11:token=token123)
+
+    Execute Command    cmd=tedge config set c8y.device.key_uri "pkcs11:token=tedge;object=tedge"
+    ${stdout}=    Tedge Reconnect Should Succeed
+    Should Contain    ${stdout}    item=cryptoki: socket (key: pkcs11:token=tedge;object=tedge)
+
 Connects to C8y using an RSA key
     [Documentation]    Test that we can connect to C8y using an RSA private key. At the time of writing the test
     ...    `tedge cert create` by default uses an ECDSA keypair, so we have to generate it manually and upload it.
@@ -162,6 +186,8 @@ Custom Setup
     Execute Command
     ...    cmd=sudo env C8Y_USER='${C8Y_CONFIG.username}' C8Y_PASSWORD='${C8Y_CONFIG.password}' tedge cert upload c8y
 
+    Set tedge-p11-server Uri    value=
+
 Remove Existing Certificates
     Execute Command    cmd=rm -f "$(tedge config get device.key_path)" "$(tedge config get device.cert_path)"
 
@@ -171,9 +197,11 @@ Set tedge-p11-server Uri
     Restart Service    tedge-p11-server
 
 Tedge Reconnect Should Succeed
-    Execute Command    tedge reconnect c8y
+    ${stdout}=    Execute Command    tedge reconnect c8y
+    RETURN    ${stdout}
 
 Tedge Reconnect Should Fail With
     [Arguments]    ${error}
-    ${stderr}=    Execute Command    tedge reconnect c8y    exp_exit_code=!0    stdout=false    stderr=true
+    ${stdout}    ${stderr}=    Execute Command    tedge reconnect c8y    exp_exit_code=!0    stdout=true    stderr=true
     Should Contain    ${stderr}    ${error}
+    RETURN    ${stdout}


Reviews:
user: login: didier-wenzek
body: Approved, with minor comments.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T13:21:23Z
user: login: reubenmiller
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T07:23:15Z
user: login: Bravo555
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T08:48:25Z
user: login: Bravo555
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T08:49:44Z
user: login: Bravo555
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T10:10:33Z
user: login: reubenmiller
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T11:40:00Z
user: login: reubenmiller
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T11:42:18Z
user: login: reubenmiller
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T12:20:02Z
user: login: Bravo555
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T12:22:36Z
user: login: Bravo555
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T12:32:54Z
user: login: Bravo555
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T12:56:31Z
user: login: Bravo555
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T12:58:17Z
user: login: reubenmiller
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T14:27:18Z
user: login: Bravo555
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T16:58:03Z
user: login: reubenmiller
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T07:15:09Z
user: login: reubenmiller
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T07:15:40Z
user: login: reubenmiller
body: Approved. Nice improvement to make it more transparent about the settings being used.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T11:41:47Z

Review comments:
diff_hunk: @@ -256,7 +257,7 @@ pub struct ConfigLogger<'a> {
     mosquitto_version: Option<&'a str>,
     cloud: &'a MaybeBorrowedCloud<'a>,
     credentials_path: Option<&'a Utf8Path>,
-    cryptoki: bool,
+    cryptoki: &'a dyn Display,
path: crates/core/tedge/src/cli/log.rs
user: login: didier-wenzek
body: Using `&'a str` would be more consistent with the other fields.
created_at: 2025-05-16T13:04:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -103,15 +104,29 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_key_uri = config.device.cryptoki_config(cloud)?.map(|c| match c {
+            CryptokiConfig::Direct(d) => d.uri,
+            CryptokiConfig::SocketService { uri, .. } => uri,
+        });
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_status = match cryptoki_key_uri {
+            None => "off".to_string(),
+            Some(None) => format!("{cryptoki_mode} (key: unspecified)"),
+            Some(Some(key)) => format!("{cryptoki_mode} (key: {key})"),
+        };
path: crates/core/tedge/src/cli/connect/command.rs
user: login: didier-wenzek
body: This is working but I'm surprised by the `mode` extracted twice via two different paths (indirectly with `config.device.cryptoki_config` and then explicitly with `config.device.cryptoki.mode`). Isn't there a risk of inconsistency?
created_at: 2025-05-16T13:20:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -103,15 +104,29 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_key_uri = config.device.cryptoki_config(cloud)?.map(|c| match c {
+            CryptokiConfig::Direct(d) => d.uri,
+            CryptokiConfig::SocketService { uri, .. } => uri,
+        });
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_status = match cryptoki_key_uri {
+            None => "off".to_string(),
+            Some(None) => format!("{cryptoki_mode} (key: unspecified)"),
path: crates/core/tedge/src/cli/connect/command.rs
user: login: reubenmiller
body: For the case where no client key uri is being set (e.g. `device.key_uri`), we should just leave the `(key: *`) part of the string out, as it gives the user the wrong impression that there is no "key" defined (when in fact the tedge-p11-server will probably have a key defined).

So I would propose that when no `device.key_uri` is defined, then we just show the mode.

```
cryptoki: <MODE>
```

Examples:

```
cryptoki: socket
cryptoki: module
```
created_at: 2025-05-19T07:23:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -256,7 +257,7 @@ pub struct ConfigLogger<'a> {
     mosquitto_version: Option<&'a str>,
     cloud: &'a MaybeBorrowedCloud<'a>,
     credentials_path: Option<&'a Utf8Path>,
-    cryptoki: bool,
+    cryptoki: &'a dyn Display,
path: crates/core/tedge/src/cli/log.rs
user: login: Bravo555
body: fixed in 8f4fdf0f
created_at: 2025-05-19T08:48:25Z
author_association: MEMBER
diff_hunk: @@ -103,15 +104,29 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_key_uri = config.device.cryptoki_config(cloud)?.map(|c| match c {
+            CryptokiConfig::Direct(d) => d.uri,
+            CryptokiConfig::SocketService { uri, .. } => uri,
+        });
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_status = match cryptoki_key_uri {
+            None => "off".to_string(),
+            Some(None) => format!("{cryptoki_mode} (key: unspecified)"),
+            Some(Some(key)) => format!("{cryptoki_mode} (key: {key})"),
+        };
path: crates/core/tedge/src/cli/connect/command.rs
user: login: Bravo555
body: fixed in c0577539f

Yeah, I've overengineered it a bit, I was using `cryptoki_config` to get the correct URI, but I could simply get URI from `cloud.key_uri`. It should now be much simpler.
created_at: 2025-05-19T08:49:44Z
author_association: MEMBER
diff_hunk: @@ -103,15 +104,29 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_key_uri = config.device.cryptoki_config(cloud)?.map(|c| match c {
+            CryptokiConfig::Direct(d) => d.uri,
+            CryptokiConfig::SocketService { uri, .. } => uri,
+        });
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_status = match cryptoki_key_uri {
+            None => "off".to_string(),
+            Some(None) => format!("{cryptoki_mode} (key: unspecified)"),
path: crates/core/tedge/src/cli/connect/command.rs
user: login: Bravo555
body: fixed in c057753
created_at: 2025-05-19T10:10:33Z
author_association: MEMBER
diff_hunk: @@ -53,6 +58,26 @@ Select Private key using PKCS#11 URI
     Set tedge-p11-server Uri    value=pkcs11:token=tedge;object=tedge
     Tedge Reconnect Should Succeed
 
+    Set tedge-p11-server Uri    value=
+
+Select Private key using a request URI
+    [Documentation]    Like above, we select the key using a URI, but this time we include it in a request, which means
+    ...    we can select different keys without restarting tedge-p11-server.
+
+    Set tedge-p11-server Uri    value=
+    Tedge Reconnect Should Succeed
+
+    Execute Command    softhsm2-util --init-token --free --label token10 --pin "123456" --so-pin "123456"
+    Execute Command    cmd=tedge config set device.key_uri pkcs11:token=token10
+    Tedge Reconnect Should Fail With    Failed to find a signing key
path: tests/RobotFramework/tests/pkcs11/private_key_storage.robot
user: login: reubenmiller
body: This surprises me a little that without the `object=tedge` component of the pkcs11 uri, that they key is not found.
created_at: 2025-05-19T11:40:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -53,6 +58,26 @@ Select Private key using PKCS#11 URI
     Set tedge-p11-server Uri    value=pkcs11:token=tedge;object=tedge
     Tedge Reconnect Should Succeed
 
+    Set tedge-p11-server Uri    value=
+
+Select Private key using a request URI
+    [Documentation]    Like above, we select the key using a URI, but this time we include it in a request, which means
+    ...    we can select different keys without restarting tedge-p11-server.
+
+    Set tedge-p11-server Uri    value=
+    Tedge Reconnect Should Succeed
+
+    Execute Command    softhsm2-util --init-token --free --label token10 --pin "123456" --so-pin "123456"
+    Execute Command    cmd=tedge config set device.key_uri pkcs11:token=token10
+    Tedge Reconnect Should Fail With    Failed to find a signing key
path: tests/RobotFramework/tests/pkcs11/private_key_storage.robot
user: login: reubenmiller
body: It might be more obvious to the reader by using an incorrect token name rather than just leaving the `object=tedge` out.

```
tedge config set device.key_uri pkcs11:token=token123
```
created_at: 2025-05-19T11:42:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -103,15 +103,25 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_key_uri = cloud.key_uri();
+        let cryptoki_status = match cryptoki_key_uri {
+            Some(key_uri) => format!("{cryptoki_mode} (key: {key_uri})"),
+            None => format!("{cryptoki_mode}"),
+        };
path: crates/core/tedge/src/cli/connect/command.rs
user: login: reubenmiller
body: There seems to be something wrong here as I would of expected to see the `device.key_uri` (not just the `c8y.key_uri`) in the connection summary:

![image](https://github.com/user-attachments/assets/78e40bf9-34fc-4ec6-8751-2693a48c72a5)

created_at: 2025-05-19T12:20:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -53,6 +58,26 @@ Select Private key using PKCS#11 URI
     Set tedge-p11-server Uri    value=pkcs11:token=tedge;object=tedge
     Tedge Reconnect Should Succeed
 
+    Set tedge-p11-server Uri    value=
+
+Select Private key using a request URI
+    [Documentation]    Like above, we select the key using a URI, but this time we include it in a request, which means
+    ...    we can select different keys without restarting tedge-p11-server.
+
+    Set tedge-p11-server Uri    value=
+    Tedge Reconnect Should Succeed
+
+    Execute Command    softhsm2-util --init-token --free --label token10 --pin "123456" --so-pin "123456"
+    Execute Command    cmd=tedge config set device.key_uri pkcs11:token=token10
+    Tedge Reconnect Should Fail With    Failed to find a signing key
path: tests/RobotFramework/tests/pkcs11/private_key_storage.robot
user: login: Bravo555
body: The logic is that if `token` attribute is present, we select that token. Only then we try to find any keys on it (in this case there are none).

The docs section mentions this although I realize now that it doesn't sound very clear:

> It is also possible to use a URI that identifies a token in settings like device.key_uri. The URI will then be used to select a token, but the key will be selected automatically, though the selected key may be wrong if there are multiple to choose from. [1]

[1]: https://thin-edge.github.io/thin-edge.io/references/hsm-support/#key-selection

i.e. after selecting the token, we automatically try to find a matching key **on that token**.
created_at: 2025-05-19T12:22:36Z
author_association: MEMBER
diff_hunk: @@ -103,15 +103,25 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_key_uri = cloud.key_uri();
+        let cryptoki_status = match cryptoki_key_uri {
+            Some(key_uri) => format!("{cryptoki_mode} (key: {key_uri})"),
+            None => format!("{cryptoki_mode}"),
+        };
path: crates/core/tedge/src/cli/connect/command.rs
user: login: Bravo555
body: Okay, so there is a reason why i did [this](https://github.com/thin-edge/thin-edge.io/pull/3623#discussion_r2095198037) on friday...

I forgot that `cloud.key_uri` will be `None`, when we actually use `device.key_uri`. I'll revert to the previous version which will be a tad more complicated, but it'll work.
created_at: 2025-05-19T12:32:54Z
author_association: MEMBER
diff_hunk: @@ -103,15 +104,29 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_key_uri = config.device.cryptoki_config(cloud)?.map(|c| match c {
+            CryptokiConfig::Direct(d) => d.uri,
+            CryptokiConfig::SocketService { uri, .. } => uri,
+        });
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_status = match cryptoki_key_uri {
+            None => "off".to_string(),
+            Some(None) => format!("{cryptoki_mode} (key: unspecified)"),
+            Some(Some(key)) => format!("{cryptoki_mode} (key: {key})"),
+        };
path: crates/core/tedge/src/cli/connect/command.rs
user: login: Bravo555
body: I had to revert this because I forgot why I did that originally: `cloud.device.key_uri` is empty if we use `device.key_uri`, and `.cryptoki_config()` actually read both and applied `device.key_uri` if the one from `cloud` was `None`.
created_at: 2025-05-19T12:56:31Z
author_association: MEMBER
diff_hunk: @@ -103,15 +103,25 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_key_uri = cloud.key_uri();
+        let cryptoki_status = match cryptoki_key_uri {
+            Some(key_uri) => format!("{cryptoki_mode} (key: {key_uri})"),
+            None => format!("{cryptoki_mode}"),
+        };
path: crates/core/tedge/src/cli/connect/command.rs
user: login: Bravo555
body: fixed in 6479650 and added checks if key is displayed correctly in 0dc27e1d7
created_at: 2025-05-19T12:58:17Z
author_association: MEMBER
diff_hunk: @@ -53,6 +58,26 @@ Select Private key using PKCS#11 URI
     Set tedge-p11-server Uri    value=pkcs11:token=tedge;object=tedge
     Tedge Reconnect Should Succeed
 
+    Set tedge-p11-server Uri    value=
+
+Select Private key using a request URI
+    [Documentation]    Like above, we select the key using a URI, but this time we include it in a request, which means
+    ...    we can select different keys without restarting tedge-p11-server.
+
+    Set tedge-p11-server Uri    value=
+    Tedge Reconnect Should Succeed
+
+    Execute Command    softhsm2-util --init-token --free --label token10 --pin "123456" --so-pin "123456"
+    Execute Command    cmd=tedge config set device.key_uri pkcs11:token=token10
+    Tedge Reconnect Should Fail With    Failed to find a signing key
path: tests/RobotFramework/tests/pkcs11/private_key_storage.robot
user: login: reubenmiller
body: Though if there is only one key for the given token, wouldn't that be enough to find it?
created_at: 2025-05-19T14:27:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -53,6 +58,26 @@ Select Private key using PKCS#11 URI
     Set tedge-p11-server Uri    value=pkcs11:token=tedge;object=tedge
     Tedge Reconnect Should Succeed
 
+    Set tedge-p11-server Uri    value=
+
+Select Private key using a request URI
+    [Documentation]    Like above, we select the key using a URI, but this time we include it in a request, which means
+    ...    we can select different keys without restarting tedge-p11-server.
+
+    Set tedge-p11-server Uri    value=
+    Tedge Reconnect Should Succeed
+
+    Execute Command    softhsm2-util --init-token --free --label token10 --pin "123456" --so-pin "123456"
+    Execute Command    cmd=tedge config set device.key_uri pkcs11:token=token10
+    Tedge Reconnect Should Fail With    Failed to find a signing key
path: tests/RobotFramework/tests/pkcs11/private_key_storage.robot
user: login: Bravo555
body: In this case, we create a new empty token, with no keys in it, which, looking back, is indeed unnecessary, because we can just use an incorrect token name.
Switched to just using a non-existent token in a21f01bb.
created_at: 2025-05-19T16:58:03Z
author_association: MEMBER
diff_hunk: @@ -53,6 +58,26 @@ Select Private key using PKCS#11 URI
     Set tedge-p11-server Uri    value=pkcs11:token=tedge;object=tedge
     Tedge Reconnect Should Succeed
 
+    Set tedge-p11-server Uri    value=
+
+Select Private key using a request URI
+    [Documentation]    Like above, we select the key using a URI, but this time we include it in a request, which means
+    ...    we can select different keys without restarting tedge-p11-server.
+
+    Set tedge-p11-server Uri    value=
+    Tedge Reconnect Should Succeed
+
+    Execute Command    softhsm2-util --init-token --free --label token10 --pin "123456" --so-pin "123456"
+    Execute Command    cmd=tedge config set device.key_uri pkcs11:token=token10
+    Tedge Reconnect Should Fail With    Failed to find a signing key
path: tests/RobotFramework/tests/pkcs11/private_key_storage.robot
user: login: reubenmiller
body: Yeah that makes the test easier to read
created_at: 2025-05-20T07:15:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -103,15 +103,25 @@ impl Command for ConnectCommand {
         let bridge_config = bridge_config(config, &self.cloud).map_err(anyhow::Error::new)?;
         let credentials_path =
             credentials_path_for(config, &self.cloud).map_err(anyhow::Error::new)?;
-        let use_cryptoki = config.device.cryptoki.mode.enabled();
+
+        let cloud = config
+            .as_cloud_config((&self.cloud).into())
+            .map_err(anyhow::Error::new)?;
+
+        let cryptoki_mode = config.device.cryptoki.mode.clone();
+        let cryptoki_key_uri = cloud.key_uri();
+        let cryptoki_status = match cryptoki_key_uri {
+            Some(key_uri) => format!("{cryptoki_mode} (key: {key_uri})"),
+            None => format!("{cryptoki_mode}"),
+        };
path: crates/core/tedge/src/cli/connect/command.rs
user: login: reubenmiller
body: Though one system test seems to be failing now.
created_at: 2025-05-20T07:15:40Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-05-14T15:09:08Z
body: ## [Codecov](https://app.codecov.io/gh/thin-edge/thin-edge.io/pull/3623?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge) Report
Attention: Patch coverage is `21.42857%` with `22 lines` in your changes missing coverage. Please review.

| [Files with missing lines](https://app.codecov.io/gh/thin-edge/thin-edge.io/pull/3623?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge) | Patch % | Lines |
|---|---|---|
| [crates/core/tedge/src/cli/connect/command.rs](https://app.codecov.io/gh/thin-edge/thin-edge.io/pull/3623?src=pr&el=tree&filepath=crates%2Fcore%2Ftedge%2Fsrc%2Fcli%2Fconnect%2Fcommand.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge#diff-Y3JhdGVzL2NvcmUvdGVkZ2Uvc3JjL2NsaS9jb25uZWN0L2NvbW1hbmQucnM=) | 0.00% | [18 Missing :warning: ](https://app.codecov.io/gh/thin-edge/thin-edge.io/pull/3623?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge) |
| [...common/tedge\_config/src/tedge\_toml/tedge\_config.rs](https://app.codecov.io/gh/thin-edge/thin-edge.io/pull/3623?src=pr&el=tree&filepath=crates%2Fcommon%2Ftedge_config%2Fsrc%2Ftedge_toml%2Ftedge_config.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge#diff-Y3JhdGVzL2NvbW1vbi90ZWRnZV9jb25maWcvc3JjL3RlZGdlX3RvbWwvdGVkZ2VfY29uZmlnLnJz) | 75.00% | [2 Missing :warning: ](https://app.codecov.io/gh/thin-edge/thin-edge.io/pull/3623?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge) |
| [crates/core/tedge/src/cli/log.rs](https://app.codecov.io/gh/thin-edge/thin-edge.io/pull/3623?src=pr&el=tree&filepath=crates%2Fcore%2Ftedge%2Fsrc%2Fcli%2Flog.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge#diff-Y3JhdGVzL2NvcmUvdGVkZ2Uvc3JjL2NsaS9sb2cucnM=) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/thin-edge/thin-edge.io/pull/3623?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge) |


:loudspeaker: Thoughts on this report? [Let us know!](https://about.codecov.io/pull-request-comment-report/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thin-edge)

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: reubenmiller
created_at: 2025-05-15T10:01:57Z
body: > If mode is off: cryptoki: disabled

It might make more sense to show the same value as used in the tedge.toml, e.g. `off`, `socket` or `module`...this would be more consistent, and also easier to debug (e.g. are you using a module or socket etc.)

author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-16T10:18:40Z
body: ### Robot Results
| :white_check_mark: Passed | :x: Failed | :next_track_button: Skipped | Total | Pass % | :stopwatch: Duration |
| --- | --- | --- | :---: | :---: | :---: |
| 634 | 0 | 3 | 634 | 100 | 1h49m39.863169999s |




author_association: CONTRIBUTOR
user: login: reubenmiller
created_at: 2025-05-19T07:26:04Z
body: @Bravo555 I noticed that we currently don't have a system test (which is run by default) for setting the `device.key_uri` (since the only place it is being used is the AWS test which is currently marked as on_demand only).

It would make sense adding a new system tests which just uses Cumulocity and sets the PKCS11 URI on the client side (and not on the tedge-p11-server)...having this test would also make it easier to see a real example of how the `tedge connect xx` info looks like in a real-world scenario.
author_association: CONTRIBUTOR
user: login: Bravo555
created_at: 2025-05-19T11:24:47Z
body: > It would make sense adding a new system tests which just uses Cumulocity and sets the PKCS11 URI on the client side

@reubenmiller New test added in f3ac02b8. Is it sufficient?
author_association: MEMBER