Skip to content

Commit 83b1de7

Browse files
committed
Add tedge cert create-key command
The command uses TedgeP11Client to create a new RSA keypair on the PKCS11 token. Signed-off-by: Marcel Guzik <marcel.guzik@cumulocity.com>
1 parent b510d88 commit 83b1de7

File tree

10 files changed

+146
-19
lines changed

10 files changed

+146
-19
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/core/tedge/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ tar = { workspace = true }
5050
tedge-agent = { workspace = true }
5151
tedge-apt-plugin = { workspace = true }
5252
tedge-mapper = { workspace = true, default-features = false }
53+
tedge-p11-server = { workspace = true }
5354
tedge-watchdog = { workspace = true }
5455
tedge-write = { workspace = true }
5556
tedge_api = { workspace = true }

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use super::renew::RenewCertCmd;
55
use super::show::ShowCertCmd;
66
use crate::certificate_is_self_signed;
77
use crate::cli::certificate::c8y;
8+
use crate::cli::certificate::create_key::CreateKeyCmd;
89
use crate::cli::common::Cloud;
910
use crate::cli::common::CloudArg;
1011
use crate::command::BuildCommand;
@@ -49,6 +50,9 @@ pub enum TEdgeCertCli {
4950
cloud: Option<CloudArg>,
5051
},
5152

53+
/// Create a new keypair
54+
CreateKey,
55+
5256
/// Renew the device certificate
5357
///
5458
/// The current certificate is left unchanged and a new certificate file is created,
@@ -200,6 +204,8 @@ impl BuildCommand for TEdgeCertCli {
200204
cmd.into_boxed()
201205
}
202206

207+
TEdgeCertCli::CreateKey => CreateKeyCmd.into_boxed(),
208+
203209
TEdgeCertCli::Show {
204210
cloud,
205211
cert_path,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use tedge_config::TEdgeConfig;
2+
3+
use crate::command::Command;
4+
use crate::log::MaybeFancy;
5+
6+
pub struct CreateKeyCmd;
7+
8+
#[async_trait::async_trait]
9+
impl Command for CreateKeyCmd {
10+
fn description(&self) -> String {
11+
"Generate a keypair.".into()
12+
}
13+
14+
async fn execute(&self, config: TEdgeConfig) -> Result<(), MaybeFancy<anyhow::Error>> {
15+
let socket_path = &config.device.cryptoki.socket_path;
16+
let pkcs11client = tedge_p11_server::client::TedgeP11Client::with_ready_check(
17+
socket_path.as_std_path().into(),
18+
);
19+
pkcs11client.create_key(None)?;
20+
eprintln!("New keypair was successfully created.");
21+
Ok(())
22+
}
23+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod c8y;
66
mod cli;
77
mod create;
88
mod create_csr;
9+
mod create_key;
910
mod error;
1011
mod remove;
1112
mod renew;

crates/extensions/tedge-p11-server/src/client.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,29 @@ impl TedgeP11Client {
149149

150150
Ok(response.0)
151151
}
152+
153+
pub fn create_key(&self, uri: Option<String>) -> anyhow::Result<()> {
154+
let stream = UnixStream::connect(&self.socket_path).with_context(|| {
155+
format!(
156+
"Failed to connect to tedge-p11-server UNIX socket at '{}'",
157+
self.socket_path.display()
158+
)
159+
})?;
160+
let mut connection = crate::connection::Connection::new(stream);
161+
debug!("Connected to socket");
162+
163+
let request = Frame1::CreateKeyRequest(uri);
164+
trace!(?request);
165+
connection.write_frame(&request)?;
166+
167+
let response = connection.read_frame()?;
168+
169+
let Frame1::CreateKeyResponse = response else {
170+
bail!("protocol error: bad response, expected sign, received: {response:?}");
171+
};
172+
173+
debug!("Sign complete");
174+
175+
Ok(())
176+
}
152177
}

crates/extensions/tedge-p11-server/src/connection.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ pub enum Frame1 {
8989
SignRequest(SignRequest),
9090
ChooseSchemeResponse(ChooseSchemeResponse),
9191
SignResponse(SignResponse),
92+
CreateKeyRequest(Option<String>),
93+
CreateKeyResponse,
9294
}
9395

9496
/// An error that can be returned to the client by the server.

crates/extensions/tedge-p11-server/src/pkcs11/mod.rs

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,23 +92,7 @@ impl Cryptoki {
9292
})
9393
}
9494

95-
pub fn signing_key(&self, uri: Option<&str>) -> anyhow::Result<Pkcs11SigningKey> {
96-
let mut config_uri = self
97-
.config
98-
.uri
99-
.as_deref()
100-
.map(|u| uri::Pkcs11Uri::parse(u).context("Failed to parse config PKCS#11 URI"))
101-
.transpose()?
102-
.unwrap_or_default();
103-
104-
let request_uri = uri
105-
.map(|uri| uri::Pkcs11Uri::parse(uri).context("Failed to parse PKCS #11 URI"))
106-
.transpose()?
107-
.unwrap_or_default();
108-
109-
config_uri.append_attributes(request_uri);
110-
let uri_attributes = config_uri;
111-
95+
fn open_session(&self, uri_attributes: &uri::Pkcs11Uri) -> anyhow::Result<Session> {
11296
let wanted_label = uri_attributes.token.as_ref();
11397
let wanted_serial = uri_attributes.serial.as_ref();
11498

@@ -139,11 +123,19 @@ impl Cryptoki {
139123
let token_info = self.context.get_token_info(slot)?;
140124
debug!(?slot_info, ?token_info, "Selected slot");
141125

142-
let session = self.context.open_ro_session(slot)?;
126+
// let session = self.context.open_ro_session(slot)?;
127+
let session = self.context.open_rw_session(slot)?;
143128
session.login(UserType::User, Some(&self.config.pin))?;
144129
let session_info = session.get_session_info()?;
145130
debug!(?session_info, "Opened a readonly session");
146131

132+
Ok(session)
133+
}
134+
135+
pub fn signing_key(&self, uri: Option<&str>) -> anyhow::Result<Pkcs11SigningKey> {
136+
let uri_attributes = self.request_uri(uri)?;
137+
let session = self.open_session(&uri_attributes)?;
138+
147139
// get the signing key
148140
let key = Self::find_key_by_attributes(&uri_attributes, &session)?;
149141
let key_type = session
@@ -197,6 +189,15 @@ impl Cryptoki {
197189
Ok(key)
198190
}
199191

192+
pub fn create_key(&self, uri: Option<&str>) -> anyhow::Result<()> {
193+
let uri_attributes = self.request_uri(uri)?;
194+
let session = self.open_session(&uri_attributes)?;
195+
196+
create_key(&session).context("Failed to create a new private key")?;
197+
198+
Ok(())
199+
}
200+
200201
fn find_key_by_attributes(
201202
uri: &uri::Pkcs11Uri,
202203
session: &Session,
@@ -227,6 +228,49 @@ impl Cryptoki {
227228

228229
Ok(key)
229230
}
231+
232+
fn request_uri<'a>(
233+
&'a self,
234+
request_uri: Option<&'a str>,
235+
) -> anyhow::Result<uri::Pkcs11Uri<'a>> {
236+
let mut config_uri = self
237+
.config
238+
.uri
239+
.as_deref()
240+
.map(|u| uri::Pkcs11Uri::parse(u).context("Failed to parse config PKCS#11 URI"))
241+
.transpose()?
242+
.unwrap_or_default();
243+
244+
let request_uri = request_uri
245+
.map(|uri| uri::Pkcs11Uri::parse(uri).context("Failed to parse PKCS #11 URI"))
246+
.transpose()?
247+
.unwrap_or_default();
248+
249+
config_uri.append_attributes(request_uri);
250+
Ok(config_uri)
251+
}
252+
}
253+
254+
fn create_key(session: &Session) -> anyhow::Result<()> {
255+
session
256+
.generate_key_pair(
257+
&Mechanism::RsaPkcsKeyPairGen,
258+
&[
259+
Attribute::Token(true),
260+
Attribute::Verify(true),
261+
Attribute::Encrypt(true),
262+
Attribute::ModulusBits(2048.into()),
263+
],
264+
&[
265+
Attribute::Token(true),
266+
Attribute::Sensitive(true),
267+
Attribute::Extractable(false),
268+
Attribute::Sign(true),
269+
Attribute::Decrypt(true),
270+
],
271+
)
272+
.context("Failed to generate keypair")?;
273+
Ok(())
230274
}
231275

232276
#[derive(Debug, Clone)]

crates/extensions/tedge-p11-server/src/server.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ impl TedgeP11Server {
5151
let response = match request {
5252
Frame1::Error(_)
5353
| Frame1::ChooseSchemeResponse { .. }
54-
| Frame1::SignResponse { .. } => {
54+
| Frame1::SignResponse { .. }
55+
| Frame1::CreateKeyResponse => {
5556
let error = ProtocolError("invalid request".to_string());
5657
let _ = connection.write_frame(&Frame1::Error(error));
5758
anyhow::bail!("protocol error: invalid request")
@@ -82,6 +83,19 @@ impl TedgeP11Server {
8283
}
8384
}
8485
}
86+
Frame1::CreateKeyRequest(uri) => {
87+
let response = self.service.create_key(uri.as_deref());
88+
match response {
89+
Ok(_) => Frame1::CreateKeyResponse,
90+
Err(err) => {
91+
let response = Frame1::Error(ProtocolError(format!(
92+
"PKCS #11 service failed: {err:#}"
93+
)));
94+
connection.write_frame(&response)?;
95+
anyhow::bail!(err);
96+
}
97+
}
98+
}
8599
};
86100

87101
connection.write_frame(&response).context("write")?;
@@ -119,6 +133,10 @@ mod tests {
119133
fn sign(&self, _request: SignRequest) -> anyhow::Result<SignResponse> {
120134
Ok(SignResponse(SIGNATURE.to_vec()))
121135
}
136+
137+
fn create_key(&self, uri: Option<&str>) -> anyhow::Result<()> {
138+
todo!()
139+
}
122140
}
123141

124142
/// Check that client successfully receives responses from the server about the requests. Tests the

crates/extensions/tedge-p11-server/src/service.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use tracing::warn;
1313
pub trait SigningService {
1414
fn choose_scheme(&self, request: ChooseSchemeRequest) -> anyhow::Result<ChooseSchemeResponse>;
1515
fn sign(&self, request: SignRequest) -> anyhow::Result<SignResponse>;
16+
fn create_key(&self, uri: Option<&str>) -> anyhow::Result<()>;
1617
}
1718

1819
#[derive(Debug)]
@@ -77,6 +78,11 @@ impl SigningService for TedgeP11Service {
7778
.context("Failed to sign using PKCS #11")?;
7879
Ok(SignResponse(signature))
7980
}
81+
82+
fn create_key(&self, uri: Option<&str>) -> anyhow::Result<()> {
83+
self.cryptoki.create_key(uri)?;
84+
Ok(())
85+
}
8086
}
8187

8288
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

0 commit comments

Comments
 (0)