#![allow(
clippy::print_stdout,
clippy::missing_assert_message,
clippy::panic_in_result_fn,
clippy::indexing_slicing,
clippy::uninlined_format_args,
clippy::useless_vec,
unused_crate_dependencies,
reason = "runnable demo: stdout output is the whole point, panic-on-failure is acceptable in a script-like context, and the binary inherits the lib's dep set"
)]
use nula_core::Keys;
use nula_core::nips::nip04;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let alice = Keys::generate()?;
let bob = Keys::generate()?;
println!("alice : {}", alice.public_key().to_hex());
println!("bob : {}", bob.public_key().to_hex());
let plaintext = "🥷 secret meeting at 21:00 sats UTC";
println!("plain : {plaintext}");
let payload = nip04::encrypt(alice.secret_key(), bob.public_key(), plaintext)?;
println!("wire : {payload}");
assert!(
payload.contains("?iv="),
"wire form must carry the IV suffix"
);
let recovered = nip04::decrypt(bob.secret_key(), alice.public_key(), &payload)?;
println!("plain': {recovered}");
assert_eq!(plaintext, recovered, "round-trip must be lossless");
let mut tampered: Vec<u8> = payload.bytes().collect();
tampered[0] ^= 0x01;
let tampered = String::from_utf8(tampered).unwrap_or_default();
let err = nip04::decrypt(bob.secret_key(), alice.public_key(), &tampered);
println!("tamper: {err:?}");
assert!(err.is_err(), "tampered ciphertext must be rejected");
Ok(())
}