Skip to content

Commit 15f67ce

Browse files
Bernd Muellerthreema-lenny
authored andcommitted
Added checks for valid PublicKeys in encryption/decryption functions
1 parent 19ba796 commit 15f67ce

4 files changed

Lines changed: 56 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,6 @@ gen
7474

7575
# MyPy Cache
7676
.mypy_cache/
77+
78+
# OS specific
79+
.DS_Store

tests/test_base.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import libnacl
2+
import libnacl.public
23
import pytest
4+
from packaging.version import Version
35

46
from threema.gateway import (
57
e2e,
@@ -25,10 +27,35 @@ def test_incorrect_ciphertext(self):
2527
e2e._pk_decrypt(key_pair, nonce, data_encrypted + b'0')
2628
assert 'decrypt' in str(exc_info.value)
2729

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+
2845
def test_valid(self):
2946
key_pair = key.Key.generate_pair()
3047
data_in = b'meow'
3148
nonce = b'0' * 24
3249
_, data_encrypted = e2e._pk_encrypt(key_pair, data_in, nonce=nonce)
3350
data_out = e2e._pk_decrypt(key_pair, nonce, data_encrypted)
3451
assert data_in == data_out
52+
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):
57+
all_zero_pk = libnacl.public.PublicKey(bytes(libnacl.crypto_box_PUBLICKEYBYTES))
58+
alice_sk, _ = key.Key.generate_pair()
59+
with pytest.raises(libnacl.CryptError) as exc_info:
60+
libnacl.public.Box(alice_sk, all_zero_pk)
61+
assert 'Unable to compute shared key' in str(exc_info)

threema/gateway/e2e.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +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+
Key.ensure_valid_public_key(public)
8586
box = libnacl.public.Box(sk=private, pk=public)
8687
return box.encrypt(data, nonce=nonce, pack_nonce=False)
8788

@@ -103,6 +104,7 @@ def _pk_decrypt(key_pair: Tuple[Key, Key], nonce: bytes, data: bytes):
103104
"""
104105
# Decrypt payload
105106
private, public = key_pair
107+
Key.ensure_valid_public_key(public)
106108
box = libnacl.public.Box(sk=private, pk=public)
107109
return box.decrypt(data, nonce=nonce)
108110

threema/gateway/key.py

Lines changed: 24 additions & 0 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.
@@ -154,3 +158,23 @@ def derive_public(private_key):
154158
Return the :class:`libnacl.public.PublicKey` instance.
155159
"""
156160
return libnacl.public.PublicKey(private_key.pk)
161+
162+
@staticmethod
163+
def ensure_valid_public_key(public_key):
164+
"""
165+
Ensure a public key is valid.
166+
167+
Arguments:
168+
- `public_key`: An instance of
169+
:class:`libnacl.public.PublicKey`.
170+
171+
Raises :class:`libnacl.CryptError` if the public key is invalid.
172+
"""
173+
if not isinstance(public_key, libnacl.public.PublicKey):
174+
raise libnacl.CryptError("Invalid public key")
175+
if len(public_key.pk) != libnacl.crypto_box_PUBLICKEYBYTES:
176+
raise libnacl.CryptError("Invalid public key")
177+
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)