nethsm_cli/cli/
openpgp.rs

1use std::path::PathBuf;
2
3use chrono::{DateTime, Utc};
4use clap::{Parser, Subcommand};
5use expression_format::ex_format;
6use nethsm::{
7    KeyId,
8    OpenPgpUserId,
9    OpenPgpVersion,
10    UserRole::{Administrator, Operator},
11};
12use strum::IntoEnumIterator;
13
14use super::BIN_NAME;
15
16#[derive(Debug, Subcommand)]
17#[command(
18    about = "OpenPGP operations",
19    long_about = ex_format!("OpenPGP operations
20
21Supports creating OpenPGP certificates for existing keys, as well as cryptographic operations using those keys.
22
23Keys may exist in specific scopes: system-wide or in namespaces (see \"{BIN_NAME} namespace\").
24While system-wide users only have access to system-wide keys, namespaced users only have access to keys in their own namespace.")
25)]
26pub enum OpenPgpCommand {
27    Add(OpenPgpAddCommand),
28    Import(OpenPgpImportCommand),
29    Sign(OpenPgpSignCommand),
30    SignState(OpenPgpSignStateCommand),
31}
32
33#[derive(Debug, Parser)]
34#[command(
35    about = "Add an OpenPGP certificate for a key",
36    long_about = ex_format!("Add an OpenPGP certificate for a key
37
38Creates an OpenPGP certificate for an existing key.
39The created certificate is then added as the key's certificate (see \"{BIN_NAME} key cert import\").
40
41System-wide users in the \"{Administrator}\" and \"{Operator}\" role can only add OpenPGP certificates for system-wide keys.
42Namespaced users in the \"{Administrator}\" and \"{Operator}\" role can only add OpenPGP certificates for keys in their own namespace.
43
44Requires authentication of a user in the \"{Operator}\" role, that has access to the targeted key (see \"{BIN_NAME} key tag\" and \"{BIN_NAME} user tag\").
45Additionally, authentication of a user in the \"{Administrator}\" role is needed to import the certificate.")
46)]
47pub struct OpenPgpAddCommand {
48    #[arg(env = "NETHSM_KEY_ID", help = "The ID of the key to use")]
49    pub key_id: KeyId,
50
51    #[arg(env = "NETHSM_OPENPGP_USERID", help = "The User ID to use for the key")]
52    pub user_id: OpenPgpUserId,
53
54    #[arg(
55        env = "NETHSM_OPENPGP_CREATED_AT",
56        help = "The optional creation time of the certificate (defaults to now)",
57        long,
58        short
59    )]
60    pub time: Option<DateTime<Utc>>,
61
62    #[arg(
63        env = "NETHSM_OPENPGP_VERSION",
64        help = ex_format!("The OpenPGP version the certificate is created with (defaults to \"{OpenPgpVersion::default()}\")"),
65        long_help = ex_format!(
66            "The OpenPGP version the certificate is created with (defaults to \"{OpenPgpVersion::default()}\")
67
68One of {:?OpenPgpVersion::iter().map(Into::into).collect::<Vec<&'static str>>()}."
69        ),
70        long,
71        short
72    )]
73    pub version: Option<OpenPgpVersion>,
74
75    #[arg(
76        env = "NETHSM_OPENPGP_CERT_GENERATE_CAN_SIGN",
77        help = "Sets the signing key flag (default to set)",
78        long_help = "Sets the signing key flag (default to set)
79
80If this option is used, the key is created with a component key that has the signing key flag set.",
81        group = "sign-group",
82        long,
83        default_value_t = true
84    )]
85    pub can_sign: bool,
86
87    #[arg(
88        env = "NETHSM_OPENPGP_CERT_GENERATE_CANNOT_SIGN",
89        help = "Clears the signing key flag",
90        long_help = "Clears the signing key flag
91
92If this option is used, the key is created without a component key that has the signing key flag set.",
93        group = "sign-group",
94        long
95    )]
96    pub cannot_sign: bool,
97}
98
99#[derive(Debug, Parser)]
100#[command(
101    about = "Create an OpenPGP signature for a message",
102    long_about = ex_format!("Create an OpenPGP signature for a message
103
104The signature is written to stdout, unless a specific path to a file is provided.
105
106System-wide users in the \"{Operator}\" role can only create OpenPGP signatures for a message using system-wide keys.
107Namespaced users in the \"{Operator}\" role can only create OpenPGP signatures for a message using keys in their own namespace.
108
109Requires authentication of a user in the \"{Operator}\" role that has access to the targeted key (see \"{BIN_NAME} key tag\" and \"{BIN_NAME} user tag\").")
110)]
111pub struct OpenPgpSignCommand {
112    #[arg(env = "NETHSM_KEY_ID", help = "The ID of the key to use")]
113    pub key_id: KeyId,
114
115    #[arg(
116        env = "NETHSM_FORCE",
117        help = "Write to output file even if it exists already",
118        long,
119        short
120    )]
121    pub force: bool,
122
123    #[arg(
124        env = "NETHSM_OPENPGP_SIGNATURE_MESSAGE",
125        help = "The path to a message for which to create a signature"
126    )]
127    pub message: PathBuf,
128
129    #[arg(
130        env = "NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE",
131        help = "The optional path to a specific output file",
132        long,
133        short
134    )]
135    pub output: Option<PathBuf>,
136}
137
138#[derive(Debug, Parser)]
139#[command(
140    about = "Create an OpenPGP signature for a hasher state",
141    long_about = ex_format!("Create an OpenPGP signature for a hasher's state
142
143Requires a valid hasher state payload on stdin as produced by the `signstar-request-signature` binary.
144
145The signature is written to stdout, unless a specific path to a file is provided.
146
147Requires authentication of a user in the \"{Operator}\" role that has access to the targeted key.")
148)]
149pub struct OpenPgpSignStateCommand {
150    #[arg(env = "NETHSM_KEY_ID", help = "The ID of the key to use")]
151    pub key_id: KeyId,
152
153    #[arg(
154        env = "NETHSM_OPENPGP_REQUEST_SIGNATURE_FILE",
155        help = "The path to a valid signature request file"
156    )]
157    pub input: PathBuf,
158
159    #[arg(
160        env = "NETHSM_FORCE",
161        help = "Write to output file even if it exists already",
162        long,
163        short
164    )]
165    pub force: bool,
166
167    #[arg(
168        env = "NETHSM_OPENPGP_SIGNATURE_OUTPUT_FILE",
169        help = "The optional path to a specific output file",
170        long,
171        short
172    )]
173    pub output: Option<PathBuf>,
174}
175
176#[derive(Debug, Parser)]
177#[command(
178    about = "Import OpenPGP TSK-formatted private key",
179    long_about = ex_format!("Import OpenPGP Transferable Secret Key (TSK) formatted private key
180
181Only TSKs with a single component key are supported.
182
183System-wide users in the \"{Administrator}\" role can only import TSKs as system-wide keys.
184Namespaced users in the \"{Administrator}\" role can only import TSKs as keys in their own namespace.
185
186Note: Although assigning tags to the new key is optional, it is highly recommended as not doing so means that all users in the same scope have access to it!
187
188Requires authentication of a user in the \"{Administrator}\" role.")
189)]
190pub struct OpenPgpImportCommand {
191    #[arg(
192        env = "NETHSM_OPENPGP_TSK_FILE",
193        help = "The path to the Transferable Secret Key file to import"
194    )]
195    pub tsk_file: PathBuf,
196
197    #[arg(
198        env = "NETHSM_OPENPGP_KEY_ID",
199        help = "An optional unique ID that is assigned to the imported key",
200        long_help = "An optional unique ID that is assigned to the imported key
201
202If none is provided a generic one is generated for the key.",
203        long,
204        short
205    )]
206    pub key_id: Option<KeyId>,
207
208    #[arg(
209        env = "NETHSM_OPENPGP_KEY_TAGS",
210        help = "An optional list of tags that are assigned to the imported key",
211        long_help = "An optional list of tags that are assigned to the imported key
212
213Tags on keys are used to grant access to those keys for users that carry the same tags.",
214        long,
215        short
216    )]
217    pub tags: Option<Vec<String>>,
218}