Beispiel #1
0
def test_user_change_password(journalist_app, test_journo):
    """Test that a journalist can successfully login after changing
    their password"""

    with journalist_app.test_client() as app:
        _login_user(app, test_journo)
        # change password
        new_pw = 'another correct horse battery staply long password'
        assert new_pw != test_journo['password']  # precondition
        app.post('/account/new-password',
                 data=dict(password=new_pw,
                           current_password=test_journo['password'],
                           token=TOTP(test_journo['otp_secret']).now()))
        # logout
        app.get('/logout')

    # start a new client/context to be sure we've cleared the session
    with journalist_app.test_client() as app:
        # login with new credentials should redirect to index page
        with InstrumentedApp(journalist_app) as ins:
            resp = app.post('/login',
                            data=dict(username=test_journo['username'],
                                      password=new_pw,
                                      token=TOTP(
                                          test_journo['otp_secret']).now()))
            ins.assert_redirects(resp, '/')
    def test_google_authenticator_delete(self):
        from talos.models import ValidationToken
        from talos.models import OneTimePasswordCredentialDirectory
        from talos.models import OneTimePasswordCredential
        from pyotp import TOTP

        self.create_user()
        self.login()
        self.add_evidence_sms()

        response = self.client.post(self.request_url, {}, format='json')

        self.assertResponseStatus(response, status.HTTP_403_FORBIDDEN)

        self.add_evidence_google()

        response = self.client.post(self.request_url, {}, format='json')

        self.assertResponseStatus(response, status.HTTP_200_OK)

        validation_token = ValidationToken.objects.last()
        self.assertEqual(validation_token.principal, self.principal)
        self.assertEqual(validation_token.type, 'otp_delete')

        sms_otp_directory = OneTimePasswordCredentialDirectory.objects.get(
            code='onetimepassword_internal_phone_sms')
        google_otp_directory = OneTimePasswordCredentialDirectory.objects.get(
            code='onetimepassword_internal_google_authenticator')

        sms_otp_credential = OneTimePasswordCredential.objects.get(
            principal=self.principal, directory=sms_otp_directory)
        google_otp_credential = OneTimePasswordCredential.objects.get(
            principal=self.principal, directory=google_otp_directory)

        totp = TOTP(sms_otp_credential.salt.decode())
        sms_code = totp.now()
        totp = TOTP(google_otp_credential.salt)
        google_code = totp.now()

        data = {
            'otp_code': sms_code,
            'google_otp_code': google_code,
            'password': self.password,
            'token': validation_token.secret
        }

        response = self.client.post(self.confirm_url, data, format='json')

        self.assertResponseStatus(response, status.HTTP_200_OK)
        self.assertEqual(
            OneTimePasswordCredential.objects.filter(
                directory=google_otp_directory).count(), 0)
Beispiel #3
0
 def is_valid(token):
     """Validate a token."""
     try:
         TOTP(token).now()
         return True
     except (binascii.Error, ValueError):
         return False
    def test_change_email_when_success(self):
        from pyotp import TOTP

        self.create_user()
        self.login()
        self.add_evidence_sms()
        self.generate_sms_code(self.principal)

        email_to_change = "*****@*****.**"
        validation_token = ValidationToken.objects.create(
            identifier='email',
            identifier_value=email_to_change,
            principal=self.principal,
            type='email_change',
        )
        code = (OneTimePasswordCredential.objects.last())

        totp = TOTP(code.salt.decode())

        data = {
            'otp_code': totp.now(),
            'password': self.password,
            'secret': validation_token.secret
        }

        response = self.client.put(self.email_change_insecure_url, data=data)

        changed_pricipal = Principal.objects.last()

        self.assertResponseStatus(response)
        self.assertEquals(changed_pricipal.email, email_to_change)
Beispiel #5
0
def test_totp_reuse_protections(journalist_app, test_journo, hardening):
    """Ensure that logging in twice with the same TOTP token fails.
    Also, ensure that last_token is updated accordingly.
    """

    with totp_window():
        token = TOTP(test_journo["otp_secret"]).now()

        with journalist_app.test_client() as app:
            login_user(app, test_journo)
            resp = app.get(url_for("main.logout"), follow_redirects=True)
            assert resp.status_code == 200

        with journalist_app.app_context():
            journo = Journalist.query.get(test_journo["id"])
            assert journo.last_token == token

        with journalist_app.test_client() as app:
            resp = app.post(
                url_for("main.login"),
                data=dict(username=test_journo["username"],
                          password=test_journo["password"],
                          token=token),
            )

            assert resp.status_code == 200
            text = resp.data.decode("utf-8")
            assert "Login failed" in text
Beispiel #6
0
def main():
    token = input("Please input your token found on your android: ")
    data = list(bytes.fromhex(token))
    assert len(data) == len(masks)
    # xor every byte with masks
    for i in range(0, len(data)):
        b = data[i]
        m = masks[i]
        b = b ^ m
        data[i] = b
    data_str = bytes(data).decode()
    secret_hex = data_str[:40]
    serial = data_str[40:]
    # use base32 to encode the first 40 bytes to be used in totp
    secret = base64.b32encode(bytes.fromhex(secret_hex)).decode()
    from pyotp import TOTP
    totp = TOTP(secret, digits=8)
    key = totp.now()
    print(secret)
    print(serial)
    print(key)

    url = "otpauth://totp/{0}:{0}?secret={1}&issuer={0}&digits=8".format(
        serial, secret)
    print(url)
    def test_password_change_secure(self):
        from talos.models import OneTimePasswordCredential
        from talos.models import Principal
        from pyotp import TOTP

        self.create_user()
        self.login()
        self.add_evidence_google()

        self.assertEqual(OneTimePasswordCredential.objects.all().count(), 1)
        google_otp_credential = OneTimePasswordCredential.objects.last()
        secret = google_otp_credential.salt
        totp = TOTP(secret)
        google_otp_code = totp.now()

        data = {
            'password': self.password,
            'new_password': '******',
            'otp_code': google_otp_code
        }

        response = self.client.put(self.url, data, format='json')

        self.assertResponseStatus(response, status.HTTP_200_OK)

        principal = Principal.objects.last()
        self.assertFalse(principal.check_password(self.password))
        self.assertTrue(principal.check_password('1234567'))
    def test_change_email_when_wrong_password(self):
        from pyotp import TOTP

        self.create_user()
        self.login()
        self.add_evidence_sms()
        self.generate_sms_code(self.principal)

        validation_token = ValidationToken.objects.create(
            identifier='email',
            identifier_value=self.email,
            principal=self.principal,
            type='email_change',
        )
        code = (OneTimePasswordCredential.objects.last())

        totp = TOTP(code.salt.decode())

        data = {
            'sms_code': totp.now(),
            'password': '******',
            'secret': validation_token.secret
        }

        response = self.client.put(self.email_change_insecure_url, data=data)

        self.assertResponseStatus(response, status.HTTP_400_BAD_REQUEST)
        self.assertListEqual(
            response.data.get('error').get('password'), ['password_invalid'])
        self.assertListEqual(
            response.data.get('details').get('password'),
            ['Password is incorrect'])
def test_valid_user_can_get_an_api_token(journalist_app, test_journo):
    with journalist_app.test_client() as app:
        valid_token = TOTP(test_journo["otp_secret"]).now()
        response = app.post(
            url_for("api.get_token"),
            data=json.dumps(
                {
                    "username": test_journo["username"],
                    "passphrase": test_journo["password"],
                    "one_time_code": valid_token,
                }
            ),
            headers=get_api_headers(),
        )

        assert response.json["journalist_uuid"] == test_journo["uuid"]
        assert (
            isinstance(
                Journalist.validate_api_token_and_get_user(response.json["token"]), Journalist
            )
            is True
        )
        assert response.status_code == 200
        assert response.json["journalist_first_name"] == test_journo["first_name"]
        assert response.json["journalist_last_name"] == test_journo["last_name"]

        assert_valid_timestamp(response.json["expiration"])
Beispiel #10
0
    def verify(self, user_name, object_dn, key):

        # Do we have read permissions for the requested attribute
        self.__check_acl(user_name, object_dn, "r")

        # Get the object for the given dn
        uuid = self.__dn_to_uuid(object_dn)
        factor_method = self.get_method_from_user(uuid)
        user_settings = self.__settings[
            uuid] if uuid in self.__settings else {}
        if factor_method == "otp":
            totp = TOTP(user_settings.get('otp_secret'))
            return totp.verify(key)

        elif factor_method == "u2f":

            challenge = user_settings.pop('_u2f_challenge_')
            data = loads(key)
            device, c, t = complete_authentication(challenge, data,
                                                   [self.facet])
            return {'keyHandle': device['keyHandle'], 'touch': t, 'counter': c}

        elif factor_method is None:
            return True

        return False
Beispiel #11
0
    def is_correct_two_factor_code(self, code: str) -> bool:
        """Verify that a TOTP/backup code is correct."""
        if not self.two_factor_secret:
            raise ValueError("User does not have 2FA enabled")

        totp = TOTP(self.two_factor_secret)

        code = code.strip().replace(" ", "").lower()

        # some possible user input (such as unicode) can cause an error in the totp
        # library, catch that and treat it the same as an invalid code
        try:
            is_valid_code = totp.verify(code)
        except TypeError:
            is_valid_code = False

        if is_valid_code:
            return True
        elif self.two_factor_backup_codes and code in self.two_factor_backup_codes:
            # Need to set the attribute so SQLAlchemy knows it changed
            self.two_factor_backup_codes = [
                backup_code
                for backup_code in self.two_factor_backup_codes
                if backup_code != code
            ]
            return True

        return False
Beispiel #12
0
    def verify(self, user_name, object_dn, key):

        # Do we have read permissions for the requested attribute
        self.__check_acl(user_name, object_dn, "r")

        # Get the object for the given dn
        user = ObjectProxy(object_dn)
        factor_method = self.get_method_from_user(user)
        user_settings = self.__settings[
            user.uuid] if user.uuid in self.__settings else {}
        if factor_method == "otp":
            totp = TOTP(user_settings.get('otp_secret'))
            return totp.verify(key)

        elif factor_method == "u2f":
            devices = [
                DeviceRegistration.wrap(device)
                for device in user_settings.get('_u2f_devices_', [])
            ]

            challenge = user_settings.pop('_u2f_challenge_')
            data = loads(key)
            c, t = verify_authenticate(devices, challenge, data, [self.facet])
            return {'touch': t, 'counter': c}

        elif factor_method is None:
            return True

        return False
Beispiel #13
0
def get_credentials(scss_dict):
    """Makes an API call to SCSS, returns credentials.

    Keyword Arguments:
    scss_dict - a dict() object containing the following keys with
    the correct corresponding values: api_key, otp, userid and url.

    Output:
    data - str(), the data returned from scss."""
    log = getLogger(__name__)
    # Setting variables based on the data passed by the scss_dict.
    api_key = scss_dict['api_key']
    otp = TOTP(scss_dict['otp']).now()
    userid = scss_dict['userid']
    url = scss_dict['url']
    user_agent = 'scss-client'
    # Building HTTP headers.
    headers = {
        'User-Agent': user_agent,
        'api-key': api_key,
        'totp': otp,
        'userid': userid
    }
    # Connecting to SCSS.  If SSL verification fails, change verify to
    # false.  This isn't recommended (as it defeats the purpose of
    # verification), but it will make the code work in an emergency.
    scss_response = post(url, headers=headers)
    if scss_response.status_code == 200:
        data = scss_response.json().get('gpg_pass')
        log.debug('Credentials successfully retrieved from SCSS')
    else:
        log.error('Unable to retrieve credentials from SCSS.  The HTTP '
                  'error code is %s', scss_response.status_code)
        exit(1)
    return data
Beispiel #14
0
def auth():
    if not current_user.can_admin:
        abort(404)
    form = TOTPForm()
    try:
        user_secret = UserMetadata.get((UserMetadata.uid == current_user.uid)
                                       & (UserMetadata.key == 'totp_secret'))
    except UserMetadata.DoesNotExist:
        return engine.get_template('admin/totp.html').render({
            'authform':
            form,
            'error':
            _('No TOTP secret found.')
        })
    if form.validate_on_submit():
        totp = TOTP(user_secret.value)
        if totp.verify(form.totp.data):
            session['apriv'] = time.time()
            return redirect(url_for('admin.index'))
        else:
            return engine.get_template('admin/totp.html').render({
                'authform':
                form,
                'error':
                _('Invalid or expired token.')
            })
    return engine.get_template('admin/totp.html').render({
        'authform': form,
        'error': None
    })
Beispiel #15
0
def index():
    if request.method == "POST":
        totp = TOTP(app.config["TOTP_SECRET"])
        if "authcode" not in request.form:
            flash("Missing OTP", 'error')
            return redirect(request.url)

        if not totp.verify(request.form["authcode"]):
            flash("Incorrect OTP", "error")
            return redirect(request.url)

        if not request.files:
            flash("No files field", "error")
            return redirect(request.url)

        files = (f for f in request.files.getlist("files") if f.filename != "")

        if not files:
            flash("No files specified", "error")
            return redirect(request.url)

        for file in files:
            file.save(os.path.join(app.config["UPLOAD_DIR"], file.filename))

        flash("Files uploaded correctly!", "success")
        return redirect("/drop")

    return render_template("drop.html")
    def otp(self, otp: str) -> None:
        updated = False

        # Some sites give the secret in chunks split by spaces for easy reading
        # lets strip those as they'll produce an invalid secret.
        otp = otp.replace(" ", "")

        if not otp and self._otp:
            # Delete existing
            self._otp = None
            self._element.delete_custom_property("otp")
            self.updated()
        elif self._otp and self._otp.secret != otp:
            # Changing an existing OTP
            self._otp.secret = otp
            updated = True
        elif otp:
            # Creating brand new OTP.
            self._otp = TOTP(otp, issuer=self.name)
            updated = True

        if updated:
            self._element.set_custom_property("otp",
                                              self._otp.provisioning_uri())
            self.updated()
Beispiel #17
0
 def __init__(self, log_to_file, bank_channel_id, bank_role_id,
              guild_server_id, totp_secret):
     super().__init__()
     self._log_to_file = log_to_file
     self._bank_channel_id = bank_channel_id
     self._bank_role_id = bank_role_id
     self._guild_server_id = guild_server_id
     self._totp = TOTP(totp_secret)
Beispiel #18
0
 def __init__(self, log_to_file, bank_channel_id, bank_role_id, totp_secret):
     super().__init__()
     self._log_to_file = log_to_file
     self._bank_channel_id = bank_channel_id
     self._bank_role_id = bank_role_id
     self._totp = TOTP(totp_secret)
     self.bank_roles = []
     self.bank_channels = []
Beispiel #19
0
def get_google_url(seed, hostname):
    return "https://chart.googleapis.com/chart?" + urlencode(
        {
            "chs": "200x200",
            "chld": "M|0",
            "cht": "qr",
            "chl": TOTP(seed).provisioning_uri(hostname)
        })
Beispiel #20
0
def get_otpauth_url(serial: str, secret: str) -> str:
    """
	Get the OTPAuth URL for the serial/secret pair
	https://github.com/google/google-authenticator/wiki/Key-Uri-Format
	"""
    totp = TOTP(secret, digits=8)

    return totp.provisioning_uri(serial, issuer_name="Blizzard")
Beispiel #21
0
def generatePin(data):
    # 3) INORDER TO GENERATE OTP FROM PYOTP MODULE WE NEED A BASE32 KEY
    encodedData = b32encode(data.encode("utf-8"))
    # 4) LET'S CONVERT THE EMAIL ID TO BASE32 SINCE EMAIL ID IS UNIQUE
    # 5) PASS THE CONVERTED STRING TO TOTP() TO GENERATE OTP
    otp = TOTP(encodedData.decode("utf-8")).now()
    # 6) GENERATE OTP
    return otp
Beispiel #22
0
 def create(self):
     """
         Create a tfa code
     """
     try:
         self._totp = TOTP(self._token)
         self._secret_code = self._totp.now()
     except Exception as e:
         Logger.error("Couldn't generate two factor code : %s" % str(e))
def main():
    parser = ArgumentParser(path.basename(__file__))
    parser.add_argument('--source-url', required=True)
    parser.add_argument('--journo-url', required=True)
    args = parser.parse_args()

    totp = TOTP('JHCOGO7VCER3EJ4L')
    auth = UserPassOtp('journalist', 'WEjwn8ZyczDhQSK24YKM8C9a', totp.now())
    client = Client(args.journo_url, auth)
Beispiel #24
0
    def set_totp(self, totp_secret):
        """Set the secret for generating MFA tokens to authorize

        Args:
          totp_secret (str): The secret token set on an Application in the Gem
            Developer Console.
        """
        self.totp = TOTP(totp_secret)
        return self
Beispiel #25
0
 def __init__(self, master=None):
     super().__init__(master)
     self.master = master
     self.totp = TOTP(get_secret())
     self.totp_qr = get_secret_qr(self.totp)
     self.create_widgets()
     self.pack()
     self.update_pin_label()
     self.set_scheduler()
Beispiel #26
0
 def login(self, secret, username, password):
     r = self.get("session")
     data = get_form_data(r.content, action="/session")
     data['login'] = username
     data['password'] = password
     r = self.post("session", data=data)
     data = get_form_data(r.content, action="/sessions/two-factor")
     data['otp'] = TOTP(secret).now()
     r = self.post("sessions/two-factor", data=data)
Beispiel #27
0
 def create(self):
     """
         Create a tfa code
     """
     try:
         self.totp = TOTP(self.secret_code)
         self.password = self.totp.now()
     except Exception as e:
         logging.error("Couldn't generate two factor code : %s" % str(e))
Beispiel #28
0
    def find_and_submit_form(self, soup, email, password, mfa_secret=None):
        error = soup.find(id="message_error")
        if error:
            message = error.get_text()
            # Enter the characters as they are given in the challenge.
            raise Exception(message)

        form = soup.find(id="ap_signin_form")

        if not form:
            mfa_form = soup.find(id="auth-mfa-form")
            if mfa_form:
                raise Exception("accounts with Amazon MFA not supported")

        data = {'metadata1': self.session()._metadata1_generator.generate()}

        for field in form.find_all('input'):
            name = field.get('name')
            if not name:
                continue
            value = field.get('value')
            data[name] = value

        if "guess" in data:
            if not self.session()._captcha_solver:
                raise Exception("captcha solver required")

            captcha = soup.find(id="ap_captcha_img")
            img = captcha.find("img")
            src = img.get("src")
            guess_uuid = self.session()._captcha_solver.solve(url=src)

            while True:
                guess = self.session()._captcha_solver.result(guess_uuid)

                if guess is None:
                    time.sleep(5)
                else:
                    data["guess"] = guess
                    break

        if "tokenCode" in data and mfa_secret:
            data['tokenCode'] = TOTP(mfa_secret).now()

        overrides = {
            "password": password,
            "email": email,
        }

        for k, v in data.items():
            if v is None:
                _v = overrides.get(k)
                if _v:
                    data[k] = _v

        return self.session()._post(form.get("action"), data=data)
Beispiel #29
0
def _login_user(app, user_dict):
    resp = app.post('/login',
                    data={
                        'username': user_dict['username'],
                        'password': user_dict['password'],
                        'token': TOTP(user_dict['otp_secret']).now()
                    },
                    follow_redirects=True)
    assert resp.status_code == 200
    assert hasattr(g, 'user')  # ensure logged in
Beispiel #30
0
def journalist_api_token(journalist_app, test_journo):
    with journalist_app.test_client() as app:
        valid_token = TOTP(test_journo['otp_secret']).now()
        response = app.post(url_for('api.get_token'),
                            data=json.dumps(
                                {'username': test_journo['username'],
                                 'passphrase': test_journo['password'],
                                 'one_time_code': valid_token}),
                            headers=utils.api_helper.get_api_headers())
        return response.json['token']