1use base64::Engine;
2use base64::engine::general_purpose::URL_SAFE_NO_PAD;
3use serde::{Deserialize, Serialize};
4
5use auths_core::ports::clock::ClockProvider;
6use auths_core::signing::{PassphraseProvider, SecureSigner};
7use auths_core::storage::keychain::KeyAlias;
8
9use crate::error::SetupError;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct PlatformClaim {
20 #[serde(rename = "type")]
22 pub claim_type: String,
23 pub platform: String,
25 pub namespace: String,
27 pub did: String,
29 pub timestamp: String,
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub signature: Option<String>,
34}
35
36pub fn create_platform_claim(
54 platform: &str,
55 namespace: &str,
56 did: &str,
57 key_alias: &KeyAlias,
58 signer: &dyn SecureSigner,
59 passphrase_provider: &dyn PassphraseProvider,
60 clock: &dyn ClockProvider,
61) -> Result<String, SetupError> {
62 let mut claim = PlatformClaim {
63 claim_type: "platform_claim".to_string(),
64 platform: platform.to_string(),
65 namespace: namespace.to_string(),
66 did: did.to_string(),
67 timestamp: clock.now().to_rfc3339(),
68 signature: None,
69 };
70
71 let unsigned_json = serde_json::to_value(&claim)
72 .map_err(|e| SetupError::PlatformVerificationFailed(format!("serialize claim: {e}")))?;
73 let canonical = json_canon::to_string(&unsigned_json)
74 .map_err(|e| SetupError::PlatformVerificationFailed(format!("canonicalize claim: {e}")))?;
75
76 let signature_bytes = signer
77 .sign_with_alias(key_alias, passphrase_provider, canonical.as_bytes())
78 .map_err(|e| SetupError::PlatformVerificationFailed(format!("sign claim: {e}")))?;
79
80 claim.signature = Some(URL_SAFE_NO_PAD.encode(&signature_bytes));
81
82 serde_json::to_string_pretty(&claim)
83 .map_err(|e| SetupError::PlatformVerificationFailed(format!("serialize signed claim: {e}")))
84}