Skip to content

Commit 5867941

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 15d995c commit 5867941

File tree

11 files changed

+166
-22
lines changed

11 files changed

+166
-22
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
@@ -6,6 +6,7 @@ use super::show::ShowCertCmd;
66
use crate::certificate_is_self_signed;
77
use crate::cli::certificate::c8y;
88
use crate::cli::certificate::create_csr::Key;
9+
use crate::cli::certificate::create_key::CreateKeyCmd;
910
use crate::cli::common::Cloud;
1011
use crate::cli::common::CloudArg;
1112
use crate::command::BuildCommand;
@@ -51,6 +52,9 @@ pub enum TEdgeCertCli {
5152
cloud: Option<CloudArg>,
5253
},
5354

55+
/// Create a new keypair
56+
CreateKey,
57+
5458
/// Renew the device certificate
5559
///
5660
/// The current certificate is left unchanged and a new certificate file is created,
@@ -220,6 +224,8 @@ impl BuildCommand for TEdgeCertCli {
220224
cmd.into_boxed()
221225
}
222226

227+
TEdgeCertCli::CreateKey => CreateKeyCmd.into_boxed(),
228+
223229
TEdgeCertCli::Show {
224230
cloud,
225231
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
@@ -157,4 +157,29 @@ impl TedgeP11Client {
157157

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

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 & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -94,23 +94,7 @@ impl Cryptoki {
9494
})
9595
}
9696

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

@@ -141,14 +125,19 @@ impl Cryptoki {
141125
let token_info = self.context.get_token_info(slot)?;
142126
debug!(?slot_info, ?token_info, "Selected slot");
143127

144-
// let supported_mechs = self.context.get_mechanism_list(slot)?;
145-
// info!(?supported_mechs);
146-
147-
let session = self.context.open_ro_session(slot)?;
128+
// let session = self.context.open_ro_session(slot)?;
129+
let session = self.context.open_rw_session(slot)?;
148130
session.login(UserType::User, Some(&self.config.pin))?;
149131
let session_info = session.get_session_info()?;
150132
debug!(?session_info, "Opened a readonly session");
151133

134+
Ok(session)
135+
}
136+
137+
pub fn signing_key(&self, uri: Option<&str>) -> anyhow::Result<Pkcs11Signer> {
138+
let uri_attributes = self.request_uri(uri)?;
139+
let session = self.open_session(&uri_attributes)?;
140+
152141
// get the signing key
153142
let key = Self::find_key_by_attributes(&uri_attributes, &session)?;
154143
let key_type = session
@@ -202,6 +191,15 @@ impl Cryptoki {
202191
Ok(key)
203192
}
204193

194+
pub fn create_key(&self, uri: Option<&str>) -> anyhow::Result<()> {
195+
let uri_attributes = self.request_uri(uri)?;
196+
let session = self.open_session(&uri_attributes)?;
197+
198+
create_key(&session).context("Failed to create a new private key")?;
199+
200+
Ok(())
201+
}
202+
205203
fn find_key_by_attributes(
206204
uri: &uri::Pkcs11Uri,
207205
session: &Session,
@@ -232,6 +230,49 @@ impl Cryptoki {
232230

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

237278
#[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)]
@@ -76,6 +77,11 @@ impl SigningService for TedgeP11Service {
7677
.context("Failed to sign using PKCS #11")?;
7778
Ok(SignResponse(signature))
7879
}
80+
81+
fn create_key(&self, uri: Option<&str>) -> anyhow::Result<()> {
82+
self.cryptoki.create_key(uri)?;
83+
Ok(())
84+
}
7985
}
8086

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

0 commit comments

Comments
 (0)