tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

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 }