"""Tests for Blinding API and BlindData.""" import pytest from i2p_crypto.eddsa_blinding import HAS_NACL pytestmark = pytest.mark.skipif(not HAS_NACL, reason="pynacl not installed") from i2p_crypto.dsa import SigType from i2p_crypto.blinding import Blinding from i2p_crypto.eddsa_blinding import EdDSABlinding from i2p_data.key_types import SigningPublicKey, SigningPrivateKey from i2p_data.blind_data import BlindData, AuthType def _make_ed25519_spk(): """Create a test Ed25519 signing public key.""" seed = b"\x42" * 32 scalar = EdDSABlinding.seed_to_scalar(seed) pub = EdDSABlinding.scalar_mult_base(scalar) return SigningPublicKey(pub, sig_type=SigType.EdDSA_SHA512_Ed25519), seed def test_generate_alpha_deterministic(): spk, _ = _make_ed25519_spk() ts = 1700000000 a1 = Blinding.generate_alpha(spk, timestamp_sec=ts) a2 = Blinding.generate_alpha(spk, timestamp_sec=ts) assert a1.to_bytes() == a2.to_bytes() assert len(a1.to_bytes()) == 32 def test_generate_alpha_daily_rotation(): spk, _ = _make_ed25519_spk() a1 = Blinding.generate_alpha(spk, timestamp_sec=1700000000) a2 = Blinding.generate_alpha(spk, timestamp_sec=1700086400) # +1 day assert a1.to_bytes() != a2.to_bytes() def test_secret_changes_alpha(): spk, _ = _make_ed25519_spk() ts = 1700000000 a1 = Blinding.generate_alpha(spk, timestamp_sec=ts) a2 = Blinding.generate_alpha(spk, timestamp_sec=ts, secret="mysecret") assert a1.to_bytes() != a2.to_bytes() def test_blind_roundtrip(): """blind(priv, alpha) * G == blind(pub, alpha*G).""" spk, seed = _make_ed25519_spk() ts = 1700000000 alpha = Blinding.generate_alpha(spk, timestamp_sec=ts) blinded_pub = Blinding.blind(spk, alpha) blinded_priv = Blinding.unblind(seed, alpha) # Verify consistency: blinded_priv * G == blinded_pub pub_from_priv = EdDSABlinding.scalar_mult_base(blinded_priv.to_bytes()) assert pub_from_priv == blinded_pub.to_bytes() def test_b32_encode_decode_roundtrip(): spk, _ = _make_ed25519_spk() address = Blinding.encode_b32(spk) assert address.endswith(".b32.i2p") assert len(address) > 10 sig_in, sig_out, decoded_spk, flags = Blinding.decode_b32(address) assert sig_in == SigType.EdDSA_SHA512_Ed25519 assert sig_out == SigType.RedDSA_SHA512_Ed25519 assert decoded_spk.to_bytes() == spk.to_bytes() assert flags == 0 def test_b32_with_flags(): spk, _ = _make_ed25519_spk() address = Blinding.encode_b32(spk, secret_required=True, per_client_auth=True) _, _, _, flags = Blinding.decode_b32(address) assert flags & 0x02 # secret_required assert flags & 0x04 # per_client_auth # --- BlindData tests --- def test_blind_data_caching(): spk, _ = _make_ed25519_spk() bd = BlindData(SigType.EdDSA_SHA512_Ed25519, SigType.RedDSA_SHA512_Ed25519, spk) ts = 1700000000 bp1 = bd.get_blinded_pubkey(ts) bp2 = bd.get_blinded_pubkey(ts) assert bp1.to_bytes() == bp2.to_bytes() def test_blind_data_daily_rotation(): spk, _ = _make_ed25519_spk() bd = BlindData(SigType.EdDSA_SHA512_Ed25519, SigType.RedDSA_SHA512_Ed25519, spk) bp1 = bd.get_blinded_pubkey(1700000000) bp2 = bd.get_blinded_pubkey(1700086400) assert bp1.to_bytes() != bp2.to_bytes() def test_credential_deterministic(): spk, _ = _make_ed25519_spk() bd = BlindData(SigType.EdDSA_SHA512_Ed25519, SigType.RedDSA_SHA512_Ed25519, spk) c1 = bd.get_credential() c2 = bd.get_credential() assert c1 == c2 assert len(c1) == 32 def test_subcredential_changes_daily(): spk, _ = _make_ed25519_spk() bd = BlindData(SigType.EdDSA_SHA512_Ed25519, SigType.RedDSA_SHA512_Ed25519, spk) sc1 = bd.get_subcredential(1700000000) sc2 = bd.get_subcredential(1700086400) assert sc1 != sc2 def test_destination_hash(): spk, _ = _make_ed25519_spk() bd = BlindData(SigType.EdDSA_SHA512_Ed25519, SigType.RedDSA_SHA512_Ed25519, spk) dh = bd.get_destination_hash(1700000000) assert len(dh) == 32