Skip to content

Commit 66b13b9

Browse files
committed
make tedge cert create use HSM
Signed-off-by: Marcel Guzik <[email protected]>
1 parent 0ec2ceb commit 66b13b9

File tree

7 files changed

+86
-20
lines changed

7 files changed

+86
-20
lines changed

crates/core/tedge/src/cli/certificate/cli.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ use tracing::debug;
3434

3535
#[derive(clap::Subcommand, Debug)]
3636
pub enum TEdgeCertCli {
37-
/// Create a self-signed device certificate
37+
/// Create a self-signed device certificate.
38+
///
39+
/// This command creates the device certificate and private key, and persists them, if they
40+
/// don't already exist.
3841
Create {
3942
/// The device identifier to be used as the common name for the certificate
4043
#[clap(long = "device-id", global = true)]

crates/core/tedge/src/cli/certificate/create.rs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::error::CertError;
22
use crate::cli::certificate::show::ShowCertCmd;
33
use crate::command::Command;
44
use crate::log::MaybeFancy;
5+
use anyhow::Context;
56
use camino::Utf8Path;
67
use camino::Utf8PathBuf;
78
use certificate::CsrTemplate;
@@ -12,6 +13,7 @@ use std::fs::Permissions;
1213
use std::os::unix::fs::PermissionsExt;
1314
use std::path::Path;
1415
use tedge_config::TEdgeConfig;
16+
use tedge_p11_server::CryptokiConfig;
1517
use tokio::fs::File;
1618
use tokio::fs::OpenOptions;
1719
use tokio::io::AsyncWriteExt;
@@ -42,8 +44,10 @@ impl Command for CreateCertCmd {
4244
format!("create a test certificate for the device {}.", self.id)
4345
}
4446

45-
async fn execute(&self, _: TEdgeConfig) -> Result<(), MaybeFancy<anyhow::Error>> {
46-
self.create_test_certificate(&self.csr_template).await?;
47+
async fn execute(&self, config: TEdgeConfig) -> Result<(), MaybeFancy<anyhow::Error>> {
48+
let cryptoki = config.device.cryptoki_config(None)?;
49+
self.create_test_certificate(&self.csr_template, cryptoki)
50+
.await?;
4751
eprintln!("Certificate created successfully");
4852
eprintln!(" => the certificate has to be uploaded to the cloud");
4953
eprintln!(" => before the device can be connected\n");
@@ -53,8 +57,23 @@ impl Command for CreateCertCmd {
5357
}
5458

5559
impl CreateCertCmd {
56-
pub async fn create_test_certificate(&self, config: &CsrTemplate) -> Result<(), CertError> {
57-
let cert = KeyCertPair::new_selfsigned_certificate(config, &self.id, &KeyKind::New)?;
60+
// test certificate here means it's not yet permanent - establishing the connection using this
61+
// new certificate will be tested during `tedge connect` and if an error happens, we will revert
62+
// to the previous certificate. If not, the certificate will be set as a permanent active
63+
// certificate and will not be tested on further connections.
64+
pub async fn create_test_certificate(
65+
&self,
66+
template: &CsrTemplate,
67+
cryptoki: Option<CryptokiConfig>,
68+
) -> Result<(), CertError> {
69+
let key_kind = if let Some(cryptoki) = cryptoki {
70+
KeyKind::from_cryptoki(cryptoki.clone(), None)
71+
.context("failed to create a remote keykind")?
72+
} else {
73+
KeyKind::New
74+
};
75+
76+
let cert = KeyCertPair::new_selfsigned_certificate(template, &self.id, &key_kind)?;
5877

5978
let cert_path = &self.cert_path;
6079
persist_new_public_key(
@@ -66,15 +85,18 @@ impl CreateCertCmd {
6685
.await
6786
.map_err(|err| err.cert_context(cert_path.clone()))?;
6887

69-
let key_path = &self.key_path;
70-
persist_new_private_key(
71-
key_path,
72-
cert.private_key_pem_string()?,
73-
&self.user,
74-
&self.group,
75-
)
76-
.await
77-
.map_err(|err| err.key_context(key_path.clone()))?;
88+
// only persist to filesystem if we just created a new key (not using HSM)
89+
if let KeyKind::New = key_kind {
90+
let key_path = &self.key_path;
91+
persist_new_private_key(
92+
key_path,
93+
cert.private_key_pem_string()?,
94+
&self.user,
95+
&self.group,
96+
)
97+
.await
98+
.map_err(|err| err.key_context(key_path.clone()))?;
99+
}
78100
Ok(())
79101
}
80102
}
@@ -238,7 +260,8 @@ mod tests {
238260
};
239261

240262
assert_matches!(
241-
cmd.create_test_certificate(&CsrTemplate::default()).await,
263+
cmd.create_test_certificate(&CsrTemplate::default(), None)
264+
.await,
242265
Ok(())
243266
);
244267
assert_eq!(parse_pem_file(&cert_path).tag(), "CERTIFICATE");
@@ -268,7 +291,7 @@ mod tests {
268291
};
269292

270293
assert!(cmd
271-
.create_test_certificate(&CsrTemplate::default())
294+
.create_test_certificate(&CsrTemplate::default(), None)
272295
.await
273296
.ok()
274297
.is_none());
@@ -293,7 +316,7 @@ mod tests {
293316
};
294317

295318
let cert_error = cmd
296-
.create_test_certificate(&CsrTemplate::default())
319+
.create_test_certificate(&CsrTemplate::default(), None)
297320
.await
298321
.unwrap_err();
299322
assert_matches!(cert_error, CertError::CertificateNotFound { .. });
@@ -315,7 +338,7 @@ mod tests {
315338
};
316339

317340
let cert_error = cmd
318-
.create_test_certificate(&CsrTemplate::default())
341+
.create_test_certificate(&CsrTemplate::default(), None)
319342
.await
320343
.unwrap_err();
321344
assert_matches!(cert_error, CertError::KeyNotFound { .. });

crates/core/tedge/src/cli/certificate/create_csr.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ mod tests {
145145

146146
// create private key and public cert with standard command
147147
assert_matches!(
148-
cmd.create_test_certificate(&CsrTemplate::default()).await,
148+
cmd.create_test_certificate(&CsrTemplate::default(), None)
149+
.await,
149150
Ok(())
150151
);
151152

crates/core/tedge/src/cli/certificate/renew.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ mod tests {
8989
};
9090

9191
// First create both cert and key
92-
cmd.create_test_certificate(&CsrTemplate::default())
92+
cmd.create_test_certificate(&CsrTemplate::default(), None)
9393
.await
9494
.unwrap();
9595

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
*** Settings ***
2+
Resource tedge_cert_create.resource
3+
4+
Suite Setup tedge-p11-server Setup ${TEDGE_P11_SERVER_VERSION}
5+
6+
7+
*** Variables ***
8+
${TEDGE_P11_SERVER_VERSION} ${EMPTY}
9+
10+
11+
*** Test Cases ***
12+
Tedge cert create should use HSM configuration
13+
Test tedge cert create uses HSM configuration
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
*** Settings ***
2+
Resource tedge_cert_create.resource
3+
4+
Suite Setup tedge-p11-server Setup ${TEDGE_P11_SERVER_VERSION}
5+
6+
7+
*** Variables ***
8+
${TEDGE_P11_SERVER_VERSION} ${EMPTY}
9+
10+
11+
*** Test Cases ***
12+
Tedge cert create should use HSM configuration
13+
Test tedge cert create uses HSM configuration
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
*** Settings ***
2+
Resource ../pkcs11_common.resource
3+
4+
5+
*** Keywords ***
6+
Test tedge cert create uses HSM configuration
7+
# here assumed that we're configured to use PKCS11 private key
8+
Execute Command tedge cert remove
9+
Execute Command tedge cert create --device-id ${DEVICE_SN}
10+
11+
Execute Command
12+
... cmd=C8Y_USER='${C8Y_CONFIG.username}' C8Y_PASSWORD='${C8Y_CONFIG.password}' tedge cert upload c8y
13+
Tedge Reconnect Should Succeed

0 commit comments

Comments
 (0)