Skip to main content

confium_deployment/identity/
actor.rs

1//! Actor identity types.
2
3use crate::identity::attributes::SignerAttributes;
4use crate::identity::token::HardwareToken;
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8/// Type of actor in a Confium deployment.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum ActorType {
12    /// Measuring instrument manufacturer.
13    Manufacturer,
14    /// Testing laboratory.
15    TestingLab,
16    /// National issuing authority officer.
17    IssuingAuthorityOfficer,
18    /// BIML director (international root quorum).
19    BimlDirector,
20    /// Quorum coordinator service.
21    QuorumCoordinator,
22    /// Independent verifier.
23    Verifier,
24}
25
26/// A reference to a signing key — either software-held or hardware-backed.
27#[derive(Debug, Clone, Serialize, Deserialize)]
28#[serde(tag = "kind", rename_all = "snake_case")]
29pub enum SigningKeyHandle {
30    /// In-process software key.
31    Software {
32        /// Key identifier.
33        key_id: String,
34        /// Algorithm identifier (e.g., "Ed25519", "ECDSA-P256").
35        algorithm: String,
36    },
37    /// Hardware-backed key (HSM, YubiKey, TPM, OpenPGP card).
38    Hardware {
39        /// Key identifier.
40        key_id: String,
41        /// Algorithm identifier.
42        algorithm: String,
43        /// Hardware reference.
44        token: HardwareToken,
45    },
46}
47
48/// A reference to an encryption key — either software-held or hardware-backed.
49#[derive(Debug, Clone, Serialize, Deserialize)]
50#[serde(tag = "kind", rename_all = "snake_case")]
51pub enum EncryptionKeyHandle {
52    /// In-process software key.
53    Software {
54        /// Key identifier.
55        key_id: String,
56        /// Algorithm identifier.
57        algorithm: String,
58    },
59    /// Hardware-backed key.
60    Hardware {
61        /// Key identifier.
62        key_id: String,
63        /// Algorithm identifier.
64        algorithm: String,
65        /// Hardware reference.
66        token: HardwareToken,
67    },
68}
69
70/// A complete actor identity.
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ActorIdentity {
73    /// Unique identifier (e.g., "biml-director-alice").
74    pub actor_id: String,
75    /// Type of actor.
76    pub actor_type: ActorType,
77    /// Quorum this actor belongs to (if any).
78    pub quorum_id: Option<String>,
79    /// Handle to the signing keypair.
80    pub signing_key: SigningKeyHandle,
81    /// Handle to the encryption keypair.
82    pub encryption_key: Option<EncryptionKeyHandle>,
83    /// X.509 certificate chain (DER-encoded), leaf first.
84    pub certificate_chain_der: Vec<Vec<u8>>,
85    /// Hardware token, if any.
86    pub hardware_token: Option<HardwareToken>,
87    /// Attribute bindings for predicate-based signing.
88    pub attributes: SignerAttributes,
89    /// When this identity was registered.
90    pub registered_at: DateTime<Utc>,
91    /// When this identity expires (if it does).
92    pub expires_at: Option<DateTime<Utc>>,
93}
94
95impl ActorIdentity {
96    /// Create a new actor identity builder.
97    pub fn builder() -> ActorIdentityBuilder {
98        ActorIdentityBuilder::default()
99    }
100}
101
102/// Builder for `ActorIdentity`.
103#[derive(Debug, Default, Clone)]
104pub struct ActorIdentityBuilder {
105    actor_id: Option<String>,
106    actor_type: Option<ActorType>,
107    quorum_id: Option<String>,
108    signing_key: Option<SigningKeyHandle>,
109    encryption_key: Option<EncryptionKeyHandle>,
110    certificate_chain_der: Vec<Vec<u8>>,
111    hardware_token: Option<HardwareToken>,
112    attributes: SignerAttributes,
113    expires_at: Option<DateTime<Utc>>,
114}
115
116impl ActorIdentityBuilder {
117    /// Set the actor ID.
118    pub fn actor_id(mut self, id: impl Into<String>) -> Self {
119        self.actor_id = Some(id.into());
120        self
121    }
122    /// Set the actor type.
123    pub fn actor_type(mut self, t: ActorType) -> Self {
124        self.actor_type = Some(t);
125        self
126    }
127    /// Set the quorum ID.
128    pub fn quorum_id(mut self, id: impl Into<String>) -> Self {
129        self.quorum_id = Some(id.into());
130        self
131    }
132    /// Set the signing key handle.
133    pub fn signing_key(mut self, key: SigningKeyHandle) -> Self {
134        self.signing_key = Some(key);
135        self
136    }
137    /// Set the encryption key handle.
138    pub fn encryption_key(mut self, key: EncryptionKeyHandle) -> Self {
139        self.encryption_key = Some(key);
140        self
141    }
142    /// Set the certificate chain.
143    pub fn certificate_chain(mut self, chain: Vec<Vec<u8>>) -> Self {
144        self.certificate_chain_der = chain;
145        self
146    }
147    /// Set the hardware token.
148    pub fn hardware_token(mut self, token: HardwareToken) -> Self {
149        self.hardware_token = Some(token);
150        self
151    }
152    /// Set the attributes.
153    pub fn attributes(mut self, attrs: SignerAttributes) -> Self {
154        self.attributes = attrs;
155        self
156    }
157    /// Set the expiry time.
158    pub fn expires_at(mut self, when: DateTime<Utc>) -> Self {
159        self.expires_at = Some(when);
160        self
161    }
162
163    /// Build the identity.
164    pub fn build(self) -> Result<ActorIdentity, IdentityError> {
165        Ok(ActorIdentity {
166            actor_id: self
167                .actor_id
168                .ok_or(IdentityError::MissingField("actor_id"))?,
169            actor_type: self
170                .actor_type
171                .ok_or(IdentityError::MissingField("actor_type"))?,
172            quorum_id: self.quorum_id,
173            signing_key: self
174                .signing_key
175                .ok_or(IdentityError::MissingField("signing_key"))?,
176            encryption_key: self.encryption_key,
177            certificate_chain_der: self.certificate_chain_der,
178            hardware_token: self.hardware_token,
179            attributes: self.attributes,
180            registered_at: Utc::now(),
181            expires_at: self.expires_at,
182        })
183    }
184}
185
186/// Identity errors.
187#[derive(Debug, thiserror::Error)]
188pub enum IdentityError {
189    /// Required field missing.
190    #[error("missing required field: {0}")]
191    MissingField(&'static str),
192    /// Actor not found.
193    #[error("actor not found: {0}")]
194    NotFound(String),
195    /// Actor already exists.
196    #[error("actor already exists: {0}")]
197    AlreadyExists(String),
198    /// Serialization error.
199    #[error("serialization error: {0}")]
200    Serde(#[from] serde_json::Error),
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206
207    #[test]
208    fn builder_produces_identity() {
209        let id = ActorIdentity::builder()
210            .actor_id("biml-director-1")
211            .actor_type(ActorType::BimlDirector)
212            .signing_key(SigningKeyHandle::Software {
213                key_id: "k1".into(),
214                algorithm: "Ed25519".into(),
215            })
216            .build()
217            .expect("build");
218        assert_eq!(id.actor_id, "biml-director-1");
219        assert_eq!(id.actor_type, ActorType::BimlDirector);
220    }
221
222    #[test]
223    fn builder_fails_without_required_fields() {
224        let result = ActorIdentity::builder().build();
225        assert!(result.is_err());
226    }
227}