Skip to content

Commit a229b98

Browse files
Merge pull request #14 from SendSafely/v1.0.6
V1.0.6
2 parents d2bd89c + aaa82ab commit a229b98

File tree

4 files changed

+57
-27
lines changed

4 files changed

+57
-27
lines changed

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
requests~=2.31.0
1+
requests~=2.32.2
22
PGPy~=0.6

sendsafely/SendSafely.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
else:
1212
from cryptography.utils import CryptographyDeprecationWarning
1313
from pgpy import PGPMessage
14-
from pgpy.constants import KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm
14+
from pgpy.constants import KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm, PubKeyAlgorithm
1515
from sendsafely.Package import Package
1616
from sendsafely.exceptions import GetPackagesException, GetUserInformationException, TrustedDeviceException, \
1717
DeletePackageException, GetPackageInformationFailedException, GetKeycodeFailedException
18-
from sendsafely.utilities import make_headers, _get_string_from_file, read_key_pair
18+
from sendsafely.utilities import make_headers, _get_string_from_file
1919

2020

2121
class SendSafely:
@@ -73,13 +73,15 @@ def generate_trusted_device_key_pair(self, description):
7373
:param description: A description of this public key to submit to SendSafely
7474
:return: The response, including key pair
7575
"""
76-
key = pgpy.PGPKey.new(pgpy.constants.PubKeyAlgorithm.RSAEncryptOrSign, 2048)
7776
email = self.get_user_information()["email"]
77+
key = pgpy.PGPKey.new(pgpy.constants.PubKeyAlgorithm.RSAEncryptOrSign, 2048)
7878
uid = pgpy.PGPUID.new('Trusted Browser', email=email)
79-
key.add_uid(uid=uid, usage={KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage},
79+
key.add_uid(uid=uid, usage={KeyFlags.Sign, KeyFlags.Certify},
8080
hashes=[HashAlgorithm.SHA256],
8181
ciphers=[SymmetricKeyAlgorithm.AES256],
8282
compression=[CompressionAlgorithm.Uncompressed])
83+
subkey = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 2048)
84+
key.add_subkey(subkey, usage={KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage})
8385
public_key = str(key.pubkey)
8486
endpoint = "/public-key"
8587
url = self.BASE_URL + endpoint

sendsafely/utilities.py

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import requests
2121
from pgpy import PGPMessage
2222
from pgpy.constants import HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm, KeyFlags
23-
23+
from pgpy.packet.subpackets.signature import FlagList
2424

2525
def _encrypt_file_part(file, server_secret, client_secret, path=True):
2626
"""
@@ -104,6 +104,48 @@ def _encrypt_message(message_to_encrypt, server_secret, client_secret):
104104
hash=HashAlgorithm.SHA256)
105105
return base64.b64encode(bytes(cipher_message)).decode('utf-8')
106106

107+
def _inject_encryption_flags(user, key_flags=True, hash=True, symmetric=True, compression=True):
108+
if not key_flags:
109+
user.selfsig._signature.subpackets.addnew('KeyFlags', hashed=True,
110+
flags={KeyFlags.EncryptCommunications,
111+
KeyFlags.EncryptStorage})
112+
user.selfsig._signature.subpackets['h_KeyFlags'] = user.selfsig._signature.subpackets['KeyFlags'][0]
113+
if not hash:
114+
user.selfsig._signature.subpackets.addnew('PreferredHashAlgorithms', hashed=True, flags=[HashAlgorithm.SHA256])
115+
if not symmetric:
116+
user.selfsig._signature.subpackets.addnew('PreferredSymmetricAlgorithms', hashed=True,
117+
flags=[SymmetricKeyAlgorithm.AES256])
118+
if not compression:
119+
user.selfsig._signature.subpackets.addnew('PreferredCompressionAlgorithms', hashed=True,
120+
flags=[CompressionAlgorithm.Uncompressed])
121+
122+
def _enforce_encryption_flags(user):
123+
# https://github.com/SecurityInnovation/PGPy/issues/257
124+
# PGPY requires KeyFlags.EncryptCommunications and KeyFlags.EncryptStorage for public key to encrypt
125+
# which we are not setting in our current APIs
126+
# the following code injects the require attributes to the public key signature to bypass PGPY check
127+
has_key_flags, has_hash, has_symmetric, has_compression = True, True, True, True
128+
key_flags = user.selfsig._signature.subpackets['h_KeyFlags']
129+
hash = user.selfsig._signature.subpackets['h_PreferredHashAlgorithms']
130+
symmetric = user.selfsig._signature.subpackets['h_PreferredSymmetricAlgorithms']
131+
compression = user.selfsig._signature.subpackets['h_PreferredCompressionAlgorithms']
132+
if (len(key_flags) > 0 and len(hash) > 0 and len(symmetric) > 0 and len(compression) > 0):
133+
key_flags, hash, symmetric, compression = key_flags[0], hash[0], symmetric[0], compression[0]
134+
else:
135+
_inject_encryption_flags(user, False, False, False, False)
136+
return
137+
138+
if not (KeyFlags.EncryptStorage in key_flags.__flags__ and KeyFlags.EncryptCommunications in key_flags.__flags__):
139+
has_key_flags = False
140+
if not HashAlgorithm.SHA256 in hash.__flags__:
141+
has_hash = False
142+
if not SymmetricKeyAlgorithm.AES256 in symmetric.__flags__:
143+
has_symmetric = False
144+
if not CompressionAlgorithm.Uncompressed in compression.__flags__:
145+
has_compression = False
146+
147+
_inject_encryption_flags(user, has_key_flags, has_hash, has_symmetric, has_compression)
148+
return user
107149

108150
def _encrypt_keycode(keycode, public_key):
109151
"""
@@ -113,11 +155,6 @@ def _encrypt_keycode(keycode, public_key):
113155
:return: The encrypted keycode
114156
"""
115157
key_pair = pgpy.PGPKey.from_blob(public_key)[0]
116-
117-
# https://github.com/SecurityInnovation/PGPy/issues/257
118-
# PGPY requires KeyFlags.EncryptCommunications and KeyFlags.EncryptStorage for public key to encrypt
119-
# which we are not setting in our current APIs
120-
# the following code injects the require attributes to the public key signature to bypass PGPY check
121158
user = None
122159
if key_pair.is_primary:
123160
if user is not None:
@@ -126,21 +163,12 @@ def _encrypt_keycode(keycode, public_key):
126163
user = next(iter(key_pair.userids))
127164

128165
if user is not None:
129-
user.selfsig._signature.subpackets.addnew('KeyFlags', hashed=True,
130-
flags={KeyFlags.EncryptCommunications,
131-
KeyFlags.EncryptStorage})
132-
user.selfsig._signature.subpackets['h_KeyFlags'] = user.selfsig._signature.subpackets['KeyFlags'][0]
133-
user.selfsig._signature.subpackets.addnew('PreferredHashAlgorithms', hashed=True, flags=[HashAlgorithm.SHA256])
134-
user.selfsig._signature.subpackets.addnew('PreferredSymmetricAlgorithms', hashed=True,
135-
flags=[SymmetricKeyAlgorithm.AES256])
136-
user.selfsig._signature.subpackets.addnew('PreferredCompressionAlgorithms', hashed=True,
137-
flags=[CompressionAlgorithm.Uncompressed])
138-
139-
message = PGPMessage.new(keycode, compression=CompressionAlgorithm.Uncompressed,
140-
cipher=SymmetricKeyAlgorithm.AES256,
141-
hash=HashAlgorithm.SHA256)
142-
cipher_message = key_pair.encrypt(message)
143-
return str(cipher_message)
166+
_enforce_encryption_flags(user)
167+
message = PGPMessage.new(keycode, compression=CompressionAlgorithm.Uncompressed,
168+
cipher=SymmetricKeyAlgorithm.AES256,
169+
hash=HashAlgorithm.SHA256)
170+
cipher_message = key_pair.encrypt(message)
171+
return str(cipher_message)
144172

145173

146174
def _decrypt_message(message_to_decrypt, server_secret, client_secret):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name='sendsafely',
5-
version='1.0.4',
5+
version='1.0.6',
66
packages=['sendsafely'],
77
description='The SendSafely Client API allows programmatic access to SendSafely and provides a layer of abstraction from our REST API, which requires developers to perform several complex tasks in a correct manner.',
88
long_description_content_type="text/markdown",

0 commit comments

Comments
 (0)