Skip to main content

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}