From 7bfa87c11bf44f64989fc1b7032886063b966f2a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 23 Aug 2018 20:42:42 +0200 Subject: [PATCH 1/6] Update API scheme to Layer 85 --- compiler/api/source/main_api.tl | 57 +++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index 9ee01cb3..d3f227ce 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -191,6 +191,7 @@ inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason; inputReportReasonPornography#2e59d922 = ReportReason; inputReportReasonOther#e1746d0a text:string = ReportReason; +inputReportReasonCopyright#9b89f93a = ReportReason; userFull#f220f3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo common_chats_count:int = UserFull; @@ -342,7 +343,7 @@ config#3213dbba flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:fla nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; -help.appUpdate#8987f311 id:int critical:Bool url:string text:string = help.AppUpdate; +help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector document:flags.1?Document url:flags.2?string = help.AppUpdate; help.noAppUpdate#c45a6536 = help.AppUpdate; help.inviteText#18cb9f78 message:string = help.InviteText; @@ -458,12 +459,11 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s account.authorizations#1250abde authorizations:Vector = account.Authorizations; -account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password; -account.password#ca39b447 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string email_unconfirmed_pattern:string = account.Password; +account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password; -account.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings; +account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings; -account.passwordInputSettings#21ffa60d flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_id:flags.2?long = account.PasswordInputSettings; +account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings; auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; @@ -864,9 +864,9 @@ secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType; secureValueTypePhone#b320aadb = SecureValueType; secureValueTypeEmail#8e3ca7ee = SecureValueType; -secureValue#b4b4b699 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile files:flags.4?Vector plain_data:flags.5?SecurePlainData hash:bytes = SecureValue; +secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector files:flags.4?Vector plain_data:flags.5?SecurePlainData hash:bytes = SecureValue; -inputSecureValue#67872e8 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile files:flags.4?Vector plain_data:flags.5?SecurePlainData = InputSecureValue; +inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector files:flags.4?Vector plain_data:flags.5?SecurePlainData = InputSecureValue; secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash; @@ -876,10 +876,13 @@ secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:s secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector text:string = SecureValueError; +secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError; +secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError; +secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector text:string = SecureValueError; secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted; -account.authorizationForm#cb976d53 flags:# selfie_required:flags.1?true required_types:Vector values:Vector errors:Vector users:Vector privacy_policy_url:flags.0?string = account.AuthorizationForm; +account.authorizationForm#ad2e1cd8 flags:# required_types:Vector values:Vector errors:Vector users:Vector privacy_policy_url:flags.0?string = account.AuthorizationForm; account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode; @@ -890,6 +893,24 @@ savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date: account.takeout#4dba4501 id:long = account.Takeout; +passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo; +passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo; + +securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo; +securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo; +securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo; + +secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings; + +inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP; +inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP; + +secureRequiredType#829d99da flags:# native_names:flags.0?true selfie_required:flags.1?true translation_required:flags.2?true type:SecureValueType = SecureRequiredType; +secureRequiredTypeOneOf#27477b4 types:Vector = SecureRequiredType; + +help.passportConfigNotModified#bfb9f457 = help.PassportConfig; +help.passportConfig#a098d6af hash:int countries_langs:DataJSON = help.PassportConfig; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -909,7 +930,7 @@ auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization; -auth.checkPassword#a63011e password_hash:bytes = auth.Authorization; +auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization; auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery; auth.recoverPassword#4ea56e92 code:string = auth.Authorization; auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode; @@ -938,11 +959,11 @@ account.updateDeviceLocked#38df3532 period:int = Bool; account.getAuthorizations#e320c158 = account.Authorizations; account.resetAuthorization#df77f3bc hash:long = Bool; account.getPassword#548a30f5 = account.Password; -account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings; -account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool; +account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings; +account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool; account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode; account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool; -account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword; +account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword; account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; account.resetWebAuthorization#2d01b9ef hash:long = Bool; account.resetWebAuthorizations#682d2594 = Bool; @@ -1080,6 +1101,7 @@ messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:stri messages.getSplitRanges#1cff7e08 = Vector; messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool; messages.getDialogUnreadMarks#22e24e22 = Vector; +messages.clearAllDrafts#7e58ee9c = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1101,7 +1123,7 @@ upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector = Bool; help.getInviteText#4d392343 = help.InviteText; help.getSupport#9cdf08cd = help.Support; @@ -1113,6 +1135,7 @@ help.getProxyData#3d7758e1 = help.ProxyData; help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate; help.acceptTermsOfService#ee72f79a id:DataJSON = Bool; help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo; +help.getPassportConfig#c661ad08 hash:int = help.PassportConfig; channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector = messages.AffectedMessages; @@ -1172,9 +1195,9 @@ phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDisc phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; -langpack.getLangPack#9ab5c58e lang_code:string = LangPackDifference; -langpack.getStrings#2e1ee318 lang_code:string keys:Vector = Vector; +langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; +langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector; langpack.getDifference#b2e4d7d from_version:int = LangPackDifference; -langpack.getLanguages#800fd57d = Vector; +langpack.getLanguages#42c6978f lang_pack:string = Vector; -// LAYER 82 +// LAYER 85 From 0e050b45e788623394a51c3a58c1213437a0b7a0 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 08:06:18 +0100 Subject: [PATCH 2/6] Attempt srp --- .../methods/password/enable_cloud_password.py | 113 +++++++++++++++--- 1 file changed, 97 insertions(+), 16 deletions(-) diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py index 80d527c4..1969453a 100644 --- a/pyrogram/client/methods/password/enable_cloud_password.py +++ b/pyrogram/client/methods/password/enable_cloud_password.py @@ -16,13 +16,29 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from hashlib import sha256, pbkdf2_hmac import os -from hashlib import sha256 from pyrogram.api import functions, types from ...ext import BaseClient +def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str): + hash1 = sha256(algo.salt1 + password.encode() + algo.salt1).digest() + hash2 = sha256(algo.salt2 + hash1 + algo.salt2).digest() + hash3 = pbkdf2_hmac("sha512", hash2, algo.salt1, 100000) + + return sha256(algo.salt2 + hash3 + algo.salt2).digest() + + +def btoi(b: bytes): + return int.from_bytes(b, "big") + + +def itob(i: int): + return i.to_bytes(256, "big") + + class EnableCloudPassword(BaseClient): def enable_cloud_password(self, password: str, hint: str = "", email: str = ""): """Use this method to enable the Two-Step Verification security feature (Cloud Password) on your account. @@ -46,21 +62,86 @@ class EnableCloudPassword(BaseClient): :class:`Error ` in case of a Telegram RPC error. """ r = self.send(functions.account.GetPassword()) + print(r) - if isinstance(r, types.account.NoPassword): - salt = r.new_salt + os.urandom(8) - password_hash = sha256(salt + password.encode() + salt).digest() + algo = r.new_algo - 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 - ) - ) + p_bytes = algo.p + p = btoi(algo.p) + + g_bytes = itob(algo.g) + g = algo.g + + B_bytes = r.srp_B or b"" + B = btoi(B_bytes) + + srp_id = r.srp_id or 0 + + x_bytes = compute_hash(algo, password) + x = btoi(x_bytes) + + g_x = pow(g, x, p) + + k_bytes = sha256(p_bytes + g_bytes).digest() + 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).digest()) + + 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).digest() + M1_bytes = sha256( + b"".join([bytes([int(i) ^ int(j)]) for (i, j) in zip(sha256(p_bytes).digest(), sha256(g_bytes).digest())]) + + sha256(algo.salt1).digest() + + sha256(algo.salt2).digest() + + A_bytes + + B_bytes + + K_bytes + ).digest() + + input_check_password = types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes) + + r2 = self.send(functions.account.UpdatePasswordSettings( + input_check_password, types.account.PasswordInputSettings( + new_algo=algo, + new_password_hash=b"", + hint="" ) - else: - return False + )) + + print(r2) + + # 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 From 89983b75ca534f1e033a3071f044a7c8a8f06d6a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 08:59:26 +0100 Subject: [PATCH 3/6] Move relevant SRP-related code into another file These functions are going to be used by all *_cloud_password methods --- pyrogram/client/methods/password/utils.py | 103 ++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 pyrogram/client/methods/password/utils.py diff --git a/pyrogram/client/methods/password/utils.py b/pyrogram/client/methods/password/utils.py new file mode 100644 index 00000000..4bf0ddec --- /dev/null +++ b/pyrogram/client/methods/password/utils.py @@ -0,0 +1,103 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# 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 . + +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) From e3459017efe74c13e9da5eef1eac03a718154c56 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 09:05:50 +0100 Subject: [PATCH 4/6] Re-implement enable_cloud_password using SRP --- .../methods/password/enable_cloud_password.py | 119 ++++-------------- 1 file changed, 21 insertions(+), 98 deletions(-) diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py index 1969453a..e4341480 100644 --- a/pyrogram/client/methods/password/enable_cloud_password.py +++ b/pyrogram/client/methods/password/enable_cloud_password.py @@ -16,34 +16,18 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from hashlib import sha256, pbkdf2_hmac import os from pyrogram.api import functions, types +from .utils import compute_hash, btoi, itob from ...ext import BaseClient -def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str): - hash1 = sha256(algo.salt1 + password.encode() + algo.salt1).digest() - hash2 = sha256(algo.salt2 + hash1 + algo.salt2).digest() - hash3 = pbkdf2_hmac("sha512", hash2, algo.salt1, 100000) - - return sha256(algo.salt2 + hash3 + algo.salt2).digest() - - -def btoi(b: bytes): - return int.from_bytes(b, "big") - - -def itob(i: int): - return i.to_bytes(256, "big") - - class EnableCloudPassword(BaseClient): - def enable_cloud_password(self, password: str, hint: str = "", email: str = ""): + def enable_cloud_password(self, password: str, hint: str = "", email: str = None): """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: password (``str``): @@ -56,92 +40,31 @@ class EnableCloudPassword(BaseClient): Recovery e-mail. Returns: - True on success, False otherwise. + True on success. Raises: :class:`Error ` in case of a Telegram RPC error. + ``ValueError`` in case there is already a cloud password enabled. """ r = self.send(functions.account.GetPassword()) - print(r) - algo = r.new_algo + if r.has_password: + raise ValueError("There is already a cloud password enabled") - p_bytes = algo.p - p = btoi(algo.p) + 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))) - g_bytes = itob(algo.g) - g = algo.g - - B_bytes = r.srp_B or b"" - B = btoi(B_bytes) - - srp_id = r.srp_id or 0 - - x_bytes = compute_hash(algo, password) - x = btoi(x_bytes) - - g_x = pow(g, x, p) - - k_bytes = sha256(p_bytes + g_bytes).digest() - 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).digest()) - - 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).digest() - M1_bytes = sha256( - b"".join([bytes([int(i) ^ int(j)]) for (i, j) in zip(sha256(p_bytes).digest(), sha256(g_bytes).digest())]) - + sha256(algo.salt1).digest() - + sha256(algo.salt2).digest() - + A_bytes - + B_bytes - + K_bytes - ).digest() - - input_check_password = types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes) - - r2 = self.send(functions.account.UpdatePasswordSettings( - input_check_password, types.account.PasswordInputSettings( - new_algo=algo, - new_password_hash=b"", - hint="" + 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 + ) ) - )) + ) - print(r2) - - # 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 + return True From 40ecc082a65482072b9234c21e9beb9466413e03 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 12:22:33 +0100 Subject: [PATCH 5/6] Re-implement change_cloud_password using SRP --- .../methods/password/change_cloud_password.py | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/pyrogram/client/methods/password/change_cloud_password.py b/pyrogram/client/methods/password/change_cloud_password.py index 4b5e86b3..2afc2fd7 100644 --- a/pyrogram/client/methods/password/change_cloud_password.py +++ b/pyrogram/client/methods/password/change_cloud_password.py @@ -17,9 +17,9 @@ # along with Pyrogram. If not, see . import os -from hashlib import sha256 from pyrogram.api import functions, types +from .utils import compute_hash, compute_check, btoi, itob from ...ext import BaseClient @@ -38,28 +38,30 @@ class ChangeCloudPassword(BaseClient): A new password hint. Returns: - True on success, False otherwise. + True on success. Raises: :class:`Error ` in case of a Telegram RPC error. + ``ValueError`` in case there is no cloud password to change. """ r = self.send(functions.account.GetPassword()) - if isinstance(r, types.account.Password): - current_password_hash = sha256(r.current_salt + current_password.encode() + r.current_salt).digest() + if not r.has_password: + raise ValueError("There is no cloud password to change") - new_salt = r.new_salt + os.urandom(8) - new_password_hash = sha256(new_salt + new_password.encode() + new_salt).digest() + 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))) - 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 - ) + 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 ) ) - else: - return False + ) + + return True From efc6023b08f1863a123c6c10b38384d498fa5a69 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 20:08:31 +0100 Subject: [PATCH 6/6] Re-implement remove_cloud_password using SRP --- .../methods/password/remove_cloud_password.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pyrogram/client/methods/password/remove_cloud_password.py b/pyrogram/client/methods/password/remove_cloud_password.py index 5a9875ff..a9320c39 100644 --- a/pyrogram/client/methods/password/remove_cloud_password.py +++ b/pyrogram/client/methods/password/remove_cloud_password.py @@ -16,9 +16,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from hashlib import sha256 - from pyrogram.api import functions, types +from .utils import compute_check from ...ext import BaseClient @@ -31,25 +30,26 @@ class RemoveCloudPassword(BaseClient): Your current password. Returns: - True on success, False otherwise. + True on success. Raises: :class:`Error ` in case of a Telegram RPC error. + ``ValueError`` in case there is no cloud password to remove. """ r = self.send(functions.account.GetPassword()) - if isinstance(r, types.account.Password): - password_hash = sha256(r.current_salt + password.encode() + r.current_salt).digest() + if not r.has_password: + raise ValueError("There is no cloud password to remove") - return self.send( - functions.account.UpdatePasswordSettings( - current_password_hash=password_hash, - new_settings=types.account.PasswordInputSettings( - new_salt=b"", - new_password_hash=b"", - hint="" - ) + self.send( + functions.account.UpdatePasswordSettings( + password=compute_check(r, password), + new_settings=types.account.PasswordInputSettings( + new_algo=types.PasswordKdfAlgoUnknown(), + new_password_hash=b"", + hint="" ) ) - else: - return False + ) + + return True