Skip to content

Commit ffedd54

Browse files
committed
Fixing python implementation
1 parent 8d7cd2e commit ffedd54

3 files changed

Lines changed: 89 additions & 64 deletions

File tree

python/http_ece/__init__.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,46 @@
88
import pyelliptic
99

1010
keys = {}
11+
labels = {}
12+
13+
def deriveKey(mode, salt, key=None, dh=None, keyid=None):
14+
def buildInfo(base, context):
15+
return b"Content-Encoding: " + base + b"\0" + context
16+
17+
def deriveDH(mode, keyid, dh):
18+
def lengthPrefix(key):
19+
return struct.pack("!H", len(key)) + key
20+
21+
if keyid is None:
22+
raise Exception(u"'keyid' is not specified with 'dh'")
23+
if not keyid in keys:
24+
raise Exception(u"'keyid' doesn't identify a key: " + keyid)
25+
if not keyid in labels:
26+
raise Exception(u"'keyid' doesn't identify a key label: " + keyid)
27+
if mode == "encrypt":
28+
senderPubKey = keys[keyid].get_pubkey()
29+
receiverPubKey = dh
30+
elif mode == "decrypt":
31+
senderPubKey = dh
32+
receiverPubKey = keys[keyid].get_pubkey()
33+
else:
34+
raise Exception(u"unknown 'mode' specified: " + mode);
35+
36+
if type(labels[keyid]) == type(u""):
37+
labels[keyid] = labels[keyid].encode("utf-8")
38+
39+
return (keys[keyid].get_ecdh_key(dh),
40+
labels[keyid] + b"\0" +
41+
lengthPrefix(receiverPubKey) + lengthPrefix(senderPubKey))
1142

12-
def deriveKey(salt, key=None, dh=None, keyid=None):
1343
if salt is None or len(salt) != 16:
1444
raise Exception(u"'salt' must be a 16 octet value")
1545

46+
context = b''
1647
if key is not None:
1748
secret = key
1849
elif dh is not None:
19-
if keyid is None:
20-
raise Exception(u"'keyid' is not specified with 'dh'")
21-
if keys[keyid] is None:
22-
raise Exception(u"'keyid' doesn't identify a key")
23-
secret = keys[keyid].get_ecdh_key(dh)
50+
(secret, context) = deriveDH(mode=mode, keyid=keyid, dh=dh)
2451
elif keyid is not None:
2552
secret = keys[keyid]
2653
if secret is None:
@@ -30,14 +57,14 @@ def deriveKey(salt, key=None, dh=None, keyid=None):
3057
algorithm=hashes.SHA256(),
3158
length=16,
3259
salt=salt,
33-
info=b"Content-Encoding: aesgcm128",
60+
info=buildInfo(b"aesgcm128", context),
3461
backend=default_backend()
3562
)
3663
hkdf_nonce = HKDF(
3764
algorithm=hashes.SHA256(),
3865
length=12,
3966
salt=salt,
40-
info=b"Content-Encoding: nonce",
67+
info=buildInfo(b"nonce", context),
4168
backend=default_backend()
4269
)
4370
return (hkdf_key.derive(secret), hkdf_nonce.derive(secret))
@@ -62,7 +89,8 @@ def decryptRecord(key, nonce, counter, buffer):
6289
data = data[1+pad:]
6390
return data
6491

65-
(key_, nonce_) = deriveKey(salt=salt, key=key, keyid=keyid, dh=dh)
92+
(key_, nonce_) = deriveKey(mode="decrypt", salt=salt,
93+
key=key, keyid=keyid, dh=dh)
6694
if rs < 2:
6795
raise Exception(u"Record size too small")
6896
rs += 16 # account for tags
@@ -87,7 +115,8 @@ def encryptRecord(key, nonce, counter, buffer):
87115
data += encryptor.tag
88116
return data
89117

90-
(key_, nonce_) = deriveKey(salt=salt, key=key, keyid=keyid, dh=dh)
118+
(key_, nonce_) = deriveKey(mode="encrypt", salt=salt,
119+
key=key, keyid=keyid, dh=dh)
91120
if rs < 2:
92121
raise Exception(u"Record size too small")
93122
rs -= 1 # account for padding

python/setup.py

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

44
setup(
55
name='http_ece',
6-
version='0.2.0',
6+
version='0.3.0',
77
author='Martin Thomson',
88
author_email='martin.thomson@gmail.com',
99
scripts=[],

python/test.py

Lines changed: 49 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,56 +14,55 @@
1414
def log(arg):
1515
if (count == 1):
1616
print(arg)
17+
1718
def b64e(arg):
1819
import base64
1920
return base64.urlsafe_b64encode(arg).decode()
2021

2122
def rlen():
22-
return struct.unpack_from('=H', os.urandom(2))[0]
23+
return struct.unpack_from("=H", os.urandom(2))[0]
2324

2425
def encryptDecrypt(length, encryptParams, decryptParams=None):
2526
if decryptParams is None:
2627
decryptParams = encryptParams
27-
log('Salt: ' + b64e(encryptParams['salt']))
28+
log("Salt: " + b64e(encryptParams["salt"]))
2829
input = os.urandom(min(length, maxLen))
29-
# input = new Buffer('I am the walrus')
30-
log('Input: ' + b64e(input))
31-
encrypted = ece.encrypt(input, salt=encryptParams.get('salt'),
32-
key=encryptParams.get('key'),
33-
keyid=encryptParams.get('keyid'),
34-
dh=encryptParams.get('dh'),
35-
rs=encryptParams.get('rs'))
36-
log('Encrypted: ' + b64e(encrypted))
37-
decrypted = ece.decrypt(encrypted, salt=decryptParams.get('salt'),
38-
key=decryptParams.get('key'),
39-
keyid=decryptParams.get('keyid'),
40-
dh=decryptParams.get('dh'),
41-
rs=decryptParams.get('rs'))
42-
log('Decrypted: ' + b64e(decrypted))
30+
# input = new Buffer("I am the walrus")
31+
log("Input: " + b64e(input))
32+
encrypted = ece.encrypt(input, salt=encryptParams.get("salt"),
33+
key=encryptParams.get("key"),
34+
keyid=encryptParams.get("keyid"),
35+
dh=encryptParams.get("dh"),
36+
rs=encryptParams.get("rs"))
37+
log("Encrypted: " + b64e(encrypted))
38+
decrypted = ece.decrypt(encrypted, salt=decryptParams.get("salt"),
39+
key=decryptParams.get("key"),
40+
keyid=decryptParams.get("keyid"),
41+
dh=decryptParams.get("dh"),
42+
rs=decryptParams.get("rs"))
43+
log("Decrypted: " + b64e(decrypted))
4344
assert input == decrypted
4445
log("----- OK");
4546

46-
4747
def useExplicitKey():
4848
params = {
49-
'key': os.urandom(16),
50-
'salt': os.urandom(16),
51-
'rs': rlen() + 1
49+
"key": os.urandom(16),
50+
"salt": os.urandom(16),
51+
"rs": rlen() + 1
5252
}
53-
log('Key: ' + b64e(params['key']))
53+
log("Key: " + b64e(params["key"]))
5454
encryptDecrypt(rlen() + 1, params)
5555

5656

5757
def exactlyOneRecord():
5858
length = min(rlen(), maxLen)
5959
params = {
60-
'key': os.urandom(16),
61-
'salt': os.urandom(16),
62-
'rs': length + 1
60+
"key": os.urandom(16),
61+
"salt": os.urandom(16),
62+
"rs": length + 1
6363
}
6464
encryptDecrypt(length, params)
6565

66-
6766
def detectTruncation():
6867
length = min(rlen(), maxLen)
6968
key = os.urandom(16)
@@ -75,73 +74,70 @@ def detectTruncation():
7574
try:
7675
ece.decrypt(encrypted[0:length + 1 + 16], salt=salt, key=key, rs=rs)
7776
except Exception as e:
78-
log('Decryption error: %s' % e.args)
79-
log('----- OK')
77+
log("Decryption error: %s" % e.args)
78+
log("----- OK")
8079
ok = True
8180

8281
if not ok:
83-
raise Exception('Decryption succeeded, but should not have')
84-
85-
82+
raise Exception("Decryption succeeded, but should not have")
8683

8784
def useKeyId():
8885
keyid = b64e(os.urandom(16))
8986
key = os.urandom(16)
9087
ece.keys[keyid] = key
9188
params = {
92-
'keyid': keyid,
93-
'salt': os.urandom(16),
94-
'rs': rlen() + 1
89+
"keyid": keyid,
90+
"salt": os.urandom(16),
91+
"rs": rlen() + 1
9592
}
9693
encryptDecrypt(rlen(), params)
9794

98-
99-
# This is a complete crap-shoot: the pre-eminent crypto library in python
100-
# doesn't even do ECDH; so this doesn't actually work
10195
def useDH():
10296
def isUncompressed(k):
10397
b1 = k.get_pubkey()[0:1]
104-
assert struct.unpack("B", b1)[0] == 4, 'is an uncompressed point'
98+
assert struct.unpack("B", b1)[0] == 4, "is an uncompressed point"
10599

106100
# the static key is used by the receiver
107-
staticKey = pyelliptic.ECC(curve='prime256v1')
101+
staticKey = pyelliptic.ECC(curve="prime256v1")
108102
isUncompressed(staticKey)
109103
staticKeyId = b64e(staticKey.get_pubkey()[1:])
110104
ece.keys[staticKeyId] = staticKey
105+
ece.labels[staticKeyId] = "P-256"
111106

112-
log('Receiver private: ' + b64e(staticKey.get_privkey()))
113-
log('Receiver public: ' + b64e(staticKey.get_pubkey()))
107+
log("Receiver private: " + b64e(staticKey.get_privkey()))
108+
log("Receiver public: " + b64e(staticKey.get_pubkey()))
114109

115110
# the ephemeral key is used by the sender
116-
ephemeralKey = pyelliptic.ECC(curve='prime256v1')
111+
ephemeralKey = pyelliptic.ECC(curve="prime256v1")
117112
isUncompressed(ephemeralKey)
118113
ephemeralKeyId = b64e(ephemeralKey.get_pubkey()[1:])
119114
ece.keys[ephemeralKeyId] = ephemeralKey
115+
ece.labels[ephemeralKeyId] = "P-256"
120116

121-
log('Sender private: ' + b64e(ephemeralKey.get_privkey()))
122-
log('Sender public: ' + b64e(ephemeralKey.get_pubkey()))
117+
log("Sender private: " + b64e(ephemeralKey.get_privkey()))
118+
log("Sender public: " + b64e(ephemeralKey.get_pubkey()))
123119

124120
encryptParams = {
125-
'keyid': ephemeralKeyId,
126-
'dh': staticKey.get_pubkey(),
127-
'salt': os.urandom(16),
128-
'rs': rlen() + 1
121+
"keyid": ephemeralKeyId,
122+
"dh": staticKey.get_pubkey(),
123+
"salt": os.urandom(16),
124+
"rs": rlen() + 1
129125
}
130126
decryptParams = {
131-
'keyid': staticKeyId,
132-
'dh': ephemeralKey.get_pubkey(),
133-
'salt': encryptParams['salt'],
134-
'rs': encryptParams['rs']
127+
"keyid": staticKeyId,
128+
"dh": ephemeralKey.get_pubkey(),
129+
"salt": encryptParams["salt"],
130+
"rs": encryptParams["rs"]
135131
}
136132

137133
encryptDecrypt(rlen(), encryptParams, decryptParams)
138134

139-
if __name__ == '__main__':
135+
if __name__ == "__main__":
140136
for i in list(range(0,count)):
141137
useExplicitKey()
142138
exactlyOneRecord()
143139
detectTruncation()
144140
useKeyId()
145141
useDH()
146142

147-
print('All tests passed.')
143+
print("All tests passed.")

0 commit comments

Comments
 (0)