lib.rs (18354B)
1 #![forbid(unsafe_code)] 2 3 use core::fmt; 4 use std::sync::Arc; 5 6 use k256::schnorr::signature::{Signer, Verifier}; 7 use k256::schnorr::{Signature, SigningKey, VerifyingKey}; 8 use pocket_types::{ 9 Kind as PocketKind, OwnedEvent as PocketOwnedEvent, Tags as PocketTags, Time as PocketTime, 10 }; 11 use secp256k1::{Keypair, Secp256k1, SecretKey}; 12 use sha2::{Digest, Sha256}; 13 use tangle_protocol::{ 14 Event, EventId, PublicKeyHex, SignatureHex, UnsignedEvent, canonical_event_json, 15 }; 16 use tokio::sync::Semaphore; 17 18 pub fn compute_event_id(event: &UnsignedEvent) -> EventId { 19 let event_id = compute_event_id_hex(event); 20 EventId::new(&event_id).expect("sha256 emits 32-byte lowercase hex") 21 } 22 23 pub fn compute_event_id_hex(event: &UnsignedEvent) -> String { 24 let canonical = canonical_event_json(event); 25 let digest = Sha256::digest(canonical.as_bytes()); 26 lower_hex(&digest) 27 } 28 29 pub fn event_id_matches(event: &Event) -> bool { 30 compute_event_id(event.unsigned()) == *event.id() 31 } 32 33 pub fn verify_event_id(event: &Event) -> Result<(), String> { 34 let expected = compute_event_id(event.unsigned()); 35 if event.id() == &expected { 36 Ok(()) 37 } else { 38 Err(format!( 39 "event id mismatch: expected {}, got {}", 40 expected, 41 event.id() 42 )) 43 } 44 } 45 46 pub fn verify_event_signature(event: &Event) -> Result<(), String> { 47 verify_event_id(event)?; 48 let event_id = 49 validated_fixed_hex_bytes(event.id().as_str(), EventId::HEX_LENGTH / 2, "event id"); 50 let pubkey = fixed_hex_bytes( 51 event.unsigned().pubkey().as_str(), 52 EventId::HEX_LENGTH / 2, 53 "public key", 54 ) 55 .expect("validated public key scalar decodes"); 56 let signature = fixed_hex_bytes(event.sig().as_str(), 64, "signature") 57 .expect("validated signature decodes"); 58 let verifying_key = VerifyingKey::from_bytes(&pubkey) 59 .map_err(|_| "event public key is not a valid secp256k1 x-only key".to_owned())?; 60 let signature = Signature::try_from(signature.as_slice()) 61 .map_err(|_| "event signature is not a valid schnorr signature".to_owned())?; 62 verifying_key 63 .verify(&event_id, &signature) 64 .map_err(|_| "event signature verification failed".to_owned()) 65 } 66 67 pub fn compute_event_id_hex_from_canonical_json(canonical: &str) -> String { 68 let digest = Sha256::digest(canonical.as_bytes()); 69 lower_hex(&digest) 70 } 71 72 pub fn verify_event_signature_bytes( 73 canonical: &str, 74 event_id: &[u8; 32], 75 pubkey: &[u8; 32], 76 signature: &[u8; 64], 77 ) -> Result<(), String> { 78 let expected_id = Sha256::digest(canonical.as_bytes()); 79 let expected_id_bytes: &[u8] = expected_id.as_ref(); 80 if expected_id_bytes != event_id { 81 return Err(format!( 82 "event id mismatch: expected {}, got {}", 83 lower_hex(&expected_id), 84 lower_hex(event_id) 85 )); 86 } 87 let verifying_key = VerifyingKey::from_bytes(pubkey) 88 .map_err(|_| "event public key is not a valid secp256k1 x-only key".to_owned())?; 89 let signature = Signature::try_from(signature.as_slice()) 90 .map_err(|_| "event signature is not a valid schnorr signature".to_owned())?; 91 verifying_key 92 .verify(event_id, &signature) 93 .map_err(|_| "event signature verification failed".to_owned()) 94 } 95 96 pub struct RelaySigner { 97 signing_key: SigningKey, 98 public_key: PublicKeyHex, 99 secret_bytes: [u8; 32], 100 } 101 102 impl RelaySigner { 103 pub fn from_secret_hex(secret: &str) -> Result<Self, String> { 104 let bytes = fixed_hex_bytes(secret, 32, "relay secret")?; 105 let bytes: [u8; 32] = bytes 106 .try_into() 107 .expect("validated relay secret length is 32 bytes"); 108 let signing_key = SigningKey::from_bytes(&bytes) 109 .map_err(|_| "relay secret is not a valid secp256k1 signing key".to_owned())?; 110 let public_key = 111 PublicKeyHex::new(&lower_hex(signing_key.verifying_key().to_bytes().as_ref())) 112 .expect("signing key emits a valid x-only public key"); 113 Ok(Self { 114 signing_key, 115 public_key, 116 secret_bytes: bytes, 117 }) 118 } 119 120 pub fn public_key(&self) -> &PublicKeyHex { 121 &self.public_key 122 } 123 124 pub fn sign_unsigned_event(&self, unsigned: UnsignedEvent) -> Event { 125 let event_id = compute_event_id(&unsigned); 126 let event_id_bytes = 127 fixed_hex_bytes(event_id.as_str(), 32, "event id").expect("event id is valid hex"); 128 let signature: Signature = self.signing_key.sign(&event_id_bytes); 129 let signature = SignatureHex::new(&lower_hex(signature.to_bytes().as_ref())) 130 .expect("schnorr signature emits valid hex"); 131 Event::new(event_id, unsigned, signature) 132 } 133 134 pub fn sign_pocket_event( 135 &self, 136 kind: PocketKind, 137 tags: &PocketTags, 138 created_at: PocketTime, 139 content: &[u8], 140 ) -> Result<PocketOwnedEvent, String> { 141 let secp = Secp256k1::new(); 142 let secret_key = SecretKey::from_byte_array(self.secret_bytes) 143 .map_err(|_| "relay secret is not a valid secp256k1 signing key".to_owned())?; 144 let keypair = Keypair::from_secret_key(&secp, &secret_key); 145 PocketOwnedEvent::sign_new(&keypair, kind, tags, created_at, content) 146 .map_err(|error| format!("Pocket event signing failed: {error}")) 147 } 148 } 149 150 impl fmt::Debug for RelaySigner { 151 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 152 formatter 153 .debug_struct("RelaySigner") 154 .field("public_key", &self.public_key) 155 .finish_non_exhaustive() 156 } 157 } 158 159 #[derive(Clone, Debug)] 160 pub struct VerificationService { 161 semaphore: Arc<Semaphore>, 162 max_concurrent: usize, 163 } 164 165 impl VerificationService { 166 pub fn new(max_concurrent: usize) -> Result<Self, String> { 167 if max_concurrent == 0 { 168 return Err("verification concurrency limit must be greater than zero".to_owned()); 169 } 170 Ok(Self { 171 semaphore: Arc::new(Semaphore::new(max_concurrent)), 172 max_concurrent, 173 }) 174 } 175 176 pub fn max_concurrent(&self) -> usize { 177 self.max_concurrent 178 } 179 180 pub fn available_permits(&self) -> usize { 181 self.semaphore.available_permits() 182 } 183 184 pub fn close(&self) { 185 self.semaphore.close(); 186 } 187 188 pub async fn verify_event(&self, event: &Event) -> Result<VerificationOutcome, String> { 189 let permit = self 190 .semaphore 191 .clone() 192 .acquire_owned() 193 .await 194 .map_err(|_| "verification service is closed".to_owned())?; 195 let result = verify_event_signature(event); 196 drop(permit); 197 result.map(|_| VerificationOutcome { 198 event_id: event.id().clone(), 199 }) 200 } 201 } 202 203 #[derive(Debug, Clone, PartialEq, Eq)] 204 pub struct VerificationOutcome { 205 event_id: EventId, 206 } 207 208 impl VerificationOutcome { 209 pub fn event_id(&self) -> &EventId { 210 &self.event_id 211 } 212 } 213 214 fn lower_hex(bytes: &[u8]) -> String { 215 const HEX: &[u8; 16] = b"0123456789abcdef"; 216 let mut output = String::with_capacity(bytes.len() * 2); 217 for byte in bytes { 218 output.push(char::from(HEX[usize::from(byte >> 4)])); 219 output.push(char::from(HEX[usize::from(byte & 0x0f)])); 220 } 221 output 222 } 223 224 fn fixed_hex_bytes(value: &str, expected: usize, scalar: &str) -> Result<Vec<u8>, String> { 225 if value.len() != expected * 2 { 226 return Err(format!( 227 "{scalar} must decode to {expected} bytes, got {} hex characters", 228 value.len() 229 )); 230 } 231 let mut output = Vec::with_capacity(expected); 232 for chunk in value.as_bytes().chunks_exact(2) { 233 let high = hex_value(chunk[0], scalar)?; 234 let low = hex_value(chunk[1], scalar)?; 235 output.push((high << 4) | low); 236 } 237 Ok(output) 238 } 239 240 fn validated_fixed_hex_bytes(value: &str, expected: usize, scalar: &str) -> Vec<u8> { 241 fixed_hex_bytes(value, expected, scalar).expect("validated hex scalar decodes") 242 } 243 244 fn hex_value(value: u8, scalar: &str) -> Result<u8, String> { 245 match value { 246 b'0'..=b'9' => Ok(value - b'0'), 247 b'a'..=b'f' => Ok(value - b'a' + 10), 248 _ => Err(format!("{scalar} must be lowercase hex")), 249 } 250 } 251 252 #[cfg(test)] 253 mod tests { 254 use super::{ 255 RelaySigner, VerificationService, compute_event_id, compute_event_id_hex, event_id_matches, 256 fixed_hex_bytes, lower_hex, verify_event_id, verify_event_signature, 257 }; 258 use k256::schnorr::signature::Signer; 259 use k256::schnorr::{Signature, SigningKey}; 260 use pocket_types::{Kind as PocketKind, OwnedTags as PocketOwnedTags, Time as PocketTime}; 261 use std::time::Duration; 262 use tangle_protocol::{ 263 Event, EventId, Kind, PublicKeyHex, SignatureHex, Tag, UnixTimestamp, UnsignedEvent, 264 }; 265 use tokio::time::timeout; 266 267 #[test] 268 fn event_id_hashes_canonical_event_bytes() { 269 let event = unsigned_event(Vec::new(), ""); 270 271 assert_eq!( 272 compute_event_id_hex(&event), 273 "da90287b43a114ad00f2a87854947df1251b9a0f148b1707b9241c73f11569ae" 274 ); 275 assert_eq!( 276 compute_event_id(&event).as_str(), 277 "da90287b43a114ad00f2a87854947df1251b9a0f148b1707b9241c73f11569ae" 278 ); 279 } 280 281 #[test] 282 fn event_id_verification_reports_match_and_mismatch() { 283 let unsigned = unsigned_event( 284 vec![Tag::from_parts("t", &["radroots"]).expect("tag")], 285 "radroots cafe", 286 ); 287 let event_id = compute_event_id(&unsigned); 288 let event = Event::new( 289 event_id, 290 unsigned.clone(), 291 SignatureHex::new(&"b".repeat(SignatureHex::HEX_LENGTH)).expect("sig"), 292 ); 293 let wrong_event = Event::new( 294 EventId::new(&"f".repeat(EventId::HEX_LENGTH)).expect("id"), 295 unsigned, 296 SignatureHex::new(&"b".repeat(SignatureHex::HEX_LENGTH)).expect("sig"), 297 ); 298 299 assert!(event_id_matches(&event)); 300 assert_eq!(verify_event_id(&event), Ok(())); 301 assert!(!event_id_matches(&wrong_event)); 302 assert_eq!( 303 verify_event_id(&wrong_event).expect_err("mismatch"), 304 format!( 305 "event id mismatch: expected {}, got {}", 306 compute_event_id(wrong_event.unsigned()), 307 wrong_event.id() 308 ) 309 ); 310 } 311 312 #[test] 313 fn schnorr_verifier_accepts_deterministically_signed_event() { 314 let event = signed_event(); 315 316 assert_eq!(verify_event_signature(&event), Ok(())); 317 } 318 319 #[test] 320 fn relay_signer_derives_public_key_and_signs_canonical_events() { 321 let secret = lower_hex(&[7_u8; 32]); 322 let signer = RelaySigner::from_secret_hex(&secret).expect("signer"); 323 let unsigned = UnsignedEvent::new( 324 signer.public_key().clone(), 325 UnixTimestamp::new(1_714_124_433), 326 Kind::new(1).expect("kind"), 327 vec![Tag::from_parts("t", &["radroots"]).expect("tag")], 328 "relay generated", 329 ); 330 331 let event = signer.sign_unsigned_event(unsigned); 332 333 assert_eq!(event.unsigned().pubkey(), signer.public_key()); 334 assert_eq!(verify_event_signature(&event), Ok(())); 335 assert_eq!( 336 format!("{signer:?}"), 337 format!( 338 "RelaySigner {{ public_key: {:?}, .. }}", 339 signer.public_key() 340 ) 341 ); 342 assert!(!format!("{signer:?}").contains(&secret)); 343 } 344 345 #[test] 346 fn relay_signer_signs_pocket_events() { 347 let secret = "7".repeat(64); 348 let signer = RelaySigner::from_secret_hex(&secret).expect("signer"); 349 let tags = PocketOwnedTags::new(&[["d", "Farm"]]).expect("tags"); 350 let event = signer 351 .sign_pocket_event( 352 PocketKind::from_u16(39_000), 353 &tags, 354 PocketTime::from_u64(20), 355 b"", 356 ) 357 .expect("event"); 358 359 event.verify().expect("verify"); 360 assert_eq!(event.pubkey().as_hex_string(), signer.public_key().as_str()); 361 assert_eq!(event.kind().as_u16(), 39_000); 362 assert_eq!( 363 event.id().as_hex_string(), 364 "b107997a285780bc383ee5aadc0a0eefc46734914103d80f765a46543622782a" 365 ); 366 } 367 368 #[test] 369 fn schnorr_verifier_rejects_bad_id_bad_pubkey_and_bad_signature() { 370 let event = signed_event(); 371 let wrong_id = Event::new( 372 EventId::new(&"f".repeat(EventId::HEX_LENGTH)).expect("id"), 373 event.unsigned().clone(), 374 event.sig().clone(), 375 ); 376 let invalid_pubkey_unsigned = UnsignedEvent::new( 377 PublicKeyHex::new(&"f".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"), 378 event.unsigned().created_at(), 379 event.unsigned().kind(), 380 event.unsigned().tags().to_vec(), 381 event.unsigned().content(), 382 ); 383 let invalid_pubkey = Event::new( 384 compute_event_id(&invalid_pubkey_unsigned), 385 invalid_pubkey_unsigned, 386 event.sig().clone(), 387 ); 388 let invalid_signature = Event::new( 389 compute_event_id(event.unsigned()), 390 event.unsigned().clone(), 391 SignatureHex::new(&"f".repeat(SignatureHex::HEX_LENGTH)).expect("sig"), 392 ); 393 let wrong_message_unsigned = UnsignedEvent::new( 394 event.unsigned().pubkey().clone(), 395 event.unsigned().created_at(), 396 event.unsigned().kind(), 397 event.unsigned().tags().to_vec(), 398 "different message", 399 ); 400 let wrong_message_signature = Event::new( 401 compute_event_id(&wrong_message_unsigned), 402 wrong_message_unsigned, 403 event.sig().clone(), 404 ); 405 406 assert!( 407 verify_event_signature(&wrong_id) 408 .expect_err("bad id") 409 .starts_with("event id mismatch") 410 ); 411 assert_eq!( 412 verify_event_signature(&invalid_pubkey).expect_err("bad pubkey"), 413 "event public key is not a valid secp256k1 x-only key" 414 ); 415 assert_eq!( 416 verify_event_signature(&invalid_signature).expect_err("bad sig"), 417 "event signature is not a valid schnorr signature" 418 ); 419 assert_eq!( 420 verify_event_signature(&wrong_message_signature).expect_err("wrong message"), 421 "event signature verification failed" 422 ); 423 } 424 425 #[test] 426 fn hex_decoder_rejects_bad_length_and_non_hex_input() { 427 assert_eq!( 428 fixed_hex_bytes("abc", 2, "sample").expect_err("length"), 429 "sample must decode to 2 bytes, got 3 hex characters" 430 ); 431 assert_eq!( 432 fixed_hex_bytes("0G", 1, "sample").expect_err("hex"), 433 "sample must be lowercase hex" 434 ); 435 assert_eq!( 436 fixed_hex_bytes("G0", 1, "sample").expect_err("hex"), 437 "sample must be lowercase hex" 438 ); 439 } 440 441 #[tokio::test] 442 async fn verification_service_accepts_valid_events_and_rejects_invalid_events() { 443 let event = signed_event(); 444 let invalid = Event::new( 445 EventId::new(&"f".repeat(EventId::HEX_LENGTH)).expect("id"), 446 event.unsigned().clone(), 447 event.sig().clone(), 448 ); 449 let service = VerificationService::new(2).expect("service"); 450 451 let outcome = service.verify_event(&event).await.expect("verified"); 452 453 assert_eq!(service.max_concurrent(), 2); 454 assert_eq!(service.available_permits(), 2); 455 assert_eq!(outcome.event_id(), event.id()); 456 assert!( 457 service 458 .verify_event(&invalid) 459 .await 460 .expect_err("invalid") 461 .starts_with("event id mismatch") 462 ); 463 } 464 465 #[tokio::test] 466 async fn verification_service_enforces_limit_and_reports_closed_state() { 467 let event = signed_event(); 468 let service = VerificationService::new(1).expect("service"); 469 let permit = service 470 .semaphore 471 .clone() 472 .acquire_owned() 473 .await 474 .expect("permit"); 475 476 assert_eq!(service.available_permits(), 0); 477 assert!( 478 timeout(Duration::from_millis(20), service.verify_event(&event)) 479 .await 480 .is_err() 481 ); 482 drop(permit); 483 assert!( 484 timeout(Duration::from_secs(1), service.verify_event(&event)) 485 .await 486 .expect("timeout") 487 .is_ok() 488 ); 489 service.close(); 490 assert_eq!( 491 service.verify_event(&event).await.expect_err("closed"), 492 "verification service is closed" 493 ); 494 } 495 496 #[test] 497 fn verification_service_rejects_zero_limit() { 498 assert_eq!( 499 VerificationService::new(0).expect_err("zero"), 500 "verification concurrency limit must be greater than zero" 501 ); 502 } 503 504 fn unsigned_event(tags: Vec<Tag>, content: &str) -> UnsignedEvent { 505 UnsignedEvent::new( 506 PublicKeyHex::new(&"1".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"), 507 UnixTimestamp::new(1_714_124_433), 508 Kind::new(1).expect("kind"), 509 tags, 510 content, 511 ) 512 } 513 514 fn signed_event() -> Event { 515 let signing_key = SigningKey::from_bytes(&[7_u8; 32]).expect("signing key"); 516 let public_key = 517 PublicKeyHex::new(&lower_hex(signing_key.verifying_key().to_bytes().as_ref())) 518 .expect("pubkey"); 519 let unsigned = UnsignedEvent::new( 520 public_key, 521 UnixTimestamp::new(1_714_124_433), 522 Kind::new(1).expect("kind"), 523 vec![Tag::from_parts("t", &["radroots"]).expect("tag")], 524 "radroots cafe", 525 ); 526 let event_id = compute_event_id(&unsigned); 527 let event_id_bytes = fixed_hex_bytes(event_id.as_str(), 32, "event id").expect("event id"); 528 let signature: Signature = signing_key.sign(&event_id_bytes); 529 let signature = SignatureHex::new(&lower_hex(signature.to_bytes().as_ref())).expect("sig"); 530 Event::new(event_id, unsigned, signature) 531 } 532 }