mirror of
https://github.com/TeamPGM/pyrogram.git
synced 2024-11-24 07:51:44 +00:00
Add messages for mismatched checks
This commit is contained in:
parent
ae028ab4b6
commit
b23e34494e
@ -982,7 +982,10 @@ class Client(Methods):
|
|||||||
# https://core.telegram.org/cdn#verifying-files
|
# https://core.telegram.org/cdn#verifying-files
|
||||||
for i, h in enumerate(hashes):
|
for i, h in enumerate(hashes):
|
||||||
cdn_chunk = decrypted_chunk[h.limit * i: h.limit * (i + 1)]
|
cdn_chunk = decrypted_chunk[h.limit * i: h.limit * (i + 1)]
|
||||||
CDNFileHashMismatch.check(h.hash == sha256(cdn_chunk).digest())
|
CDNFileHashMismatch.check(
|
||||||
|
h.hash == sha256(cdn_chunk).digest(),
|
||||||
|
"h.hash == sha256(cdn_chunk).digest()"
|
||||||
|
)
|
||||||
|
|
||||||
yield decrypted_chunk
|
yield decrypted_chunk
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ def unpack(
|
|||||||
auth_key_id: bytes,
|
auth_key_id: bytes,
|
||||||
stored_msg_ids: List[int]
|
stored_msg_ids: List[int]
|
||||||
) -> Message:
|
) -> Message:
|
||||||
SecurityCheckMismatch.check(b.read(8) == auth_key_id)
|
SecurityCheckMismatch.check(b.read(8) == auth_key_id, "b.read(8) == auth_key_id")
|
||||||
|
|
||||||
msg_key = b.read(16)
|
msg_key = b.read(16)
|
||||||
aes_key, aes_iv = kdf(auth_key, msg_key, False)
|
aes_key, aes_iv = kdf(auth_key, msg_key, False)
|
||||||
@ -70,7 +70,7 @@ def unpack(
|
|||||||
data.read(8) # Salt
|
data.read(8) # Salt
|
||||||
|
|
||||||
# https://core.telegram.org/mtproto/security_guidelines#checking-session-id
|
# https://core.telegram.org/mtproto/security_guidelines#checking-session-id
|
||||||
SecurityCheckMismatch.check(data.read(8) == session_id)
|
SecurityCheckMismatch.check(data.read(8) == session_id, "data.read(8) == session_id")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
message = Message.read(data)
|
message = Message.read(data)
|
||||||
@ -88,39 +88,40 @@ def unpack(
|
|||||||
|
|
||||||
# https://core.telegram.org/mtproto/security_guidelines#checking-sha256-hash-value-of-msg-key
|
# https://core.telegram.org/mtproto/security_guidelines#checking-sha256-hash-value-of-msg-key
|
||||||
# 96 = 88 + 8 (incoming message)
|
# 96 = 88 + 8 (incoming message)
|
||||||
SecurityCheckMismatch.check(msg_key == sha256(auth_key[96:96 + 32] + data.getvalue()).digest()[8:24])
|
SecurityCheckMismatch.check(
|
||||||
|
msg_key == sha256(auth_key[96:96 + 32] + data.getvalue()).digest()[8:24],
|
||||||
|
"msg_key == sha256(auth_key[96:96 + 32] + data.getvalue()).digest()[8:24]"
|
||||||
|
)
|
||||||
|
|
||||||
# https://core.telegram.org/mtproto/security_guidelines#checking-message-length
|
# https://core.telegram.org/mtproto/security_guidelines#checking-message-length
|
||||||
data.seek(32) # Get to the payload, skip salt (8) + session_id (8) + msg_id (8) + seq_no (4) + length (4)
|
data.seek(32) # Get to the payload, skip salt (8) + session_id (8) + msg_id (8) + seq_no (4) + length (4)
|
||||||
payload = data.read()
|
payload = data.read()
|
||||||
padding = payload[message.length:]
|
padding = payload[message.length:]
|
||||||
SecurityCheckMismatch.check(12 <= len(padding) <= 1024)
|
SecurityCheckMismatch.check(12 <= len(padding) <= 1024, "12 <= len(padding) <= 1024")
|
||||||
SecurityCheckMismatch.check(len(payload) % 4 == 0)
|
SecurityCheckMismatch.check(len(payload) % 4 == 0, "len(payload) % 4 == 0")
|
||||||
|
|
||||||
# https://core.telegram.org/mtproto/security_guidelines#checking-msg-id
|
# https://core.telegram.org/mtproto/security_guidelines#checking-msg-id
|
||||||
SecurityCheckMismatch.check(message.msg_id % 2 != 0)
|
SecurityCheckMismatch.check(message.msg_id % 2 != 0, "message.msg_id % 2 != 0")
|
||||||
|
|
||||||
if len(stored_msg_ids) > STORED_MSG_IDS_MAX_SIZE:
|
if len(stored_msg_ids) > STORED_MSG_IDS_MAX_SIZE:
|
||||||
del stored_msg_ids[:STORED_MSG_IDS_MAX_SIZE // 2]
|
del stored_msg_ids[:STORED_MSG_IDS_MAX_SIZE // 2]
|
||||||
|
|
||||||
if stored_msg_ids:
|
if stored_msg_ids:
|
||||||
# Ignored message: msg_id is lower than all of the stored values
|
|
||||||
if message.msg_id < stored_msg_ids[0]:
|
if message.msg_id < stored_msg_ids[0]:
|
||||||
raise SecurityCheckMismatch
|
raise SecurityCheckMismatch("The msg_id is lower than all the stored values")
|
||||||
|
|
||||||
# Ignored message: msg_id is equal to any of the stored values
|
|
||||||
if message.msg_id in stored_msg_ids:
|
if message.msg_id in stored_msg_ids:
|
||||||
raise SecurityCheckMismatch
|
raise SecurityCheckMismatch("The msg_id is equal to any of the stored values")
|
||||||
|
|
||||||
time_diff = (message.msg_id - MsgId()) / 2 ** 32
|
time_diff = (message.msg_id - MsgId()) / 2 ** 32
|
||||||
|
|
||||||
# Ignored message: msg_id belongs over 30 seconds in the future
|
|
||||||
if time_diff > 30:
|
if time_diff > 30:
|
||||||
raise SecurityCheckMismatch
|
raise SecurityCheckMismatch("The msg_id belongs to over 30 seconds in the future. "
|
||||||
|
"Most likely the client time has to be synchronized.")
|
||||||
|
|
||||||
# Ignored message: msg_id belongs over 300 seconds in the past
|
|
||||||
if time_diff < -300:
|
if time_diff < -300:
|
||||||
raise SecurityCheckMismatch
|
raise SecurityCheckMismatch("The msg_id belongs to over 300 seconds in the past. "
|
||||||
|
"Most likely the client time has to be synchronized.")
|
||||||
|
|
||||||
bisect.insort(stored_msg_ids, message.msg_id)
|
bisect.insort(stored_msg_ids, message.msg_id)
|
||||||
|
|
||||||
|
@ -45,21 +45,21 @@ class SecurityError(Exception):
|
|||||||
"""Generic security error."""
|
"""Generic security error."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check(cls, cond: bool):
|
def check(cls, cond: bool, msg: str):
|
||||||
"""Raises this exception if the condition is false"""
|
"""Raises this exception if the condition is false"""
|
||||||
if not cond:
|
if not cond:
|
||||||
raise cls
|
raise cls(f"Check failed: {msg}")
|
||||||
|
|
||||||
|
|
||||||
class SecurityCheckMismatch(SecurityError):
|
class SecurityCheckMismatch(SecurityError):
|
||||||
"""Raised when a security check mismatch occurs."""
|
"""Raised when a security check mismatch occurs."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, msg: str = None):
|
||||||
super().__init__("A security check mismatch has occurred.")
|
super().__init__("A security check mismatch has occurred." if msg is None else msg)
|
||||||
|
|
||||||
|
|
||||||
class CDNFileHashMismatch(SecurityError):
|
class CDNFileHashMismatch(SecurityError):
|
||||||
"""Raised when a CDN file hash mismatch occurs."""
|
"""Raised when a CDN file hash mismatch occurs."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, msg: str = None):
|
||||||
super().__init__("A CDN file hash mismatch has occurred.")
|
super().__init__("A CDN file hash mismatch has occurred." if msg is None else msg)
|
||||||
|
@ -211,33 +211,51 @@ class Auth:
|
|||||||
# Security checks
|
# Security checks
|
||||||
#######################
|
#######################
|
||||||
|
|
||||||
SecurityCheckMismatch.check(dh_prime == prime.CURRENT_DH_PRIME)
|
SecurityCheckMismatch.check(dh_prime == prime.CURRENT_DH_PRIME, "dh_prime == prime.CURRENT_DH_PRIME")
|
||||||
log.debug("DH parameters check: OK")
|
log.debug("DH parameters check: OK")
|
||||||
|
|
||||||
# https://core.telegram.org/mtproto/security_guidelines#g-a-and-g-b-validation
|
# https://core.telegram.org/mtproto/security_guidelines#g-a-and-g-b-validation
|
||||||
g_b = int.from_bytes(g_b, "big")
|
g_b = int.from_bytes(g_b, "big")
|
||||||
SecurityCheckMismatch.check(1 < g < dh_prime - 1)
|
SecurityCheckMismatch.check(1 < g < dh_prime - 1, "1 < g < dh_prime - 1")
|
||||||
SecurityCheckMismatch.check(1 < g_a < dh_prime - 1)
|
SecurityCheckMismatch.check(1 < g_a < dh_prime - 1, "1 < g_a < dh_prime - 1")
|
||||||
SecurityCheckMismatch.check(1 < g_b < dh_prime - 1)
|
SecurityCheckMismatch.check(1 < g_b < dh_prime - 1, "1 < g_b < dh_prime - 1")
|
||||||
SecurityCheckMismatch.check(2 ** (2048 - 64) < g_a < dh_prime - 2 ** (2048 - 64))
|
SecurityCheckMismatch.check(
|
||||||
SecurityCheckMismatch.check(2 ** (2048 - 64) < g_b < dh_prime - 2 ** (2048 - 64))
|
2 ** (2048 - 64) < g_a < dh_prime - 2 ** (2048 - 64),
|
||||||
|
"2 ** (2048 - 64) < g_a < dh_prime - 2 ** (2048 - 64)"
|
||||||
|
)
|
||||||
|
SecurityCheckMismatch.check(
|
||||||
|
2 ** (2048 - 64) < g_b < dh_prime - 2 ** (2048 - 64),
|
||||||
|
"2 ** (2048 - 64) < g_b < dh_prime - 2 ** (2048 - 64)"
|
||||||
|
)
|
||||||
log.debug("g_a and g_b validation: OK")
|
log.debug("g_a and g_b validation: OK")
|
||||||
|
|
||||||
# https://core.telegram.org/mtproto/security_guidelines#checking-sha1-hash-values
|
# https://core.telegram.org/mtproto/security_guidelines#checking-sha1-hash-values
|
||||||
answer = server_dh_inner_data.write() # Call .write() to remove padding
|
answer = server_dh_inner_data.write() # Call .write() to remove padding
|
||||||
SecurityCheckMismatch.check(answer_with_hash[:20] == sha1(answer).digest())
|
SecurityCheckMismatch.check(
|
||||||
|
answer_with_hash[:20] == sha1(answer).digest(),
|
||||||
|
"answer_with_hash[:20] == sha1(answer).digest()"
|
||||||
|
)
|
||||||
log.debug("SHA1 hash values check: OK")
|
log.debug("SHA1 hash values check: OK")
|
||||||
|
|
||||||
# https://core.telegram.org/mtproto/security_guidelines#checking-nonce-server-nonce-and-new-nonce-fields
|
# https://core.telegram.org/mtproto/security_guidelines#checking-nonce-server-nonce-and-new-nonce-fields
|
||||||
# 1st message
|
# 1st message
|
||||||
SecurityCheckMismatch.check(nonce == res_pq.nonce)
|
SecurityCheckMismatch.check(nonce == res_pq.nonce, "nonce == res_pq.nonce")
|
||||||
# 2nd message
|
# 2nd message
|
||||||
server_nonce = int.from_bytes(server_nonce, "little", signed=True)
|
server_nonce = int.from_bytes(server_nonce, "little", signed=True)
|
||||||
SecurityCheckMismatch.check(nonce == server_dh_params.nonce)
|
SecurityCheckMismatch.check(nonce == server_dh_params.nonce, "nonce == server_dh_params.nonce")
|
||||||
SecurityCheckMismatch.check(server_nonce == server_dh_params.server_nonce)
|
SecurityCheckMismatch.check(
|
||||||
|
server_nonce == server_dh_params.server_nonce,
|
||||||
|
"server_nonce == server_dh_params.server_nonce"
|
||||||
|
)
|
||||||
# 3rd message
|
# 3rd message
|
||||||
SecurityCheckMismatch.check(nonce == set_client_dh_params_answer.nonce)
|
SecurityCheckMismatch.check(
|
||||||
SecurityCheckMismatch.check(server_nonce == set_client_dh_params_answer.server_nonce)
|
nonce == set_client_dh_params_answer.nonce,
|
||||||
|
"nonce == set_client_dh_params_answer.nonce"
|
||||||
|
)
|
||||||
|
SecurityCheckMismatch.check(
|
||||||
|
server_nonce == set_client_dh_params_answer.server_nonce,
|
||||||
|
"server_nonce == set_client_dh_params_answer.server_nonce"
|
||||||
|
)
|
||||||
server_nonce = server_nonce.to_bytes(16, "little", signed=True)
|
server_nonce = server_nonce.to_bytes(16, "little", signed=True)
|
||||||
log.debug("Nonce fields check: OK")
|
log.debug("Nonce fields check: OK")
|
||||||
|
|
||||||
@ -248,6 +266,8 @@ class Auth:
|
|||||||
|
|
||||||
log.info(f"Done auth key exchange: {set_client_dh_params_answer.__class__.__name__}")
|
log.info(f"Done auth key exchange: {set_client_dh_params_answer.__class__.__name__}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
log.info(f"Retrying due to {type(e).__name__}: {e}")
|
||||||
|
|
||||||
if retries_left:
|
if retries_left:
|
||||||
retries_left -= 1
|
retries_left -= 1
|
||||||
else:
|
else:
|
||||||
|
@ -188,7 +188,8 @@ class Session:
|
|||||||
self.auth_key_id,
|
self.auth_key_id,
|
||||||
self.stored_msg_ids
|
self.stored_msg_ids
|
||||||
)
|
)
|
||||||
except SecurityCheckMismatch:
|
except SecurityCheckMismatch as e:
|
||||||
|
log.info(f"Discarding packet: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
messages = (
|
messages = (
|
||||||
|
Loading…
Reference in New Issue
Block a user