rite_model/params/crypto.rs
1//! Params for the key and data actions.
2
3use rite_sdk::{KeyAlgorithm, KeyUsages, SignAlgorithm, WrapScheme};
4use schemars::{JsonSchema, Schema, SchemaGenerator};
5use serde::{Deserialize, Serialize};
6
7/// Params for `generate_key` action.
8#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
9#[serde(deny_unknown_fields)]
10#[non_exhaustive]
11pub struct GenerateKeyParams {
12 /// Cryptographic algorithm (e.g., `"RSA-4096"`, `"ECDSA-P256"`).
13 #[serde(default = "default_algorithm")]
14 pub algorithm: KeyAlgorithm,
15 /// What the key is permitted to do, and whether it may leave the backend.
16 #[serde(default)]
17 pub policy: Option<KeyPolicyParams>,
18 /// Backend-specific slot hint.
19 #[serde(default)]
20 pub slot: Option<String>,
21}
22
23/// The `policy:` block, mirroring [`KeyPolicy`](rite_sdk::KeyPolicy) field
24/// for field.
25///
26/// Every field is optional and falls back to the restrictive choice: a
27/// persistent, sensitive, non-extractable key. The usages fall back to what the
28/// algorithm permits, by
29/// [`KeyPolicy::default_for`](rite_sdk::KeyPolicy::default_for), so a keypair
30/// may sign and verify and a symmetric key may wrap and unwrap. A ceremony that
31/// wraps a key it generated has to say `extractable: true`, because otherwise
32/// the key cannot leave the backend and the wrap step will refuse.
33///
34/// This is PKCS#11 vocabulary: what the token permits. The `KeyUsage`
35/// extension in a certificate is a different thing, settled by `profile:` on
36/// `issue_certificate`.
37#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
38#[serde(deny_unknown_fields)]
39#[non_exhaustive]
40pub struct KeyPolicyParams {
41 /// Key survives the session. `CKA_TOKEN`.
42 #[serde(default)]
43 pub persistent: Option<bool>,
44 /// Key material is never revealed in plaintext. `CKA_SENSITIVE`.
45 #[serde(default)]
46 pub sensitive: Option<bool>,
47 /// Key may be wrapped and leave the backend. `CKA_EXTRACTABLE`.
48 #[serde(default)]
49 pub extractable: Option<bool>,
50 /// Key may only be wrapped by a trusted wrapping key.
51 /// `CKA_WRAP_WITH_TRUSTED`.
52 #[serde(default)]
53 pub wrap_with_trusted_only: Option<bool>,
54 /// Operations the key is permitted to perform: `sign`, `verify`,
55 /// `encrypt`, `decrypt`, `wrap`, `unwrap`, `derive`.
56 #[serde(default)]
57 pub usages: Option<Vec<KeyUsage>>,
58}
59
60impl KeyPolicyParams {
61 /// Resolve the declared policy against a base.
62 ///
63 /// The base is the caller's to choose, because what a key may do when the
64 /// ceremony says nothing follows from its algorithm: see
65 /// [`KeyPolicy::default_for`](rite_sdk::KeyPolicy::default_for).
66 #[must_use]
67 pub fn resolve_from(&self, defaults: &rite_sdk::KeyPolicy) -> rite_sdk::KeyPolicy {
68 let usages = self.usages.as_ref().map_or(defaults.usages, |usages| {
69 usages
70 .iter()
71 .fold(KeyUsages::empty(), |all, usage| all | usage.0)
72 });
73 rite_sdk::KeyPolicy {
74 persistent: self.persistent.unwrap_or(defaults.persistent),
75 sensitive: self.sensitive.unwrap_or(defaults.sensitive),
76 extractable: self.extractable.unwrap_or(defaults.extractable),
77 wrap_with_trusted_only: self
78 .wrap_with_trusted_only
79 .unwrap_or(defaults.wrap_with_trusted_only),
80 usages,
81 }
82 }
83}
84
85/// One name under `policy: { usages: [...] }`, a PKCS#11 usage.
86///
87/// What the token permits the key to do. The X.509 `KeyUsage` extension is a
88/// different thing and comes from `profile:` on `issue_certificate`.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
90#[serde(into = "String", try_from = "String")]
91pub struct KeyUsage(KeyUsages);
92
93impl KeyUsage {
94 /// The one usage this name stands for.
95 #[must_use]
96 pub fn usage(self) -> KeyUsages {
97 self.0
98 }
99}
100
101impl From<KeyUsage> for String {
102 fn from(usage: KeyUsage) -> String {
103 usage.0.describe()
104 }
105}
106
107impl TryFrom<String> for KeyUsage {
108 type Error = String;
109
110 fn try_from(name: String) -> Result<Self, Self::Error> {
111 KeyUsages::usage_named(&name).map(KeyUsage).ok_or_else(|| {
112 format!(
113 "unknown key usage '{name}'. Supported: {}",
114 KeyUsages::all_names().join(", ")
115 )
116 })
117 }
118}
119
120impl JsonSchema for KeyUsage {
121 fn schema_name() -> std::borrow::Cow<'static, str> {
122 "KeyUsage".into()
123 }
124
125 fn json_schema(_: &mut SchemaGenerator) -> Schema {
126 super::schema::names(KeyUsages::all_names().into_iter())
127 }
128}
129
130fn default_algorithm() -> KeyAlgorithm {
131 KeyAlgorithm::Rsa4096
132}
133
134impl Default for GenerateKeyParams {
135 fn default() -> Self {
136 Self {
137 algorithm: default_algorithm(),
138 policy: None,
139 slot: None,
140 }
141 }
142}
143
144/// Params for `wrap_key` action.
145#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
146#[serde(deny_unknown_fields)]
147#[non_exhaustive]
148pub struct WrapKeyParams {
149 /// The wrapping scheme, defaulting to `"CMS-AES-256-GCM"`.
150 ///
151 /// Which encapsulation a scheme takes still follows the recipient key and
152 /// is never an author's choice. What the author does choose is where the
153 /// blob is going: CMS carries its own algorithm identifiers and is what a
154 /// ceremony archives, while the raw mechanisms are what a cloud KMS import
155 /// accepts and describe nothing about themselves.
156 ///
157 /// A raw scheme is a weaker record: `rite verify` cannot re-derive its
158 /// algorithms from the artifact, and says so rather than reporting the
159 /// wrap as checked.
160 ///
161 /// The AES schemes wrap under a symmetric key the backend holds, so they
162 /// are reachable only from a step reading `wrapping_key:`. They name the
163 /// KEK size, because the RFC 3394 and RFC 5649 object identifiers are
164 /// per-size and the output carries neither.
165 #[serde(default)]
166 pub scheme: Option<WrapScheme>,
167 /// The recipient's expected fingerprint, `"sha256:<hex>"` over its SPKI
168 /// DER, for a step that reads `recipient:`.
169 ///
170 /// Declaring it is what turns the recorded recipient from "whatever key
171 /// was present" into a value the ceremony committed to in advance. On
172 /// mismatch the step fails: a key wrapped to the wrong recipient cannot be
173 /// unwrapped again.
174 //
175 // A value in any other shape can never equal what the runtime computes,
176 // which renders every fingerprint in lowercase, so the step would fail in
177 // the room, after the keys exist.
178 #[serde(default)]
179 #[schemars(
180 regex(pattern = r"^sha256:[0-9a-f]{64}$"),
181 extend(
182 "x-message" = "'expect_recipient' must be a fingerprint of the form \
183 'sha256:<64 lowercase hex digits>'"
184 )
185 )]
186 pub expect_recipient: Option<String>,
187}
188
189/// Params for `sign_data` action.
190#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
191#[serde(deny_unknown_fields)]
192#[non_exhaustive]
193pub struct SignDataParams {
194 /// Signature algorithm. Defaults to the one implied by the key.
195 ///
196 /// Worth setting for an RSA key, which can sign under either
197 /// `"RSA-PKCS1-SHA256"` (the default) or `"RSA-PSS-SHA256"`. Every other
198 /// key type admits exactly one algorithm, so naming it only restates the
199 /// key.
200 #[serde(default)]
201 pub algorithm: Option<SignAlgorithm>,
202 /// Optional display message.
203 #[serde(default)]
204 pub message: Option<String>,
205}
206
207/// Params for `verify_signature` action.
208#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
209#[serde(deny_unknown_fields)]
210#[non_exhaustive]
211pub struct VerifySignatureParams {
212 /// Signature algorithm. Defaults to the one implied by the public key.
213 ///
214 /// Required when checking an RSA-PSS signature, since an RSA key alone
215 /// does not say which scheme was used.
216 #[serde(default)]
217 pub algorithm: Option<SignAlgorithm>,
218 /// Optional display message.
219 #[serde(default)]
220 pub message: Option<String>,
221}
222
223/// The identity of a key a ceremony declares in advance, in whichever form
224/// the key has one.
225///
226/// A value in neither form can never equal what the runtime computes, so the
227/// mismatch is reported before the room rather than during it.
228const KEY_IDENTITY: &str = r"^(sha256:[0-9a-f]{64}|cmac-aes:[0-9a-f]{6})$";
229
230/// Params for `unwrap_key` action.
231#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
232#[serde(deny_unknown_fields)]
233#[non_exhaustive]
234pub struct UnwrapKeyParams {
235 /// What the ceremony is restoring, as a key algorithm name.
236 ///
237 /// Nothing travels with a wrapped key saying what it is, so a recovered
238 /// blob is read as whatever this names. Required to recover a symmetric
239 /// key, whose bytes are indistinguishable from any other bytes of the same
240 /// length. Optional for a keypair, which names itself once its DER parses,
241 /// and checked against what came out when given.
242 #[serde(default)]
243 pub algorithm: Option<KeyAlgorithm>,
244 /// The expected identity of the recovered key: `"sha256:<hex>"` over its
245 /// SPKI DER for a keypair, `"cmac-aes:<hex>"` for a symmetric key.
246 ///
247 /// Usually the value the origin ceremony's `generate_key` step
248 /// recorded. On mismatch the step fails rather than importing a key the
249 /// ceremony did not mean to restore.
250 #[serde(default)]
251 #[schemars(
252 regex(pattern = KEY_IDENTITY),
253 extend("x-message" = EXPECT_KEY_MESSAGE)
254 )]
255 pub expect_key: Option<String>,
256 /// Label for the unwrapped key.
257 #[serde(default = "unwrapped_key_label")]
258 pub label: String,
259 /// What the recovered key is permitted to do, and whether it may leave
260 /// the backend again.
261 ///
262 /// Nothing travels with a wrapped key that says what it may do, so the
263 /// receiving ceremony declares that. Without a `policy:` the key may sign,
264 /// verify and be wrapped again; a key restored to serve as a wrapping key
265 /// has to say `usages: [wrap, unwrap]`.
266 #[serde(default)]
267 pub policy: Option<KeyPolicyParams>,
268}
269
270const EXPECT_KEY_MESSAGE: &str = "'expect_key' must name the key either as \
271 'sha256:<64 lowercase hex digits>', the fingerprint of a public key, or as \
272 'cmac-aes:<6 lowercase hex digits>', the check value of a symmetric key";
273
274fn unwrapped_key_label() -> String {
275 "unwrapped-key".to_string()
276}
277
278impl Default for UnwrapKeyParams {
279 fn default() -> Self {
280 Self {
281 algorithm: None,
282 expect_key: None,
283 label: unwrapped_key_label(),
284 policy: None,
285 }
286 }
287}
288
289/// Params for `import_key` action.
290#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
291#[serde(deny_unknown_fields)]
292#[non_exhaustive]
293pub struct ImportKeyParams {
294 /// What the bytes are, as a key algorithm name.
295 ///
296 /// Required, because raw material says nothing about itself: a symmetric
297 /// algorithm reads the bytes as the key itself, every other one reads them
298 /// as PKCS#8 DER. `unwrap_key` can leave this out for a keypair because the
299 /// artifact it reads carries a description; an artifact of plain bytes
300 /// carries nothing.
301 pub algorithm: KeyAlgorithm,
302 /// The expected identity of the imported key: `"sha256:<hex>"` over its
303 /// SPKI DER for a keypair, `"cmac-aes:<hex>"` for a symmetric key.
304 ///
305 /// Optional, since a ceremony may be lifting material whose identity it
306 /// learns only afterwards. Where it is known it is the only evidence
307 /// available about material this ceremony did not itself produce.
308 #[serde(default)]
309 #[schemars(
310 regex(pattern = KEY_IDENTITY),
311 extend("x-message" = EXPECT_KEY_MESSAGE)
312 )]
313 pub expect_key: Option<String>,
314 /// Label for the imported key.
315 #[serde(default = "imported_key_label")]
316 pub label: String,
317 /// What the imported key is permitted to do, and whether it may leave the
318 /// backend again.
319 ///
320 /// Nothing travels with raw key material saying what it may do, so the
321 /// ceremony declares it, exactly as at `unwrap_key`.
322 #[serde(default)]
323 pub policy: Option<KeyPolicyParams>,
324}
325
326fn imported_key_label() -> String {
327 "imported-key".to_string()
328}
329
330/// Params for `encrypt_data` action.
331#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
332#[serde(deny_unknown_fields)]
333#[non_exhaustive]
334pub struct EncryptDataParams {
335 /// The container, defaulting to `"CMS-AES-256-GCM"`.
336 ///
337 /// The only value this action accepts today. It is written out rather than
338 /// implied because a second content cipher means a second container, and a
339 /// ceremony that names the one it used stays readable when there are two.
340 //
341 // Narrower than `wrap_key`'s vocabulary on purpose: the container is the
342 // only thing `encrypt_data` writes.
343 #[serde(default)]
344 #[schemars(schema_with = "content_schemes")]
345 pub scheme: Option<WrapScheme>,
346}
347
348/// The schemes that carry content, which are the ones `encrypt_data` writes.
349fn content_schemes(_: &mut SchemaGenerator) -> Schema {
350 super::schema::names(
351 WrapScheme::ALL
352 .iter()
353 .filter(|scheme| scheme.carries_content())
354 .map(|scheme| scheme.as_str()),
355 )
356}