use rite_sdk::SignAlgorithm;
use schemars::{JsonSchema, Schema, SchemaGenerator};
use serde::{Deserialize, Serialize};
pub const PIV_SLOT: &str = r"^(piv:)?(9[acde]|8[2-9a-fA-F]|9[0-5])$";
const PIV_SLOT_MESSAGE: &str = "'slot' must be a PIV slot: 9a, 9c, 9d, 9e, or a retired slot \
from 82 to 95";
pub const PIV_SIGN_ALGORITHMS: [SignAlgorithm; 3] = [
SignAlgorithm::EcdsaSha256,
SignAlgorithm::EcdsaSha384,
SignAlgorithm::RsaPkcs1Sha256,
];
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct PivReadCertificateParams {
#[schemars(regex(pattern = PIV_SLOT), extend("x-message" = PIV_SLOT_MESSAGE))]
pub slot: String,
#[serde(default)]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct PivSignParams {
#[serde(default = "default_sign_slot")]
#[schemars(regex(pattern = PIV_SLOT), extend("x-message" = PIV_SLOT_MESSAGE))]
pub slot: String,
#[serde(default = "default_sign_algorithm")]
#[schemars(schema_with = "piv_sign_algorithms")]
pub algorithm: SignAlgorithm,
#[serde(default)]
pub message: Option<String>,
}
fn default_sign_slot() -> String {
"9c".to_string()
}
fn default_sign_algorithm() -> SignAlgorithm {
SignAlgorithm::EcdsaSha256
}
fn piv_sign_algorithms(_: &mut SchemaGenerator) -> Schema {
let mut schema = super::schema::names(PIV_SIGN_ALGORITHMS.iter().map(|a| a.as_str()));
schema.insert(
"default".to_string(),
default_sign_algorithm().as_str().into(),
);
schema
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct AttestSlotParams {
#[schemars(regex(pattern = PIV_SLOT), extend("x-message" = PIV_SLOT_MESSAGE))]
pub slot: String,
#[serde(default)]
pub message: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_certificate_params_deserializes() {
let json = serde_json::json!({ "slot": "9a" });
let params: PivReadCertificateParams = serde_json::from_value(json).unwrap();
assert_eq!(params.slot, "9a");
assert!(params.message.is_none());
}
#[test]
fn read_certificate_params_rejects_missing_slot() {
let json = serde_json::json!({});
let result = serde_json::from_value::<PivReadCertificateParams>(json);
assert!(result.is_err());
}
#[test]
fn sign_params_defaults() {
let json = serde_json::json!({});
let params: PivSignParams = serde_json::from_value(json).unwrap();
assert_eq!(params.slot, "9c");
assert_eq!(params.algorithm, SignAlgorithm::EcdsaSha256);
assert!(params.message.is_none());
}
#[test]
fn sign_params_explicit_values() {
let json = serde_json::json!({
"slot": "9d",
"algorithm": "RSA-PKCS1-SHA256",
"message": "Sign the key"
});
let params: PivSignParams = serde_json::from_value(json).unwrap();
assert_eq!(params.slot, "9d");
assert_eq!(params.algorithm, SignAlgorithm::RsaPkcs1Sha256);
assert_eq!(params.message.as_deref(), Some("Sign the key"));
}
}