signer.rs (3627B)
1 //! Object-safe signer service-provider interface. 2 3 use core::{future::Future, pin::Pin}; 4 5 #[cfg(not(feature = "std"))] 6 use alloc::{boxed::Box, sync::Arc}; 7 #[cfg(feature = "std")] 8 use std::{boxed::Box, sync::Arc}; 9 10 use crate::{ 11 AuthoredSignEvidence, Error, SignReceipt, SignRequest, SignerStatus, error::Kind, 12 receipt::verify_identity, 13 }; 14 15 pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; 16 17 /// Protocol-neutral, caller-driven signing service-provider interface. 18 /// 19 /// Implementations must document their durable remote-effect point. Dropping 20 /// a future after that point does not imply rollback. Replay behavior is 21 /// advertised through signer status and every successful result must use a 22 /// verified receipt or authored-evidence constructor. 23 pub trait Signer: Send + Sync { 24 fn status(&self) -> BoxFuture<'_, Result<SignerStatus, Error>>; 25 26 /// Signs one already-authorized exact plan. 27 /// 28 /// Implementations must observe the request's millisecond deadline and 29 /// cancellation signal throughout the operation, preserve its stable 30 /// signer request ID for remote replay, and create success only through 31 /// [`SignReceipt::from_signed_event`]. 32 fn sign(&self, request: SignRequest) -> BoxFuture<'_, Result<SignReceipt, Error>>; 33 34 /// Signs an authored plan while retaining any verified result of started work. 35 /// 36 /// An override must honor deadline and cancellation before starting work, 37 /// but may return exact evidence received afterward. Such evidence is not 38 /// permission to resume stopped work. The composing host owns polling and 39 /// durable reconciliation; dropping this future does not undo a signature. 40 /// 41 /// The default delegates to [`Self::sign`] and preserves existing adapters. 42 /// It cannot recover evidence that the adapter discards. Blossom requests 43 /// and already-cancelled requests are rejected before invoking that adapter. 44 fn sign_authored_evidence( 45 &self, 46 request: SignRequest, 47 ) -> BoxFuture<'_, Result<AuthoredSignEvidence, Error>> { 48 Box::pin(async move { 49 if request.authored_plan().is_none() { 50 return Err(Error::new(Kind::InvalidArgument)); 51 } 52 if request.cancellation_signal().is_cancelled() { 53 return Err(Error::new(Kind::SignerCancelled)); 54 } 55 let receipt = self.sign(request.clone()).await?; 56 verify_identity( 57 receipt.operation_kind(), 58 receipt.intent_id(), 59 receipt.signer_request_id(), 60 &request, 61 )?; 62 AuthoredSignEvidence::from_signed_event( 63 &request, 64 receipt.signed_event().clone(), 65 receipt.completed_at_unix_ms(), 66 ) 67 }) 68 } 69 } 70 71 /// Shared signer handle used by composing hosts without selecting a runtime. 72 pub type DynSigner = Arc<dyn Signer>; 73 74 #[cfg(test)] 75 mod tests { 76 use super::*; 77 78 struct Stub; 79 80 impl Signer for Stub { 81 fn status(&self) -> BoxFuture<'_, Result<SignerStatus, Error>> { 82 Box::pin(async { Ok(SignerStatus::unavailable()) }) 83 } 84 85 fn sign(&self, _request: SignRequest) -> BoxFuture<'_, Result<SignReceipt, Error>> { 86 Box::pin(async { Err(Error::new(Kind::SignerUnavailable)) }) 87 } 88 } 89 90 #[test] 91 fn signer_remains_dyn_send_and_sync() { 92 fn assert_dyn(_: &dyn Signer) {} 93 fn assert_send_sync<T: Send + Sync + ?Sized>() {} 94 assert_dyn(&Stub); 95 assert_send_sync::<dyn Signer>(); 96 } 97 }