Skip to content

Commit 511fe69

Browse files
committed
Significant improvement to python code
1 parent 6a409da commit 511fe69

File tree

4 files changed

+115
-53
lines changed

4 files changed

+115
-53
lines changed

nodejs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "http_ece",
3-
"version": "0.6.0",
3+
"version": "0.6.3",
44
"description": "Encrypted Content-Encoding for HTTP",
55
"homepage": "https://github.com/martinthomson/encrypted-content-encoding",
66
"bugs": "https://github.com/martinthomson/encrypted-content-encoding/issues",

python/http_ece/__init__.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
keys = {}
1515
labels = {}
1616

17-
MAX_BUFFER_SIZE = pow(2, 31) - 1
18-
MIN_BUFFER_SIZE = 3
17+
MAX_RECORD_SIZE = pow(2, 31) - 1
18+
MIN_RECORD_SIZE = 3
1919
KEY_LENGTH = 16
2020

2121
# Valid content types (ordered from newest, to most obsolete)
@@ -76,11 +76,9 @@ def length_prefix(key):
7676
if mode == "encrypt":
7777
sender_pub_key = key or keymap[keyid].get_pubkey()
7878
receiver_pub_key = dh
79-
elif mode == "decrypt":
79+
else:
8080
sender_pub_key = dh
8181
receiver_pub_key = key or keymap[keyid].get_pubkey()
82-
else:
83-
raise ECEException(u"unknown 'mode' specified: " + mode)
8482
if version == "aes128gcm":
8583
context = b"WebPush: info\x00" + receiver_pub_key + sender_pub_key
8684
else:
@@ -92,6 +90,8 @@ def length_prefix(key):
9290

9391
if version not in versions:
9492
raise ECEException(u"Invalid version")
93+
if mode not in ['encrypt', 'decrypt']:
94+
raise ECEException(u"unknown 'mode' specified: " + mode)
9595
if salt is None or len(salt) != 16:
9696
raise ECEException(u"'salt' must be a 16 octet value")
9797
if dh is not None:
@@ -160,8 +160,9 @@ def iv(base, counter):
160160
return base[:4] + struct.pack("!Q", counter ^ mask)
161161

162162

163-
def decrypt(content, salt, key=None, keyid=None, keymap=None, keylabels=None,
164-
dh=None, rs=4096, auth_secret=None, version="aesgcm", **kwargs):
163+
def decrypt(content, salt=None, key=None, dh=None, auth_secret=None,
164+
keyid=None, keymap=None, keylabels=None,
165+
rs=4096, version="aesgcm", **kwargs):
165166
"""
166167
Decrypt a data block
167168
@@ -212,7 +213,8 @@ def decrypt_record(key, nonce, counter, content):
212213
lambda x, y: x << 8 | y, struct.unpack(
213214
"!" + ("B" * pad_size), data[0:pad_size])
214215
)
215-
if data[pad_size:pad_size+pad] != (b"\x00" * pad):
216+
if pad_size + pad > len(data) or \
217+
data[pad_size:pad_size+pad] != (b"\x00" * pad):
216218
raise ECEException(u"Bad padding")
217219
data = data[pad_size + pad:]
218220
return data
@@ -231,12 +233,12 @@ def decrypt_record(key, nonce, counter, content):
231233
if version == "aes128gcm":
232234
try:
233235
content_header = parse_content_header(content)
234-
except ECEException as ex:
235-
raise ECEException("Could not parse the content header: " +
236-
ex.message)
236+
except:
237+
raise ECEException("Could not parse the content header")
237238
salt = content_header['salt']
238239
keyid = content_header['key_id'] or '' if keyid is None else keyid
239240
content = content_header['content']
241+
rs = content_header['rs']
240242

241243
(key_, nonce_) = derive_key(mode="decrypt", version=version,
242244
salt=salt, key=key,
@@ -259,8 +261,9 @@ def decrypt_record(key, nonce, counter, content):
259261
return result
260262

261263

262-
def encrypt(content, salt=None, key=None, keyid=None, keymap=None, keylabels=None,
263-
dh=None, rs=4096, auth_secret=None, version="aesgcm", **kwargs):
264+
def encrypt(content, salt=None, key=None, dh=None, auth_secret=None,
265+
keyid=None, keymap=None, keylabels=None,
266+
rs=4096, version="aesgcm", **kwargs):
264267
"""
265268
Encrypt a data block
266269
@@ -290,6 +293,7 @@ def encrypt_record(key, nonce, counter, buf):
290293
modes.GCM(iv(nonce, counter)),
291294
backend=default_backend()
292295
).encryptor()
296+
293297
data = encryptor.update((b"\0" * pad_size) + buf)
294298
data += encryptor.finalize()
295299
data += encryptor.tag
@@ -309,14 +313,10 @@ def compose_aes128gcm(salt, content, rs, keyid):
309313
:type keyid: str
310314
311315
"""
312-
if len(salt) != 16:
313-
raise ECEException("Invalid salt")
314316
if len(keyid) > 255:
315317
raise ECEException("keyid is too long")
316318
header = salt
317-
if rs < MIN_BUFFER_SIZE:
318-
raise ECEException("Too little content")
319-
if rs > MAX_BUFFER_SIZE:
319+
if rs > MAX_RECORD_SIZE:
320320
raise ECEException("Too much content")
321321
header += struct.pack("!L", rs)
322322
header += struct.pack("!B", len(keyid))

python/http_ece/tests/test_ece.py

Lines changed: 95 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -57,33 +57,31 @@ class TestEce(unittest.TestCase):
5757
def setUp(self):
5858
self.keymap = {'valid': pyelliptic.ECC(curve="prime256v1")}
5959
self.keylabels = {'valid': 'P-256'}
60+
self.m_key = os.urandom(16)
6061
self.m_salt = os.urandom(16)
61-
self.m_dh = os.urandom(16)
6262

6363
def tearDown(self):
6464
self.keymap = None
6565
self.keylabels = None
6666

67-
def test_derive_key_no_keyid(self):
67+
def test_derive_key_no_keyid_dh(self):
6868
with assert_raises(ECEException) as ex:
6969
ece.derive_key('encrypt',
7070
version='aes128gcm',
7171
salt=self.m_salt,
72-
key=None,
73-
dh=self.m_dh,
72+
dh='bogus',
7473
keyid=None,
7574
keymap=self.keymap,
7675
auth_secret=None,
7776
)
7877
eq_(ex.exception.message, "'keyid' is not specified with 'dh'")
7978

80-
def test_derive_key_invalid_key(self):
79+
def test_derive_key_invalid_keyid_dh(self):
8180
with assert_raises(ECEException) as ex:
8281
ece.derive_key('encrypt',
8382
version='aes128gcm',
8483
salt=self.m_salt,
85-
key=None,
86-
dh=self.m_dh,
84+
dh='bogus',
8785
keyid="invalid",
8886
keymap=self.keymap,
8987
auth_secret=None,
@@ -95,36 +93,19 @@ def test_derive_key_invalid_mode(self):
9593
ece.derive_key('invalid',
9694
version='aes128gcm',
9795
salt=self.m_salt,
98-
key=None,
99-
dh=self.m_dh,
96+
key=self.m_key,
10097
keyid="valid",
10198
keymap=self.keymap,
10299
auth_secret=None,
103100
)
104101
eq_(ex.exception.message, "unknown 'mode' specified: invalid")
105102

106-
"""
107-
def test_derive_key_invalid_label(self):
108-
ece.keys['invalid'] = ece.keys['valid']
109-
with assert_raises(ECEException) as ex:
110-
ece.derive_key('encrypt',
111-
salt=self.m_salt,
112-
key=None,
113-
dh=self.m_dh,
114-
keyid="invalid",
115-
auth_secret=None,
116-
)
117-
eq_(ex.exception.message, "'keyid' doesn't identify a key label: "
118-
"invalid")
119-
"""
120-
121103
def test_derive_key_invalid_salt(self):
122104
with assert_raises(ECEException) as ex:
123105
ece.derive_key('encrypt',
124106
version='aes128gcm',
125107
salt=None,
126-
key=None,
127-
dh=self.m_dh,
108+
key=self.m_key,
128109
keyid="valid",
129110
keymap=self.keymap,
130111
auth_secret=None,
@@ -136,8 +117,6 @@ def test_derive_key_invalid_version(self):
136117
ece.derive_key('encrypt',
137118
version='invalid',
138119
salt=self.m_salt,
139-
key=None,
140-
dh=None,
141120
keyid="valid",
142121
keymap=self.keymap,
143122
auth_secret=None,
@@ -150,8 +129,6 @@ def test_derive_key_no_secret(self):
150129
ece.derive_key('encrypt',
151130
version='aes128gcm',
152131
salt=self.m_salt,
153-
key=None,
154-
dh=None,
155132
keyid="valid",
156133
keymap=self.keymap,
157134
auth_secret=None,
@@ -164,7 +141,6 @@ def test_derive_key_keyid_from_keys(self):
164141
version='aes128gcm',
165142
salt=self.m_salt,
166143
key=None,
167-
dh=None,
168144
keyid="valid",
169145
keymap=self.keymap,
170146
auth_secret=None,
@@ -176,6 +152,94 @@ def test_iv_bad_counter(self):
176152
eq_(ex.exception.message, "Counter too big")
177153

178154

155+
class TestEceChecking(unittest.TestCase):
156+
157+
def setUp(self):
158+
self.m_key = os.urandom(16)
159+
self.m_input = os.urandom(5)
160+
# This header is specific to the padding tests, but can be used elsewhere
161+
self.m_header = b'\xaa\xd2\x05}3S\xb7\xff7\xbd\xe4*\xe1\xd5\x0f\xda'
162+
self.m_header += struct.pack('!L', 4096) + b'\0'
163+
164+
def test_encrypt_small_rs(self):
165+
with assert_raises(ECEException) as ex:
166+
ece.encrypt(
167+
self.m_input,
168+
version='aes128gcm',
169+
key=self.m_key,
170+
rs=2,
171+
)
172+
eq_(ex.exception.message, "Record size too small")
173+
174+
def test_decrypt_small_rs(self):
175+
header = os.urandom(16) + struct.pack('!L', 2) + b'\0'
176+
with assert_raises(ECEException) as ex:
177+
ece.decrypt(
178+
header + self.m_input,
179+
version='aes128gcm',
180+
key=self.m_key,
181+
rs=2,
182+
)
183+
eq_(ex.exception.message, "Record size too small")
184+
185+
def test_encrypt_bad_version(self):
186+
with assert_raises(ECEException) as ex:
187+
ece.encrypt(
188+
self.m_input,
189+
version='bogus',
190+
key=self.m_key,
191+
)
192+
eq_(ex.exception.message, "Invalid version")
193+
194+
def test_decrypt_bad_version(self):
195+
with assert_raises(ECEException) as ex:
196+
ece.decrypt(
197+
self.m_input,
198+
version='bogus',
199+
key=self.m_key,
200+
)
201+
eq_(ex.exception.message, "Invalid version")
202+
203+
def test_decrypt_bad_header(self):
204+
with assert_raises(ECEException) as ex:
205+
ece.decrypt(
206+
os.urandom(4),
207+
version='aes128gcm',
208+
key=self.m_key,
209+
)
210+
eq_(ex.exception.message, "Could not parse the content header")
211+
212+
def test_encrypt_long_keyid(self):
213+
with assert_raises(ECEException) as ex:
214+
ece.encrypt(
215+
self.m_input,
216+
version='aes128gcm',
217+
key=self.m_key,
218+
keyid=b64e(os.urandom(192)), # 256 bytes
219+
)
220+
eq_(ex.exception.message, "keyid is too long")
221+
222+
def test_overlong_padding(self):
223+
with assert_raises(ECEException) as ex:
224+
ece.decrypt(
225+
self.m_header + b'\xbb\xc1\xb9ev\x0b\xf0E\xd1u\x11\xac\x82\xae\x96\x96\x98{l\x13\xe2C\xf0',
226+
version='aes128gcm',
227+
key=b'd\xc7\x0ed\xa7%U\x14Q\xf2\x08\xdf\xba\xa0\xb9r',
228+
keyid=b64e(os.urandom(192)), # 256 bytes
229+
)
230+
eq_(ex.exception.message, "Bad padding")
231+
232+
def test_nonzero_padding(self):
233+
with assert_raises(ECEException) as ex:
234+
ece.decrypt(
235+
self.m_header + b'\xbb\xc6\xb1\x1dF:~\x0f\x07+\xbe\xaaD\xe0\xd6.K\xe5\xf9]%\xe3\x86q\xe0~',
236+
version='aes128gcm',
237+
key=b'd\xc7\x0ed\xa7%U\x14Q\xf2\x08\xdf\xba\xa0\xb9r',
238+
keyid=b64e(os.urandom(192)), # 256 bytes
239+
)
240+
eq_(ex.exception.message, "Bad padding")
241+
242+
179243
class TestEceIntegration(unittest.TestCase):
180244

181245
def setUp(self):
@@ -395,8 +459,6 @@ def _run(self, mode):
395459
outp = 'input'
396460

397461
for data in self.legacy_data:
398-
print(mode)
399-
print(repr(data))
400462
p = data['params'][mode]
401463
keyid=p.get('keyid', '')
402464
if 'keys' in data:

python/setup.py

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

1111
setup(
1212
name='http_ece',
13-
version='0.6.2',
13+
version='0.6.3',
1414
author='Martin Thomson',
1515
author_email='[email protected]',
1616
scripts=[],

0 commit comments

Comments
 (0)