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