canon_protocol/
signature.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5/// Canon Protocol Signature
6/// Conforms to canon-protocol.org/signature@0.1.0
7#[derive(Debug, Serialize, Deserialize)]
8pub struct CanonSignature {
9    pub signature_version: String,
10    pub manifest_hash: String,
11    pub signature: SignatureData,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub metadata: Option<SignatureMetadata>,
14}
15
16/// Signature algorithm enum
17#[derive(Debug, Clone, Serialize, Deserialize)]
18#[serde(rename_all = "kebab-case")]
19pub enum SignatureAlgorithm {
20    #[serde(rename = "ed25519")]
21    Ed25519,
22    #[serde(rename = "rsa-4096")]
23    Rsa4096,
24}
25
26#[derive(Debug, Serialize, Deserialize)]
27pub struct SignatureData {
28    pub algorithm: SignatureAlgorithm,
29    pub key_id: String, // Format: publisher/keys/id
30    pub signature: String,
31    pub signed_at: DateTime<Utc>,
32}
33
34/// Optional signature metadata
35#[derive(Debug, Serialize, Deserialize)]
36pub struct SignatureMetadata {
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub tool: Option<String>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub environment: Option<SignatureEnvironment>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub notes: Option<String>,
43}
44
45/// Signature environment enum
46#[derive(Debug, Clone, Serialize, Deserialize)]
47#[serde(rename_all = "lowercase")]
48pub enum SignatureEnvironment {
49    Development,
50    Staging,
51    Production,
52}
53
54/// Publisher public keys manifest
55/// Conforms to canon-protocol.org/keys@0.1.0
56#[derive(Debug, Serialize, Deserialize)]
57pub struct PublisherKeys {
58    pub version: String,
59    pub keys: HashMap<String, PublicKey>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub revoked_keys: Option<HashMap<String, RevokedKey>>,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub contact: Option<SecurityContact>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub metadata: Option<KeysMetadata>,
66}
67
68/// Key algorithm enum
69#[derive(Debug, Clone, Serialize, Deserialize)]
70#[serde(rename_all = "kebab-case")]
71pub enum KeyAlgorithm {
72    #[serde(rename = "ed25519")]
73    Ed25519,
74    #[serde(rename = "rsa-4096")]
75    Rsa4096,
76}
77
78/// Key usage enum
79#[derive(Debug, Clone, Serialize, Deserialize)]
80#[serde(rename_all = "lowercase")]
81pub enum KeyUsage {
82    Signing,
83    Encryption,
84    Both,
85}
86
87#[derive(Debug, Serialize, Deserialize)]
88pub struct PublicKey {
89    pub algorithm: KeyAlgorithm,
90    pub public_key: String,
91    pub created_at: DateTime<Utc>,
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub expires_at: Option<DateTime<Utc>>,
94    #[serde(default)]
95    pub revoked: bool,
96    pub usage: KeyUsage,
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub comment: Option<String>,
99}
100
101/// Revoked key entry
102#[derive(Debug, Serialize, Deserialize)]
103pub struct RevokedKey {
104    pub revoked_at: DateTime<Utc>,
105    pub reason: RevocationReason,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub comment: Option<String>,
108}
109
110/// Key revocation reason enum
111#[derive(Debug, Clone, Serialize, Deserialize)]
112#[serde(rename_all = "snake_case")]
113pub enum RevocationReason {
114    KeyRotation,
115    Compromise,
116    Superseded,
117    Cessation,
118    Other,
119}
120
121/// Security contact information
122#[derive(Debug, Serialize, Deserialize)]
123pub struct SecurityContact {
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub email: Option<String>,
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub url: Option<String>,
128}
129
130/// Additional keys metadata
131#[derive(Debug, Serialize, Deserialize)]
132pub struct KeysMetadata {
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub rotation_policy: Option<String>,
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub backup_keys: Option<Vec<String>>,
137}
138
139// Keep for backward compatibility (will be removed in future versions)
140pub use PublicKey as PublisherKey;