use sanctum::upgrade::{ run_upgrade, validate_trusted_comment_version, verify_minisign_signature, verify_release_manifest_signature, UpgradeOptions, SANCTUM_RELEASE_PUBKEY, SANCTUM_RELEASE_PUBKEY_BACKUP, }; #[test] fn test_minisign_signature_valid() { let payload = b"Hello Sanctum Release 0.7.0 Security Testing\n"; let sig_text = "\ untrusted comment: signature from minisign secret key\n\ RUSsVphPgr8155LCucDq8RzDmWMzWElzhBmHrS5p+qJlOCGU4AQpn2F28MVPFrnoGPYEHx7/6TlKg2b45DlYuBQ0+ASeLIzMLwc=\n\ trusted comment: timestamp:1789750897\tfile:test_payload.txt\thashed\n\ 0l7KsNJkDTo2uOKL8UAiaJOWuhwhGTVZ5fn0JFMdxXE9riYixQO9YnBpSVqz8iCDhWrz8hJBnmUrIWnaRaXVCA==\n"; let res = verify_minisign_signature(payload, sig_text, SANCTUM_RELEASE_PUBKEY); assert!( res.is_ok(), "Gültige Minisign-Signatur muss erfolgreich verifiziert werden: {:?}", res.err() ); } #[test] fn test_minisign_signature_tampered_payload() { let tampered_payload = b"Hello Sanctum Release 0.7.0 Tampered Payload!\n"; let sig_text = "\ untrusted comment: signature from minisign secret key\n\ RUSsVphPgr8155LCucDq8RzDmWMzWElzhBmHrS5p+qJlOCGU4AQpn2F28MVPFrnoGPYEHx7/6TlKg2b45DlYuBQ0+ASeLIzMLwc=\n\ trusted comment: timestamp:1789750897\tfile:test_payload.txt\thashed\n\ 0l7KsNJkDTo2uOKL8UAiaJOWuhwhGTVZ5fn0JFMdxXE9riYixQO9YnBpSVqz8iCDhWrz8hJBnmUrIWnaRaXVCA==\n"; let res = verify_minisign_signature(tampered_payload, sig_text, SANCTUM_RELEASE_PUBKEY); assert!( res.is_err(), "Manipulierte Binärdaten müssen die Minisign-Prüfung verfehlen" ); assert!(res.unwrap_err().to_string().contains("FEHLGESCHLAGEN")); } #[test] fn test_minisign_signature_wrong_key() { let payload = b"Hello Sanctum Release 0.7.0 Security Testing\n"; let sig_text = "\ untrusted comment: signature from minisign secret key\n\ RUSsVphPgr8155LCucDq8RzDmWMzWElzhBmHrS5p+qJlOCGU4AQpn2F28MVPFrnoGPYEHx7/6TlKg2b45DlYuBQ0+ASeLIzMLwc=\n\ trusted comment: timestamp:1789750897\tfile:test_payload.txt\thashed\n\ 0l7KsNJkDTo2uOKL8UAiaJOWuhwhGTVZ5fn0JFMdxXE9riYixQO9YnBpSVqz8iCDhWrz8hJBnmUrIWnaRaXVCA==\n"; // Ein anderer, unautorisierter Public Key let other_key = "RWSb0a70vF1X8qIjc7xi28Gmw+cIWbMipOy4L6ToJEUPkWDw3c0qIjc7"; let res = verify_minisign_signature(payload, sig_text, other_key); assert!( res.is_err(), "Signaturprüfung mit fremdem Schlüssel muss fehlschlagen" ); } #[test] fn test_upgrade_blocks_unofficial_url_without_insecure_flag() { let options = UpgradeOptions { check_only: true, yes: false, force: false, base_url: "https://evil-attacker.org/sanctum".into(), insecure_url: false, }; let res = run_upgrade(&options); assert!( res.is_err(), "Inoffizielle Server-URL ohne --insecure-url muss blockiert werden" ); let err_msg = res.unwrap_err().to_string(); assert!( err_msg.contains("Sicherheitsverstoß") || err_msg.contains("nicht vertrauenswürdig"), "Fehler muss Sicherheitsverstoß anzeigen: {err_msg}" ); } #[test] fn test_u01_reject_legacy_unhashed_signature() { let payload = b"Hello Sanctum Release 0.7.0 Security Testing\n"; let sig_text = "\ untrusted comment: signature from minisign secret key\n\ RUSsVphPgr8155LCucDq8RzDmWMzWElzhBmHrS5p+qJlOCGU4AQpn2F28MVPFrnoGPYEHx7/6TlKg2b45DlYuBQ0+ASeLIzMLwc=\n\ trusted comment: timestamp:1789750897\tfile:test_payload.txt\thashed\n\ 0l7KsNJkDTo2uOKL8UAiaJOWuhwhGTVZ5fn0JFMdxXE9riYixQO9YnBpSVqz8iCDhWrz8hJBnmUrIWnaRaXVCA==\n"; // Gültige pre-hashed Signatur muss weiterhin erfolgreich geprüft werden assert!(verify_minisign_signature(payload, sig_text, SANCTUM_RELEASE_PUBKEY).is_ok()); // Modifiziere die Signatur, um eine Legacy-Signatur (Ed statt ED) zu simulieren // In Base64 entspricht 'ED' (0x45, 0x44) dem Präfix 'RU' // 'Ed' (0x45, 0x64) entspricht in Base64 dem Präfix 'RW' let lines: Vec<&str> = sig_text.lines().collect(); let mut sig_line = lines[1].to_string(); assert!(sig_line.starts_with("RU")); sig_line.replace_range(0..2, "RW"); let legacy_sig_text = format!( "{}\n{}\n{}\n{}\n", lines[0], sig_line, lines[2], lines[3] ); let res = verify_minisign_signature(payload, &legacy_sig_text, SANCTUM_RELEASE_PUBKEY); assert!( res.is_err(), "U-01: Legacy-Signaturen (ohne Pre-Hashing) müssen strikt abgewiesen werden" ); } #[test] fn test_u02_validate_trusted_comment_version_matching() { // Standard Minisign format with -t "version:0.8.0" let tc1 = "timestamp:1789750897\tversion:0.8.0\tfile:SHA256SUMS.txt\thashed"; assert!(validate_trusted_comment_version(tc1, "0.8.0").is_ok()); assert!(validate_trusted_comment_version(tc1, "v0.8.0").is_ok()); // Alternative tags like tag:v0.8.0 or release:0.8.0 let tc2 = "release:0.8.0"; assert!(validate_trusted_comment_version(tc2, "0.8.0").is_ok()); let tc3 = "tag:v0.8.0"; assert!(validate_trusted_comment_version(tc3, "0.8.0").is_ok()); } #[test] fn test_u02_reject_mismatched_release_tag_replay() { // An attacker replays a valid signature from v0.7.0 for an update claiming to be v0.8.0 let tc_old = "timestamp:1789750897\tversion:0.7.0\tfile:SHA256SUMS.txt\thashed"; let res = validate_trusted_comment_version(tc_old, "v0.8.0"); assert!( res.is_err(), "U-02: Ältere oder abweichende Version im Trusted Comment muss abgewiesen werden" ); let err_msg = res.unwrap_err().to_string(); assert!( err_msg.contains("U-02") && err_msg.contains("Replay-Angriff"), "Fehlermeldung muss auf Replay-Angriff und U-02 hinweisen: {err_msg}" ); // Empty or completely missing version token let tc_no_ver = "timestamp:1789750897\tfile:SHA256SUMS.txt\thashed"; let res2 = validate_trusted_comment_version(tc_no_ver, "0.8.0"); assert!(res2.is_err()); } #[test] fn test_u03_verify_signature_with_backup_key_fallback() { let payload = b"Hello Sanctum Release 0.8.0 Backup Key Testing\n"; let backup_sig = "\ untrusted comment: signature from minisign secret key\n\ RUQEeUSLlX2p4HrXDO2TYykyNK0C41Vdrq6MIts14mQgNDClGC2RkBV3jHRbP7IclWv+h/IuzvyYi9yz8/RqfhpDVwhqtvC02gk=\n\ trusted comment: version:0.8.0\n\ zjZAS4uy1PH+0S9KQ0EU435lpBczU3lULyS5SkWf093iCEBc1ULiscwE5gWkEkex3ecPdRugopT+fKaZBQQnBg==\n"; // 1. Direct verification against primary key MUST fail (key rotation scenario) let prim_res = verify_minisign_signature(payload, backup_sig, SANCTUM_RELEASE_PUBKEY); assert!( prim_res.is_err(), "Backup-Signatur darf nicht gegen den Primärschlüssel verifizieren" ); // 2. Direct verification against backup key MUST succeed let back_res = verify_minisign_signature(payload, backup_sig, SANCTUM_RELEASE_PUBKEY_BACKUP); assert!( back_res.is_ok(), "Backup-Signatur muss gegen SANCTUM_RELEASE_PUBKEY_BACKUP gültig sein: {:?}", back_res.err() ); // 3. Fallback verification via verify_release_manifest_signature MUST succeed transparently let fallback_res = verify_release_manifest_signature(payload, backup_sig); assert!( fallback_res.is_ok(), "verify_release_manifest_signature muss via Backup-Schlüssel erfolgreich sein" ); assert_eq!(fallback_res.unwrap(), "version:0.8.0"); } #[test] fn test_u03_verify_signature_rejects_untrusted_third_party_key() { let fake_sig = "\ untrusted comment: signature from minisign secret key\n\ RUQEeUSLlX2p4HrXDO2TYykyNK0C41Vdrq6MIts14mQgNDClGC2RkBV3jHRbP7IclWv+h/IuzvyYi9yz8/RqfhpDVwhqtvC02gk=\n\ trusted comment: version:0.8.0\n\ zjZAS4uy1PH+0S9KQ0EU435lpBczU3lULyS5SkWf093iCEBc1ULiscwE5gWkEkex3ecPdRugopT+fKaZBQQnBg==\n"; // Tampered payload with neither primary nor backup key matching let tampered = b"Tampered data not signed by either key"; let res = verify_release_manifest_signature(tampered, fake_sig); assert!( res.is_err(), "Weder Primär- noch Backup-Schlüssel dürfen manipulierte Nutzdaten akzeptieren" ); }