cloacina/security/key_manager.rs
1/*
2 * Copyright 2025-2026 Colliery Software
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Key manager trait and associated types.
18//!
19//! The [`KeyManager`] trait defines the interface for managing Ed25519 signing keys,
20//! trusted public keys, and trust relationships between organizations.
21
22use crate::database::universal_types::{UniversalTimestamp, UniversalUuid};
23use async_trait::async_trait;
24use thiserror::Error;
25
26/// Errors that can occur during key management operations.
27#[derive(Debug, Error)]
28pub enum KeyError {
29 #[error("Key not found: {0}")]
30 NotFound(UniversalUuid),
31
32 #[error("Key has been revoked: {0}")]
33 Revoked(UniversalUuid),
34
35 #[error("Key name already exists for this organization: {0}")]
36 DuplicateName(String),
37
38 #[error("Invalid key format: {0}")]
39 InvalidFormat(String),
40
41 #[error("Invalid PEM format: {0}")]
42 InvalidPem(String),
43
44 #[error("Encryption error: {0}")]
45 Encryption(String),
46
47 #[error("Decryption error: {0}")]
48 Decryption(String),
49
50 #[error("Trust relationship already exists")]
51 TrustAlreadyExists,
52
53 #[error("Trust relationship not found")]
54 TrustNotFound,
55
56 #[error("Database error: {0}")]
57 Database(String),
58}
59
60/// Information about a signing key (excludes private key material).
61#[derive(Debug, Clone)]
62pub struct SigningKeyInfo {
63 pub id: UniversalUuid,
64 pub org_id: UniversalUuid,
65 pub key_name: String,
66 /// SHA256 hex fingerprint of the public key
67 pub fingerprint: String,
68 /// 32-byte Ed25519 public key
69 pub public_key: Vec<u8>,
70 pub created_at: UniversalTimestamp,
71 pub revoked_at: Option<UniversalTimestamp>,
72}
73
74impl SigningKeyInfo {
75 /// Check if this key is currently active (not revoked).
76 pub fn is_active(&self) -> bool {
77 self.revoked_at.is_none()
78 }
79}
80
81/// Information about a trusted public key for verification.
82#[derive(Debug, Clone)]
83pub struct TrustedKeyInfo {
84 pub id: UniversalUuid,
85 pub org_id: UniversalUuid,
86 /// SHA256 hex fingerprint of the public key
87 pub fingerprint: String,
88 /// 32-byte Ed25519 public key
89 pub public_key: Vec<u8>,
90 /// Optional human-readable name
91 pub key_name: Option<String>,
92 pub trusted_at: UniversalTimestamp,
93 pub revoked_at: Option<UniversalTimestamp>,
94}
95
96impl TrustedKeyInfo {
97 /// Check if this key is currently trusted (not revoked).
98 pub fn is_active(&self) -> bool {
99 self.revoked_at.is_none()
100 }
101}
102
103/// Public key export in multiple formats.
104#[derive(Debug, Clone)]
105pub struct PublicKeyExport {
106 /// SHA256 hex fingerprint of the public key
107 pub fingerprint: String,
108 /// PEM-encoded public key (Ed25519 SubjectPublicKeyInfo format)
109 pub public_key_pem: String,
110 /// Raw 32-byte Ed25519 public key
111 pub public_key_raw: Vec<u8>,
112}
113
114/// Trait for managing signing keys, trusted keys, and trust relationships.
115///
116/// Implementations must be thread-safe (`Send + Sync`) and should NOT cache
117/// results to ensure key revocations take effect immediately.
118#[async_trait]
119pub trait KeyManager: Send + Sync {
120 /// Generate a new Ed25519 signing keypair and store it encrypted in the database.
121 ///
122 /// # Arguments
123 ///
124 /// * `org_id` - Organization that owns this key
125 /// * `name` - Human-readable name for the key (must be unique per org)
126 /// * `master_key` - 32-byte AES-256 key for encrypting the private key
127 ///
128 /// # Returns
129 ///
130 /// Information about the created signing key.
131 async fn create_signing_key(
132 &self,
133 org_id: UniversalUuid,
134 name: &str,
135 master_key: &[u8],
136 ) -> Result<SigningKeyInfo, KeyError>;
137
138 /// Get information about a signing key (without the private key).
139 async fn get_signing_key_info(&self, key_id: UniversalUuid)
140 -> Result<SigningKeyInfo, KeyError>;
141
142 /// Get the decrypted signing key for signing operations.
143 ///
144 /// # Arguments
145 ///
146 /// * `key_id` - The signing key ID
147 /// * `master_key` - 32-byte AES-256 key for decrypting the private key
148 ///
149 /// # Returns
150 ///
151 /// Tuple of (public_key, private_key) as raw bytes.
152 async fn get_signing_key(
153 &self,
154 key_id: UniversalUuid,
155 master_key: &[u8],
156 ) -> Result<(Vec<u8>, Vec<u8>), KeyError>;
157
158 /// Export a public key in multiple formats for distribution.
159 async fn export_public_key(&self, key_id: UniversalUuid) -> Result<PublicKeyExport, KeyError>;
160
161 /// Import an external public key to trust for verification.
162 ///
163 /// # Arguments
164 ///
165 /// * `org_id` - Organization that will trust this key
166 /// * `public_key` - 32-byte Ed25519 public key
167 /// * `name` - Optional human-readable name
168 async fn trust_public_key(
169 &self,
170 org_id: UniversalUuid,
171 public_key: &[u8],
172 name: Option<&str>,
173 ) -> Result<TrustedKeyInfo, KeyError>;
174
175 /// Import a public key from PEM format and add it to trusted keys.
176 async fn trust_public_key_pem(
177 &self,
178 org_id: UniversalUuid,
179 pem: &str,
180 name: Option<&str>,
181 ) -> Result<TrustedKeyInfo, KeyError>;
182
183 /// Revoke a signing key (prevents future signing operations).
184 async fn revoke_signing_key(&self, key_id: UniversalUuid) -> Result<(), KeyError>;
185
186 /// Revoke a trusted key (prevents future verification with this key).
187 async fn revoke_trusted_key(&self, key_id: UniversalUuid) -> Result<(), KeyError>;
188
189 /// Grant trust from a parent organization to a child organization.
190 ///
191 /// When trust is granted, the parent org implicitly trusts all keys
192 /// that are trusted by the child org.
193 async fn grant_trust(
194 &self,
195 parent_org: UniversalUuid,
196 child_org: UniversalUuid,
197 ) -> Result<(), KeyError>;
198
199 /// Revoke trust grant between organizations.
200 async fn revoke_trust(
201 &self,
202 parent_org: UniversalUuid,
203 child_org: UniversalUuid,
204 ) -> Result<(), KeyError>;
205
206 /// List all signing keys for an organization.
207 async fn list_signing_keys(
208 &self,
209 org_id: UniversalUuid,
210 ) -> Result<Vec<SigningKeyInfo>, KeyError>;
211
212 /// List all trusted keys for an organization, including inherited keys via ACLs.
213 async fn list_trusted_keys(
214 &self,
215 org_id: UniversalUuid,
216 ) -> Result<Vec<TrustedKeyInfo>, KeyError>;
217
218 /// Find a trusted key by fingerprint for verification.
219 ///
220 /// Searches both directly trusted keys and inherited keys via ACLs.
221 async fn find_trusted_key(
222 &self,
223 org_id: UniversalUuid,
224 fingerprint: &str,
225 ) -> Result<Option<TrustedKeyInfo>, KeyError>;
226}