1use rite_sdk::SignAlgorithm;
4use schemars::{JsonSchema, Schema, SchemaGenerator};
5use serde::{Deserialize, Serialize};
6
7pub const PIV_SLOT: &str = r"^(piv:)?(9[acde]|8[2-9a-fA-F]|9[0-5])$";
13
14const PIV_SLOT_MESSAGE: &str = "'slot' must be a PIV slot: 9a, 9c, 9d, 9e, or a retired slot \
15 from 82 to 95";
16
17pub const PIV_SIGN_ALGORITHMS: [SignAlgorithm; 3] = [
21 SignAlgorithm::EcdsaSha256,
22 SignAlgorithm::EcdsaSha384,
23 SignAlgorithm::RsaPkcs1Sha256,
24];
25
26#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
28#[serde(deny_unknown_fields)]
29#[non_exhaustive]
30pub struct PivReadCertificateParams {
31 #[schemars(regex(pattern = PIV_SLOT), extend("x-message" = PIV_SLOT_MESSAGE))]
34 pub slot: String,
35 #[serde(default)]
37 pub message: Option<String>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
42#[serde(deny_unknown_fields)]
43#[non_exhaustive]
44pub struct PivSignParams {
45 #[serde(default = "default_sign_slot")]
47 #[schemars(regex(pattern = PIV_SLOT), extend("x-message" = PIV_SLOT_MESSAGE))]
48 pub slot: String,
49 #[serde(default = "default_sign_algorithm")]
51 #[schemars(schema_with = "piv_sign_algorithms")]
52 pub algorithm: SignAlgorithm,
53 #[serde(default)]
55 pub message: Option<String>,
56}
57
58fn default_sign_slot() -> String {
59 "9c".to_string()
60}
61
62fn default_sign_algorithm() -> SignAlgorithm {
63 SignAlgorithm::EcdsaSha256
64}
65
66fn piv_sign_algorithms(_: &mut SchemaGenerator) -> Schema {
67 let mut schema = super::schema::names(PIV_SIGN_ALGORITHMS.iter().map(|a| a.as_str()));
68 schema.insert(
69 "default".to_string(),
70 default_sign_algorithm().as_str().into(),
71 );
72 schema
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
77#[serde(deny_unknown_fields)]
78#[non_exhaustive]
79pub struct AttestSlotParams {
80 #[schemars(regex(pattern = PIV_SLOT), extend("x-message" = PIV_SLOT_MESSAGE))]
82 pub slot: String,
83 #[serde(default)]
85 pub message: Option<String>,
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn read_certificate_params_deserializes() {
94 let json = serde_json::json!({ "slot": "9a" });
95 let params: PivReadCertificateParams = serde_json::from_value(json).unwrap();
96 assert_eq!(params.slot, "9a");
97 assert!(params.message.is_none());
98 }
99
100 #[test]
101 fn read_certificate_params_rejects_missing_slot() {
102 let json = serde_json::json!({});
103 let result = serde_json::from_value::<PivReadCertificateParams>(json);
104 assert!(result.is_err());
105 }
106
107 #[test]
108 fn sign_params_defaults() {
109 let json = serde_json::json!({});
110 let params: PivSignParams = serde_json::from_value(json).unwrap();
111 assert_eq!(params.slot, "9c");
112 assert_eq!(params.algorithm, SignAlgorithm::EcdsaSha256);
113 assert!(params.message.is_none());
114 }
115
116 #[test]
117 fn sign_params_explicit_values() {
118 let json = serde_json::json!({
119 "slot": "9d",
120 "algorithm": "RSA-PKCS1-SHA256",
121 "message": "Sign the key"
122 });
123 let params: PivSignParams = serde_json::from_value(json).unwrap();
124 assert_eq!(params.slot, "9d");
125 assert_eq!(params.algorithm, SignAlgorithm::RsaPkcs1Sha256);
126 assert_eq!(params.message.as_deref(), Some("Sign the key"));
127 }
128}