@@ -6,14 +6,20 @@ use super::show::ShowCertCmd;
66use crate :: certificate_is_self_signed;
77use crate :: cli:: certificate:: c8y;
88use crate :: cli:: certificate:: create_csr:: Key ;
9+ use crate :: cli:: certificate:: create_key:: CreateKeyHsmCmd ;
10+ use crate :: cli:: certificate:: create_key:: EcCurve ;
11+ use crate :: cli:: certificate:: create_key:: KeyType ;
12+ use crate :: cli:: certificate:: create_key:: RsaBits ;
913use crate :: cli:: common:: Cloud ;
1014use crate :: cli:: common:: CloudArg ;
1115use crate :: command:: BuildCommand ;
1216use crate :: command:: Command ;
1317use crate :: CertificateShift ;
1418use crate :: ConfigError ;
1519use anyhow:: anyhow;
20+ use anyhow:: Context ;
1621use c8y_api:: http_proxy:: C8yEndPoint ;
22+ use camino:: Utf8Path ;
1723use camino:: Utf8PathBuf ;
1824use certificate:: CsrTemplate ;
1925use clap:: ValueHint ;
@@ -51,6 +57,73 @@ pub enum TEdgeCertCli {
5157 cloud : Option < CloudArg > ,
5258 } ,
5359
60+ /// Generate a new keypair on the PKCS #11 token and select it to be used.
61+ ///
62+ /// Can be used to generate a keypair on the TOKEN. If TOKEN argument is not provided, the
63+ /// command prints the available tokens.
64+ ///
65+ /// If TOKEN is provided, the command generates an RSA or an ECDSA keypair on the token. When
66+ /// using RSA, `--bits` is used to set the size of the key, when using ECDSA, `--curve` is used.
67+ ///
68+ /// After the key is generated, tedge config is updated to use the new key using
69+ /// `device.key_uri` property. Depending on the selected cloud, we use `device.key_uri` setting
70+ /// for that cloud, e.g. `create-key-hsm c8y` will write to `c8y.device.key_uri`.
71+ CreateKeyHsm {
72+ /// Human readable description (CKA_LABEL attribute) for the key.
73+ #[ arg( long, default_value = "tedge" ) ]
74+ label : String ,
75+
76+ /// Key identifier for the keypair (CKA_ID attribute).
77+ ///
78+ /// If provided and no object exists on the token with the same ID, this will be the ID of
79+ /// the new keypair. If an object with this ID already exists, the operation will return an
80+ /// error. If not provided, a random ID will be generated and used by the keypair.
81+ ///
82+ /// The id shall be provided as a sequence of hex digits without `0x` prefix, optionally
83+ /// separated by spaces, e.g. `--id 010203` or `--id "01 02 03"`.
84+ #[ arg( long) ]
85+ id : Option < String > ,
86+
87+ /// The type of the key.
88+ #[ arg( long, default_value = "ecdsa" ) ]
89+ r#type : KeyType ,
90+
91+ /// The size of the RSA keys in bits. Should only be used with --type rsa.
92+ #[ arg( long, default_value = "2048" , group = "key_params" ) ]
93+ bits : RsaBits ,
94+
95+ /// The curve (size) of the ECDSA key. Should only be used with --type ecdsa.
96+ #[ arg( long, default_value = "p256" , group = "key_params" ) ]
97+ curve : EcCurve ,
98+
99+ /// User PIN value for logging into the PKCS #11 token.
100+ ///
101+ /// This flag can be used to provide a PIN when creating a new key without needing to update
102+ /// tedge-config, which can be helpful when initializing keys on new tokens.
103+ ///
104+ /// Note that in contrast to the URI of the key, which will be written to tedge-config
105+ /// automatically when the keypair is created, PIN will not be written automatically and may
106+ /// be needed to written manually using tedge config set (if not using tedge-p11-server with
107+ /// the correct default PIN).
108+ #[ arg( long) ]
109+ pin : Option < String > ,
110+
111+ /// Path where public key will be saved when a keypair is generated.
112+ #[ arg( long) ]
113+ outfile_pubkey : Option < Box < Utf8Path > > ,
114+
115+ // can't document subcommands here because one would have to document variants of the enum
116+ // but this type is used in other places
117+ #[ clap( subcommand) ]
118+ cloud : Option < CloudArg > ,
119+
120+ /// The URI of the token where the keypair should be created.
121+ ///
122+ /// If this argument is missing, a list of available initialized tokens will be shown. The
123+ /// token needs to be initialized to be able to generate keys.
124+ token : Option < String > ,
125+ } ,
126+
54127 /// Renew the device certificate
55128 ///
56129 /// The current certificate is left unchanged and a new certificate file is created,
@@ -220,6 +293,42 @@ impl BuildCommand for TEdgeCertCli {
220293 cmd. into_boxed ( )
221294 }
222295
296+ TEdgeCertCli :: CreateKeyHsm {
297+ bits,
298+ label,
299+ r#type,
300+ curve,
301+ id,
302+ pin,
303+ outfile_pubkey,
304+
305+ cloud,
306+ token,
307+ } => {
308+ let cloud: Option < Cloud > = cloud. map ( <_ >:: try_into) . transpose ( ) ?;
309+ let cloud_config = cloud
310+ . as_ref ( )
311+ . map ( |c| config. as_cloud_config ( ( c) . into ( ) ) )
312+ . transpose ( ) ?;
313+ let cryptoki_config = config
314+ . device
315+ . cryptoki_config ( cloud_config) ?
316+ . context ( "Cryptoki config is not enabled" ) ?;
317+
318+ CreateKeyHsmCmd {
319+ cryptoki_config,
320+ label,
321+ r#type,
322+ bits,
323+ curve,
324+ id,
325+ pin,
326+ outfile_pubkey,
327+ cloud,
328+ token,
329+ }
330+ . into_boxed ( )
331+ }
223332 TEdgeCertCli :: Show {
224333 cloud,
225334 cert_path,
0 commit comments