Skip to content

Commit 5518944

Browse files
committed
Convert python to use cryptography
1 parent 7b0610b commit 5518944

File tree

3 files changed

+51
-28
lines changed

3 files changed

+51
-28
lines changed

python/http_ece/__init__.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from cryptography.hazmat.primitives.ciphers import (
1010
Cipher, algorithms, modes
1111
)
12-
from pyelliptic import ecc
12+
from cryptography.hazmat.primitives.asymmetric import ec
1313

1414
keys = {}
1515
labels = {}
@@ -74,20 +74,23 @@ def derive_dh(mode, version, private_key, dh, label):
7474
def length_prefix(key):
7575
return struct.pack("!H", len(key)) + key
7676

77+
encoded = private_key.public_key().public_numbers().encode_point()
7778
if mode == "encrypt":
78-
sender_pub_key = private_key.get_pubkey()
79+
sender_pub_key = encoded
7980
receiver_pub_key = dh
8081
else:
8182
sender_pub_key = dh
82-
receiver_pub_key = private_key.get_pubkey()
83+
receiver_pub_key = encoded
8384

8485
if version == "aes128gcm":
8586
context = b"WebPush: info\x00" + receiver_pub_key + sender_pub_key
8687
else:
8788
context = (label + b"\0" + length_prefix(receiver_pub_key) +
8889
length_prefix(sender_pub_key))
8990

90-
return private_key.get_ecdh_key(dh), context
91+
numbers = ec.EllipticCurvePublicNumbers.from_encoded_point(ec.SECP256R1(), dh)
92+
pubkey = numbers.public_key(default_backend())
93+
return private_key.exchange(ec.ECDH(), pubkey), context
9194

9295
if version not in versions:
9396
raise ECEException(u"Invalid version")
@@ -251,7 +254,8 @@ def unpad(data, last):
251254
keymap = keys
252255
if keylabels is None:
253256
keylabels = labels
254-
if keyid is not None and keyid in keymap and isinstance(keymap[keyid], ecc.ECC):
257+
if keyid is not None and keyid in keymap and \
258+
isinstance(keymap[keyid], ec.EllipticCurvePrivateKey):
255259
private_key = keymap[keyid]
256260

257261
if version == "aes128gcm":
@@ -318,7 +322,7 @@ def encrypt(content, salt=None, key=None,
318322
:type key: object
319323
:param keyid: Internal key identifier for private key info
320324
:type keyid: str
321-
:param dh: Remote Diffie Hellman sequence
325+
:param dh: Remote Diffie Hellman sequence (omit for aes128gcm)
322326
:type dh: str
323327
:param rs: Record size
324328
:type rs: int
@@ -382,7 +386,8 @@ def compose_aes128gcm(salt, content, rs, keyid):
382386
if salt is None:
383387
salt = os.urandom(16)
384388
version = "aes128gcm"
385-
if keyid is not None and keyid in keymap and isinstance(keymap[keyid], ecc.ECC):
389+
if keyid is not None and keyid in keymap and \
390+
isinstance(keymap[keyid], ec.EllipticCurvePrivateKey):
386391
private_key = keymap[keyid]
387392

388393
(key_, nonce_) = derive_key(mode="encrypt", version=version,
@@ -412,7 +417,7 @@ def compose_aes128gcm(salt, content, rs, keyid):
412417
counter += 1
413418
if version == "aes128gcm":
414419
if keyid is None and private_key is not None:
415-
kid = private_key.get_pubkey()
420+
kid = private_key.public_key().public_numbers().encode_point()
416421
else:
417422
kid = (keyid or '').encode('utf-8')
418423
return compose_aes128gcm(salt, result, rs, keyid=kid)

python/http_ece/tests/test_ece.py

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import base64
22
import json
33
import os
4-
import pyelliptic
54
import struct
65
import unittest
6+
from cryptography.hazmat.backends import default_backend
7+
from cryptography.hazmat.primitives.asymmetric import ec
78

89
from nose.tools import eq_, assert_raises
910

10-
1111
import http_ece as ece
1212
from http_ece import ECEException
1313

@@ -43,11 +43,14 @@ def b64d(arg):
4343
return None
4444
return base64.urlsafe_b64decode(str(arg) + '===='[:len(arg) % 4:])
4545

46+
def make_key():
47+
return ec.generate_private_key(ec.SECP256R1(), default_backend())
48+
4649

4750
class TestEce(unittest.TestCase):
4851

4952
def setUp(self):
50-
self.keymap = {'valid': pyelliptic.ECC(curve="prime256v1")}
53+
self.keymap = {'valid': make_key()}
5154
self.keylabels = {'valid': 'P-256'}
5255
self.m_key = os.urandom(16)
5356
self.m_salt = os.urandom(16)
@@ -396,34 +399,47 @@ def use_key_id(self, version):
396399
decrypt_params, version=version)
397400

398401
def use_dh(self, version):
402+
def pubbytes(k):
403+
return k.public_key().public_numbers().encode_point()
404+
405+
def privbytes(k):
406+
d = k.private_numbers().private_value
407+
b = b''
408+
for i in range(0, k.private_numbers().public_numbers.curve.key_size, 32):
409+
b = struct.pack("!L", (d >> i) & 0xffffffff) + b
410+
return b
411+
412+
def logec(s, k):
413+
logbuf(s + " private", privbytes(k))
414+
logbuf(s + " public", pubbytes(k))
415+
399416
def is_uncompressed(k):
400-
b1 = k.get_pubkey()[0:1]
417+
b1 = pubbytes(k)[0:1]
401418
assert struct.unpack("B", b1)[0] == 4, "is an uncompressed point"
402419

403420
# the static key is used by the receiver
404-
static_key = pyelliptic.ECC(curve="prime256v1")
421+
static_key = make_key()
405422
is_uncompressed(static_key)
406423

407-
logbuf("Receiver private", static_key.get_privkey())
408-
logbuf("Receiver public", static_key.get_pubkey())
424+
425+
logec("receiver", static_key)
409426

410427
# the ephemeral key is used by the sender
411-
ephemeral_key = pyelliptic.ECC(curve="prime256v1")
428+
ephemeral_key = make_key()
412429
is_uncompressed(ephemeral_key)
413430

414-
logbuf("Sender private", ephemeral_key.get_privkey())
415-
logbuf("Sender public", ephemeral_key.get_pubkey())
431+
logec("sender", ephemeral_key)
416432

417433
auth_secret = os.urandom(16)
418434

419435
if version != "aes128gcm":
420-
decrypt_dh = ephemeral_key.get_pubkey()
436+
decrypt_dh = pubbytes(ephemeral_key)
421437
else:
422438
decrypt_dh = None
423439

424440
encrypt_params = {
425441
"private_key": ephemeral_key,
426-
"dh": static_key.get_pubkey(),
442+
"dh": pubbytes(static_key),
427443
"auth_secret": auth_secret,
428444
}
429445
decrypt_params = {
@@ -484,11 +500,14 @@ def _run(self, mode):
484500

485501
if 'keys' in data:
486502
key = None
487-
private_key = pyelliptic.ECC(
488-
curve='prime256v1',
489-
pubkey=b64d(data['keys'][local]['public']),
490-
privkey=b64d(data['keys'][local]['private']),
491-
)
503+
decode_pub = ec.EllipticCurvePublicNumbers.from_encoded_point
504+
pubnum = decode_pub(ec.SECP256R1(), b64d(data['keys'][local]['public']))
505+
d = 0
506+
dbin = b64d(data['keys'][local]['private'])
507+
for i in range(0, len(dbin), 4):
508+
d = (d << 32) + struct.unpack('!L', dbin[i:i + 4])[0]
509+
privnum = ec.EllipticCurvePrivateNumbers(d, pubnum)
510+
private_key = privnum.private_key(default_backend())
492511
else:
493512
key = b64d(p['key'])
494513
private_key = None

python/setup.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
setup(
1212
name='http_ece',
13-
version='0.7.3',
13+
version='0.8.0',
1414
author='Martin Thomson',
1515
author_email='[email protected]',
1616
scripts=[],
@@ -27,8 +27,7 @@
2727
],
2828
keywords='crypto http',
2929
install_requires=[
30-
'pyelliptic==1.5.7',
31-
'cryptography',
30+
'cryptography~=1.1.0',
3231
],
3332
tests_require=[
3433
'nose',

0 commit comments

Comments
 (0)