An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

test(crypto): add MM-90.AC1.1–AC1.5 tests for verify_genesis_op

authored by

Malpercio and committed by
Tangled
f23db27d ce48e442

+119
+119
crates/crypto/src/plc.rs
··· 518 518 "alsoKnownAs should contain 'at://alice.example.com', got: {also_known_as:?}" 519 519 ); 520 520 } 521 + 522 + // ── MM-90 verify_genesis_op tests ────────────────────────────────────────── 523 + 524 + /// Returns (signing_key_uri, PlcGenesisOp) for MM-90 verification tests. 525 + /// build_did_plc_genesis_op signs with signing_key_bytes; verify_genesis_op 526 + /// must receive signing_kp.key_id as its rotation_key argument. 527 + fn make_op_for_verify() -> (DidKeyUri, PlcGenesisOp) { 528 + let rotation_kp = generate_p256_keypair().expect("rotation keypair"); 529 + let signing_kp = generate_p256_keypair().expect("signing keypair"); 530 + let private_key_bytes = *signing_kp.private_key_bytes; 531 + let op = build_did_plc_genesis_op( 532 + &rotation_kp.key_id, 533 + &signing_kp.key_id, 534 + &private_key_bytes, 535 + "alice.example.com", 536 + "https://relay.example.com", 537 + ) 538 + .expect("genesis op"); 539 + (signing_kp.key_id, op) 540 + } 541 + 542 + /// MM-90.AC1.1: verify_genesis_op returns correct fields 543 + #[test] 544 + fn verify_valid_op_returns_correct_fields() { 545 + let (signing_key, op) = make_op_for_verify(); 546 + let result = verify_genesis_op(&op.signed_op_json, &signing_key); 547 + 548 + assert!(result.is_ok(), "verify should succeed"); 549 + let verified = result.unwrap(); 550 + 551 + assert!(verified.did.starts_with("did:plc:"), "DID should start with 'did:plc:'"); 552 + assert_eq!(verified.did.len(), 32, "DID should be 32 chars total (did:plc: + 24 suffix)"); 553 + assert!( 554 + verified.also_known_as.contains(&"at://alice.example.com".to_string()), 555 + "also_known_as should contain 'at://alice.example.com'" 556 + ); 557 + assert!( 558 + verified.verification_methods.contains_key("atproto"), 559 + "verification_methods should contain 'atproto' key" 560 + ); 561 + assert_eq!( 562 + verified.atproto_pds_endpoint, 563 + Some("https://relay.example.com".to_string()), 564 + "atproto_pds_endpoint should be set correctly" 565 + ); 566 + } 567 + 568 + /// MM-90.AC1.2: DID from verify_genesis_op matches build_did_plc_genesis_op 569 + #[test] 570 + fn verify_did_matches_build_did_plc_genesis_op() { 571 + let (signing_key, genesis_op) = make_op_for_verify(); 572 + let verified_result = verify_genesis_op(&genesis_op.signed_op_json, &signing_key); 573 + 574 + assert!(verified_result.is_ok(), "verify should succeed"); 575 + let verified_op = verified_result.unwrap(); 576 + 577 + assert_eq!( 578 + verified_op.did, genesis_op.did, 579 + "DID from verify_genesis_op should match DID from build_did_plc_genesis_op" 580 + ); 581 + } 582 + 583 + /// MM-90.AC1.3: Signature verification fails with wrong rotation key 584 + #[test] 585 + fn verify_wrong_rotation_key_returns_error() { 586 + let (_, op) = make_op_for_verify(); 587 + let wrong_kp = generate_p256_keypair().expect("wrong keypair"); 588 + 589 + let result = verify_genesis_op(&op.signed_op_json, &wrong_kp.key_id); 590 + 591 + assert!( 592 + matches!(result, Err(CryptoError::PlcOperation(_))), 593 + "Verify with wrong rotation key should return CryptoError::PlcOperation" 594 + ); 595 + } 596 + 597 + /// MM-90.AC1.4: Corrupted signature returns error 598 + #[test] 599 + fn verify_corrupted_signature_returns_error() { 600 + let (signing_key, op) = make_op_for_verify(); 601 + 602 + // Parse JSON, corrupt the signature field, re-serialize 603 + let mut v: serde_json::Value = 604 + serde_json::from_str(&op.signed_op_json).expect("valid JSON"); 605 + let sig_str = v["sig"].as_str().expect("sig is a string"); 606 + let mut sig_bytes = URL_SAFE_NO_PAD 607 + .decode(sig_str) 608 + .expect("sig should be valid base64url"); 609 + sig_bytes[0] ^= 0xff; // Flip all bits in first byte 610 + let corrupted_sig = URL_SAFE_NO_PAD.encode(&sig_bytes); 611 + v["sig"] = serde_json::json!(corrupted_sig); 612 + let corrupted_json = serde_json::to_string(&v).expect("re-serialize JSON"); 613 + 614 + let result = verify_genesis_op(&corrupted_json, &signing_key); 615 + 616 + assert!( 617 + matches!(result, Err(CryptoError::PlcOperation(_))), 618 + "Verify with corrupted signature should return CryptoError::PlcOperation" 619 + ); 620 + } 621 + 622 + /// MM-90.AC1.5: Unknown fields in JSON are rejected 623 + #[test] 624 + fn verify_unknown_fields_returns_error() { 625 + let (signing_key, op) = make_op_for_verify(); 626 + 627 + // Parse JSON, add an unknown field, re-serialize 628 + let mut v: serde_json::Value = 629 + serde_json::from_str(&op.signed_op_json).expect("valid JSON"); 630 + v["unknownField"] = serde_json::json!("surprise"); 631 + let modified_json = serde_json::to_string(&v).expect("re-serialize JSON"); 632 + 633 + let result = verify_genesis_op(&modified_json, &signing_key); 634 + 635 + assert!( 636 + matches!(result, Err(CryptoError::PlcOperation(_))), 637 + "Verify with unknown fields should return CryptoError::PlcOperation" 638 + ); 639 + } 521 640 }