Browse Source

implemented HMAC crypto

pull/18/merge
Rossen Georgiev 9 years ago
parent
commit
5b3f60db7d
  1. 39
      steam/core/cm.py
  2. 44
      steam/core/crypto.py
  3. 5
      steam/core/msg.py

39
steam/core/cm.py

@ -100,6 +100,7 @@ class CMClient(EventEmitter):
self.connected = False
self.key = None
self.hmac_secret = None
self.steam_id = None
self.session_id = None
@ -124,7 +125,10 @@ class CMClient(EventEmitter):
data = message.serialize()
if self.key:
data = crypto.encrypt(data, self.key)
if self.hmac_secret:
data = crypto.symmetric_encrypt_HMAC(data, self.key, self.hmac_secret)
else:
data = crypto.symmetric_encrypt(data, self.key)
self.connection.put_message(data)
@ -139,7 +143,15 @@ class CMClient(EventEmitter):
continue
if self.key:
message = crypto.decrypt(message, self.key)
if self.hmac_secret:
try:
message = crypto.symmetric_decrypt_HMAC(message, self.key, self.hmac_secret)
except RuntimeError as e:
logger.exception(e)
gevent.spawn(self.disconnect)
return
else:
message = crypto.symmetric_decrypt(message, self.key)
self._parse_message(message)
@ -176,14 +188,20 @@ class CMClient(EventEmitter):
def _handle_encrypt_request(self, msg):
logger.debug("Securing channel")
if msg.body.protocolVersion != 1:
raise RuntimeError("Unsupported protocol version")
if msg.body.universe != EUniverse.Public:
raise RuntimeError("Unsupported universe")
try:
if msg.body.protocolVersion != 1:
raise RuntimeError("Unsupported protocol version")
if msg.body.universe != EUniverse.Public:
raise RuntimeError("Unsupported universe")
except RuntimeError as e:
logger.exception(e)
gevent.spawn(self.disconnect)
return
resp = Msg(EMsg.ChannelEncryptResponse)
key, resp.body.key = crypto.generate_session_key()
challenge = msg.body.challenge
key, resp.body.key = crypto.generate_session_key(challenge)
resp.body.crc = binascii.crc32(resp.body.key) & 0xffffffff
self.send_message(resp)
@ -192,12 +210,15 @@ class CMClient(EventEmitter):
if msg.body.eresult != EResult.OK:
logger.debug("Failed to secure channel: %s" % msg.body.eresult)
self.disconnect()
gevent.spawn(self.disconnect)
return
logger.debug("Channel secured")
self.key = key
if challenge:
self.hmac_secret = key[:16]
self.emit('channel_secured')
def _handle_multi(self, msg):
@ -210,7 +231,7 @@ class CMClient(EventEmitter):
if len(data) != msg.body.size_unzipped:
logger.fatal("Unzipped size mismatch")
self.disconnect()
gevent.spawn(self.disconnect)
return
else:
data = msg.body.message_body

44
steam/core/crypto.py

@ -2,6 +2,7 @@ from base64 import b64decode
from Crypto import Random
from Crypto.Cipher import PKCS1_OAEP, AES
from Crypto.PublicKey import RSA
from Crypto.Hash import HMAC, SHA
public_key = """
MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDf7BrWLBBmLBc1OhSwfFkRf53T
@ -15,21 +16,52 @@ pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s: s[0:-ord(s[-1])]
def generate_session_key():
def generate_session_key(hmac_secret=''):
session_key = Random.new().read(32)
cipher = PKCS1_OAEP.new(RSA.importKey(b64decode(public_key)))
encrypted_session_key = cipher.encrypt(session_key)
encrypted_session_key = cipher.encrypt(session_key + hmac_secret)
return (session_key, encrypted_session_key)
def encrypt(message, key):
def symmetric_encrypt(message, key):
iv = Random.new().read(BS)
return symmetric_encrypt_with_iv(message, key, iv)
def symmetric_encrypt_HMAC(message, key, hmac_secret):
random_bytes = Random.new().read(3)
hmac = HMAC.new(hmac_secret, digestmod=SHA)
hmac.update(random_bytes)
hmac.update(message)
iv = hmac.digest()[:13] + random_bytes
return symmetric_encrypt_with_iv(message, key, iv)
def symmetric_encrypt_with_iv(message, key, iv):
encrypted_iv = AES.new(key, AES.MODE_ECB).encrypt(iv)
cyphertext = AES.new(key, AES.MODE_CBC, iv).encrypt(pad(message))
return encrypted_iv + cyphertext
def symmetric_decrypt(cyphertext, key):
iv = symmetric_decrypt_iv(cyphertext, key)
return symmetric_decrypt_with_iv(cyphertext, key, iv)
def symmetric_decrypt_HMAC(cyphertext, key, hmac_secret):
iv = symmetric_decrypt_iv(cyphertext, key)
message = symmetric_decrypt_with_iv(cyphertext, key, iv)
hmac = HMAC.new(hmac_secret, digestmod=SHA)
hmac.update(iv[-3:])
hmac.update(message)
if iv[:13] != hmac.digest()[:13]:
raise RuntimeError("Unable to decrypt message. HMAC does not match.")
return message
def symmetric_decrypt_iv(cyphertext, key):
return AES.new(key, AES.MODE_ECB).decrypt(cyphertext[:BS])
def decrypt(cyphertext, key):
iv = AES.new(key, AES.MODE_ECB).decrypt(cyphertext[:BS])
def symmetric_decrypt_with_iv(cyphertext, key, iv):
message = AES.new(key, AES.MODE_CBC, iv).decrypt(cyphertext[BS:])
return unpad(message)

5
steam/core/msg.py

@ -301,6 +301,7 @@ class ChannelEncryptRequest:
else:
self.protocolVersion = 1
self.universe = EUniverse.Invalid
self.challenge = ''
def serialize(self):
return struct.pack("<II", self.protocolVersion, self.universe)
@ -312,9 +313,13 @@ class ChannelEncryptRequest:
self.universe = EUniverse(universe)
if len(data) >= 16:
self.challenge = data[8:]
def __str__(self):
return '\n'.join(["protocolVersion: %s" % self.protocolVersion,
"universe: %s" % repr(self.universe),
"challenge: %s" % repr(self.challenge),
])

Loading…
Cancel
Save