evidence.rs (9635B)
1 use super::*; 2 use radroots_signing::{AuthoredSignEvidence, Signer, SignerStatus, signer::BoxFuture}; 3 use std::{ 4 sync::atomic::{AtomicUsize, Ordering}, 5 task::{Context, Poll, Waker}, 6 }; 7 8 fn ready<T>(mut future: BoxFuture<'_, T>) -> T { 9 match future 10 .as_mut() 11 .poll(&mut Context::from_waker(Waker::noop())) 12 { 13 Poll::Ready(value) => value, 14 Poll::Pending => panic!("fixture must complete without an executor"), 15 } 16 } 17 18 #[test] 19 fn late_and_cancelled_evidence_preserves_exact_bytes_without_active_success() { 20 let request = request(); 21 let event = signed_event(); 22 let evidence = AuthoredSignEvidence::from_signed_event(&request, event.clone(), DEADLINE_MS) 23 .expect("late signature remains a fact"); 24 assert_eq!(evidence.operation_kind(), request.operation_kind()); 25 assert_eq!(evidence.intent_id(), request.intent_id()); 26 assert_eq!(evidence.signer_request_id(), request.signer_request_id()); 27 assert_eq!(evidence.observed_at_unix_ms(), DEADLINE_MS); 28 assert_eq!(evidence.signed_event(), &event); 29 assert_eq!( 30 SignReceipt::from_signed_event(&request, event.clone(), DEADLINE_MS) 31 .unwrap_err() 32 .kind(), 33 Kind::DeadlineExceeded 34 ); 35 request.cancellation_signal().cancel(); 36 let later = evidence 37 .revalidate(&request, DEADLINE_MS + 1) 38 .expect("retain after cancellation"); 39 assert_eq!(later.signed_event(), &event); 40 assert_eq!(later.observed_at_unix_ms(), DEADLINE_MS + 1); 41 assert_eq!( 42 SignReceipt::from_signed_event(&request, event.clone(), DEADLINE_MS - 1) 43 .unwrap_err() 44 .kind(), 45 Kind::SignerCancelled 46 ); 47 assert_eq!( 48 AuthoredSignEvidence::from_signed_event(&request, event, 0) 49 .unwrap_err() 50 .kind(), 51 Kind::InvalidArgument 52 ); 53 assert_eq!( 54 later.revalidate(&request, 0).unwrap_err().kind(), 55 Kind::InvalidArgument 56 ); 57 let debug = format!("{later:?}"); 58 assert!(debug.contains(later.signed_event().id_str())); 59 assert!(!debug.contains("exact signing plan")); 60 assert!(!debug.contains(&later.signed_event().wire().sig)); 61 assert_eq!(later, later.clone()); 62 #[cfg(feature = "serde")] 63 { 64 let json = serde_json::to_value(&later).expect("serialize evidence"); 65 assert_eq!(json["observed_at_unix_ms"], DEADLINE_MS + 1); 66 assert!(json.get("signed_event").is_some()); 67 } 68 } 69 70 #[test] 71 fn evidence_rejects_wrong_intent_even_for_the_identical_signed_event() { 72 let expected = request(); 73 let evidence = 74 AuthoredSignEvidence::from_signed_event(&expected, signed_event(), DEADLINE_MS).unwrap(); 75 for identity in [intent(3, 2), intent(1, 3)] { 76 let other = SignRequest::new( 77 OperationId::SyncPush, 78 identity, 79 actor(ActorSource::ExplicitPublicKey, [AuthorRole::Any]), 80 plan(), 81 policy(), 82 ) 83 .unwrap(); 84 assert_eq!(other.expected_event_id(), expected.expected_event_id()); 85 assert_eq!( 86 evidence.revalidate(&other, DEADLINE_MS).unwrap_err().kind(), 87 Kind::SignerOutputInvalid 88 ); 89 } 90 let other_kind = SignRequest::new( 91 OperationId::SyncPull, 92 intent(1, 2), 93 actor(ActorSource::ExplicitPublicKey, [AuthorRole::Any]), 94 plan(), 95 policy(), 96 ) 97 .unwrap(); 98 assert_eq!( 99 evidence 100 .revalidate(&other_kind, DEADLINE_MS) 101 .unwrap_err() 102 .kind(), 103 Kind::SignerOutputInvalid 104 ); 105 let other_plan = AuthoredEventPlan::from_generic( 106 GenericEventDraft::new( 107 "radroots.social.geochat.v1", 108 20_000, 109 CREATED_AT, 110 Vec::new(), 111 "different plan", 112 public_key().to_hex(), 113 ) 114 .unwrap(), 115 ) 116 .unwrap(); 117 let other_request = SignRequest::new( 118 OperationId::SyncPush, 119 intent(1, 2), 120 actor(ActorSource::ExplicitPublicKey, [AuthorRole::Any]), 121 other_plan, 122 policy(), 123 ) 124 .unwrap(); 125 assert_eq!( 126 evidence 127 .revalidate(&other_request, DEADLINE_MS) 128 .unwrap_err() 129 .kind(), 130 Kind::SignerOutputInvalid 131 ); 132 } 133 134 #[test] 135 fn late_evidence_keeps_cryptographic_and_exact_plan_checks() { 136 let request = request(); 137 let other_keys = Keys::new(SecretKey::from_hex(&"11".repeat(32)).unwrap()); 138 for event in [ 139 signed_event_with( 140 &other_keys, 141 20_000, 142 "exact signing plan", 143 CREATED_AT, 144 Vec::new(), 145 ), 146 signed_event_with( 147 &keys(), 148 20_000, 149 "exact signing plan", 150 CREATED_AT + 1, 151 Vec::new(), 152 ), 153 signed_event_with( 154 &keys(), 155 20_001, 156 "exact signing plan", 157 CREATED_AT, 158 Vec::new(), 159 ), 160 signed_event_with( 161 &keys(), 162 20_000, 163 "exact signing plan", 164 CREATED_AT, 165 vec![nostr::Tag::parse(["t", "wrong"]).unwrap()], 166 ), 167 signed_event_with(&keys(), 20_000, "wrong content", CREATED_AT, Vec::new()), 168 ] { 169 assert_eq!( 170 AuthoredSignEvidence::from_signed_event(&request, event, DEADLINE_MS + 1) 171 .unwrap_err() 172 .kind(), 173 Kind::SignerOutputInvalid 174 ); 175 } 176 let invalid = invalid_signature_event(); 177 assert_eq!( 178 AuthoredSignEvidence::from_signed_event(&request, invalid, DEADLINE_MS + 1) 179 .unwrap_err() 180 .kind(), 181 Kind::SignerOutputInvalid 182 ); 183 } 184 185 struct LegacySigner { 186 receipt_request: SignRequest, 187 calls: AtomicUsize, 188 fail: bool, 189 } 190 impl Signer for LegacySigner { 191 fn status(&self) -> BoxFuture<'_, Result<SignerStatus, Error>> { 192 Box::pin(async { Ok(SignerStatus::unavailable()) }) 193 } 194 fn sign(&self, _request: SignRequest) -> BoxFuture<'_, Result<SignReceipt, Error>> { 195 self.calls.fetch_add(1, Ordering::SeqCst); 196 Box::pin(async { 197 if self.fail { 198 return Err(Error::new(Kind::SignerUnavailable)); 199 } 200 SignReceipt::from_signed_event(&self.receipt_request, signed_event(), DEADLINE_MS - 1) 201 }) 202 } 203 } 204 205 #[test] 206 fn default_hook_preserves_legacy_adapters_and_rejects_wrong_receipt_binding() { 207 let signer = LegacySigner { 208 receipt_request: request(), 209 calls: AtomicUsize::new(0), 210 fail: false, 211 }; 212 let object: &dyn Signer = &signer; 213 let result = ready(object.sign_authored_evidence(request())).unwrap(); 214 assert_eq!(result.intent_id(), request().intent_id()); 215 assert_eq!(result.observed_at_unix_ms(), DEADLINE_MS - 1); 216 assert_eq!(signer.calls.load(Ordering::SeqCst), 1); 217 let wrong = SignRequest::new( 218 OperationId::SyncPush, 219 intent(4, 2), 220 actor(ActorSource::ExplicitPublicKey, [AuthorRole::Any]), 221 plan(), 222 policy(), 223 ) 224 .unwrap(); 225 assert_eq!( 226 ready(object.sign_authored_evidence(wrong)) 227 .unwrap_err() 228 .kind(), 229 Kind::SignerOutputInvalid 230 ); 231 let unavailable = LegacySigner { 232 receipt_request: request(), 233 calls: AtomicUsize::new(0), 234 fail: true, 235 }; 236 assert_eq!( 237 ready(unavailable.sign_authored_evidence(request())) 238 .unwrap_err() 239 .kind(), 240 Kind::SignerUnavailable 241 ); 242 } 243 244 #[test] 245 fn blossom_credentials_cannot_use_authored_evidence_or_invoke_default_signer() { 246 let request = blossom_request(); 247 let event = signed_event_with( 248 &keys(), 249 24_242, 250 request.content(), 251 request.created_at(), 252 request 253 .tags() 254 .iter() 255 .cloned() 256 .map(nostr::Tag::parse) 257 .collect::<Result<Vec<_>, _>>() 258 .unwrap(), 259 ); 260 assert!(SignReceipt::from_signed_event(&request, event.clone(), DEADLINE_MS - 1).is_ok()); 261 for time in [DEADLINE_MS - 1, DEADLINE_MS, DEADLINE_MS + 1] { 262 assert_eq!( 263 AuthoredSignEvidence::from_signed_event(&request, event.clone(), time) 264 .unwrap_err() 265 .kind(), 266 Kind::InvalidArgument 267 ); 268 } 269 assert_eq!( 270 SignReceipt::from_signed_event(&request, event.clone(), DEADLINE_MS) 271 .unwrap_err() 272 .kind(), 273 Kind::DeadlineExceeded 274 ); 275 request.cancellation_signal().cancel(); 276 assert_eq!( 277 SignReceipt::from_signed_event(&request, event, DEADLINE_MS - 1) 278 .unwrap_err() 279 .kind(), 280 Kind::SignerCancelled 281 ); 282 let signer = LegacySigner { 283 receipt_request: super::request(), 284 calls: AtomicUsize::new(0), 285 fail: false, 286 }; 287 assert_eq!( 288 ready(signer.sign_authored_evidence(request)) 289 .unwrap_err() 290 .kind(), 291 Kind::InvalidArgument 292 ); 293 assert_eq!(signer.calls.load(Ordering::SeqCst), 0); 294 } 295 296 #[test] 297 fn default_hook_does_not_invoke_an_adapter_for_already_cancelled_work() { 298 let request = request(); 299 request.cancellation_signal().cancel(); 300 let signer = LegacySigner { 301 receipt_request: super::request(), 302 calls: AtomicUsize::new(0), 303 fail: false, 304 }; 305 assert_eq!( 306 ready(signer.sign_authored_evidence(request)) 307 .unwrap_err() 308 .kind(), 309 Kind::SignerCancelled 310 ); 311 assert_eq!(signer.calls.load(Ordering::SeqCst), 0); 312 }