From 1e96d9b43569e4e61cdb1a9b0a16d4cf1d4b489c Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Mon, 9 Mar 2020 15:13:12 -0700 Subject: [PATCH] Fix v1 login by calculating public exp instead of hardcode 257. The code was using a hardcoded 257 as the RSA public exponent, but it was raising RSA Invalid Construct. From reading MEGA's webclient js I found that the public exponent sometimes defaults to 257, but in other cases is calculated from a modular inverse on the private exponent and phi=p-1*q-1. --- src/mega/mega.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/mega/mega.py b/src/mega/mega.py index e125292..93b5fed 100644 --- a/src/mega/mega.py +++ b/src/mega/mega.py @@ -21,7 +21,8 @@ from .errors import ValidationError, RequestError from .crypto import ( a32_to_base64, encrypt_key, base64_url_encode, encrypt_attr, base64_to_a32, base64_url_decode, decrypt_attr, a32_to_str, get_chunks, str_to_a32, - decrypt_key, mpi_to_int, stringhash, prepare_key, make_id, makebyte + decrypt_key, mpi_to_int, stringhash, prepare_key, make_id, makebyte, + modular_inverse ) logger = logging.getLogger(__name__) @@ -132,14 +133,27 @@ class Mega: rsa_private_key[i] = mpi_to_int(private_key[:bytelength]) private_key = private_key[bytelength:] - encrypted_sid = mpi_to_int(base64_url_decode(resp['csid'])) - rsa_decrypter = RSA.construct( - ( - rsa_private_key[0] * rsa_private_key[1], 257, - rsa_private_key[2], rsa_private_key[0], - rsa_private_key[1] - ) + first_factor_p = rsa_private_key[0] + second_factor_q = rsa_private_key[1] + private_exponent_d = rsa_private_key[2] + # In MEGA's webclient javascript, they assign [3] to a variable + # called u, but I do not see how it corresponds to pycryptodome's + # RSA.construct and it does not seem to be necessary. + rsa_modulus_n = first_factor_p * second_factor_q + phi = (first_factor_p - 1) * (second_factor_q - 1) + public_exponent_e = modular_inverse(private_exponent_d, phi) + + rsa_components = ( + rsa_modulus_n, + public_exponent_e, + private_exponent_d, + first_factor_p, + second_factor_q, ) + rsa_decrypter = RSA.construct(rsa_components) + + encrypted_sid = mpi_to_int(base64_url_decode(resp['csid'])) + sid = '%x' % rsa_decrypter._decrypt(encrypted_sid) sid = binascii.unhexlify('0' + sid if len(sid) % 2 else sid) self.sid = base64_url_encode(sid[:43])