2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-29 13:27:47 +00:00

Merge branch 'layer-85' into future

# Conflicts:
#	compiler/api/source/main_api.tl
#	pyrogram/client/methods/password/change_cloud_password.py
#	pyrogram/client/methods/password/enable_cloud_password.py
#	pyrogram/client/methods/password/remove_cloud_password.py
This commit is contained in:
Dan 2018-12-25 14:34:56 +01:00
commit 1e3612c781
4 changed files with 175 additions and 70 deletions

View File

@ -16,6 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import os
from pyrogram.api import functions, types
from .utils import compute_hash, compute_check, btoi, itob
from ...ext import BaseClient from ...ext import BaseClient
@ -37,33 +41,30 @@ class ChangeCloudPassword(BaseClient):
A new password hint. A new password hint.
Returns: Returns:
True on success, False otherwise. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` in case there is no cloud password to change.
""" """
raise NotImplementedError( r = self.send(functions.account.GetPassword())
"Cloud password methods are currently not available. "
"See https://github.com/pyrogram/pyrogram/issues/178" if not r.has_password:
raise ValueError("There is no cloud password to change")
r.new_algo.salt1 += os.urandom(32)
new_hash = btoi(compute_hash(r.new_algo, new_password))
new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p)))
self.send(
functions.account.UpdatePasswordSettings(
password=compute_check(r, current_password),
new_settings=types.account.PasswordInputSettings(
new_algo=r.new_algo,
new_password_hash=new_hash,
hint=new_hint
)
)
) )
# r = self.send(functions.account.GetPassword()) return True
#
# if isinstance(r, types.account.Password):
# current_password_hash = sha256(r.current_salt + current_password.encode() + r.current_salt).digest()
#
# new_salt = r.new_salt + os.urandom(8)
# new_password_hash = sha256(new_salt + new_password.encode() + new_salt).digest()
#
# return self.send(
# functions.account.UpdatePasswordSettings(
# current_password_hash=current_password_hash,
# new_settings=types.account.PasswordInputSettings(
# new_salt=new_salt,
# new_password_hash=new_password_hash,
# hint=new_hint
# )
# )
# )
# else:
# return False

View File

@ -16,6 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import os
from pyrogram.api import functions, types
from .utils import compute_hash, btoi, itob
from ...ext import BaseClient from ...ext import BaseClient
@ -23,10 +27,10 @@ class EnableCloudPassword(BaseClient):
def enable_cloud_password(self, def enable_cloud_password(self,
password: str, password: str,
hint: str = "", hint: str = "",
email: str = "") -> bool: email: str = None) -> bool:
"""Use this method to enable the Two-Step Verification security feature (Cloud Password) on your account. """Use this method to enable the Two-Step Verification security feature (Cloud Password) on your account.
This password will be asked when you log in on a new device in addition to the SMS code. This password will be asked when you log-in on a new device in addition to the SMS code.
Args: Args:
password (``str``): password (``str``):
@ -39,32 +43,31 @@ class EnableCloudPassword(BaseClient):
Recovery e-mail. Recovery e-mail.
Returns: Returns:
True on success, False otherwise. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` in case there is already a cloud password enabled.
""" """
raise NotImplementedError( r = self.send(functions.account.GetPassword())
"Cloud password methods are currently not available. "
"See https://github.com/pyrogram/pyrogram/issues/178" if r.has_password:
raise ValueError("There is already a cloud password enabled")
r.new_algo.salt1 += os.urandom(32)
new_hash = btoi(compute_hash(r.new_algo, password))
new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p)))
self.send(
functions.account.UpdatePasswordSettings(
password=types.InputCheckPasswordEmpty(),
new_settings=types.account.PasswordInputSettings(
new_algo=r.new_algo,
new_password_hash=new_hash,
hint=hint,
email=email
)
)
) )
# r = self.send(functions.account.GetPassword()) return True
#
# if isinstance(r, types.account.NoPassword):
# salt = r.new_salt + os.urandom(8)
# password_hash = sha256(salt + password.encode() + salt).digest()
#
# return self.send(
# functions.account.UpdatePasswordSettings(
# current_password_hash=salt,
# new_settings=types.account.PasswordInputSettings(
# new_salt=salt,
# new_password_hash=password_hash,
# hint=hint,
# email=email
# )
# )
# )
# else:
# return False

View File

@ -16,6 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from .utils import compute_check
from ...ext import BaseClient from ...ext import BaseClient
@ -29,30 +31,26 @@ class RemoveCloudPassword(BaseClient):
Your current password. Your current password.
Returns: Returns:
True on success, False otherwise. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` in case there is no cloud password to remove.
""" """
raise NotImplementedError( r = self.send(functions.account.GetPassword())
"Cloud password methods are currently not available. "
"See https://github.com/pyrogram/pyrogram/issues/178" if not r.has_password:
raise ValueError("There is no cloud password to remove")
self.send(
functions.account.UpdatePasswordSettings(
password=compute_check(r, password),
new_settings=types.account.PasswordInputSettings(
new_algo=types.PasswordKdfAlgoUnknown(),
new_password_hash=b"",
hint=""
)
)
) )
# r = self.send(functions.account.GetPassword()) return True
#
# if isinstance(r, types.account.Password):
# password_hash = sha256(r.current_salt + password.encode() + r.current_salt).digest()
#
# return self.send(
# functions.account.UpdatePasswordSettings(
# current_password_hash=password_hash,
# new_settings=types.account.PasswordInputSettings(
# new_salt=b"",
# new_password_hash=b"",
# hint=""
# )
# )
# )
# else:
# return False

View File

@ -0,0 +1,103 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import hashlib
import os
from pyrogram.api import types
def btoi(b: bytes) -> int:
return int.from_bytes(b, "big")
def itob(i: int) -> bytes:
return i.to_bytes(256, "big")
def sha256(data: bytes) -> bytes:
return hashlib.sha256(data).digest()
def xor(a: bytes, b: bytes) -> bytes:
return bytes(i ^ j for i, j in zip(a, b))
def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str) -> bytes:
hash1 = sha256(algo.salt1 + password.encode() + algo.salt1)
hash2 = sha256(algo.salt2 + hash1 + algo.salt2)
hash3 = hashlib.pbkdf2_hmac("sha512", hash2, algo.salt1, 100000)
return sha256(algo.salt2 + hash3 + algo.salt2)
def compute_check(r: types.account.Password, password: str) -> types.InputCheckPasswordSRP:
algo = r.current_algo
p_bytes = algo.p
p = btoi(algo.p)
g_bytes = itob(algo.g)
g = algo.g
B_bytes = r.srp_B
B = btoi(B_bytes)
srp_id = r.srp_id
x_bytes = compute_hash(algo, password)
x = btoi(x_bytes)
g_x = pow(g, x, p)
k_bytes = sha256(p_bytes + g_bytes)
k = btoi(k_bytes)
kg_x = (k * g_x) % p
while True:
a_bytes = os.urandom(256)
a = btoi(a_bytes)
A = pow(g, a, p)
A_bytes = itob(A)
u = btoi(sha256(A_bytes + B_bytes))
if u > 0:
break
g_b = (B - kg_x) % p
ux = u * x
a_ux = a + ux
S = pow(g_b, a_ux, p)
S_bytes = itob(S)
K_bytes = sha256(S_bytes)
M1_bytes = sha256(
xor(sha256(p_bytes), sha256(g_bytes))
+ sha256(algo.salt1)
+ sha256(algo.salt2)
+ A_bytes
+ B_bytes
+ K_bytes
)
return types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes)