A Python port of the Invisible Internet Project (I2P)
1"""Tests for i2p_crypto EdDSA — Ed25519 signing/verification."""
2
3import pytest
4
5
6class TestEdDSAEngine:
7 def test_sign_verify_roundtrip(self):
8 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
9 kp = EdDSAKeyPair.generate()
10 data = b"Hello, Ed25519!"
11 sig = EdDSAEngine.sign(data, kp.private_key)
12 assert len(sig) == 64
13 assert EdDSAEngine.verify(data, sig, kp.public_key)
14
15 def test_verify_wrong_data(self):
16 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
17 kp = EdDSAKeyPair.generate()
18 sig = EdDSAEngine.sign(b"correct", kp.private_key)
19 assert not EdDSAEngine.verify(b"wrong", sig, kp.public_key)
20
21 def test_verify_wrong_key(self):
22 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
23 kp1 = EdDSAKeyPair.generate()
24 kp2 = EdDSAKeyPair.generate()
25 sig = EdDSAEngine.sign(b"data", kp1.private_key)
26 assert not EdDSAEngine.verify(b"data", sig, kp2.public_key)
27
28 def test_sign_with_offset_length(self):
29 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
30 kp = EdDSAKeyPair.generate()
31 data = b"XXXhelloXXX"
32 sig = EdDSAEngine.sign(data, kp.private_key, offset=3, length=5)
33 assert EdDSAEngine.verify(b"hello", sig, kp.public_key)
34
35 def test_verify_with_offset_length(self):
36 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
37 kp = EdDSAKeyPair.generate()
38 sig = EdDSAEngine.sign(b"hello", kp.private_key)
39 data = b"XXXhelloXXX"
40 assert EdDSAEngine.verify(data, sig, kp.public_key, offset=3, length=5)
41
42 def test_deterministic_signatures(self):
43 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
44 kp = EdDSAKeyPair.from_private_bytes(bytes(range(32)))
45 sig1 = EdDSAEngine.sign(b"deterministic", kp.private_key)
46 sig2 = EdDSAEngine.sign(b"deterministic", kp.private_key)
47 assert sig1 == sig2 # Ed25519 is deterministic
48
49 def test_empty_message(self):
50 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
51 kp = EdDSAKeyPair.generate()
52 sig = EdDSAEngine.sign(b"", kp.private_key)
53 assert EdDSAEngine.verify(b"", sig, kp.public_key)
54
55 def test_large_message(self):
56 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
57 kp = EdDSAKeyPair.generate()
58 data = bytes(range(256)) * 100 # 25.6 KB
59 sig = EdDSAEngine.sign(data, kp.private_key)
60 assert EdDSAEngine.verify(data, sig, kp.public_key)
61
62 def test_cross_verify_known_seed(self):
63 """Sign with known seed, verify with derived public key."""
64 from i2p_crypto.eddsa import EdDSAEngine, public_key_from_private
65 seed = bytes.fromhex(
66 "c5aa8df43f9f837bedb7442f31dcb7b1"
67 "66d38535076f094b85ce3a2e0b4458f7"
68 )
69 pub = public_key_from_private(seed)
70 msg = b"test cross-verify"
71 sig = EdDSAEngine.sign(msg, seed)
72 assert EdDSAEngine.verify(msg, sig, pub)
73
74 def test_signature_length(self):
75 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
76 kp = EdDSAKeyPair.generate()
77 for msg in [b"", b"x", b"y" * 10000]:
78 sig = EdDSAEngine.sign(msg, kp.private_key)
79 assert len(sig) == EdDSAEngine.SIGNATURE_LENGTH
80
81 def test_corrupted_signature_rejected(self):
82 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
83 kp = EdDSAKeyPair.generate()
84 sig = bytearray(EdDSAEngine.sign(b"data", kp.private_key))
85 sig[0] ^= 0xFF
86 assert not EdDSAEngine.verify(b"data", bytes(sig), kp.public_key)
87
88 def test_one_shot_alias(self):
89 from i2p_crypto.eddsa import EdDSAEngine
90 assert EdDSAEngine.sign_one_shot is EdDSAEngine.sign
91 assert EdDSAEngine.verify_one_shot is EdDSAEngine.verify
92
93
94class TestEdDSAKeyPair:
95 def test_generate(self):
96 from i2p_crypto.eddsa import EdDSAKeyPair
97 kp = EdDSAKeyPair.generate()
98 assert len(kp.private_key) == 32
99 assert len(kp.public_key) == 32
100
101 def test_from_private_bytes(self):
102 from i2p_crypto.eddsa import EdDSAKeyPair
103 seed = bytes(range(32))
104 kp = EdDSAKeyPair.from_private_bytes(seed)
105 assert kp.private_key == seed
106
107 def test_sign_verify_via_keypair(self):
108 from i2p_crypto.eddsa import EdDSAKeyPair
109 kp = EdDSAKeyPair.generate()
110 sig = kp.sign(b"test")
111 assert kp.verify(b"test", sig)
112 assert not kp.verify(b"wrong", sig)
113
114 def test_reproducible_public_key(self):
115 from i2p_crypto.eddsa import EdDSAKeyPair
116 seed = bytes(range(32))
117 kp1 = EdDSAKeyPair.from_private_bytes(seed)
118 kp2 = EdDSAKeyPair.from_private_bytes(seed)
119 assert kp1.public_key == kp2.public_key
120
121 def test_different_keys_different_public(self):
122 from i2p_crypto.eddsa import EdDSAKeyPair
123 kp1 = EdDSAKeyPair.generate()
124 kp2 = EdDSAKeyPair.generate()
125 assert kp1.public_key != kp2.public_key
126
127 def test_keypair_sign_matches_engine(self):
128 from i2p_crypto.eddsa import EdDSAEngine, EdDSAKeyPair
129 kp = EdDSAKeyPair.from_private_bytes(bytes(range(32)))
130 msg = b"consistency check"
131 sig_kp = kp.sign(msg)
132 sig_engine = EdDSAEngine.sign(msg, kp.private_key)
133 assert sig_kp == sig_engine
134
135
136class TestPublicKeyFromPrivate:
137 def test_derive_public_key(self):
138 from i2p_crypto.eddsa import public_key_from_private, EdDSAKeyPair
139 seed = bytes(range(32))
140 kp = EdDSAKeyPair.from_private_bytes(seed)
141 derived = public_key_from_private(seed)
142 assert derived == kp.public_key
143
144 def test_deterministic_derivation(self):
145 from i2p_crypto.eddsa import public_key_from_private
146 seed = b"\xab" * 32
147 pub1 = public_key_from_private(seed)
148 pub2 = public_key_from_private(seed)
149 assert pub1 == pub2
150 assert len(pub1) == 32
151
152 def test_different_seeds_different_pubkeys(self):
153 from i2p_crypto.eddsa import public_key_from_private
154 pub1 = public_key_from_private(b"\x00" * 32)
155 pub2 = public_key_from_private(b"\x01" + b"\x00" * 31)
156 assert pub1 != pub2