Skip to content

Commit a4a0d8f

Browse files
authored
Fix: ecdsa token verify (#884)
A fix for the issue in the verify_signature function at [src/aleph/toolkit/ecdsa.py:70](vscode-webview://0f3rqv1fdlfd4j34lfp31cchhs6ehdk6u11l37dcchfnrvjlj0ou/src/aleph/toolkit/ecdsa.py#L70). The Problem: ```python public_key = PublicKey.from_hex(public_key_hex) # This method doesn't exist ``` The Fix: ```python public_key = PublicKey(bytes.fromhex(public_key_hex)) # Correct way to create PublicKey ``` What was happening: - When verify_auth_token called verify_signature, it would throw an AttributeError because PublicKey.from_hex() doesn't exist in the coincurve library - The exception was caught by the except Exception: block and returned False - So verify_auth_token always returned False, even with valid tokens and matching keypairs
1 parent f64dc9c commit a4a0d8f

File tree

2 files changed

+206
-1
lines changed

2 files changed

+206
-1
lines changed

src/aleph/toolkit/ecdsa.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def verify_signature(message: str, signature_b64: str, public_key_hex: str) -> b
6464
True if signature is valid, False otherwise
6565
"""
6666
try:
67-
public_key = PublicKey.from_hex(public_key_hex)
67+
public_key = PublicKey(bytes.fromhex(public_key_hex))
6868
signature = base64.b64decode(signature_b64)
6969
message_bytes = message.encode("utf-8")
7070
return public_key.verify(signature, message_bytes)

tests/toolkit/test_ecdsa.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import base64
2+
import time
3+
from unittest.mock import patch
4+
5+
from aleph.toolkit.ecdsa import (
6+
create_auth_token,
7+
generate_key_pair,
8+
generate_key_pair_from_private_key,
9+
sign_message,
10+
verify_auth_token,
11+
verify_signature,
12+
)
13+
14+
15+
def test_generate_key_pair():
16+
"""Test key pair generation."""
17+
private_key, public_key = generate_key_pair()
18+
19+
# Keys should be hex strings
20+
assert isinstance(private_key, str)
21+
assert isinstance(public_key, str)
22+
23+
# Private key should be 64 hex characters (32 bytes)
24+
assert len(private_key) == 64
25+
26+
# Public key should be 66 hex characters (33 bytes compressed)
27+
assert len(public_key) == 66
28+
29+
# Public key should start with 02 or 03 (compressed format)
30+
assert public_key.startswith(("02", "03"))
31+
32+
33+
def test_generate_key_pair_from_private_key():
34+
"""Test key pair generation from existing private key."""
35+
test_private_key = (
36+
"646fa150ca94320b8eca3bd28d106703f3602dfb13b6b982cc17d17fd1182f85"
37+
)
38+
39+
private_key, public_key = generate_key_pair_from_private_key(test_private_key)
40+
41+
# Should return the same private key
42+
assert private_key == test_private_key
43+
44+
# Public key should be correctly derived
45+
assert isinstance(public_key, str)
46+
assert len(public_key) == 66
47+
assert public_key.startswith(("02", "03"))
48+
49+
50+
def test_sign_and_verify_message():
51+
"""Test message signing and verification."""
52+
private_key, public_key = generate_key_pair()
53+
message = "test message"
54+
55+
# Sign the message
56+
signature = sign_message(message, private_key)
57+
58+
# Signature should be base64 encoded
59+
assert isinstance(signature, str)
60+
# Should be valid base64
61+
base64.b64decode(signature)
62+
63+
# Verify the signature
64+
is_valid = verify_signature(message, signature, public_key)
65+
assert is_valid is True
66+
67+
# Verification should fail with wrong message
68+
is_valid_wrong = verify_signature("wrong message", signature, public_key)
69+
assert is_valid_wrong is False
70+
71+
# Verification should fail with wrong public key
72+
_, wrong_public_key = generate_key_pair()
73+
is_valid_wrong_key = verify_signature(message, signature, wrong_public_key)
74+
assert is_valid_wrong_key is False
75+
76+
77+
def test_create_and_verify_auth_token():
78+
"""Test authentication token creation and verification."""
79+
private_key, public_key = generate_key_pair()
80+
81+
# Create a token
82+
token = create_auth_token(private_key)
83+
84+
# Token should be base64 encoded
85+
assert isinstance(token, str)
86+
# Should be valid base64
87+
token_data = base64.b64decode(token).decode("utf-8")
88+
89+
# Token should contain timestamp and signature separated by colon
90+
parts = token_data.split(":", 1)
91+
assert len(parts) == 2
92+
93+
timestamp_str, signature = parts
94+
# Timestamp should be a valid integer
95+
timestamp = int(timestamp_str)
96+
assert timestamp > 0
97+
98+
# Signature should be valid base64
99+
base64.b64decode(signature)
100+
101+
# Verify the token
102+
is_valid = verify_auth_token(token, public_key)
103+
assert is_valid is True
104+
105+
106+
def test_verify_auth_token_with_wrong_public_key():
107+
"""Test token verification fails with wrong public key."""
108+
private_key, _ = generate_key_pair()
109+
_, wrong_public_key = generate_key_pair()
110+
111+
token = create_auth_token(private_key)
112+
113+
# Should fail with wrong public key
114+
is_valid = verify_auth_token(token, wrong_public_key)
115+
assert is_valid is False
116+
117+
118+
def test_verify_auth_token_expired():
119+
"""Test token verification fails when token is expired."""
120+
private_key, public_key = generate_key_pair()
121+
122+
# Mock time to create an old token
123+
old_timestamp = int(time.time()) - 600 # 10 minutes ago
124+
125+
with patch("aleph.toolkit.ecdsa.time.time", return_value=old_timestamp):
126+
token = create_auth_token(private_key)
127+
128+
# Should fail with default max_age (5 minutes)
129+
is_valid = verify_auth_token(token, public_key)
130+
assert is_valid is False
131+
132+
# Should pass with larger max_age
133+
is_valid_long = verify_auth_token(token, public_key, max_age_seconds=700)
134+
assert is_valid_long is True
135+
136+
137+
def test_verify_auth_token_future_timestamp():
138+
"""Test token verification handles future timestamps within tolerance."""
139+
private_key, public_key = generate_key_pair()
140+
141+
# Mock time to create a future token
142+
future_timestamp = int(time.time()) + 60 # 1 minute in future
143+
144+
with patch("aleph.toolkit.ecdsa.time.time", return_value=future_timestamp):
145+
token = create_auth_token(private_key)
146+
147+
# Should pass as it uses abs() for time difference
148+
is_valid = verify_auth_token(token, public_key)
149+
assert is_valid is True
150+
151+
152+
def test_verify_auth_token_malformed():
153+
"""Test token verification fails with malformed tokens."""
154+
_, public_key = generate_key_pair()
155+
156+
# Invalid base64
157+
is_valid = verify_auth_token("invalid_base64!", public_key)
158+
assert is_valid is False
159+
160+
# Valid base64 but wrong format (no colon)
161+
malformed_token = base64.b64encode("no_colon_here".encode()).decode()
162+
is_valid = verify_auth_token(malformed_token, public_key)
163+
assert is_valid is False
164+
165+
# Valid base64 but invalid timestamp
166+
malformed_data = "not_a_number:MEQCIHFHKoBPyY3pCMY9x5gS4P1"
167+
malformed_token = base64.b64encode(malformed_data.encode()).decode()
168+
is_valid = verify_auth_token(malformed_token, public_key)
169+
assert is_valid is False
170+
171+
172+
def test_verify_signature_with_invalid_inputs():
173+
"""Test verify_signature handles invalid inputs gracefully."""
174+
_, public_key = generate_key_pair()
175+
176+
# Invalid base64 signature
177+
is_valid = verify_signature("message", "invalid_base64!", public_key)
178+
assert is_valid is False
179+
180+
# Invalid public key
181+
is_valid = verify_signature("message", "dGVzdA==", "invalid_public_key")
182+
assert is_valid is False
183+
184+
185+
def test_token_roundtrip_with_known_values():
186+
"""Test token creation and verification with known test values."""
187+
# Use known values for reproducible testing
188+
test_private_key = (
189+
"50b44756efbcb9266d974af8a8bcecb97d960fd8ddaadd31ecf2082c757fcaad"
190+
)
191+
test_public_key = (
192+
"023d3b6f2e92e5d30b8d75291087051f6ef9425abbb626bebc3a5b358bce6007ee"
193+
)
194+
195+
# Create token
196+
token = create_auth_token(test_private_key)
197+
198+
# Verify it works
199+
is_valid = verify_auth_token(token, test_public_key)
200+
assert is_valid is True
201+
202+
# Verify with wrong key fails
203+
_, wrong_key = generate_key_pair()
204+
is_valid_wrong = verify_auth_token(token, wrong_key)
205+
assert is_valid_wrong is False

0 commit comments

Comments
 (0)