Skip to content

Commit 8e899c7

Browse files
committed
Fix remove packed nonce for file messages
Add secret key encryption functions to threema.gateway.e2e Add nonce option to encrypt functions in threema.gateway.e2e
1 parent 1cff100 commit 8e899c7

2 files changed

Lines changed: 69 additions & 15 deletions

File tree

threema/gateway/e2e.py

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,15 @@
3030
)
3131

3232

33-
def encrypt(private, public, data):
33+
def encrypt(private, public, data, nonce=None):
3434
"""
35-
Encrypt a message.
35+
Encrypt a message by using public-key encryption.
3636
3737
Arguments:
3838
- `private`: Private key of the sender.
3939
- `public`: The public key of the recipient.
4040
- `data`: Message data (bytes).
41+
- `nonce`: A predefined nonce.
4142
4243
Return a tuple of bytes containing the nonce and the encrypted
4344
data.
@@ -49,12 +50,12 @@ def encrypt(private, public, data):
4950
padding = bytes([padding_length] * padding_length)
5051

5152
# Assemble and encrypt the payload
52-
return pk_encrypt_raw(private, public, data + padding)
53+
return pk_encrypt_raw(private, public, data + padding, nonce=nonce)
5354

5455

5556
def decrypt(private, public, nonce, data):
5657
"""
57-
Decrypt a message.
58+
Decrypt a message by using public-key decryption.
5859
5960
Arguments:
6061
- `private`: Private key of the sender.
@@ -81,26 +82,27 @@ def decrypt(private, public, nonce, data):
8182
return DeliveryReceipt(payload=payload)
8283

8384

84-
def pk_encrypt_raw(private, public, data):
85+
def pk_encrypt_raw(private, public, data, nonce=None):
8586
"""
86-
Encrypt data.
87+
Encrypt data by using public-key encryption.
8788
8889
Arguments:
8990
- `private`: Private key of the sender.
9091
- `public`: The public key of the recipient.
91-
- `image`: Data (bytes).
92+
- `data`: Data (bytes).
93+
- `nonce`: A predefined nonce.
9294
9395
Return a tuple of bytes containing the nonce and the encrypted
9496
data.
9597
"""
9698
# Assemble and encrypt the payload
9799
box = libnacl.public.Box(sk=private, pk=public)
98-
return box.encrypt(data, pack_nonce=False)
100+
return box.encrypt(data, nonce=nonce, pack_nonce=False)
99101

100102

101103
def pk_decrypt_raw(private, public, nonce, data):
102104
"""
103-
Decrypt data.
105+
Decrypt data by using public-key decryption.
104106
105107
Arguments:
106108
- `private`: Private key of the sender.
@@ -112,7 +114,45 @@ def pk_decrypt_raw(private, public, nonce, data):
112114
"""
113115
# Decrypt payload
114116
box = libnacl.public.Box(sk=private, pk=public)
115-
return box.decrypt(data, nonce)
117+
return box.decrypt(data, nonce=nonce)
118+
119+
120+
def sk_encrypt_raw(key, data, nonce=None):
121+
"""
122+
Encrypt data by using secret-key encryption.
123+
124+
Arguments:
125+
- `key`: The secret key.
126+
- `data`: Data (bytes).
127+
- `nonce`: A predefined nonce.
128+
129+
Return a tuple of bytes containing the nonce and the encrypted
130+
data.
131+
"""
132+
# Assemble and encrypt the payload
133+
box = libnacl.secret.SecretBox(key=key)
134+
# Note: Workaround for libnacl which lacks `pack_nonce` option
135+
# (see: )
136+
#return box.encrypt(data, nonce=nonce, pack_nonce=False)
137+
data = box.encrypt(data, nonce=nonce)
138+
nonce_length = libnacl.crypto_secretbox_NONCEBYTES
139+
return data[:nonce_length], data[nonce_length:]
140+
141+
142+
def sk_decrypt_raw(key, nonce, data):
143+
"""
144+
Decrypt data by using secret-key decryption.
145+
146+
Arguments:
147+
- `key`: The secret key.
148+
- `nonce`: The nonce of the encrypted message.
149+
- `data`: Encrypted data (bytes).
150+
151+
Return the decrypted data.
152+
"""
153+
# Decrypt payload
154+
box = libnacl.secret.SecretBox(key=key)
155+
return box.decrypt(data, nonce=nonce)
116156

117157

118158
class Message(metaclass=abc.ABCMeta):
@@ -558,15 +598,17 @@ def send(self):
558598
with open(self.file_path, mode='rb') as file:
559599
self.file_content = file.read()
560600

601+
# Create symmetric key
602+
key, hex_key = Key.generate_secret_key()
603+
561604
# Encrypt and upload file
562-
box = libnacl.secret.SecretBox()
563-
file_data = box.encrypt(self.file_content, nonce=self.nonce['file'])
605+
_, file_data = sk_encrypt_raw(key, self.file_content, nonce=self.nonce['file'])
564606
file_id = self.connection.upload(file_data)
565607

566608
# Build JSON
567609
content = {
568610
'b': file_id,
569-
'k': box.hex_sk().decode('utf-8'),
611+
'k': hex_key.decode('utf-8'),
570612
'm': self.mime_type,
571613
'n': os.path.basename(self.file_path),
572614
's': len(self.file_content),
@@ -581,8 +623,8 @@ def send(self):
581623
self.thumbnail_content = file.read()
582624

583625
# Encrypt and upload thumbnail
584-
thumbnail_data = box.encrypt(self.thumbnail_content,
585-
nonce=self.nonce['thumbnail'])
626+
_, thumbnail_data = sk_encrypt_raw(key, self.thumbnail_content,
627+
nonce=self.nonce['thumbnail'])
586628
thumbnail_id = self.connection.upload(thumbnail_data)
587629

588630
# Update JSON

threema/gateway/key.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import hmac
77

88
import libnacl.public
9+
import libnacl.secret
910
import libnacl.encode
1011

1112
__all__ = (
@@ -127,6 +128,17 @@ def generate_pair():
127128
public_key = libnacl.public.PublicKey(private_key.pk)
128129
return private_key, public_key
129130

131+
@staticmethod
132+
def generate_secret_key():
133+
"""
134+
Generate a new secret key box.
135+
136+
Return a tuple of the key's :class:`bytes` and hex-encoded
137+
representation.
138+
"""
139+
box = libnacl.secret.SecretBox()
140+
return box.sk, box.hex_sk()
141+
130142
@staticmethod
131143
def derive_public(private_key):
132144
"""

0 commit comments

Comments
 (0)