Skip to content

Commit 6fab5e9

Browse files
committed
fixup! Added checks for valid PublicKeys in encryption/decryption functions
1 parent 3a41771 commit 6fab5e9

3 files changed

Lines changed: 37 additions & 32 deletions

File tree

tests/test_base.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import pytest
2-
from packaging.version import Version
31
import libnacl
42
import libnacl.public
3+
import pytest
4+
from packaging.version import Version
5+
56
from threema.gateway import (
67
e2e,
78
key,
@@ -26,6 +27,21 @@ def test_incorrect_ciphertext(self):
2627
e2e._pk_decrypt(key_pair, nonce, data_encrypted + b'0')
2728
assert 'decrypt' in str(exc_info.value)
2829

30+
def test_invalid_non_contributory_public_key(self):
31+
all_zero_public_key = libnacl.public.PublicKey(
32+
bytes(libnacl.crypto_box_PUBLICKEYBYTES))
33+
key_pair = key.Key.generate_secret_key, all_zero_public_key
34+
data_in = b"meow"
35+
nonce = b"0" * 24
36+
37+
with pytest.raises(libnacl.CryptError) as exc_info:
38+
e2e._pk_encrypt(key_pair, data_in, nonce=nonce)
39+
assert "Invalid public key (non-contributory)" in str(exc_info.value)
40+
41+
with pytest.raises(libnacl.CryptError) as exc_info:
42+
e2e._pk_decrypt(key_pair, nonce, bytes(5))
43+
assert "Invalid public key (non-contributory)" in str(exc_info.value)
44+
2945
def test_valid(self):
3046
key_pair = key.Key.generate_pair()
3147
data_in = b'meow'
@@ -34,25 +50,12 @@ def test_valid(self):
3450
data_out = e2e._pk_decrypt(key_pair, nonce, data_encrypted)
3551
assert data_in == data_out
3652

37-
def test_invalid_pk(self):
38-
all_zero_pk = libnacl.public.PublicKey(bytes(libnacl.crypto_box_PUBLICKEYBYTES))
39-
key_pair = key.Key.generate_secret_key, all_zero_pk
40-
data_in = b'meow'
41-
nonce = b'0' * 24
42-
with pytest.raises(libnacl.CryptError) as exc_info:
43-
e2e._pk_encrypt(key_pair, data_in, nonce=nonce)
44-
assert 'Invalid public key' in str(exc_info.value)
45-
46-
with pytest.raises(libnacl.CryptError) as exc_info:
47-
e2e._pk_decrypt(key_pair, nonce, bytes(5))
48-
assert 'Invalid public key' in str(exc_info.value)
49-
50-
@pytest.mark.skipif(Version(libnacl.sodium_version_string().decode("ascii")) < Version("1.0.7"),
51-
reason="no zero-result check on X25519 in this version of libsodium")
52-
def test_contributory(self):
53+
@pytest.mark.skipif(
54+
Version(libnacl.sodium_version_string().decode("ascii")) < Version("1.0.7"),
55+
reason="no zero-result check on X25519 in this libnacl backend")
56+
def test_libsodium_non_contributory_public_key(self):
5357
all_zero_pk = libnacl.public.PublicKey(bytes(libnacl.crypto_box_PUBLICKEYBYTES))
5458
alice_sk, _ = key.Key.generate_pair()
5559
with pytest.raises(libnacl.CryptError) as exc_info:
56-
alice_box = libnacl.public.Box(alice_sk, all_zero_pk)
60+
libnacl.public.Box(alice_sk, all_zero_pk)
5761
assert 'Unable to compute shared key' in str(exc_info)
58-

threema/gateway/e2e.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ def _pk_encrypt(key_pair: Tuple[Key, Key], data: bytes, nonce: Optional[bytes] =
8282
"""
8383
# Assemble and encrypt the payload
8484
private, public = key_pair
85-
if not Key.is_valid_public_key(public):
86-
raise libnacl.CryptError("Invalid public key")
85+
Key.ensure_valid_public_key(public)
8786
box = libnacl.public.Box(sk=private, pk=public)
8887
return box.encrypt(data, nonce=nonce, pack_nonce=False)
8988

@@ -105,8 +104,7 @@ def _pk_decrypt(key_pair: Tuple[Key, Key], nonce: bytes, data: bytes):
105104
"""
106105
# Decrypt payload
107106
private, public = key_pair
108-
if not Key.is_valid_public_key(public):
109-
raise libnacl.CryptError("Invalid public key")
107+
Key.ensure_valid_public_key(public)
110108
box = libnacl.public.Box(sk=private, pk=public)
111109
return box.decrypt(data, nonce=nonce)
112110

threema/gateway/key.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ def hash(message, hash_type):
4242
return hmac.new(HMAC.keys[hash_type], message.encode('ascii'), hashlib.sha256)
4343

4444

45+
_NON_CONTRIBUTORY_PUBLIC_KEY = libnacl.public.PublicKey(
46+
bytes(libnacl.crypto_box_PUBLICKEYBYTES))
47+
48+
4549
class Key:
4650
"""
4751
Encode or decode a key.
@@ -156,21 +160,21 @@ def derive_public(private_key):
156160
return libnacl.public.PublicKey(private_key.pk)
157161

158162
@staticmethod
159-
def is_valid_public_key(public_key):
163+
def ensure_valid_public_key(public_key):
160164
"""
161-
Check if the public key is valid.
165+
Ensure a public key is valid.
162166
163167
Arguments:
164168
- `public_key`: An instance of
165169
:class:`libnacl.public.PublicKey`.
166170
167-
Return True if the key is a valid public key, False if not.
171+
Raises :class:`libnacl.CryptError` if the public key is invalid.
168172
"""
169173
if not isinstance(public_key, libnacl.public.PublicKey):
170-
return False
174+
raise libnacl.CryptError("Invalid public key")
171175
if len(public_key.pk) != libnacl.crypto_box_PUBLICKEYBYTES:
172-
return False
176+
raise libnacl.CryptError("Invalid public key")
173177

174-
# Reject all-zero public keys
175-
zero_public_key = libnacl.public.PublicKey(bytes(libnacl.crypto_box_PUBLICKEYBYTES))
176-
return public_key != zero_public_key
178+
# Reject all-zero public keys (mon-contributory)
179+
if public_key == _NON_CONTRIBUTORY_PUBLIC_KEY:
180+
raise libnacl.CryptError("Invalid public key (non-contributory)")

0 commit comments

Comments
 (0)